文章目录
注册中心,提供者,消费者
在微服务中有很多模块, 一个微服务(使用者)要怎么去调用另一个微服务(提供者)?
- 原始: 把url[域名,端口号,方法名]存放在数据库中, 调用的时候访问数据库
- 现在: 使用注册中心
注册中心实际上就是存储服务的地址信息
- Zookeeper, Eureka, Consul, Nacos
注册与发现
-
生产者启动的时候将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调用该服务
搭建Nacos环境
首先在github官网需要下载nacos服务器(类似下载zookeeper那样)
双击startup.cmd启动
如果报错可以启动单机模型, 而非集群模型(没有配置集群)
startup.cmd -m standalone
使用http://127.0.0.1:8848/nacos 访问nacos注册与配置中心网页版
需要登录, 默认用户名和密码都是nacos
Nacos常用api接口
服务注册(post)
curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'
服务发现(get)
curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName'
发布配置(post)
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"
获取配置(get)
curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"
在postman测试
- 注册
- 发现
host中发现了两个以上服务,说明是一个集群
版本说明
组件版本对应关系
与springboot和springcloud版本对应
在项目中整合Nacos
父工程maven依赖
nacos服务器端已经启动成功, 新建一个maven项目,添加依赖(版本非常重要):
nacos依赖名为 spring-cloud-starter-alibaba-nacos-discovery
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.boot.version>2.3.12.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR12</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.7.RELEASE</spring.cloud.alibaba.version>
</properties>
<dependencies>
<!-- springboot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<!-- springcloud依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>runtime</scope>
</dependency>
<!-- springcloud-alibaba依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
</dependency>
<!-- nacos依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring.cloud.alibaba.version}</version>
</dependency>
</dependencies>
服务提供者
maven: 仅需要继承父工程
yaml文件:
server:
port: 8001
spring:
application:
name: nacos-provider
cloud:
nacos:
server-addr: http://127.0.0.1:8848
# nacos的服务器地址
服务名spring.application.name一定要有, 而且名称中只能使用中划线不能使用下划线
启动主程序, 在nacos页面中的服务列表看到有新增的服务, 说明注册成功
暴露的接口服务
@RestController
public class ProviderService{
@Value(value="${server.port}")
private String port;
@GetMapping("/provider/get")
public String get(){
return "蚂蚁课堂 " + port;
}
}
服务消费者
maven: 一样只需要继承父工程
yaml: 除了端口号和应用名其他都和提供者一样
server:
port: 7001
spring:
application:
name: nacos-consumer
cloud:
nacos:
server-addr: http://127.0.0.1:8848
# nacos服务器的地址
调用提供者的接口
@RestController
public class ConsumerService{
@Resource
private DiscoveryClient discoveryClient;
@Resource(name = "myRestTemplate")
private RestTemplate restTemplate;
@GetMapping("/consumer/get")
public String get(){
List<ServiceInstance> instances = discoveryClient.getInstances("nacos-provider");
ServiceInstance serviceInstance = instances.get(0);
return restTemplate.getForObject(serviceInstance.getUri() + "/get", String.class);
}
@Bean(name = "myRestTemplate")
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
第二个服务提供者
除了端口号, 其他的都和第一个服务提供者一模一样
服务名称, 方法名称一定要一样, 这样才能和第一个提供者构成一个集群
运行成功后, 这时候就能在服务列表里看到两个提供者和一个消费者
手写负载均衡算法
有两个服务名和方法完全相同的提供者, 所以说消费者调用时(根据服务名调用)时会调用哪一个呢?
这就取决于负载均衡算法, 常用的算法有
- hash一致性
- 轮询
- 随机
- 权重
现在我们自己实现一个负载均衡器 , 并注入到spring容器
采用设计模式, 首先写主接口
@Component
public interface MyLoadBalancer{
ServiceInstance getSingleService(List<ServiceInstance> instances);
}
然后写实现类, 例如使用轮询算法
@Component
public class RotationLoadBalancer implements MyLoadBalancer{
private static Integer num = 0;
/**
* 轮询算法
* @param instances 服务列表
* @return 选出的服务
*/
public ServiceInstance getSingleService(List<ServiceInstance> instances){
num ++;
System.out.println("共有" + instances.size() + "个地址, 选择第" + (num % instances.size() + 1) + "个地址");
return instances.get(num % instances.size());
}
}
然后在消费者的调用服务方法中引用它
@Resource
private RotationLoadBalancer loadBalancer;
// 注入
@GetMapping("/consumer/get")
public String get(){
List<ServiceInstance> instances = discoveryClient.getInstances("nacos-provider");
ServiceInstance instance = loadBalancer.getSingleService(instances);
// 调用自己写的负载均衡算法
System.out.println("服务id: " + instance.getServiceId());
System.out.println("Uri:" + instance.getUri());
System.out.println("端口号:" + instance.getPort());
return restTemplate.getForObject(instance.getUri() + "/provider/get", String.class);
}
服务的上线和下线
进行服务的详情, 可以选择对服务进行上线和下线
下线之后的服务会被负载均衡算法忽略
手动下线的之后的服务, 如果不手动上线, 即使下次重启也不会再次上线