Nacos 分布式注册与发现功能|分布式配置中心
产生背景rpc远程调用中。服务的url 的治理
rpc的远程调用框架: HttpClient,grpc,dubbo,rest,openfeign
传统的rpc 调用有哪些问题:
1,超时问题
2,安全问题
3,服务与服务之间url地址管理
在我们微服务架构通讯,服务之间依赖关系非常大,如果通过传统的方式管理我们服务的url地址的情况下,一旦地址发生变化的情况下,还需要人工修改rpc远程调用地址。
每个服务的url管理地址发出复杂,所以这是我们采用服务url治理技术,可以实现对我们整个实现动态服务注册与发现、本地负载均衡、容错等。
image.png
服务治理的基本概念
服务治理的基本概念:
在rpc的远程调用中,服务与服务之间的依赖关系非常大,服务url地址管理非常复杂,所以这时候需要我们对服务的url实现治理。通过服务治理可以实现服务注册与发现,负载均衡,容错等。
image.png
rpc远程调用中, 地址中包括 域名和端口号/调用的方法名称
192.168.111.111:8080/getUser
把每个服务器地址和端口人工放到数据库表中:
Id serviced ip 端口号
Mayikt-member 192.168... 8082
Mayikt-member 192.168... 8081
基于数据库形式实现服务url治理
缺点: 维护成本高, 没有完全绝对的实现动态智能。
思考是否有更好的方案?
微服务的注册中心
整个微服务架构中最为核心的肯定是 注册中心
注册中心实际上就是存放我们的服务的地址信息,能够实现动态感知:
注册中心: Dubbo依赖zookeeper,Eureka,Consul,Nacos,Redis 数据库
image.png
服务注册中心的概念:
每次调用该服务的时候如果地址写死了,一点接口发生了改变的情况下,这时候需要重新发布版本才连接接口调用地址,所以需要一个注册中心统一管理沃恩的服务注册与发现。
注册中心,我们的服务注册到我们的的注册中心,key为服务名称,value为该服务 调用地址,该类型为集合类型。Eureka,consul,zookeeper,nacos等
服务注册: 我们生产者项目启动的时候,会将当前服务自己的信息地址注册到注册中心。
服务发现: 消费者从我们的注册中心获取生产者调用的地址(集合),在使用负载均衡策略获取集群中某个地址实现本地的rpc的远程调用。
微服务调用接口常用名词
Nacos与Eureka区别:
(1)springcloud eureka是注册中心,负责微服务的注册与发现,起到承上启下的作用,在微服务架构中相当于人体的 大脑,很重要,nacos是阿里巴巴出的,功能类似eureka,
(2)nacos的部署方式与springcloud eureka不太一样,euraka是需要创建springboot项目,然后将euraka服务端通过gav的方式加载进来,然后部署项目。
(3)nacos是直接从阿里巴巴nacos的官网下载jar包,启动服务。
Eureka与Zookeeper的区别:
Zookeeper的设计理念就是分布式协调服务,保证数据(配置数据,状态数据)在多个服务系统之间保证一致性,这也不难看出Zookeeper是属于CP特性(Zookeeper的核心算法是Zab,保证分布式系统下,数据如何在多个服务之间保证数据同步)。Eureka是吸取Zookeeper问题的经验,先保证可用性。
生产者: 提供接口被其他服务的调用
消费者: 调用生产者接口实现消费
服务注册: 将当前服务地址注册到服务发现。
image.png
服务注册原理和实现:
1,生产者启动的时候key=服务站的名称 value ip和端口号注册到我们的微服务注册中心上。
mayikt-member 192.168.0.149:8080
mayikt-member 192.168.0.149:8081
2,注册存放服务地址列表类型: key唯一,列表是list集合。
May<Key,List(String)>{Mayikt-member:["192.168.0.149"]}
3,我们的消费者从我们注册中心上根据服务名称查询服务地址列表(集合)
mayikt-member ==["192.168.0.149:8080" " 192.168.0.149:8081"]
4,消费者获取到集群列表后采用负载均衡器选择一个地址实现rpc 远程调用。
N****acos的****基本的介绍
Nacos可以实现分布式服务注册与发现/分布式配置中心框架。
官网的介绍: <u>https://nacos.io/zh-cn/docs/what-is-nacos.html</u>
N****acos的****环境的准备
Nacos可以在linux/windows/Mac版本上都可以安装
具体安装教程地址:<u>https://nacos.io/zh-cn/docs/quick-start.html</u>
手动实现服务注册与发现
1.实现服务注册 发送post请求:
2.实现服务发现
<u>http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName</u>
详细步骤操作:<u>https://nacos.io/zh-cn/docs/quick-start.html</u>
Nacos整合SpringCloud
maven 依赖信息:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.taotao</groupId>
<artifactId>meite-member</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>meite-member</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
会员服务注册:
package com.taotao.meitemember.service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* @author aping
* @time 2020/7/24 12:38
*/
@RestController
public class MemberService {
@RequestMapping("/getUser")
public String getUser(Integer userId){
return "麦田的守望者";
}
}
接口完成后对会员进行注册:
拿到配置文件,对项目命名:
spring:
application:
name: meitem-member
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
再进行代码测试,自动实现对服务进行注册
package com.taotao.meitemember;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
public class MeiteMemberApplication {
public static void main(String[] args) {
SpringApplication.run(MeiteMemberApplication.class, args);
}
}
会员服务(生产者)
服务接口
package com.taotao.meitemember.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* @author aping
* @time 2020/7/24 12:38
*/
@RestController
public class MemberService {
@Value("${server.port}")
private String serverport;
@RequestMapping("/getUser")
public String getUser(Integer userId){
return "麦田的守望者的端口:"+serverport;
}
}
配置文件
application.yml文件
spring:
application:
name: meitem-member
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8084
订单服务消费者
订单调用会员服务
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.taotao</groupId>
<artifactId>meite-order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>meite-order</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.netflix.ribbon/ribbon-loadbalancer -->
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-loadbalancer</artifactId>
<version>2.7.13</version>
<scope>runtime</scope>
</dependency>
<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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置yml
spring:
application:
name: meitem-order
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8083
启动类:
package com.taotao.meiteorder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class MeiteOrderApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MeiteOrderApplication.class, args);
}
}
调用接口:
package com.taotao.meiteorder.order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.List;
/**
* @author aping
* @time 2020/7/24 12:56
*/
@RestController
public class OrderService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private LoadBalancerClient loadBalancerClient;
@RequestMapping("/orderToMember")
public String orderToMember() {
// 从注册中心上获取该注册服务列表
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("meitem-member");
ServiceInstance serviceInstance = serviceInstanceList.get(0);
URI rpcMemberUrl = serviceInstance.getUri();
String result = restTemplate.getForObject(rpcMemberUrl + "/getUser?userId="+66, String.class);
return "订单调用会员获取结果:" + result;
}
}
image.png
负载均衡算法
package com.taotao.meiteorder.order;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
/**
* @author aping
* @time 2020/7/24 17:08
*/
public interface LoadBalancer {
ServiceInstance geySingleAddres(List<ServiceInstance> serviceInstanceList);
}
package com.taotao.meiteorder.order;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author aping
* @time 2020/7/24 17:10
*/
@Component
public class RotationLoadBalancer implements LoadBalancer {
private AtomicInteger atomicInteger=new AtomicInteger(0);
@Override
public ServiceInstance geySingleAddres(List<ServiceInstance> serviceInstanceList) {
int index=atomicInteger.incrementAndGet()%2;
ServiceInstance serviceInstance=serviceInstanceList.get(index);
return serviceInstance;
}
}
package com.taotao.meiteorder.order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.List;
/**
* @author aping
* @time 2020/7/24 12:56
*/
@RestController
public class OrderService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private LoadBalancer loadBalancer;
@RequestMapping("/orderToMember")
public String orderToMember() {
// 从注册中心上获取该注册服务列表
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("meitem-member");
ServiceInstance serviceInstance = serviceInstanceList.get(0);
URI rpcMemberUrl = serviceInstance.getUri();
String result = restTemplate.getForObject(rpcMemberUrl + "/getUser?userId="+66, String.class);
return "订单调用会员获取结果:" + result;
}
@RequestMapping("/orderToMember2")
public String orderToMember2(){
//从注册中心获取注册列表
List<ServiceInstance> serviceInstances=discoveryClient.getInstances("meitem-member");
ServiceInstance serviceInstance=loadBalancer.geySingleAddres(serviceInstances);
URI uri=serviceInstance.getUri();
//使用rest形式实现rpc调用
String result=restTemplate.getForObject(uri+"/getUser?userId="+66,String.class);
return "顶顶那会员获取结果:"+result;
}
}
http://localhost:8083/orderToMember2
rpc远程调用设计到本地负载均衡的算法
1,从注册中心获取服务集群的列表
2,从列表中选择一个
负载均衡的算法有哪些?
1,一致性hash 计算
2, 轮询,权重
3,随机。
采用设计模式
假设我们有2台服务器集群:
访问次数%集群size
1%2=1
2%2=0
3%2=1
4%2=0
1%1=0
image.png
Nacos 与其他注册对比分析:
访问之后在服务列表发现2个集群。下线原理是在把他的集合中移除:
image.png
故障转移:集合中取下一个值
编程思维能力就是 举一反三
网友评论