中文官方文档
开源地址
【注】一切模块均是maven
项目创建
父依赖
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.vinjcentgroupId>
<artifactId>springcloudartifactId>
<packaging>pompackaging>
<version>0.0.1-SNAPSHOTversion>
<modules>
<module>springcloud-provider-dept-8001module>
<module>springcloud-consumer-dept-80module>
modules>
<name>springcloudname>
<description>SpringCloud Alibabadescription>
<properties>
<java.version>1.8java.version>
<spring.cloud.alibaba.version>2.2.5.RELEASEspring.cloud.alibaba.version>
<spring.boot.version>2.3.11.RELEASEspring.boot.version>
<spring.cloud.version>Hoxton.SR8spring.cloud.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${spring.cloud.alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${spring.boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
官方:一个更易于构建云原生态应用的动态服务(Nacos Discovery)、服务配置(Nacos Config)和服务管理平台
Nacos 的关键特性包括:
主流的注册中心
CAP C–一致性 A–可用性 P–分区容错性
功能 | Nacos | Eureka | Consul | CoreDNS | Zookeeper |
---|---|---|---|---|---|
一致性 | CP + AP | AP | CP | — | CP |
健康检查 | TCP/HTTP/MYSQL/Client/Beat | Client/Beat | TCP/HTTP/gRPC | — | Keep Alive |
负载均衡策略 | 权重/metadata/Selector | Ribbon | Fabio | RoundRobin | — |
雪崩保护 | 有 | 有 | 无 | 无 | 无 |
自动注销实例 | 支持 | 支持 | 支持 | 不支持 | 支持 |
访问协议 | HTTP/DNS | HTTP | HTTP/DNS | DNS | TCP |
监听支持 | 支持 | 支持 | 支持 | 不支持 | 支持 |
多数据中心 | 支持 | 支持 | 支持 | 不支持 | 不支持 |
跨注册中心同步 | 支持 | 不支持 | 支持 | 不支持 | 不支持 |
SpringCloud集成 | 支持 | 支持 | 支持 | 不支持 | 支持 |
Dubbo集成 | 支持 | 不支持 | 支持 | 不支持 | 支持 |
K8S集成 | 支持 | 不支持 | 支持 | 支持 | 不支持 |
在Windows上部署
选择 2.0.1 版本
set MODE="standalone"
用户名、密码 都为 nacos
1)导入依赖
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
2)编写实体类Department
@Data
@NoArgsConstructor
@Accessors(chain = true) // 链式写法
public class Department implements Serializable { // 实体类 orm 类表关系映射
private Long deptId; // 主键
private String deptName; // 部门名
// 这个数据存在哪个数据库的字段 微服务,一个服务对应一个数据库,同一个信息可能存在不同的数据库
private String db_source;
public Department(String deptName) {
this.deptName = deptName;
}
/**
* 链式写法:
* Department dept = new Department();
*
* 原来dept.setName();
*
* 加了之后:
* dept
* .setDeptName()
* .setDeptId()
* .setDb_source();
*
*/
}
<dependencies>
<dependency>
<groupId>com.vinjcentgroupId>
<artifactId>springcloud-apiartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
dependencies>
application.yml
# 端口号
server:
port: 8001
spring:
application:
name: springcloud-provider-dept # 应用名称
# 配置nacos
cloud:
nacos:
# nacos 服务地址
server-addr: 127.0.0.1:8848
discovery:
username: nacos # nacos用户名
password: nacos
namespace: public # 分隔开发环境和测试环境使用
官方手册
创建一个springcloud-consumer-dept-80模块
导入依赖
<dependencies>
<dependency>
<groupId>com.vinjcentgroupId>
<artifactId>springcloud-apiartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
dependencies>
@Configuration
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate(RestTemplateBuilder builder){
return builder.build();
}
}
@RestController
@RequestMapping("/consumer")
@SuppressWarnings("all")
public class DeptController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/dept/msg")
public String getMessage(){
return restTemplate.getForObject("http://springcloud-provider-dept/dept/msg",String.class);
}
}
application.yml
文件# 端口号
server:
port: 80
@RestController
@RequestMapping("/dept")
@SuppressWarnings("all")
public class DeptController {
@Value("${server.port}")
private int port;
@GetMapping("/msg")
public String getMessage(){
return "获取服务成功!"+port;
}
}
问题:
需要给消费者springcloud-consumer-dept-80添加负载均衡
原因:nacos本身没有办法解析服务名称到我们的nacos服务地址
再次访问http://localhost/consumer/dept/msg
具体流程
扩展
默认的负载均衡机制:轮询
钥匙 | 默认值 | 描述 | |
---|---|---|---|
服务器地址 | spring.cloud.nacos.discovery.server-addr |
Nacos Server监听的IP和端口 | |
服务名称 | spring.cloud.nacos.discovery.service |
${spring.application.name} |
命名当前服务 |
重量 | spring.cloud.nacos.discovery.weight |
1 |
取值范围:1~100,数值越大,权重越大 |
网卡名称 | spring.cloud.nacos.discovery.network-interface |
如果不指定IP地址,则注册的IP地址为网卡的IP地址。如果也没有指定,则默认使用第一块网卡的 IP 地址。 | |
注册IP地址 | spring.cloud.nacos.discovery.ip |
最高优先级 | |
注册端口 | spring.cloud.nacos.discovery.port |
-1 |
默认会自动检测。不需要配置。 |
命名空间 | spring.cloud.nacos.discovery.namespace |
一个典型的场景是隔离不同环境的服务注册,例如测试和生产环境之间的资源(配置、服务等)隔离 | |
访问密钥 | spring.cloud.nacos.discovery.access-key |
阿里云账号accesskey | |
密钥 | spring.cloud.nacos.discovery.secret-key |
阿里云账号秘钥 | |
元数据 | spring.cloud.nacos.discovery.metadata |
您可以以 Map 格式为您的服务定义一些元数据 | |
日志文件名 | spring.cloud.nacos.discovery.log-name |
||
集群名称 | spring.cloud.nacos.discovery.cluster-name |
DEFAULT |
Nacos的集群名称 |
端点 | spring.cloud.nacos.discovery.endpoint |
特定区域中某项服务的域名。您可以使用此域名动态检索服务器地址 | |
是否集成 Ribbon | ribbon.nacos.enabled |
true |
在大多数情况下设置为 true |
启用 Nacos Watch | spring.cloud.nacos.discovery.watch.enabled |
true |
设置为 false 监视 |
Nacos官方文档
在Linux虚拟机上部署
Linux配置环境
1)64 bit OS Linux/Unix/Mac,推荐使用Linux系统
2)64 bit JDK 1.8+
3)Maven 3.2.x+
4)nginx 作为负载均衡(SLB)
https://github.com/alibaba/nacos/releases
选择 2.0.1 版本
选择Linux系统的安装包
千万要注意,Nacos下载的是2.0+版本的,端口号不能相连在一起,必须+2,如:7002、7004、7006
最后面有张图就是连在一起的报错结果!然后又得重新开始
当然,跟着我的步骤就好了!
# 解压三次 并附带它们的端口号
# nacos-7070
[root@vinjcent nacos]# tar -zxvf nacos-server-2.0.1.tar.gz
[root@vinjcent nacos]# mv nacos nacos-7070
# nacos-7080
[root@vinjcent nacos]# tar -zxvf nacos-server-2.0.1.tar.gz
[root@vinjcent nacos]# mv nacos nacos-7080
# nacos-7090
[root@vinjcent nacos]# tar -zxvf nacos-server-2.0.1.tar.gz
[root@vinjcent nacos]# mv nacos nacos-7090
解压完后的三个文件显示
1)修改nacos-7070的application.properties文件
vim ./nacos-7070/conf/application.properties
在window版的nacos中,该目录nacos-server-2.0.1\nacos\conf
下有一个nacos-mysql.sql文件
在数据库中创建一个名为 nacos 的数据库,执行这个 sql 文件(使用的是Linux虚拟机下的数据库)
5)设置 nacos 集群的关联端口
在/usr/local/nacos/nacos-7070/conf
下复制一份文件
cp cluster.conf.example cluster.conf # 复制一份名为cluster.conf的文件
编辑cluster.conf文件
vim cluster.conf
编辑内容如下
回到\bin
目录下编辑startup.sh文件(可以设置,可以不设置)
vim startup.sh
由于个人虚拟机内存较小,如果你的虚拟机够大,就不需要更改了
更改内容如图所示
6)测试nacos-7070是否可以启动
# 在Linux虚拟机中开启端口7070
[root@vinjcent bin]# firewall-cmd --zone=public --add-port=7070/tcp --permanent
# 重启防火墙
systemctl restart firewalld.service
在\bin
目录下启动startup.sh
./startup.sh
使用命令查看是否启动成功(如果这里报错了,就是因为你是用的是你本机的数据库,需要在Linux系统安装数据库!)
[root@vinjcent bin]# tail -f ../logs/start.out
这个有点长,慢慢等
启动成功!
在本机(自己电脑)访问 Linux IP:7070/nacos/index.html#/login
配置成功!
7)将/nacos-7070/conf/
路径下的application.properties文件分别给nacos-7080、nacos-7090 进行覆盖
# 覆盖nacos-7080/conf/application.properties文件
[root@vinjcent nacos]# cp ./nacos-7070/conf/application.properties ./nacos-7080/conf/application.properties
# 覆盖nacos-7090/conf/application.properties文件
[root@vinjcent nacos]# cp ./nacos-7070/conf/application.properties ./nacos-7090/conf/application.properties
8)将/nacos-7070/conf/
路径下的cluster.conf文件也分别复制到nacos-7080、nacos-7090
[root@vinjcent nacos]# cp ./nacos-7070/conf/cluster.conf ./nacos-7080/conf/cluster.conf
[root@vinjcent nacos]# cp ./nacos-7070/conf/cluster.conf ./nacos-7090/conf/cluster.conf
9)将nacos-7070/bin/
路径下的startup.sh文件也分别给nacos-7080、nacos-7090进行覆盖
[root@vinjcent nacos]# cp ./nacos-7070/bin/startup.sh ./nacos-7080/bin/startup.sh
cp: overwrite ‘./nacos-7080/bin/startup.sh’? y
[root@vinjcent nacos]# cp ./nacos-7070/bin/startup.sh ./nacos-7090/bin/startup.sh
cp: overwrite ‘./nacos-7090/bin/startup.sh’? y
10)分别修改nacos-7080、nacos-7090的application.properties文件里的端口号
[root@vinjcent nacos]# vim ./nacos-7080/conf/application.properties
[root@vinjcent nacos]# vim ./nacos-7090/conf/application.properties
nacos-7080
nacos-7090
11)在Linux虚拟机中,开启防火墙端口7080、7090
# 开启7080端口
[root@vinjcent nacos]# firewall-cmd --zone=public --add-port=7080/tcp --permanent
# 开启7090端口
[root@vinjcent nacos]# firewall-cmd --zone=public --add-port=7090/tcp --permanent
# 重启防火墙
[root@vinjcent nacos]# systemctl restart firewalld.service
firewall-cmd --list-ports # 只看端口信息
可以看到已经将7080、7090端口开启
12)分别启动nacos-7080、nacos-7090的startup.sh
文件
[root@vinjcent nacos]# sh ./nacos-7080/bin/startup.sh
[root@vinjcent nacos]# sh ./nacos-7090/bin/startup.sh
下面这个图片就是使用7001、7002、7003相连端口报错的信息,所以我不得不重新修改端口再来一遍(请勿模仿!)
个人建议下载一个XShell 和 Xftp 方便文件的修改
访问
http://虚拟机的IP:7080/nacos/index.html#/login
http://虚拟机的IP:7090/nacos/index.html#/login
用户名、密码都是nacos
登陆后查看红色区域的页面
出现该页面即搭建Nacos集群成功!
修改springcloud-provider-dept-8001模块下的application.yml
文件
IRule
是所有负载均衡策略的父接口,里边的核心方法就是choose方法,用来选择一个服务实例
AbstractLoadBalancerRule
AbstractLoadBalancerRule是一个抽象类,类里主要定义了一个ILoadBalancer,就是我们上文所说的负载均衡器,用于定义它的目的主要是辅助负责均衡策略选取合适的服务端实例
RandomRule
是一种负载均衡策略就是随机选择一个服务实例,看源码可以知道,在 RandomRule 的无参构造方法中初始化了一个 Random 对象,然后在它重写的 choose 方法又调用了,choose(ILoadBalancer lb.Obect key) 这个重载的 choose 方法,在这个重载的 choose 方法中,每次利用 random 对象生成一个不大于服务实例总数的随机数,并将该数作为下标所以获取一个服务实例
RoundRobinRule
RoundRobinRule 这种负载均衡策略叫做线性轮询负载均衡策略。这个类的 choose(ILoadBalancer lb.Objectkey) 整体逻辑是这样的:开启一个计数器 count,在 while 循环中遍历服务清单,获取清单之前先通过 incrementAndGetModulo 方法获取一个下标,这个下标是一个不断自增的数,先加1然后和服务清单总数取模之后获取到的(所以这个下标从来不会越界),拿着下标再去服务清单列表中取服务,每次循环计数器都会加1,如果连续10次都没有取到服务,则会报一个警告No available alive servers after 10 tries from load balancer:ILoadBalancer
RetryRule (在轮询的基础上进行重试)
这种负载均衡策略带有重试功能。首先 RetryRule 中又定义了一个subRule,它的实现类是 RoundRobinRule,然后在 RetryRule 的 choose(ILoadBalancer lb,Object key) 方法中,每次还是采用 RoundRobinRule 中的 choose 规则来选择一个服务实例,如果选到的实例正常就返回,如果选择的服务实例为null或者已经失效,则在失效时间 deadline 之前不断的进行重试 (重试时获取服务的策略还是 RoundRobinRule 中定义的策略),如果超过了 deadline 还是没取到则会返回一个null
WeightedResponseTimeRule(权重–nacos的NacosRule,Nacos还扩展了一个自己的基于配置的权重扩展)
WeightedResponseTimeRule 是 RoundRobinRule 的一个子类,在 WeightedResponseTimeRule 中对 RoundRobinRule 的功能进行了扩展, WeightedResponseTimeRule 中会根据每一个实例的运行情况来给计算出该实例的一个权重,然后在挑选实例的时候则根据权重进行挑选,这样能够实现更优的实例调用。WeightedResponseTimeRule 中有一个名叫DynamicServerWeightTask的定时任务 默认情况下每隔30秒会计算一次各个服务实例的权重,权重的计算规则也很简单:如果一个服务的平均响应时间越短则权重越大,那么该服务实例被选中执行任务的概率也就越大
ClientConfigEnabledRoundRobinRule
ClientConfigEnabledRoundRobinRule 选择策略内部定义了 RoundRobinRule,choose 方法还是采用了 RoundRobinRule 的 choose 方法,所以它的选择策略和 RoundRobinRule的选择策略一致
BestAvailableRule
BestAvailableRule 继承自 ClientConfiqEnabledRoundRobinRule,它在ClientConficEnabledRoundRobinRule 的基础上主要增加了根据loadBalancerStats中保存的服务实例的状态信息来过滤掉失效的服务实例的功能,然后顺便找出并发请求最小的服务实例来使用,然而 loadBalancerStats 有可能为null,如果 loadBalancerStats 为null,则 BestAvailableRule 将采用它的父类即 ClientConfigEnabledRoundRobinRule 的服务选取策略(线性轮询)
ZoneAvoidanceRule(默认规则,复合判断server所在区域的性能和server的可用性选择服务器)
ZoneAvoidanceRule 是 PredicateBasedRule 的一个实现类,只不过这里多了一个过滤条件,ZoneAvoidanceRule 中的过滤条件是以 ZoneAvoidancePredicate 为主过滤条件和以 AvailabilityPredicate 为次过滤条件组成的一个叫做 CompositePredicate 的组合过滤条件,过滤成功之后,继续采用线性轮询(RoundRobinRule)的方式从过滤结果中选择一个出来
AvailabilityFilteringRule(先过滤掉故障实例,再选择并发较小的实例)
过滤掉一直连接失败的被标记为 circuit tripped 的后端 Server,并过滤掉那些高并发的后端Server或者使用一个 AvailabilityPredicate 来包含过滤server的逻辑,实质上就是检查status里记录的各个Server的运行状态
方式一(使用注解)
1)在消费端springcloud-consumer-dept-80模块下,创建一个RibbonStrategy.class类,注入一个IRule的bean
注意该包位置必须不在主启动类的包下,因为会被@ComponentScan注解扫描到
测试
@RibbonClients(value = {
@RibbonClient(name = "ProviderApplication1",configuration = xxx.class) //随机策略
@RibbonClient(name = "ProviderApplication2",configuration = xxx.class) //重试策略
})
public class ConfigBean {
...
}
@RibbonClients指定某个服务提供方使用的均衡策略
被@ComponentScan
注解扫描到,对于一个类配置了多个@RibbonClient
,相当于全局配置的效果,比如上面一个是随机策略,一个是重试策略,如果将此类放在与主启动类同一个包下,重试策略将会失效,使得所有服务提供者使用同一个策略
如图所示
2)修改模块springcloud-consumer-dept-80中的application.yml
文件
server:
port: 80
spring:
application:
name: springcloud-consumer-dept
# 配置nacos
cloud:
nacos:
# nacos 服务地址
server-addr: 192.168.159.100:7070,192.168.159.100:7080,192.168.159.100:7090
discovery:
username: nacos # nacos用户名
password: nacos
namespace: public # 分隔开发环境和测试环境使用
3)在主启动类上加上注解@RibbonClients
@SpringBootApplication
@RibbonClients(value = {
/**
* name 提供者应用名称
* configuration 使用的是哪个负载均衡配置类
*/
@RibbonClient(name = "springcloud-provider-dept",configuration = RibbonStrategy.class)
})
public class SpringCloud_Consumer_80 {
public static void main(String[] args) {
SpringApplication.run(SpringCloud_Consumer_80.class,args);
}
}
4)复制springcloud-provider-dept-8001模块,改名为springcloud-provider-dept-8002、springcloud-provider-dept-8003
将其application.yml文件的端口分别改为8002、8003
5)
在Linux已经启动了Nacos的条件下,访问网页
http://192.168.159.100:7070/nacos/index.html#/ 或
http://192.168.159.100:7080/nacos/index.html#/ 或
http://192.168.159.100:7090/nacos/index.html#/ 其中一个
启动springcloud-provider-dept-8001
启动springcloud-provider-dept-8002
启动springcloud-provider-dept-8003
启动springcloud-consumer-dept-80
不断访问http://localhost/consumer/dept/msg
查看输出结果
方式二(使用配置文件)
1)将springcloud-consumer-dept-80项目下的主启动类的@RibbonClients
注解注释掉
2)在该项目下修改application.yml
文件
server:
port: 80
spring:
application:
name: springcloud-consumer-dept
# 配置nacos
cloud:
nacos:
# nacos 服务地址
server-addr: 192.168.159.100:7070,192.168.159.100:7080,192.168.159.100:7090
discovery:
username: nacos # nacos用户名
password: nacos
namespace: public # 分隔开发环境和测试环境使用
# 调用的服务提供方应用名称
springcloud-provider-dept:
ribbon:
# 复制NacosRule类的引用路径,使用的是NacosRule权重策略
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
3)访问nacos注册中心,修改两个提供者的权重
如果报错
caused: errCode: 500, errMsg: do metadata operation failed ;caused: com.alibaba.nacos.consistency.exception.ConsistencyException: com.alibaba.nacos.core.distributed.raft.exception.NoLeaderException: The Raft Group [naming_instance_metadata] did not find the Leader node;caused: com.alibaba.nacos.core.distributed.raft.exception.NoLeaderException: The Raft Group [naming_instance_metadata] did not find the Leader node;
解决办法
在Linux安装的nacos文件下删除以下的三个文件即可
而如果使用windows则删除这两个文件
再次修改,刷新页面
修改成功!
4)测试
启动springcloud-consumer-dept-80模块
多次访问http://localhost/consumer/dept/msg
查看结果(权重越大,就越频繁调用该服务提供者)
通过实现IRule接口可以自定义负载策略,主要的选择服务逻辑是在choose方法中
1)在springcloud-consumer-dept-80模块的ribbon包下创建一个MyRibbon.class类
public class MyRibbon extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
ILoadBalancer loadBalancer = this.getLoadBalancer();
//获得当前请求服务的实例
List<Server> reachableServers = loadBalancer.getReachableServers();
//根据服务的数量,获取范围内的随机数
int random = ThreadLocalRandom.current().nextInt(reachableServers.size());
//根据随机数获取服务
Server server = reachableServers.get(random);
//可以判断获取该服务是否存活
//不存活就继续获取
return server;
}
}
2)将其配置到Ribbon策略中
方式一
方式二
application.yml
修改引用路径3)测试
重新启动springcloud-consumer-dept-80模块
不断访问http://localhost/consumer/dept/msg,看结果
如果自己想写一个负载均衡配置策略,可以观摩一下RandomRule.clss类策略
Ribbon的饿加载模式
在使用Spring Cloud的Ribbon或Feign来实现服务调用的时候,如果机器或网络环境等原因不是很好的话,有时候会发现这样一个问题:服务消费方调用服务提供方接口的时候,第一次请求经常会超时,而之后的调用就没有问题了
问题的原因
造成第一次服务调用出现失败的原因主要是Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初始化的,而是在调用的时候才会去创建相应的Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建RibbonClient的时间,这样一来如果创建时间速度较慢,同时设置的超时时间又比较短的话,很容易就会出现上面所描述的显现
解决的方法
ribbon:
eager-load:
# 开启饥饿加载
enabled: true
# 配置服务名为springcloud-provider-dept使用ribbon饥饿加载,多个服务可以使用","分隔
clients: springcloud-provider-dept