记录一次搭建最简单分布式的踩坑过程···

所需服务与环境

  • docker 20.10.12
  • mysql 5.7.37
  • redis
  • nacos 1.4.2
  • sentinel 1.8.0
  • nginx
  • user-server
  • consumer-server
  • gateway-server

先来列一列所踩的一部分坑

前言:被这些坑浪费了两天时间

1、使用docker-compose运行基于Mysql的Nacos

背景:Nacos的启动需要依赖MySQL数据库,使用了:

1
2
3
# 意思是Nacos服务的运行依赖Mysql服务,Nacos会在Mysql之后启动
depends_on:
- mysql

执行后遇到的问题:

1、有时候Mysql会在Nacos之后启动,depends_on没效果,Nacos启动失败

原因:很简单,就是mysql没运行,Naocs连接不上Mysql。

目前还找不到depends_on没效果的原因,可能是docker-compose的BUG吧···

2、Mysql在Naocs之前启动,但是Nacos还是启动失败

原因:Mysql虽然启动了,但是没完全启动,这时Nacos就开始启动,导致Nacos连接不上Mysql。

刚开始还不知道为什么,后来查资料才知道depends_on参数是以容器是否Rinning来判定是否完成启动的,而这不是真正的完全启动,所以导致Naocs还是连接不上Mysql

解决方法:使用wait-for脚本,检测Mysql的3306端口是否开启,开启后再运行Nacos

新的问题:很多时候端口虽然开了,但服务还没完全启动,这种方法只能增大Nacos运行成功的机率

终极方案,运行Mysql后手动重启Nacos:docker service update nacos --force

wait-for脚本代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/bin/sh

# The MIT License (MIT)
#
# Copyright (c) 2017 Eficode Oy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

VERSION="2.2.2"

set -- "$@" -- "$TIMEOUT" "$QUIET" "$PROTOCOL" "$HOST" "$PORT" "$result"
TIMEOUT=120
QUIET=0
# The protocol to make the request with, either "tcp" or "http"
PROTOCOL="tcp"

echoerr() {
if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
}

usage() {
exitcode="$1"
cat << USAGE >&2
Usage:
$0 host:port|url [-t timeout] [-- command args]
-q | --quiet Do not output any status messages
-t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout
-v | --version Show the version of this tool
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit "$exitcode"
}

wait_for() {
case "$PROTOCOL" in
tcp)
if ! command -v nc >/dev/null; then
echoerr 'nc command is missing!'
exit 1
fi
;;
wget)
if ! command -v wget >/dev/null; then
echoerr 'wget command is missing!'
exit 1
fi
;;
esac

TIMEOUT_END=$(($(date +%s) + TIMEOUT))

while :; do
case "$PROTOCOL" in
tcp)
nc -w 1 -z "$HOST" "$PORT" > /dev/null 2>&1
;;
http)
wget --timeout=1 -q "$HOST" -O /dev/null > /dev/null 2>&1
;;
*)
echoerr "Unknown protocol '$PROTOCOL'"
exit 1
;;
esac

result=$?

if [ $result -eq 0 ] ; then
if [ $# -gt 7 ] ; then
for result in $(seq $(($# - 7))); do
result=$1
shift
set -- "$@" "$result"
done

TIMEOUT=$2 QUIET=$3 PROTOCOL=$4 HOST=$5 PORT=$6 result=$7
shift 7
exec "$@"
fi
exit 0
fi

if [ $TIMEOUT -ne 0 -a $(date +%s) -ge $TIMEOUT_END ]; then
echo "Operation timed out" >&2
exit 1
fi

sleep 1
done
}

while :; do
case "$1" in
http://*|https://*)
HOST="$1"
PROTOCOL="http"
shift 1
;;
*:* )
HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
shift 1
;;
-v | --version)
echo $VERSION
exit
;;
-q | --quiet)
QUIET=1
shift 1
;;
-q-*)
QUIET=0
echoerr "Unknown option: $1"
usage 1
;;
-q*)
QUIET=1
result=$1
shift 1
set -- -"${result#-q}" "$@"
;;
-t | --timeout)
TIMEOUT="$2"
shift 2
;;
-t*)
TIMEOUT="${1#-t}"
shift 1
;;
--timeout=*)
TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
break
;;
--help)
usage 0
;;
-*)
QUIET=0
echoerr "Unknown option: $1"
usage 1
;;
*)
QUIET=0
echoerr "Unknown argument: $1"
usage 1
;;
esac
done

if ! [ "$TIMEOUT" -ge 0 ] 2>/dev/null; then
echoerr "Error: invalid timeout '$TIMEOUT'"
usage 3
fi

case "$PROTOCOL" in
tcp)
if [ "$HOST" = "" ] || [ "$PORT" = "" ]; then
echoerr "Error: you need to provide a host and port to test."
usage 2
fi
;;
http)
if [ "$HOST" = "" ]; then
echoerr "Error: you need to provide a host to test."
usage 2
fi
;;
esac

wait_for "$@"

3、Mysql可以连接上了,但是Nacos还是启动失败

原因:没有指定Naocs的运行模式,默认是集群模式,运行时没有加上运行模式的参数,再加上没有配置集群,所以启动失败,因为我是测试demo,所以就加上参数指定Naocs运行模式:

1
2
# docker swarm 使用单机模式,否则报错
- MODE=standalone

解决以上问题后,能把服务跑起来了

docker-compose-middleware.yml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
version: '3'
services:
redis:
image: "redis:latest"
networks:
- demo-network
ports:
- "6379:6379"
deploy:
placement:
constraints: [node.role == manager]
mysql:
image: "mysql:5.7.37"
networks:
- demo-network
ports:
- "3306:3306"
volumes:
- /opt/mysql_docker/conf:/etc/mysql/conf.d
- /opt/mysql_docker/logs:/logs
- /opt/mysql_docker/data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=123456
deploy:
placement:
constraints: [node.role == manager]
sentinel:
image: "bladex/sentinel-dashboard:1.8.0"
networks:
- demo-network
ports:
- "8858:8858"
deploy:
placement:
constraints: [node.role == manager]
nacos:
image: "nacos/nacos-server:1.4.2"
networks:
- demo-network
ports:
- "8848:8848"
environment:
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=mysql
- MYSQL_SERVICE_PORT=3306
- MYSQL_SERVICE_DB_NAME=nacos
- MYSQL_SERVICE_USER=nacos
- MYSQL_SERVICE_PASSWORD=123456
- MYSQL_DATABASE_NUM=1
- JVM_XMS=256m
- JVM_XMX=256m
- JVM_XMN=256m
# docker swarm 使用单机模式,否则报错
- MODE=standalone
volumes:
- nacos-data:/home/nacos
- ./wait-for:/home/nacos/wait-for
deploy:
placement:
constraints: [node.role == manager]
# 有时候nacos还是会第一个启动。。。可能BUG吧
depends_on:
- redis
- mysql
- sentinel
# wait-for脚本中超时时间调长一点
command: sh -c '/home/nacos/wait-for mysql:3306 -- ./bin/docker-startup.sh'

# 必须这样创建网络:
# docker network create demo-network --driver overlay --scope swarm --attachable
networks:
demo-network:
# 开启使用已经创建的网络(不自己创建)
external: true
volumes:
nacos-data:


# docker run -it --rm --name busybox1 --network my-net busybox sh

docker-compose-app.xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
version: '3'
services:
user-server:
image: "user-server:latest"
networks:
- demo-network

deploy:
placement:
constraints: [node.role == manager]
ports:
- "8080:8080"

consumer-server:
image: "consumer-server:latest"
networks:
- demo-network
deploy:
placement:
constraints: [node.role == manager]
ports:
- "8085:8085"
nginx:
image: "nginx:latest"
networks:
- demo-network
ports:
- "80:80"
volumes:
- /opt/docker-compose/swarm/default.conf:/etc/nginx/conf.d/default.conf
deploy:
placement:
constraints: [node.role == manager]
depends_on:
- user-server

networks:
demo-network:
external: true

但新的坑又出现了···

问题:Nacos配置改变了,但是应用服务中的配置不更新,重启应用服务都没用,只能重启Naocs服务才能刷新

原因:未知,可能docker stack命令运行docker-compose有BUG吧

解决方法:使用指令docker service create单个运行服务,服务多的话可以写脚本自动一个个运行。

配置、命令、代码

APP服务镜像生成dockerfile

1
2
3
4
5
FROM java:8-alpine
COPY ./gateway.jar /tmp/gateway.jar
EXPOSE 10010
ENTRYPOINT ["java","-jar","/tmp/gateway.jar"]

1
docker build -t gateway-server:1.0 .

Nginx的Default.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
server {
listen 80;
listen [::]:80;
server_name localhost;

#access_log /var/log/nginx/host.access.log main;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location = /a.html {
root /usr/share/nginx/html;
}

location /user {
proxy_pass http://user-server:8080;
}
location /consumer {
proxy_pass http://consumer-server:8085;
}
location /gateway/ {
proxy_pass http://gateway-server:10010/;
}



#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

服务启动命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
docker service create  --constraint node.role==manager \
--replicas 1 \
--name nginx \
--network demo-network \
-p 80:80 \
--mount type=volume,src=nginx,dst=/etc/nginx \
nginx:latest


docker service create --constraint node.role==manager \
--replicas 1 \
--name gateway-server \
--network demo-network \
-p 10010:10010 \
gateway-server:1.0


docker service create \
--replicas 3 \
--name consumer-server \
--network demo-network \
-p 8085:8085 \
consumer-server:1.0



docker service create \
--replicas 3 \
--name user-server \
--network demo-network \
-p 8080:8080 \
user-server:1.0



docker service create --constraint node.role==manager \
--replicas 1 \
--name sentinel \
--network demo-network \
-p 8858:8858 \
bladex/sentinel-dashboard:1.8.0


docker service create --constraint node.role==manager \
--replicas 1 \
--name redis \
--network demo-network \
-p 6379:6379 \
redis:latest


docker service create --constraint node.role==manager \
--replicas 1 \
--name nacos \
--network demo-network \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_SERVICE_HOST=mysql \
-e MYSQL_SERVICE_PORT=3306 \
-e MYSQL_SERVICE_DB_NAME=nacos \
-e MYSQL_SERVICE_USER=nacos \
-e MYSQL_SERVICE_PASSWORD=123456 \
-e MYSQL_DATABASE_NUM=1 \
-e JVM_XMS=256m \
-e JVM_XMX=256m \
-e JVM_XMN=256m \
-e MODE=standalone \
--mount type=volume,src=nacos-data,dst=/home/nacos \
-p 8848:8848 \
nacos/nacos-server:1.4.2


docker service create --constraint node.role==manager \
--replicas 1 \
--name mysql \
--network demo-network \
-e MYSQL_ROOT_PASSWORD=123456 \
--mount type=volume,src=mysql_conf,dst=/etc/mysql/conf.d \
--mount type=volume,src=mysql_log,dst=/logs \
--mount type=volume,src=mysql_data,dst=/var/lib/mysql \
-p 3306:3306 \
mysql:5.7.37

spring_cloud_demo父工程

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>


<groupId>org.example</groupId>
<artifactId>spring_cloud_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>user_service</module>
<module>regist</module>
<module>gateway</module>
<module>consumer</module>
</modules>
<packaging>pom</packaging>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/>
</parent>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<spring.cloud-version>Hoxton.SR9</spring.cloud-version>
<!--<spring-alibaba.version>2.2.7.RELEASE</spring-alibaba.version>-->
<spring-alibaba.version>2.2.6.RELEASE</spring-alibaba.version>
</properties>

<dependencies>

<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>


</dependencies>

<dependencyManagement>
<dependencies>
<!-- springCloud: 导入SpringCloud的父工程 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

</dependencies>

</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

user-server服务

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>user_service</artifactId>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--alibaba的 Nacos依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Spring的健康检测依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

测试的UserController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.example.user_service.controller;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RefreshScope
public class userController {

@Value("${company}")
private String company;

@Value("${companya}")
private String companya;

@Value("${company1}")
private String company1;

@GetMapping("/user")
public String queryUser() {
String str0 = "user服务:" + company;
String str1 = "user服务:" + company1;
String str2 = "共享配置:" + companya;
//String str = "user服务:";
System.out.println(str0);
System.out.println(str1);
System.out.println(str2);
//try {
// Thread.sleep(2000);
//} catch (InterruptedException e) {
// e.printStackTrace();
//}
return str0 + "\n" + str1 + "\n" + str2;
}
}

bootstrap.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
nacos-url: nacos:8848
spring:
application:
name: user-server

cloud:
nacos:
discovery:
server-addr: ${nacos-url} # nacos服务地址
config:
server-addr: ${nacos-url}
file-extension: yaml
shared-configs: shared.yml
# profiles:
# active: dev
management:
endpoints:
web:
exposure:
include: "*" # 暴露健康检测的接口

Nacos配置中心的配置

1

consumer-server服务

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>consumer</artifactId>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<!--Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--alibaba的 Nacos依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Spring的健康检测依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>


</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

测试的ConsumerController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.example.consumer.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.consumer.feign.UserClient;
//import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@RefreshScope
public class ConsumerContorller {

@Autowired
private RestTemplate restTemplate;

@Autowired
private DiscoveryClient discoveryClient;

@Value("${server.port}")
private String port;

@Value("${ccc}")
private String c;

private final UserClient userClient;

public ConsumerContorller(@Qualifier("com.example.consumer.feign.UserClient") UserClient userClient) {
this.userClient = userClient;
}

@GetMapping("/consumer")
@SentinelResource(value = "consumer1", blockHandler = "block")
//@HystrixCommand(fallbackMethod="consumerFail")
public String consumer() {
//int i = 1/0;
//// 根据服务id(spring.application.name),获取服务实例列表
//List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
//// 取出一个服务实例
//ServiceInstance instance = instances.get(0);
//// 从实例中获取host和port,组成url
////String url = String.format("http://%s:%s/user", instance.getHost(), instance.getPort());
//String url = "http://user-service/user";
//restTemplate.getForObject(url, String.class);
//return restTemplate.getForObject(url, String.class);

// 根据服务id(spring.application.name),获取服务实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("user-server");
// 取出一个服务实例
ServiceInstance instance = instances.get(0);
// 从实例中获取host和port,组成url
String url = String.format("http://%s:%s/user", instance.getHost(), instance.getPort());
System.out.println(url);
System.out.println(port);
System.out.println(c);


// 查询
if (c.equals("1")) {
System.out.println("消费者");
return restTemplate.getForObject(url, String.class);
}else {
return userClient.queryUser();
}

//return userClient.queryUser();
}

private String consumerFail() {
return "服务被降级了";
}
public String block(BlockException be) {
return "服务被限流了";
}
}

测试的`UserClient接口

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.consumer.feign;

import com.example.consumer.feign.fallback.UserClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(value = "user-server", fallback = UserClientFallback.class)
//@FeignClient(value = "user-server")
public interface UserClient {
@GetMapping("/user")
String queryUser();
}

测试的UserClientFallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.consumer.feign.fallback;

import com.example.consumer.feign.UserClient;
import org.springframework.stereotype.Component;

@Component
// 超时则在这里返回数据
public class UserClientFallback implements UserClient {
@Override
public String queryUser() {
String str = "抱歉:请求超时,请稍后重试";
System.out.println(str);
return str;
}
}

bootstrap.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
nacos-url: nacos:8848
spring:
application:
name: consumer-server
cloud:
nacos:
discovery:
server-addr: ${nacos-url} # nacos服务地址
config:
server-addr: ${nacos-url}
file-extension: yaml
shared-configs: shared.yml
management:
endpoints:
web:
exposure:
include: "*" # 暴露健康检测的接口

Nacos配置中心的配置

1

Gateway服务

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>gateway</artifactId>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

<!--alibaba的 Nacos依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Spring的健康检测依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>

<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- gateway与sentinel适配依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

测试的IpKeyResolver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.example.geteway.ratelimit;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Objects;

@Component
public class IpKeyResolver implements KeyResolver {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostName());
}
}

bootstrap.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
nacos-url: nacos:8848
spring:
application:
name: gateway-server
cloud:
nacos:
discovery:
server-addr: ${nacos-url} # nacos服务地址
config:
server-addr: ${nacos-url}
file-extension: yaml
shared-configs: shared.yml
management:
endpoints:
web:
exposure:
include: "*" # 暴露健康检测的接口

Nacos配置中心的配置

1