无感部署 Blue-Green部署
FCAT 2024/2/19 无感部署springbootnginx
要实现无感部署,即在更新应用程序时不影响用户访问接口,可以考虑使用以下方法:
1、使用负载均衡器:在部署应用程序时,通过负载均衡器将流量分发到多个实例上。这样,当更新一个实例时,其他实例仍然可以继续提供服务,从而保证用户访问接口的稳定性。
2、Blue-Green 部署:使用Blue-Green 部署策略,即同时部署两个版本的应用程序,一个作为主要版本(Blue),一个作为备用版本(Green)。当需要更新应用程序时,先将新版本部署为备用版本,然后逐渐将流量切换到新版本,最终将旧版本下线。这样可以保证用户在更新过程中不受影响。
3、Canary 部署:使用Canary 部署策略,即逐步将新版本应用程序部署到生产环境中的一小部分节点,然后逐渐扩大部署范围。这样可以在更新过程中逐步验证新版本的稳定性,避免一次性影响所有用户。
通过以上方法,您可以实现无感部署,确保在更新应用程序时不影响用户访问接口。同时,建议在部署过程中进行充分的测试和监控,以确保更新过程的稳定性和可用性。
# 环境
- 服务器:CentOS 7.9.2009
- docker-compose版本:3.9
- docker版本:20.10.12
- 根目录在/docker/mes
# docker-compose.yml
version: "3.9"
services:
fcat-mes-main-blue:
image: adoptopenjdk/openjdk8-openj9:alpine-slim
container_name: fcat-mes-main-blue
restart: always
ports:
- "16219:11001"
volumes:
- ./fcat-mes-main-blue/app:/app
- /etc/localtime:/etc/localtime
environment:
- TZ=Asia/Shanghai
command: [
'java',
'-Xmx600m',
'-jar',
'/app/fcat-mes-main.jar'
]
deploy:
resources:
limits:
memory: 800M
fcat-mes-main-green:
image: adoptopenjdk/openjdk8-openj9:alpine-slim
container_name: fcat-mes-main-green
restart: always
ports:
- "16220:11001"
volumes:
- ./fcat-mes-main-green/app:/app
- /etc/localtime:/etc/localtime
environment:
- TZ=Asia/Shanghai
command: [
'java',
'-Xmx600m',
'-jar',
'/app/fcat-mes-main.jar'
]
deploy:
resources:
limits:
memory: 800M
fcat-nginx:
image: nginx:1.21.0
container_name: fcat-nginx
restart: always
privileged: true
environment:
- TZ=Asia/Shanghai
ports:
- 16221:16221
volumes:
- /etc/localtime:/etc/localtime
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/log:/var/log/nginx
- ./nginx/web:/web
- ./logs:/logs
deploy:
resources:
limits:
memory: 1024M
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
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
# nginx/conf.d/main.conf
server {
listen 16221;
listen [::]:16221;
charset utf-8,gbk;
client_max_body_size 10m; # 设置最大上传文件大小为10MB
location /dev-api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://mes_main_service/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
upstream mes_main_service{
server fcat-mes-main-blue:11001;
server fcat-mes-main-green:11001 weight=100;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# nginx/conf.d/blue.conf_bak
server {
listen 16221;
listen [::]:16221;
charset utf-8,gbk;
client_max_body_size 10m; # 设置最大上传文件大小为10MB
location /dev-api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://mes_main_service/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
upstream mes_main_service{
server fcat-mes-main-blue:11001 weight=100;
server fcat-mes-main-green:11001 ;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# nginx/conf.d/green.conf_bak
server {
listen 16221;
listen [::]:16221;
charset utf-8,gbk;
client_max_body_size 10m; # 设置最大上传文件大小为10MB
location /dev-api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://mes_main_service/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
upstream mes_main_service{
server fcat-mes-main-blue:11001;
server fcat-mes-main-green:11001 weight=100;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 运行服务
docker-compose up -d
1
curl http://192.168.9.126:16219/test/version
{
"version": "v1"
}
curl http://192.168.9.126:16220/test/version
{
"version": "v2"
}
curl http://192.168.9.126:16221/dev-api/test/version
{
"version": "v2"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# bluegreen.txt
green
1
# deploy.sh
#!/bin/bash
# 记录当前使用蓝色版本还是绿色版本
filename="bluegreen.txt"
# 读取当前使用版本
content=$(cat "$filename")
# 蓝色版本
blue_app_name="fcat-mes-main-blue"
# 绿色版本
green_app_name="fcat-mes-main-green"
# jar应用名称
jar_name="fcat-mes-main"
# 指定要匹配的字符串
search_string="Application Start Success"
echo "当前部署版本:$content"
case $content in
"blue")
rm -rf nginx/conf.d/main.conf
cp nginx/conf.d/green.conf_bak nginx/conf.d/main.conf
rm -rf $green_app_name/app/$jar_name.jar_bak
cp $green_app_name/app/$jar_name.jar $green_app_name/app/$jar_name.jar_bak
rm -rf $green_app_name/app/$jar_name.jar
cp $jar_name.jar $green_app_name/app/
echo "" > output.log
docker-compose restart $green_app_name
timeout 60 docker-compose logs -f --tail=0 $green_app_name | while IFS= read -r line; do
echo "$line"
if echo "$line" | grep -q "$search_string"; then
pkill -P $$ # 杀死timeout进程
break
fi
done > output.log 2>&1
if grep -q "$search_string" output.log; then
echo "$search_string"
docker-compose exec fcat-nginx nginx -s reload
echo "green" > bluegreen.txt
else
echo "Error detected in logs. Rolling back..."
# 在这里编写回滚操作的代码
rm -rf nginx/conf.d/main.conf
cp nginx/conf.d/blue.conf_bak nginx/conf.d/main.conf
rm -rf $green_app_name/app/$jar_name.jar
cp $green_app_name/app/$jar_name.jar_bak $green_app_name/app/$jar_name.jar
docker-compose restart $green_app_name
fi
;;
"green")
rm -rf nginx/conf.d/main.conf
cp nginx/conf.d/blue.conf_bak nginx/conf.d/main.conf
rm -rf $blue_app_name/app/$jar_name.jar_bak
cp $blue_app_name/app/$jar_name.jar $blue_app_name/app/$jar_name.jar_bak
rm -rf $blue_app_name/app/$jar_name.jar
cp $jar_name.jar $blue_app_name/app/
echo "" > output.log
docker-compose restart $blue_app_name
timeout 60 docker-compose logs -f --tail=0 $blue_app_name | while IFS= read -r line; do
echo "$line"
if echo "$line" | grep -q "$search_string"; then
echo "Found the search string. Exiting..."
pkill -P $$ # 杀死timeout进程
break
fi
done > output.log 2>&1
if grep -q "$search_string" output.log; then
echo "$search_string"
docker-compose exec fcat-nginx nginx -s reload
echo "blue" > bluegreen.txt
else
echo "Error detected in logs. Rolling back..."
# 在这里编写回滚操作的代码
rm -rf nginx/conf.d/main.conf
cp nginx/conf.d/green.conf_bak nginx/conf.d/main.conf
rm -rf $blue_app_name/app/$jar_name.jar
cp $blue_app_name/app/$jar_name.jar_bak $blue_app_name/app/$jar_name.jar
docker-compose restart $blue_app_name
fi
;;
"*")
echo "bluegreen.txt 内容不对"
;;
esac
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
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
# deploy-ifms.sh
#!/bin/bash
cd /docker/mes/
# 记录当前使用蓝色版本还是绿色版本
filename="ifms-bluegreen.txt"
# 读取当前使用版本
content=$(cat "$filename")
# 蓝色版本
blue_app_name="ifms-admin-blue"
# 绿色版本
green_app_name="ifms-admin-green"
# jar应用名称
jar_name="ifms-admin"
# 指定要匹配的字符串
search_string="Application Start Success"
error_string="Application Start Failed"
# 抓取的日志记录文件
log_file="ifms-output.log"
# nginx配置文件,不需要.conf后缀
nginx_file="mes"
wait_minutes=120
# 公司的ID
companyId=1
echo "当前部署版本:$content"
# 参数:json_data='{"companyId": "11", "status": "1", "msg": "中文哈哈"}'
send_mail(){
local json_data=$1
echo "$json_data"
# HTTP 接口地址和参数
url="http://www.tsuantone.top:16020/system/mail/deploy1"
# 发送 POST 请求,指定 Content-Type 为 application/json
response=$(curl -s -X POST -H "Content-Type: application/json" -d "$json_data" "$url")
# 输出响应结果
echo "Response: $response"
}
escape_string() {
local string="$1"
escaped_string=$(echo "$string" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g; s/\t/\\t/g')
echo "$escaped_string"
}
case $content in
"blue")
rm -rf nginx/conf.d/$nginx_file.conf
cp nginx/conf.d/$nginx_file-green.conf_bak nginx/conf.d/$nginx_file.conf
rm -rf $green_app_name/app/$jar_name.jar_bak
cp $green_app_name/app/$jar_name.jar $green_app_name/app/$jar_name.jar_bak
rm -rf $green_app_name/app/$jar_name.jar
cp $jar_name.jar $green_app_name/app/
echo "" > $log_file
cd /docker/mes/
docker-compose restart $green_app_name
timeout $wait_minutes docker-compose logs -f --tail=0 $green_app_name | while IFS= read -r line; do
echo "$line"
if echo "$line" | grep -q "$search_string"; then
pkill -P $$ # 杀死timeout进程
break
fi
done > $log_file 2>&1
content_log=$(cat $log_file)
# 去掉shell终端的颜色显示
content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
#content_log=$(escape_string "$original_string")
echo "$content_log"
if grep -q "$search_string" $log_file; then
echo "$search_string"
json_data=$(jq -n --arg company_id "$companyId" --arg status "1" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
send_mail "$json_data"
cd /docker/mes/
docker-compose restart fcat-nginx
echo "green" > $filename
else
echo "$error_string"
json_data=$(jq -n --arg company_id "$companyId" --arg status "0" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
send_mail "$json_data"
# 在这里编写回滚操作的代码
rm -rf nginx/conf.d/$nginx_file.conf
cp nginx/conf.d/$nginx_file-blue.conf_bak nginx/conf.d/$nginx_file.conf
rm -rf $green_app_name/app/$jar_name.jar
cp $green_app_name/app/$jar_name.jar_bak $green_app_name/app/$jar_name.jar
docker-compose restart $green_app_name
fi
;;
"green")
rm -rf nginx/conf.d/$nginx_file.conf
cp nginx/conf.d/$nginx_file-blue.conf_bak nginx/conf.d/$nginx_file.conf
rm -rf $blue_app_name/app/$jar_name.jar_bak
cp $blue_app_name/app/$jar_name.jar $blue_app_name/app/$jar_name.jar_bak
rm -rf $blue_app_name/app/$jar_name.jar
cp $jar_name.jar $blue_app_name/app/
echo "" > $log_file
docker-compose restart $blue_app_name
timeout $wait_minutes docker-compose logs -f --tail=0 $blue_app_name | while IFS= read -r line; do
echo "$line"
if echo "$line" | grep -q "$search_string"; then
pkill -P $$ # 杀死timeout进程
break
fi
done > $log_file 2>&1
content_log=$(cat $log_file)
# 去掉shell终端的颜色显示
content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
#content_log=$(escape_string "$original_string")
echo "$content_log"
if grep -q "$search_string" $log_file; then
echo "$search_string"
json_data=$(jq -n --arg company_id "$companyId" --arg status "1" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
send_mail "$json_data"
cd /docker/mes/
docker-compose restart fcat-nginx
echo "blue" > $filename
else
echo "$error_string"
json_data=$(jq -n --arg company_id "$companyId" --arg status "0" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
send_mail "$json_data"
# 在这里编写回滚操作的代码
rm -rf nginx/conf.d/$nginx_file.conf
cp nginx/conf.d/$nginx_file-green.conf_bak nginx/conf.d/$nginx_file.conf
rm -rf $blue_app_name/app/$jar_name.jar
cp $blue_app_name/app/$jar_name.jar_bak $blue_app_name/app/$jar_name.jar
docker-compose restart $blue_app_name
fi
;;
"*")
echo "bluegreen.txt 内容不对"
;;
esac
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
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
# deploy-fcat.sh
#!/bin/bash
# 记录当前使用蓝色版本还是绿色版本
filename="ruoyi-admin-bluegreen.txt"
# 读取当前使用版本
content=$(cat "$filename")
# 蓝色版本
blue_app_name="ruoyi-admin-blue"
# 绿色版本
green_app_name="ruoyi-admin-green"
# jar应用名称
jar_name="ruoyi-admin"
jar_path="/root/ruoyi-vue/ruoyi-admin/target/"
# 指定要匹配的字符串
search_string="Application Start Success"
error_string="Application Start Failed"
# 抓取的日志记录文件
log_file="ruoyi-admin-output.log"
# nginx配置文件,不需要.conf后缀
nginx_file="ruoyi"
# 检测启动日志等待时间
wait_minutes=120
# 切换nginx的蓝绿版本,需要留时间处理请求中的接口返回
wait_inteface_deal_minutes=30
# 名称
name="FCAT"
echo "当前部署版本:$content"
# 参数:json_data='{"name": "FCAT", "status": "1", "msg": "中文哈哈"}'
send_mail(){
local json_data=$1
echo "$json_data"
# HTTP 接口地址和参数
url="https://localhost/prod-api/system/mail/deploy"
# 发送 POST 请求,指定 Content-Type 为 application/json
response=$(curl -s -X POST -H "Content-Type: application/json" -d "$json_data" -k "$url")
# 输出响应结果
echo "Response: $response"
}
escape_string() {
local string="$1"
escaped_string=$(echo "$string" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g; s/\t/\\t/g')
echo "$escaped_string"
}
case $content in
"blue")
rm -rf nginx/conf.d/$nginx_file.conf
cp nginx/conf.d/$nginx_file-green.conf_bak nginx/conf.d/$nginx_file.conf
rm -rf $green_app_name/jar/$jar_name.jar_bak
cp $green_app_name/jar/$jar_name.jar $green_app_name/jar/$jar_name.jar_bak
rm -rf $green_app_name/jar/$jar_name.jar
cp $jar_path$jar_name.jar $green_app_name/jar/
echo "" > $log_file
docker-compose restart $green_app_name
timeout $wait_minutes docker-compose logs -f --tail=0 $green_app_name | while IFS= read -r line; do
echo "$line"
if echo "$line" | grep -q "$search_string"; then
pkill -P $$ # 杀死timeout进程
break
fi
done > $log_file 2>&1
content_log=$(cat $log_file)
# 去掉shell终端的颜色显示
content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
#content_log=$(escape_string "$original_string")
echo "$content_log"
if grep -q "$search_string" $log_file; then
echo "$search_string"
docker-compose exec fcat-nginx nginx -s reload
sleep $wait_inteface_deal_minutes
json_data=$(jq -n --arg name "$name" --arg status "1" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
send_mail "$json_data"
echo "green" > $filename
docker-compose stop $blue_app_name
else
echo "$error_string"
json_data=$(jq -n --arg name "$name" --arg status "0" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
send_mail "$json_data"
# 在这里编写回滚操作的代码
rm -rf nginx/conf.d/$nginx_file.conf
cp nginx/conf.d/$nginx_file-blue.conf_bak nginx/conf.d/$nginx_file.conf
rm -rf $green_app_name/jar/$jar_name.jar
cp $green_app_name/jar/$jar_name.jar_bak $green_app_name/jar/$jar_name.jar
docker-compose restart $green_app_name
fi
;;
"green")
rm -rf nginx/conf.d/$nginx_file.conf
cp nginx/conf.d/$nginx_file-blue.conf_bak nginx/conf.d/$nginx_file.conf
rm -rf $blue_app_name/jar/$jar_name.jar_bak
cp $blue_app_name/jar/$jar_name.jar $blue_app_name/jar/$jar_name.jar_bak
rm -rf $blue_app_name/jar/$jar_name.jar
cp $jar_path$jar_name.jar $blue_app_name/jar/
echo "" > $log_file
docker-compose restart $blue_app_name
timeout $wait_minutes docker-compose logs -f --tail=0 $blue_app_name | while IFS= read -r line; do
echo "$line"
if echo "$line" | grep -q "$search_string"; then
pkill -P $$ # 杀死timeout进程
break
fi
done > $log_file 2>&1
content_log=$(cat $log_file)
# 去掉shell终端的颜色显示
content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
#content_log=$(escape_string "$original_string")
echo "$content_log"
if grep -q "$search_string" $log_file; then
echo "$search_string"
docker-compose exec fcat-nginx nginx -s reload
sleep $wait_inteface_deal_minutes
json_data=$(jq -n --arg name "$name" --arg status "1" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
send_mail "$json_data"
echo "blue" > $filename
docker-compose stop $green_app_name
else
echo "$error_string"
json_data=$(jq -n --arg name "$name" --arg status "0" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
send_mail "$json_data"
# 在这里编写回滚操作的代码
rm -rf nginx/conf.d/$nginx_file.conf
cp nginx/conf.d/$nginx_file-green.conf_bak nginx/conf.d/$nginx_file.conf
rm -rf $blue_app_name/jar/$jar_name.jar
cp $blue_app_name/jar/$jar_name.jar_bak $blue_app_name/jar/$jar_name.jar
docker-compose restart $blue_app_name
fi
;;
"*")
echo "$filename内容不对"
;;
esac
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
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
# 上传jar包到服务器
根目录在/docker/mes/fcat-mes-main.jar
sh deploy.sh
1
curl http://192.168.9.126:16219/test/version
{
"version": "v3"
}
curl http://192.168.9.126:16220/test/version
{
"version": "v2"
}
curl http://192.168.9.126:16221/dev-api/test/version
{
"version": "v3"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14