Nacos服务注册中心
Nacos基本介绍
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
Nacos官网: Nacos 配置中心简介, Nacos 是什么 | Nacos 官网
微服务架构思想-
新项目 Nacos 服务注册中心/分布式配置中心
动态化--Nacos2.0
1.新产品特性
将gRPC应用到Nacos中,并支持多项功能的连接管理
支持负载均衡
Jraft支持命名模块的元数据操作
支持基本连接限制。
支持健康检查
支持升级和降级
2.增强功能
异步执行一些耗时的操作。
多语言SDK
公制
完全支持注册自定义实例
用户首次订阅服务时支持单推
通过阈值支持健康保护
3.重构
命名客户端重构网络代理
重构和调整v1 openAPI
NacosServer端环境搭建
服务注册中心 如何设计
- 服务注册中心 web页面 管理服务注册信息内容
- 服务注册中心Api接口 服务注册
- 提供jar 能够被客户端支持 实现服务注册。
NacosServer 服务注册中心 web页面和服务注册中心Api接口
Nacos 阿里巴巴推出 java语言
Nacos 2.0 发布,性能提升 10 倍
本节课采用Nacos2.0分享。
Nacos 下载地址:Releases · alibaba/nacos · GitHub
进入到:nacos-server-2.0.3\nacos\bin startup.cmd 启动即可。
访问:127.0.0.1:8848/nacos
默认账户密码:nacos/nacos
服务的注册中心不需要外网访问---
Nacos2.0 启动常见错误
- 默认Nacos是集群方式启动,初学者建议先改成单机版本。
修改:D:\path\cloud\nacos\bin startup.cmd 改成:set MODE="standalone"
在双击启动: startup.cmd
手动注册服务与发现
1.实现服务注册
发送post请求:
'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=boyatop-member&ip=20.18.7.10&port=8080'
2.实现服务发现
http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=boyatop-member
详细步骤操作:Nacos 快速开始 | Nacos 官网
注意:发送请求类型是为Post类型
一直刷新观察控制台,大概要等到30s才会把实例剔除(大概15s设置不健康)
该知识点设计服务续约问题
超纲内容
如何查看源码呢?
- spring架构体系
- 通过日志分析法
NacosClient环境搭建
Maven依赖
| <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>***.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.66</version> </dependency> <dependency> <groupId>org.apache.http***ponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.5</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>0.2.2.RELEASE</version> </dependency> </dependencies> |
配置文件
| server: port: 8080 spring: application: name: boyatop-member #服务名称 在 注册中心展示服务名称 -- cloud: nacos: discovery: server-addr: 127.0.0.1:8848 |
启动项目
| @SpringBootApplication public class AppMember { public static void main(String[] args) { SpringApplication.run(AppMember.class); } /** * 会员 8080 8081 * 订单 8070 8071 */ } |
启动客户端项目,查看该日志 服务注册成功。
查看nacos服务器端:
Resttemplate
RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。RestTemplate 继承自 InterceptingHttpA***essor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现。接下来我们就来看看这些操作方法的使用。
底层是基于HttpClient封装的。
整合Resttemplate
| @Bean public RestTemplate restTemplate() { return new RestTemplate(); } @Autowired private RestTemplate restTemplate; @RequestMapping("/orderToMember") public String orderToMember() { // HttpClient 工具类 实现RPC远程调用 // String memberUrl = "http://192.168.75.1:9091/getMember"; /** * 根据服务名称 从注册中心 获取 会员的接口地址 * 服务提供 启动多个集群 * */ List<ServiceInstance> instances = discoveryClient.getInstances("boyatop-member"); ServiceInstance serviceInstance = instances.get(0); // 会员服务的ip和端口 String memberUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "getMember"; ResponseEntity<String> response = restTemplate.getForEntity(memberUrl, String.class); return "订单服务调用会员服务:" + response.getBody(); } |
本地负载均衡算法
消费者获取到注册中心多个地址,到底应该选择那个地址呢?
负载均衡算法:轮询、随机、权重等。
| public interface LoadBalance { /** * 使用负载均衡算法 实现轮询机制 * @param serviceId */ ServiceInstance getInstances(String serviceId); } |
轮询算法
轮询算法实现思路:
例如在集合中 多个接口地址
[192.168.110.1:8080,192.168.110.2:8081]
0 1
第一次访问:1%2=1
第二次访问:2%2=0
第三次访问:3%2=1
第四次访问:4%2=0
[192.168.110.1:8080,192.168.110.2:8081,192.168.110.2:8082]
0 1 3
第一次访问:1%3=1
第二次访问:2%3=2
第三次访问:3%3=0
第四次访问:4%3=1
第五次访问:5%3=2
第六次访问:6%3=0
| @***ponent public class RoundLoadBalance implements LoadBalance { @Autowired private DiscoveryClient discoveryClient; private AtomicInteger atomi***ount = new AtomicInteger(-1); @Override public ServiceInstance getInstances(String serviceId) { List<ServiceInstance> instances = discoveryClient.getInstances("boyatop-member"); if (instances == null || instances.size() == 0) { return null; } // 轮询算法实现 int index = atomi***ount.incrementAndGet() % instances.size(); return instances.get(index); } } |
随机算法
| @***ponent public class RandomLoadBalance implements LoadBalance { @Autowired private DiscoveryClient discoveryClient; public ServiceInstance getInstances(String serviceId) { List<ServiceInstance> instances = discoveryClient.getInstances("boyatop-member"); if (instances == null || instances.size() == 0) { return null; } Random r = new Random(); int index = r.nextInt(instances.size() ); return instances.get(index); } } |
故障转移算法
| @RequestMapping("/orderToMember") public String orderToMember() { // HttpClient 工具类 实现RPC远程调用 // String memberUrl = "http://192.168.75.1:9091/getMember"; /** * 根据服务名称 从注册中心 获取 会员的接口地址 * 服务提供 启动多个集群 */ // List<ServiceInstance> instances = discoveryClient.getInstances("boyatop-member"); // ServiceInstance serviceInstance = instances.get(0); // ServiceInstance serviceInstance = roundLoadBalance.getInstances("boyatop-member"); // 会员服务的ip和端口 List<ServiceInstance> instances = discoveryClient.getInstances("boyatop-member"); for (int i = 0; i < instances.size(); i++) { ServiceInstance serviceInstance = instances.get(i); String memberUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "getMember"; try { ResponseEntity<String> response = restTemplate.getForEntity(memberUrl, String.class); if (response == null) { continue; } return "订单服务调用会员服务:" + response.getBody(); } catch (Exception e) { log.error("<e:{}>", e); } } return "fail"; } |
轮询算法
[192.168.110.1:8080,192.168.110.1:8081]
Index=0=192.168.110.1:8080
Index=1=192.168.110.1:8081
权重算法如何 比例:
1:1
第一次访问 192.168.110.1:8080
第二次访问 192.168.110.1:8081
轮询机制 默认权重1:1
2:1
[192.168.110.1:8080,192.168.110.1:8081]
权重比例 2:1
Index=0 192.168.110.1:8080 权重=2
Index=1 192.168.110.1:8081 权重=1
2(index=0):1(index=1)
第一次访问 192.168.110.1:8080
第二次访问 192.168.110.1:8080
第三次访问 192.168.110.1:8081
第四次访问 192.168.110.1:8080
第五次访问 192.168.110.1:8080
第六次访问 192.168.110.1:8081
Index=0 192.168.110.1:8080 权重=2
Index=1 192.168.110.1:8081 权重=1
2(index=0):1(index=1)
权重的底层实现逻辑
【192.168.110.1:8080,192.168.110.1:8080
192.168.110.1:8081】
第一次访问1%3=1 ===192.168.110.1:8080
第二次访问2%3=2 ===192.168.110.1:8081
第三次访问3%3=0 ===192.168.110.1:8080
第四次访问4%3=1 ===192.168.110.1:8080
第五次访问5%3=2 ===192.168.110.1:8081
第六次访问6%3=0 ===192.168.110.1:8080
第七次访问7%3=1 ===192.168.110.1:8080
第八次访问8%3=1 ===192.168.110.1:8081
| @***ponent public class WeightLoadBalance implements LoadBalance { @Autowired private DiscoveryClient discoveryClient; private AtomicInteger atomicInteger = new AtomicInteger(-1); @Override public ServiceInstance getInstances(String serviceId) { List<ServiceInstance> instances = discoveryClient.getInstances(serviceId); if (instances == null) { return null; } ArrayList<ServiceInstance> weightInstances = new ArrayList<>(); instances.forEach(serviceInstance -> { Map<String, String> metadata = serviceInstance.getMetadata(); Double weight = Double.parseDouble(metadata.get("nacos.weight")); for (int i = 0; i < weight; i++) { weightInstances.add(serviceInstance); } }); int index = atomicInteger.incrementAndGet() % weightInstances.size();// ==i++ return weightInstances.get(index); } } |