目录
1.安装配置Nacos
1.1下载
1.2解压安装
1.3配置
1.4访问
2.服务注册
2.1添加依赖
2.2配置
2.3访问
3.Nacos服务分级存储模型
4.配置集群
4.1给服务提供者user-server配置集群
4.2给服务消耗者order-server配置集群
配置集群
同集群策略NacosRule
5.根据权重负载均衡
6.环境隔离
6.1namespace
6.1.1创建命名空间namespace
6.1.2配置使用新namespace
7.临时实例
8.nacos注册中心细节分析与eureka对比
Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富,在国内受欢迎程度较高。
官网地址:home
1.安装配置Nacos
1.1下载
1.2解压安装
将压缩包解压到任意非中文目录下
1.3配置
以下配置可不修改
- 打开配置文件(nacos安装目录 -> conf -> application.properties)
- 修改数据源配置
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root
- 在数据库中执行sql文件(nacos安装目录 -> conf -> nacos-mysql.sql)
- 在nacos安装目录下的bin目录中运行startup.cmd,默认是集群模式启动,单机模式启动需要指定模式,在bin目录下执行命令:
startup.cmd -m standalone
通过修改nacos.core.auth.enabled为true,就可以开启nacos认证。开启后客户端必须配置username、password
1.4访问
启动后访问http://192.168.31.119:8848/nacos/index.html,输入用户名和密码默认都是nacos
2.服务注册
Nacos本身就是一个 SprintBoot 项目,直接使用,不再需要去额外搭建一个像Eureka的注册中心。
2.1添加依赖
代码地址:cloud-demo-nacos: springcloud使用nacos
在cloud-demo父工程中添加spring-cloud-alibaba的版本管理依赖
<!--nacos的管理依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.9.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency>
在需要的服务user-service 和 order-service 中的pom文件中引入nacos-discovery依赖
<!--nacos客户端依赖包--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--负载均衡依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency>
2.2配置
在需要的服务user-service 和 order-service 中的application.yml文件中配置nacos服务地址
spring: cloud: nacos: server-addr: localhost:8848 #nacos服务地址
启动服务后,在nacos注册中心可看到
2.3访问
访问http://localhost:8080/order/101,负载均衡。
3.Nacos服务分级存储模型
每个服务可以有多个实例,例如我们的 user-server,可以有:
- 127.0.0.1:8081
- 127.0.0.1:8082
- 127.0.0.1:8083
为了防止机器出现问题,我们会将一个访问的多个实例部署到多个机房,例如:
- 127.0.0.1:8081,在杭州机房
- 127.0.0.1:8082,在杭州机房
- 127.0.0.1:8083,在上海机房
Nacos就将同一机房内的同一个服务的多个实例,划分为一个集群。
Nacos服务分级存储模型
- 一级是服务,例如userservice
- 二级是集群,例如杭州或上海
- 三级是实例,例如杭州机房的某台部署了user-server的服务器
微服务互相访问时,应该尽可能选择本地集群的服务,跨集群调用延迟较高,本地访问速度更快。当本集群内不可用时,才访问其它集群。例如:杭州机房内的order-server应该优先访问同机房的user-server。
4.配置集群
4.1给服务提供者user-server配置集群
修改user-server的application.yml文件,添加集群配置:
spring: cloud: nacos: server-addr: localhost:8848 #nacos服务地址 discovery: cluster-name: HZ # 配置集群名称
启动两个user-server实例后,将HZ改为SH,再启动第三个实例,查看nacos中心
4.2给服务消耗者order-server配置集群
配置集群
spring: cloud: nacos: server-addr: localhost:8848 #nacos服务地址 discovery: cluster-name: HZ # 配置集群名称
这样优先会寻找与自己同集群的服务
同集群策略NacosRule
使用Ribbon时这样配置:
第一种使用配置文件
user-server: # 给需要调用的微服务配置负载均衡规则 ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #同集群优先
第二种在配置类注入bean
@Configuration
public class OrderConfiguration {
/**
* 创建RestTemplate并注入spring容器
* @return
*/
@Bean
@LoadBalanced //开启负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
public IRule iRule(){
//默认为轮询规则,这里自定义为同集群规则
return new NacosRule();
}
}
使用nacos+loadbalance:
优先会寻找与自己同集群的服务,否则根据随机权重选择
修改配置类
package cn.itcast.order.config;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.client.RestTemplate;
@Configuration
@LoadBalancerClients(defaultConfiguration = {OrderConfiguration.class})
public class OrderConfiguration {
/**
* 创建RestTemplate并注入spring容器
* @return
*/
@Bean
@LoadBalanced //开启负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new NacosSameClusterWeightedRule(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
自定义策略类:
package cn.itcast.order.config;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.utils.StringUtils;
import com.alibaba.nacos.client.naming.core.Balancer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
@Slf4j
// 自定义负载均衡实现需要实现 ReactorServiceInstanceLoadBalancer 接口 以及重写choose方法
public class NacosSameClusterWeightedRule implements ReactorServiceInstanceLoadBalancer {
// 注入当前服务的nacos的配置信息
@Resource
private NacosDiscoveryProperties nacosDiscoveryProperties;
// loadbalancer 提供的访问当前服务的名称
final String serviceId;
// loadbalancer 提供的访问的服务列表
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
public NacosSameClusterWeightedRule(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
}
/**
* 服务器调用负载均衡时调的方法
* 此处代码内容与 RandomLoadBalancer 一致
*/
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next().map((serviceInstances) -> {
return this.processInstanceResponse(supplier, serviceInstances);
});
}
/**
* 对负载均衡的服务进行筛选的方法
* 此处代码内容与 RandomLoadBalancer 一致
*/
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
/**
* 对负载均衡的服务进行筛选的方法
* 自定义
* 此处的 instances 实例列表 只会提供健康的实例 所以不需要担心如果实例无法访问的情况
*/
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
return new EmptyResponse();
}
// 获取当前服务所在的集群名称
String currentClusterName = nacosDiscoveryProperties.getClusterName();
System.out.println("-----------");
System.out.println(currentClusterName);
System.out.println("------------");
// 过滤在同一集群下注册的服务 根据集群名称筛选的集合
List<ServiceInstance> sameClusterNameInstList = instances.stream().filter(i-> StringUtils.equals(i.getMetadata().get("nacos.cluster"),currentClusterName)).collect(Collectors.toList());
System.out.println("-----------");
for (ServiceInstance serviceInstance : sameClusterNameInstList) {
System.out.println(serviceInstance);
}
System.out.println("------------");
ServiceInstance sameClusterNameInst;
if (sameClusterNameInstList.isEmpty()) {
// 如果为空,则根据权重直接过滤所有服务列表
sameClusterNameInst = getHostByRandomWeight(instances);
} else {
// 如果不为空,则根据权重直接过滤所在集群下的服务列表
sameClusterNameInst = getHostByRandomWeight(sameClusterNameInstList);
}
return new DefaultResponse(sameClusterNameInst);
}
private ServiceInstance getHostByRandomWeight(List<ServiceInstance> sameClusterNameInstList){
List<Instance> list = new ArrayList<>();
Map<String,ServiceInstance> dataMap = new HashMap<>();
// 此处将 ServiceInstance 转化为 Instance 是为了接下来调用nacos中的权重算法,由于入参不同,所以需要转换,此处建议打断电进行参数调试,以下是我目前为止所用到的参数,转化为map是为了最终方便获取取值到的服务对象
sameClusterNameInstList.forEach(i->{
Instance ins = new Instance();
Map<String, String> metadata = i.getMetadata();
ins.setInstanceId(metadata.get("nacos.instanceId"));
ins.setWeight(new BigDecimal(metadata.get("nacos.weight")).doubleValue());
ins.setClusterName(metadata.get("nacos.cluster"));
ins.setEphemeral(Boolean.parseBoolean(metadata.get("nacos.ephemeral")));
ins.setHealthy(Boolean.parseBoolean(metadata.get("nacos.healthy")));
ins.setPort(i.getPort());
ins.setIp(i.getHost());
ins.setServiceName(i.getServiceId());
ins.setMetadata(metadata);
list.add(ins);
// key为服务ID,值为服务对象
dataMap.put(metadata.get("nacos.instanceId"),i);
});
// 调用nacos官方提供的负载均衡权重算法
Instance hostByRandomWeightCopy = ExtendBalancer.getHostByRandomWeightCopy(list);
// 根据最终ID获取需要返回的实例对象
return dataMap.get(hostByRandomWeightCopy.getInstanceId());
}
}
class ExtendBalancer extends Balancer {
/**
* 根据权重选择随机选择一个
*/
public static Instance getHostByRandomWeightCopy(List<Instance> hosts) {
return getHostByRandomWeight(hosts);
}
}
5.根据权重负载均衡
如果机器性能有差异,希望性能好的负载更多。
Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。权重在0-1之间,越大则访问频率越高,权重修改为 0,则该实例永远不会被访问。
配置方法:
1.在Nacos控制台,找到实例,点击编辑
2.将权重设置为0.1,测试可以发现8081被访问到的频率大大降低
6.环境隔离
6.1namespace
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离。
- Nacos 中可以有多个 namespace
- namespace 下可以有 group、service 等
- 不同 namespace 之间相互隔离,例如不同 namespace 的服务互相不可见
6.1.1创建命名空间namespace
在Nacos控制台可以创建namespace,用来隔离不同环境
填写一个新的命名空间信息
确定保存
6.1.2配置使用新namespace
在服务order-server的application.yml文件中配置
spring: cloud: nacos: server-addr: localhost:8848 #nacos服务地址 discovery: cluster-name: HZ # 配置集群名称 namespace: 6f7b1d2a-1470-43e2-9960-3f8aadf7181d #配置命名空间namespace的ID
在nacos控制台可看到
此时访问order-server:http://localhost:8080/order/101,因为 namespace 不同,会导致找不到 user-server,控制台会报错:
7.临时实例
Nacos 的服务实例分为两种类型:
- 临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型。
- 非临时实例:如果实例宕机,不会从服务列表剔除,也可以叫永久实例。
配置非临时实例
spring: cloud: nacos: server-addr: localhost:8848 #nacos服务地址 discovery: cluster-name: HZ # 配置集群名称 ephemeral: false # 设置为非临时实例
Nacos 集群默认采用AP方式(可用性),当集群中存在非临时实例时,采用CP模式(一致性);而 Eureka 采用AP方式,不可切换。
8.nacos注册中心细节分析与eureka对比
- 服务提供者启动时都会将自己的信息提交给注册中心保留
- 服务消费者需要服务时,找注册中心拉取服务信息并缓存,间隔30更新一次
- 服务消费者获得服务列表后,根据负载均衡,挑选一个实例,发起远程调用
- eureka对所有实例都是心跳检测;nacos对临时实例采用心跳检测,非临时实例主动询问
- eureka的服务消费者一直定时拉取服务列表;nacos平时也是定时拉取,在服务列表发生变更时会主动给向消费者推送消息