服务注册与发现Nacos

455 阅读5分钟

服务注册与发现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");
}

启动消费者项目访问即可