2024-06-24 22:32:25
配置 Eureka Server
1. 创建一个新的 Spring Boot 项目
使用 Spring Initializr 创建一个新的 Spring Boot 项目,选择以下依赖:
- Spring Boot DevTools
- Spring Web
- Eureka Server
或者你可以手动在你的 pom.xml
文件中添加这些依赖:
1 | <dependencies> |
确保父 POM 文件包含 Spring Cloud 版本管理配置:
1 | <dependencyManagement> |
2. 在主类上添加 @EnableEurekaServer
注解
创建一个 Spring Boot 主类,并添加 @EnableEurekaServer
注解:
1 | package com.example.eurekaserver; |
3. 配置 application.yml
在 src/main/resources
目录下创建一个 application.yml
文件,并添加以下配置:
1 | server: |
配置 Eureka Client
1. 在现有的 Spring Boot 项目中添加 Eureka Client 依赖
在你的客户端服务的 pom.xml
文件中添加以下依赖:
1 | <dependencies> |
2. 在主类上添加 @EnableEurekaClient
注解
在客户端服务的主类上添加 @EnableEurekaClient
注解:
1 | package com.example.clientservice; |
3. 配置 application.yml
在 src/main/resources
目录下创建一个 application.yml
文件,并添加以下配置:
1 | server: |
Ribbon组件(负载均衡)
- 实现负载均衡
1 | 加 注解在RestTemplate请求发送类上 |
- 调整负载均衡
- 总体实现负载均衡策略
1 |
|
某个服务地负载均衡策略
1
2
3
4
5
6
7
8
9######随机负载均衡算法#####
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#####按地区随机负载均衡算法#####
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule实现饥饿加载
1
2
3
4
5
6
7ribbon:
eager-load:
enabled: true
clients: user-service#指定加载地服务名称
- 多个服务中地第一个
- 第二个
- 第三个负载均衡原理
-
被LoadBalancerInterceptor.class负载均衡拦截器拦截
1
2
3
4
5
6public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}execute
接口传入serviceName
调用执行下一步在RibbonLoadBalancerClient.class中实现的execute接口
1
2
3
4public <T> T execute(String serviceId//这里接受的是serviceName
, LoadBalancerRequest<T> request) throws IOException {
return this.execute(serviceId, (LoadBalancerRequest)request, (Object)null);
}下一步执行execute函数
1
2
3
4
5
6
7
8
9
10public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
*********ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);*********
*******Server server = this.getServer(loadBalancer, hint);*********
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonServer ribbonServer = new RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
}
}loadBalancer中的allServerList存储某个serviceName的全部服务器端口地址
之后开始负载均衡选择服务器
1
Server server = this.getServer(loadBalancer, hint);
下一步跳到getServer函数
1
2
3protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");//chooseServer是负载均衡选择函数
}调用父类chooseServer
1
return super.chooseServer(key);
跳到父类chooseServer继续走到
1
return this.rule.choose(key);
rule是一个IRule对象(IRule通过传入的key选择负载均衡算法)
1
protected IRule rule;//IRule中I为接口缩写
查看IRule实现类其为负载均衡的调用算法
RoundRobinRule.class//轮询算法
- RandomRule.class//随机算法 - | 内置负载均衡规则类 | 规则描述 | | ----------------------------- | ------------------------------------------------------------ | | **RoundRobinRule** | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 | | **AvailabilityFilteringRule** | 对以下两种服务器进行忽略:<br>(1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。<br>(2)并发数过高的服务器。如果一台服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的<clientName>.<clientConfigNamespace>.ActiveConnectionsLimit属性进行配置。 | | **WeightedResponseTimeRule** | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 | | **ZoneAvoidanceRule** | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务器做轮询。 | | **BestAvailableRule** | 忽略哪些短路的服务器,并选择并发数较低的服务器。 | | **RandomRule** | 随机选择一个可用的服务器。 | | **RetryRule** | 重试机制的选择逻辑 |
Nacos注册中心
启动命令
startup.cmd -m standalone
注册步骤
- 父工程引入spring-cloud-alilbaba管理依赖
1
2
3
4
5
6
7<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>子工程引入nacos客户端依赖
1
2
3
4
5<!-- nacos客户端依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>pom文件添加
1
2
3
4
5
6spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
cluster-name: HLJ #添加集群(可选)命名空间的配置
1
2
3
4
5
6
7cloud:
nacos:
discovery:
server-addr: localhost:8848
cluster-name: ZC
######namespcce为命名空间配置######
namespace: 67a8c122-3a65-431a-be1a-b11422ca4e08
特点
临时示例
- 采用心跳检测(有一定周期)
非临时示例
nacos主动询问是否还活着(发现速度快)
如果下线nacos不会删除他
设置代码
1
ephemeral: false
主动推送变更配置消息(相比定时拉取服务pull更加及时)
Nacos配置管理(更加灵活,实时更新,便捷高效)
实现原理
- 读取nacos中的配置文件后和本地配置文件合并
- 吧nacos地址需要先知道的信息放在
bootstrap.yml
文件
实现步骤
添加nacos的配置管理依赖
1
2
3
4
5<!--nacos配置管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>把需要提前加载的信息放在bootstrap.yml中
- bootstrap中的命名优先级没有application中的命名优先级高(经过多次实验得出感觉有点不对劲啊)
1
2
3
4
5
6
7
8
9
10spring:
application:
name: orderservice
profiles:
active: dev
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml
配置热更新
- 通过
@value
注解注入配置,结合@RefreshScope
来刷新 - 通过
@ConfigruationProperties
注入来实现自动刷新
- 通过
Nacos集群搭建
创建高可用主从Mysql数据库
在Docker中创建网络以此来连接主从Docker容器
1 | docker network create mysql-cluster-net |
创建服务
- 创建Mysql主服务
1
2
3
4
5
6
7
8
9
10
11docker run -d \
--name mysqlp1 \
--network mysql-cluster-net \
-p 3305:3306 \
-e MYSQL_ROOT_PASSWORD=password \
-e MYSQL_REPLICATION_USER=repl \
-e MYSQL_REPLICATION_PASSWORD=replpassword \
-v /root/mysql_cluster/mysqlp1/data:/var/lib/mysql \
-v /root/mysql_cluster/mysqlp1/conf:/etc/mysql/conf.d \
-v /root/mysql_cluster/mysqlp1/init:/docker-entrypoint-initdb.d \
mysql:latest
- 代码解释
1
2
3
4
5
6
7
8
9
10
11docker run -d \ # 在后台(detached mode)运行 Docker 容器
--name mysqlp1 \ # 将容器命名为 'mysqlp1'
--network mysql-cluster-net \ # 连接到 'mysql-cluster-net' 网络
-p 3305:3306 \ # 将主机的端口 3305 映射到容器的端口 3306(MySQL 默认端口)
-e MYSQL_ROOT_PASSWORD=password \ # 设置 MySQL root 用户的密码为 'password'
-e MYSQL_REPLICATION_USER=repl \ # 设置 MySQL 复制用户名为 'repl'
-e MYSQL_REPLICATION_PASSWORD=replpassword \ # 设置 MySQL 复制用户的密码为 'replpassword'
-v /root/mysql_cluster/mysqlp1/data:/var/lib/mysql \ # 挂载主机目录 '/root/mysql_cluster/mysqlp1/data' 到容器目录 '/var/lib/mysql',用于 MySQL 数据存储
-v /root/mysql_cluster/mysqlp1/conf:/etc/mysql/conf.d \ # 挂载主机目录 '/root/mysql_cluster/mysqlp1/conf' 到容器目录 '/etc/mysql/conf.d',用于 MySQL 配置文件
-v /root/mysql_cluster/mysqlp1/init:/docker-entrypoint-initdb.d \ # 挂载主机目录 '/root/mysql_cluster/mysqlp1/init' 到容器目录 '/docker-entrypoint-initdb.d',用于初始化脚本
mysql:latest # 指定使用 'latest' 标签的 MySQL Docker 镜像
- 创建从Mysql服务
1
2
3
4
5
6
7
8
9
10
11docker run -d \
--name mysqlr1 \
--network mysql-cluster-net \
-p 3304:3306 \
-e MYSQL_ROOT_PASSWORD=password \
-e MYSQL_REPLICATION_USER=repl \
-e MYSQL_REPLICATION_PASSWORD=replpassword \
-v /root/mysql_cluster/mysqlr1/data:/var/lib/mysql \
-v /root/mysql_cluster/mysqlr1/conf:/etc/mysql/conf.d \
-v /root/mysql_cluster/mysqlr1/init:/docker-entrypoint-initdb.d \
mysql:latest- 代码解释同上
配置文件修改
配置主Mysql配置文件
打开映射出来的数据卷
vim /root/mysql_cluster/mysqlp1/conf
写入配置文件内容1
2
3
4
5
6
7
8
9
10
11[mysqld]
server-id=1
log_bin=master-bin
log_bin-index=master-bin.index
skip-name-resolve
port=3306
max_connections=200
max_connect_errors=10
character-set-server=utf8
default-storage-engine=INNODB
default_authentication_plugin=mysql_native_password
1 |
|
- 配置从Mysql服务器配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13[mysqld]
server-id=2
relay-log-index=slave-relay-bin.index
relay-log=slave-relay-bin
log_bin=mysql-bin
log-slave-updates=1
skip-name-resolve
port=3306
max_connections=200
max_connect_errors=10
character-set-server=utf8
default-storage-engine=INNODB
default_authentication_plugin=mysql_native_password
- 配置文件解释同上
初始化主从配置
- 主服务配置
- 进入主服务
1
docker exec -it mysqlp1 mysql -u root -ppassword
- 设置复制用户的操作
1
2
3CREATE USER 'repl'@'%' IDENTIFIED BY 'replpassword';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES; - 获取日志消息输出结过为mysql数据的二进制文件需要记下名称和地址
1
SHOW MASTER STATUS;
- 输出结果示例File和Position需要记忆以后有用
1
2
3
4
5+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 | 154 | | | |
+------------------+----------+--------------+------------------+-------------------+
- 输出结果示例File和Position需要记忆以后有用
- 从服务器配置
- 进入从服务
1
docker exec -it mysql-slave mysql -u root -ppassword
1
1 |
1 |
1 |
- 连接到主mysql和规定复制用户消息
1
2
3
4
5
6CHANGE MASTER TO
MASTER_HOST='mysqlp1',
MASTER_USER='repl',
MASTER_PASSWORD='replpassword',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=154;1
1 |
- 启动复制
1
START SLAVE;
1
- 记录复制状态
1
SHOW SLAVE STATUS \G;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24- ***如果输出中 Slave_IO_Running 和 Slave_SQL_Running 都是 Yes,说明主从复制配置成功***。
## 配置检测
1. 在主服务新建数据库和表格并且插入数据
```java
-- 创建数据库
CREATE DATABASE test_database;
-- 切换到新创建的数据库
USE test_database;
-- 创建表
CREATE TABLE test_table (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
age INT
);
-- 切换到指定的数据库
USE test_database;
-- 向表中插入数据
INSERT INTO test_table (name, age) VALUES
('Alice', 25),
('Bob', 30),
('Charlie', 28);
- 在从服务中检测是否出现主服务中的数据库
1
show databases;
- 成功示例
主服务
从服务
- 成功示例
错误处理
高版本的mysql可能出现 MySQL 8.0 默认使用 caching_sha2_password 认证插件,而连接需要 SSL(安全连接)。
第一种解决办法在配置其他插件
1
2
3
4
5docker exec -it mysql-master mysql -u root -ppassword
-- 在MySQL命令行中执行以下命令
ALTER USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'replpassword';
FLUSH PRIVILEGES;之后需要重新配置从服务器
1
2
3
4
5
6
7
8
9
10
11
12
13docker exec -it mysql-slave mysql -u root -ppassword
-- 在MySQL命令行中执行以下命令
STOP SLAVE;
CHANGE MASTER TO
MASTER_HOST='mysql-master',
MASTER_USER='repl',
MASTER_PASSWORD='replpassword',
MASTER_LOG_FILE='mysql-bin.000002',
MASTER_LOG_POS=根据实际情况填写可能会改变;
START SLAVE;
-- 检查复制状态
SHOW SLAVE STATUS \G;第二种解决办法
- 生成SSL证书
使用 MySQL 自带工具生成SSL证书:1
mysql_ssl_rsa_setup --datadir=/path/to/mysql/data
- 配置主服务器使用SSL在主服务器的 my.cnf 文件中添加:
1
2
3
4
5[mysqld]
...
ssl-ca=/path/to/ca.pem
ssl-cert=/path/to/server-cert.pem
ssl-key=/path/to/server-key.pem - 配置从服务器使用SSL在从服务器的 my.cnf 文件中添加:
1
2
3
4
5[mysqld]
...
ssl-ca=/path/to/ca.pem
ssl-cert=/path/to/client-cert.pem
ssl-key=/path/to/client-key.pem - 然后在从服务器上重新配置主从复制,确保启用SSL连接:
1
2
3
4
5
6
7
8
9
10
11
12
13docker exec -it mysql-slave mysql -u root -ppassword
-- 在MySQL命令行中执行以下命令
STOP SLAVE;
CHANGE MASTER TO
MASTER_HOST='mysql-master',
MASTER_USER='repl',
MASTER_PASSWORD='replpassword',
MASTER_LOG_FILE='mysql-bin.000002',
MASTER_LOG_POS=根据实际情况填写可能会改变;
START SLAVE;
-- 检查复制状态
SHOW SLAVE STATUS \G;
1
- 生成SSL证书
本地连接
云配置
- 通过阿里云
- 配置出占规则(我们需要这两个端口可以被外界访问)
- 我们需要添加Docker中两个服务器的映射端口
- 主服务器为3305 从服务器为3304
- 点击手动添加并且填写端口
- 宝塔配置出站规则
- 进入宝塔按照图示
本地配置
- 本文通过IDEA来进行远程连接需要专业版
- 打开Idea数据库
- 在填写端口那里填写主从服务的映射端口
创建nacos集群
配置数据库中nacos的注册信息库
sql语句
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
193
194
195
196
197
198CREATE TABLE `config_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(255) DEFAULT NULL,
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`app_name` varchar(128) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`c_desc` varchar(256) DEFAULT NULL,
`c_use` varchar(64) DEFAULT NULL,
`effect` varchar(64) DEFAULT NULL,
`type` varchar(64) DEFAULT NULL,
`c_schema` text,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_aggr */
/******************************************/
CREATE TABLE `config_info_aggr` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(255) NOT NULL COMMENT 'group_id',
`datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
`content` longtext NOT NULL COMMENT '内容',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`app_name` varchar(128) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_beta */
/******************************************/
CREATE TABLE `config_info_beta` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_tag */
/******************************************/
CREATE TABLE `config_info_tag` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_tags_relation */
/******************************************/
CREATE TABLE `config_tags_relation` (
`id` bigint(20) NOT NULL COMMENT 'id',
`tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`nid` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`nid`),
UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = group_capacity */
/******************************************/
CREATE TABLE `group_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = his_config_info */
/******************************************/
CREATE TABLE `his_config_info` (
`id` bigint(64) unsigned NOT NULL,
`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`data_id` varchar(255) NOT NULL,
`group_id` varchar(128) NOT NULL,
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL,
`md5` varchar(32) DEFAULT NULL,
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`src_user` text,
`src_ip` varchar(50) DEFAULT NULL,
`op_type` char(10) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`nid`),
KEY `idx_gmt_create` (`gmt_create`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = tenant_capacity */
/******************************************/
CREATE TABLE `tenant_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
CREATE TABLE `tenant_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`kp` varchar(128) NOT NULL COMMENT 'kp',
`tenant_id` varchar(128) default '' COMMENT 'tenant_id',
`tenant_name` varchar(128) default '' COMMENT 'tenant_name',
`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
`gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
`gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
CREATE TABLE `users` (
`username` varchar(50) NOT NULL PRIMARY KEY,
`password` varchar(500) NOT NULL,
`enabled` boolean NOT NULL
);
CREATE TABLE `roles` (
`username` varchar(50) NOT NULL,
`role` varchar(50) NOT NULL,
UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
);
CREATE TABLE `permissions` (
`role` varchar(50) NOT NULL,
`resource` varchar(255) NOT NULL,
`action` varchar(8) NOT NULL,
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
修改nacos配置文件
application.properties**将原本被注释的注释符删除
1
2
3
4
5
6
7
8
9
10
11
12server.port=8847#配置端口号
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://121.41.121.72:3305/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=passwordcluster.conf中(原本的名称是cluster.conf.example需要修改成cluster.conf)
1
2
3
4192.168.1.232:8845
192.168.1.232:8846
192.168.1.232:8847
#配置要为网络地址不然可能会出现无法识别的情况复制3份并且对每一份修改端口
启动注意启动需要删除-m来启动集群启动
1
startup.cmd standalone
docker配置nacos
开始代码
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
26docker run -d \
--name nacosp1 \
--network mysql-cluster-net \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--privileged=true \
--restart=always \
-e JVM_XMS=256m \
-e JVM_XMX=256m \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_SERVICE_HOST=mysqlp1 \
-e MYSQL_SERVICE_PORT=3305 \
-e MYSQL_SERVICE_DB_NAME=nacos \
-e MYSQL_SERVICE_USER=root \
-e MYSQL_SERVICE_PASSWORD=password \
-v /root/nacos-clu/nacosp1/logs:/home/nacos/logs \
-v /root/nacos-clu/nacosp1/init.d/custom.properties:/home/nacos/init.d/custom.properties \
-v /root/nacos-clu/nacosp1/jdbc.properties:/home/nacos/conf/jdbc.properties \
-v /root/nacos-clu/nacosp1/data:/home/nacos/data \
nacos/nacos-server:latest
配置NGINX来实现负载均衡
修改nginx.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
44worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# Load balancing for Nacos cluster
upstream nacos-cluster {
server 192.168.1.232:8845;
server 192.168.1.232:8846;
server 192.168.1.232:8847;
}
server {
listen 80;
server_name localhost;
location /nacos {
proxy_pass http://nacos-cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}启动nginx.exe
由于nginx不会在每次启动自动关闭所以需要下面的一些命令检查nginx进程
- taskkill /F /IM nginx.exe ##删除nginx进程
tasklist | findstr nginx ##查看nginx进程
最后在服务中配置访问nginx的端口来实现对nacos的负载均衡
1
server-addr: localhost:80
错误整理
- com.alibaba.nacos.api.exception.NacosException: caused: Raft put failed, key:com.alibaba.nacos.naming.domains.meta.public##DEFAULT_GROUP@@userservice, value:Service{name=‘DEFAULT_GROUP@@userservice’, protectThreshold=0.0, appName=’null’, groupName=’DEFAULT_GROUP’, metadata={}};
这是由于配置的地址为127.0.0.1:端口号应该跟改为本机ip网络地址
这是因为在进行集群中单一个进行测试时间在服务中使用localhost:端口号应该使用cluter.conf中的ip地址这样就不会出错
- nginx配置不生效无法访问nacos
- 因为每次nginx启动后在此启动原来启动的不会自动暂停这样就会导致配置文件不生效
- 查看nginx进程的命令tasklist | findstr nginx
- 删除nginx进程的命令tasklist | findstr nginx
- 哎每一步骤都有错误烦死了不过解决了还挺有趣的哈哈哈
HTTP客户端Feign(原来调用的restTemplate)
为什么要替换restTemplate
- 代码可读性不行,不好理解
- 参数复杂不好修改太不优雅了
配置过程
添加坐标
1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>启动文件添加启动注解
1
使用过程
创建接口文件
1
2
3
4
5//配置请求的服务名称
public interface userserviceClient {
//和springboot一样的请求样式
User findById(; Long id)
}
Fine的自定义配置
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level |
修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder |
响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder |
请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign.Contract |
支持的注解格式 | 默认是SpringMVC的注解 |
feign.Retryer |
失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试机制 |
修改配置
feign: client: config: default: ###dafault为全局配置 修改为服务名称为非全局配置 logger-level: FULL
2. 在**EnableFeignClients**注解上引用这个类(这个注解在启动类上)1
2
3
4
5
6
7
8
9
10
11
12
- 通过类加载
1. 创建类
```java
public class feignDefaultConfig {
@Bean
public Logger.Level feignConfig(){
return Logger.Level.FULL;
}
}1
性能优化
Feign底层的客户端实现:
- 底层客户端实现
URLConnection:默认实现,不支持连接池
Apache HttpClient:支持连接池
OKHttp:支持连接池
如何实现
引入坐标文件
1
2
3
4
5<!-- httpclient请求组件-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>配置连接参数
1
2
3
4httpclient:
enabled: true # 是否开启
max-connections: 200 #最大连接数
max-connections-per-route: 50 # 每个单节点最大连接数
- 日志级别不选full
Feign的最佳实现
- 方式一(不推荐):给消费者的FeignClient和提供者的controller定义统一接口
- 方式二(): 将FeignClient抽取为独立模块,并且把接口有关的POJO,默认的Feign配置都放在这个模块中
Feign的最佳实现操作
错误整理
导入的包无法创建feign实例
原因FetgnClient不在SpringbootApplication的扫描包范围内
在EnableFeignClients中添加其他项目的包
1
@EnableFeignClients(class=****.class,defaultConfiguration = feignDefaultConfig.class)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# GateWay网关
## 配置启动
1. 添加依赖
```pom
<!-- 网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- nacos服务发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
配置yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22server:
port: 10010 # 配置服务器的端口号为 10010
spring:
application:
name: gataway # 配置应用程序的名称为 gataway
cloud:
nacos:
server-addr: localhost:80 # 配置 Nacos 服务器的地址为 localhost:80
gateway:
routes:
- id: user-service # 这是第一个路由的唯一标识符,命名为 user-service
uri: lb://userservice # 配置目标服务的 URI,为 userservice,使用负载均衡(lb)方式
predicates:
- Path=/user/** # 路由的匹配条件,匹配所有以 /user/ 开头的请求路径
- id: order-service # 这是第二个路由的唯一标识符,命名为 order-service
uri: lb://order-service # 配置目标服务的 URI,为 order-service,使用负载均衡(lb)方式
predicates:
- Path=/order/** # 路由的匹配条件,匹配所有以 /order/ 开头的请求路径
predicates断言工厂
作用**读取配置中的断言字符串,并且转为过滤规则
断言工厂种类
名称 说明 示例 After 是某个时间点后的请求 - After=2037-01-20T17:42:47.789-07:00[America/Denver] Before 是某个时间点之前的请求 - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] Between 是某两个时间点之间的请求 - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] Cookie 请求必须包含某些cookie - Cookie=chocolate, ch.p Header 请求必须包含某些header - Header=X-Request-Id, \d+ Host 请求必须是访问某个host(域名) - Host=**.somehost.org, **.anotherhost.org Method 请求方式必须是指定方式 - Method=GET,POST Path 请求路径必须符合指定规则 - Path=/red/{segment},/blue/** Query 请求参数必须包含指定参数 - Query=name, Jack或者- Query=name RemoteAddr 请求者的ip必须是指定范围 - RemoteAddr=192.168.1.1/24 Weight 权重处理 [SpringCloud predicate factory](Spring Cloud Gateway)
路由过滤器
[Spring Cloud GatewayFilter Factories](Spring Cloud Gateway)
查阅文档更改yml配置文件
配置在单一服务只对单一服务生效
1
2
3
4
5
6
7
8gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
filters:
- AddResponseHeader=Truth, Fucking全局生效
1
2default-filters:
- **************
GlobalFilter全局过滤器(可自定义,比网关自带的过滤器更灵活)
实现GlobalFilter接口(创建一个类)
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
public class AuthorizetFilter implements GlobalFilter, Ordered {
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求参数
ServerHttpRequest request = exchange.getRequest();
// 获取对话
MultiValueMap<String, String> queryParams = request.getQueryParams();
// 从对话中获取Authorization参数
String authorization = queryParams.getFirst("Authorization");
// 获取
// 判断
if(authorization.equals("admin")){
return chain.filter(exchange);
}
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
public int getOrder() {
return -1;
}
}
过滤器执行顺序
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器
每一个过滤器都会有一个**@Order配置值越小越优先执行但是路由的过滤器和DefaultFilter的Order由Spring指定按照声明的顺序从1递增**
filters: - AddRequestHeader=Truth, Fucking order为1 - AddRequestHeader=Truth, Fucking order为2
1
2
3
4
5
- ```yml
default-filters:
- AddRequestHeader=Truth, Fucking order为1
- AddRequestHeader=Truth, Fucking order为2
特殊情况当过滤器的Order值都相同时执行顺序是
DefaultFilter—>路由过滤器—>GlobalFilter
原理刨析
当前路由的过滤器和defaultFilter本质是一个东西都叫GateWayFilter都是通过AddRequestHeaderGatewayFilterFactory这个类来读取配置文件最后生成一个真正的路由器
- 通过阅读RouterDefinitionRouteLocator.class中的getFilters函数可知道这两个的执行顺序是靠Order来绝定的
- 通过阅读FilteringWebHandler.class中的handle函数可知到根据上面的getFilters首先获取路由过滤器和DefaultFilter的排序之后和GlobalFilter合并为GatewayFilter最后根据Order排序,组织过滤器链
在网关中所有的GlobalFilter都可以被适配成GatewayFilter
网关的跨域问题解决
跨域问题是因为浏览器和ajax
网关处理跨域只需要简单配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19spring:
cloud:
gateway:
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
- "http://www.leyou.com"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期