服务注册与发现Nacos
服务治理的基本概念
在RPC远程调用过程中,服务与服务之间依赖关系非常大,服务Url地址管理非常复杂,所以这时候需要对我们服务的url实现治理,通过服务治理可以实现服务注册与发现、负载均衡、容错等。
在RPC远程调用中:IP地址:端口号/方法名
里面的方法名获取不会改变,但是ip地址和端口号是会变的,那么怎么是动态的实现呢?
- 数据库?可以,但是需要人工修改,繁琐,维护成本高,没有完全实现动态化
- 注册中心?是的,整个微服务架构的核心就是它
注册中心:实际就是存放我们的服务的地址信息,能够实现动态感知。
注册中心:Dubbo依赖Zookeeper、Eureka、Consul、Nacos、Redis、数据库
服务注册中心的概念
每次调用该服务如果地址直接写死的话,一旦接口发生变化的情况下,这时候需要重新发布版本才可以该接口调用地址,所以需要一个注册中心统一管理我们的服务注册与发现。
注册中心:我们的服务注册到我们注册中心,key为服务名称、value为该服务调用地址,该类型为集合类型。Eureka、consul、zookeeper、nacos等。
服务注册:我们生产者项目启动的时候,会将当前服务自己的信息地址注册到注册中心。
服务发现: 消费者从我们的注册中心上获取生产者调用的地址(集合),在使用负载均衡的策略获取集群中某个地址实现本地rpc远程调用。
微服务调用接口常用名词
生产者:提供接口被其他服务调用
消费者:调用生产者接口实现消费
服务注册:将当前服务地址注册到
服务发现:
服务注册原理实现
1、提供者启动的时候会把IP地址和端口号注册到我们的微服务注册中心上。
123.56.78.123:8080
2、注册存放服务地址列表类型:key唯一,列表是list集合。
Map<Key,List(String)>{
provider:["123.56.78.123:8080"]
}
3、我们的消费者从我们注册中心上根据服务名(provider)称查询服务地址列表(集合)
provider-->123.56.78.123:8080,123.56.78.123:8081,123.56.78.123:8082
4、消费者获取到集群列表之后,采用负载均衡器选择一个地址实现rpc远程调用。
Nacos的基本介绍
Nacos可以实现分布式服务注册与发现/分布式配置中心框架。
官网的介绍: nacos.io/zh-cn/docs/…
Nacos的环境准备
Nacos可以在linux/windows/Mac版本上都可以安装 github.com/alibaba/nac…
我这里安装的是1.3.1版本,1.4.1版本的下载太慢
然后解压即可,然后进入bin目录,双击startup.cmd即可启动(windows默认为单机启动,集群启动后面会说到)
然后访问http://127.0.0.1:8848/nacos,账号密码默认都为 nacos/nacos
SpringCloud整合nacos
创建父工程(普通的maven项目):alibaba-nacos
删除src目录
添加pom依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
创建子模块:provider(提供者)
application.yml
server:
port: 8080
spring:
cloud:
nacos:
discovery:
#服务注册地址
server-addr: 127.0.0.1:8848
application:
# 服务名称(可以随便取)
name: provider
服务接口:
@RestController
public class ProviderService {
@Value("${server.port}")
private String serverPort;
/**
* 服务提供的接口
*
* @return
*/
@RequestMapping("/getProvider")
public String getProvider() {
return "Provider端口号:" + serverPort;
}
}
主启动类:
@SpringBootApplication
//这里不需要像Eureka一样写注解,这里自动实现服务注册
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
启动项目先测试以下: http://localhost:8080/getProvider
然后去Nacos的界面看看 : http://127.0.0.1:8848/nacos,成功注册进来,是不是比Eureka简单不要太多
创建子模块:consumer(消费者)
application.yml
server:
port: 8090
先实现一个简单的rest调用,我们知道,我们现在只有一个服务,但是它获取的是一个集合,所以我们获取第1个即可
ConsumerService
@RestController
public class ConsumerService {
@Autowired
private DiscoveryClient discoveryClient;
//由于RestTemplate类源码中没有注入到Bean中,所以我们下面直接在主启动类注入即可
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/getToProvider")
public Object getToProvider() {
//1、根据服务名称从注册中心获取集群列表
//这个返回的是一个集合类型是因为有可能这个服务名是一个集群
List<ServiceInstance> instances = discoveryClient.getInstances("provider");
//2、列表中任意选择一个 实现本地的RPC调用
ServiceInstance serviceInstance = instances.get(0);
//这个uri就是 127.0.0.1:8080
URI uri = serviceInstance.getUri();
//是不是就是变相的访问: 127.0.0.1:8080/getProvider
String result = restTemplate.getForObject(uri + "/getProvider", String.class);
return "消费者获取的结果:" + result;
}
主启动类
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
//我们自己注入RestTemplate
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
启动项目访问:http://localhost:8090/getToProvider
手写负载均衡算法
创建LoadBalancer接口
public interface LoadBalancer {
/**
* 根据多个不同的地址 返回单个调用rpc地址
*
* @param serviceInstances
* @return
*/
ServiceInstance getSingleAddres(List<ServiceInstance> serviceInstances);
}
实现LoadBalancer接口
@Component
public class RotationLoadBalancer implements LoadBalancer {
/**
* 从0开始计数
*/
private AtomicInteger atomicInteger = new AtomicInteger(0);
//获取单个的服务地址
@Override
public ServiceInstance getSingleAddres(List<ServiceInstance> serviceInstances) {
//首先获取服务的个数
int index = atomicInteger.incrementAndGet() % serviceInstances.size();
//然后进行取模来轮询
ServiceInstance serviceInstance = serviceInstances.get(index);
//返回即可
return serviceInstance;
}
}
修改ConsumerService
@RestController
public class ConsumerService {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
@Autowired
private RotationLoadBalancer loadBalancer;
@RequestMapping("/getToProvider")
public Object getToProvider() {
List<ServiceInstance> instances = discoveryClient.getInstances("provider");
ServiceInstance singleAddres = loadBalancer.getSingleAddres(instances);
String result = restTemplate.getForObject(singleAddres.getUri() + "/getProvider", String.class);
return "消费者获取的结果:" + result;
}
}
在启动项目之前,我们先修改IDEA的启动配置,设置可以启动多个项目
现在就可以启动消费者了
访问:http://localhost:8090/getToProvider ,不停的刷新,发现在轮询
你还可以查看Nacos界面
负载均衡器
在SpringCloud第一代中使用Ribbon、SpringCloud第二代中直接采用自研发loadbalancer即可,默认使用的Ribbon。
基于Ribbon实现本地负载均衡
只需在ConsumerService里面添加一个方法
@RequestMapping("/getToRibbonProvider")
public Object getToRibbonProvider() {
return restTemplate.getForObject("http://provider/getProvider", String.class);
}
修改消费者主启动类(添加以一个注解)
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
@LoadBalanced // 添加的注解
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
启动消费者项目访问即可,是一样的效果
LoadBalancerClient均衡器
使用起来也很简单,只需要在ConsumerService也添加一个方法即可
@Autowired
private LoadBalancerClient loadBalancerClient;
@RequestMapping("/getToLoadBalancerClientProvider")
public Object getToLoadBalancerClientProvider() {
return loadBalancerClient.choose("provider");
}
启动消费者项目访问即可