093:Nacos实现服务注册与发现
1 SpringCloudalibaba与SpringCloud区别
课程内容:
- 微服务架构核心设计思想服务治理
- 基于Nacos实现服务注册与发现
- 使用Rest模版实现RPC调用
- 手写Rpc客户端负载均衡算法
SpringCloud与SpringCloudAlibaba的区别
SpringCloidAlibaba实际上对SpringCloud实现了扩展组件,能够完美整合到SpringCloud开发的组件中。
- nacos 分布式注册中心、分布式配置中心,SpringCloud中Eureka+Config组合;
- 如果使用了SpringCloudAlibaba,建议最好使用alibaba整个体系的产品。
总结:SpringCloud Alibaba实际上对SpringCloud做扩展组件开发:naocs、seata分布式事务解决框架、SchedulerX、Alibaba Cloud OSS等,目的是推广阿里云的产品。
2 微服务服务治理核心概念
Nacos分布式注册与发现功能|分布式配置中心
产生背景:rpc远程调用中,服务的url的治理。
Rpc的远程调用框架:HttpClient、gprc、dubbo、rest、openfeign等。
传统的rpc远程调用中存在哪些问题?
- 超时的问题
- 安全的问题
- 服务与服务之间URL地址管理
在微服务架构通讯中,服务与服务之间依赖关系非常大,如果通过传统的方式管理服务的url地址,一旦地址发生变化的情况下,还需要人工修改rpc远程调用地址。
每个服务的url地址管理非常复杂,所以这时候采用服务url治理技术,可以实现对整个服务实现动态服务注册与发现、本地负载均衡、容错等。
3 传统服务注册中心实现方式
rpc远程调用中,地址中包括:域名和端口号/调用的方法名称
eg: 192.168.212.110:8080/getUser
传统服务注册中心实现方式:基于数据库形式实现服务url治理,把每个服务地址信息和端口号人工存放到数据库表中。
Id serviceId ip 端口号
1 Mayikt-member 192.168… 8082
2 Mayikt-member 192.168… 8081
缺点:维护成本非常高、没有完全绝对实现动态化智能化
4 分布式注册中心的实现原理
思考是否有更好的方案:微服务中的注册中心。
整个微服务架构中最为核心的组件肯定是注册中心。
注册中心:实际就是存放整个微服务中服务的地址信息,能够实现动态感知。
注册中心实现方式:Dubbo依赖Zookeeper、Eureka 、Consul、Nacos、Redis、数据库
注册中心实现原理:
服务注册:提供服务接口地址信息存放
生产者:提供接口被其他的服务调用
消费者:调用接口实现消费。
服务注册原理实现:
- 生产者启动的时候,将“key=服务的名称,value=ip和端口号”注册到微服务注册中心上。
Mayikt-member 192.168.212.110:8080
Mayikt-member 192.168.212.110:8081 - 注册存放服务地址列表类型: key唯一,列表是list集合。
Map<Key,List(String)>
{Mayikt-member:[“192.168.212.110:8080”,“ 192.168.212.110:8081”]} - 消费者从注册中心上根据服务名称查询服务地址列表(集合)
Mayikt-member = [“192.168.212.110:8080”,“ 192.168.212.110:8081”] - 消费者获取到集群列表之后,采用负载均衡器选择一个地址实现rpc远程调用。
5 Nacos的基本的介绍
Nacos的基本的介绍
Nacos可以实现分布式服务注册与发现/分布式配置中心框架
官网的介绍: https://nacos.io/zh-cn/docs/what-is-nacos.html
默认的端口号:8848 访问地址http://127.0.0.1:8848/nacos
默认的账号和密码:nacos nacos
6 使用命令形式对Nacos实现注册
模拟实现服务注册与发现
服务注册:
http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=meite-member &ip=192.168.212.110&port=8080
http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=meite-member &ip=192.168.212.110&port=8081
服务查询
http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=meite-member
测试效果:
7 创建生产者实现服务的注册
Nacos整合SpringCloud
Maven依赖信息
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<!-- springboot 整合web组件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
</dependencies>
application.yml
spring:
application:
## 服务名称
name: mayikt-member
cloud:
nacos:
discovery:
## nacos注册地址
server-addr: 127.0.0.1:8848
server:
port: 8080
会员服务(生产者)
@RestController
public class MemberService {
@Value("${server.port}")
private String serverPort;
/**
* 会员服务提供接口被订单服务调用
*
* @param userId
* @return
*/
@RequestMapping("/getUser")
public String getUser(Integer userId) {
return "每特教育|蚂蚁课堂,端口号为:" + serverPort;
}
}
@SpringBootApplication
public class AppMember {
public static void main(String[] args) {
SpringApplication.run(AppMember.class);
}
}
8 创建消费者调用生产者服务
application.yml
spring:
application:
## 服务名称
name: mayikt-order
cloud:
nacos:
discovery:
## nacos注册地址
server-addr: 127.0.0.1:8848
server:
port: 8090
订单服务(消费者)
@RestController
public class OrderService {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
/**
* 订单服务调用会员服务接口
*
* @return
*/
@RequestMapping("/orderToMember")
public Object orderToMember() {
// 1.根据服务名称从注册中心上获取集群列表地址
List<ServiceInstance> instances = discoveryClient.getInstances("mayikt-member");
// 2.列表中任意选择实现本地rpc调用 rest
ServiceInstance serviceInstance = instances.get(0);
String result = restTemplate.getForObject(serviceInstance.getUri() + "/getUser", String.class);
return "订单调用会员返回结果:" + result;
}
}
@SpringBootApplication
public class AppOrder {
public static void main(String[] args) {
SpringApplication.run(AppOrder.class);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
RestTemplate不是SpringCloud写的,本身Spring支持Http协议调用。
测试结果:
9 纯手写本地负载均衡器轮询算法
从注册中心获取服务集群的列表,从列表选择一个,负载均衡算法有哪些:
一致性hash计算、轮询、权重、随机
采用什么设计模式:策略模式
轮询算法:访问次数%集群size。
假设有两台服务器集群,1%2=1;2%2=0;3%2=1;4%2=0
public interface LoadBalancer {
/**
* 从注册中心集群列表中获取单个地址
* @param serviceInstances
* @return
*/
ServiceInstance getSingleAddress(List<ServiceInstance> serviceInstances);
}
@Component
public class RotationLoadBalancer implements LoadBalancer {
// 从0开始计数
private AtomicInteger atomicInteger = new AtomicInteger(0);
@Override
public ServiceInstance getSingleAddress(List<ServiceInstance> serviceInstances) {
int index = atomicInteger.incrementAndGet() % serviceInstances.size();
return serviceInstances.get(index);
}
}
@RestController
public class OrderService {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancer loadBalancer;
/**
* 订单服务调用会员服务接口
*
* @return
*/
@RequestMapping("/orderToMember")
public Object orderToMember() {
// 1.根据服务名称从注册中心上获取集群列表地址
List<ServiceInstance> instances = discoveryClient.getInstances("mayikt-member");
// 采用负载均衡算法
ServiceInstance singleAddress = loadBalancer.getSingleAddress(instances);
URI rpcMemberUrl = singleAddress.getUri();
String result = restTemplate.getForObject(rpcMemberUrl + "/getUser", String.class);
return "订单调用会员返回结果:" + result;
}
}