Skip to content

Nacos 服务注册与配置中心

简介

安装

  • docker 安装
shell
docker run -d -p 8848:8848 -p 9848:9848 -e MODE=standalone --name nacos nacos/nacos-server:v2.4.3

nacos本地单机启动.png

服务注册中心

引入依赖

xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

整合配置

  • application.yml 中配置 nacos 地址,默认 127.0.0.1:8848
yml
spring:
  application:
    name: service-order
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
  • 开启服务注册/发现功能
java
@EnableDiscoveryClient //核心注解
@SpringBootApplication
public class OrderMainApplication {

	public static void main(String[] args) {
		SpringApplication.run(OrderMainApplication.class, args);
	}

}

查看效果

访问 http://127.0.0.1:8848/nacos/ 可以看到 service-order 服务注册成功

nacos服务注册成功.png

服务发现

在代码里面可以通过 DiscoveryClientNacosServiceDiscovery 从注册中心获取服务

java
@SpringBootTest(classes = ProductMainApplication.class)
public class DiscoveryTest {

    @Resource
    private DiscoveryClient discoveryClient;

    @Resource
    private NacosDiscoveryClient nacosDiscoveryClient;

    @Test
    void discoveryClientTest() {
        for (String service : discoveryClient.getServices()) {
            List<ServiceInstance> instances = discoveryClient.getInstances(service);
            for (ServiceInstance instance : instances) {
                System.out.println("ip:" + instance.getHost());
                System.out.println("port:" + instance.getPort());
            }
        }
    }

    @Test
    void nacosDiscoveryClientTest() {
        for (String service : nacosDiscoveryClient.getServices()) {
            List<ServiceInstance> instances = nacosDiscoveryClient.getInstances(service);
            for (ServiceInstance instance : instances) {
                System.out.println("ip:" + instance.getHost());
                System.out.println("port:" + instance.getPort());
            }
        }
    }
    
}

远程调用

向容器注册 RestTemplate

java
@Configuration
public class OrderServiceConfig {
    
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
}

先通过 discoveryClient 获取服务信息,再通过 RestTemplate 远程调用

java

@Resouce
RestTemplate restTemplate;

// 第一版 获取商品服务信息,远程调用
private Product getProductFromRemote(Long productId) {
        // 1、获取商品服务所在的所有机器 IP 和 port
        List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
        ServiceInstance serviceInstance = instances.get(0);
        // http://127.0.0.1:9000/product/1
        String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/product/" + productId;

        log.info("远程请求:{}",url);
        return restTemplate.getForObject(url, Product.class);
    }

小结

  • 使用 RestTemplate 可以进行远程调用
  • 必须精确指定调用地址和端口
  • 如果远程宕机将不可用

期望:可以负载均衡调用,不用担心远程宕机

负载均衡

引入依赖

xml
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

LoadBalancerClient

loadBalancerClient.choose("service-product") 会负载均衡地获取 service-product 的服务地址

java
@Resource
LoadBalancerClient loadBalancerClient;

//第二版 通过 loadBalancerClient 负载均衡远程调用
private Product getProductFromRemoteWithLoadBalance(Long productId) {
    // 1、获取商品服务所在的所有机器 IP 和 port
    ServiceInstance instance = loadBalancerClient.choose("service-product");
    String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/product/" + productId;

    log.info("远程请求:{}",url);
    return restTemplate.getForObject(url, Product.class);
}

基于注解的负载均衡

  • RestTemplate 添加注解 @LoadBalanced
java
@Configuration
public class OrderServiceConfig {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
}
  • 基于注解的负载均衡,不需要 LoadBalancerClient
java
//第三版 通过 restTemplate 加注解 负载均衡远程调用
public Product getProductFromRemoteWithLoadBalance2(Long productId) {
    // restTemplate 负载均衡自动替换 service-product 为服务 IP + port
    String url = "http://service-product/product/" + productId;
    return restTemplate.getForObject(url, Product.class);
}

面试题: 如果注册中心宕机,远程调用是否可用?

  • 从未调用过,如果宕机,调用直接失败
  • 调用过,如果宕机,不影响,因为有缓存名单
  • 调用过,如果对方服务也宕机,则调用会阻塞后失败

注册中心调用.png

配置中心

依赖引入

xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

配置整合

  • application.yml 中配置 spring.config.import
yaml
spring:
  config:
    import: nacos:service-order.properties
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
  • nacos 中编写配置文件

nacos编写配置文件.png

编写代码进行测试

java
//用于自动刷新 配置中心配置值
@RefreshScope
@RestController
public class OrderController {

    @Value("${order.timeout}")
    String orderTimeout;

    @Value("${order.auto-confirm}")
    String orderAutoConfirm;

    @GetMapping("/config")
    public String config() {
        return "orderTimeout:" + orderTimeout + ",orderAutoConfirm:" + orderAutoConfirm;
    }
}
  • 成功读取到,而且 @RefreshScope 支持自动刷新

nacos 配置中心验证.png

ConfigurationProperties 自动绑定

ConfigurationProperties 推荐

java
@Data
@Component
// springboot 的自动绑定功能可以批量读取 nacos 的配置中心配置,而且自动刷新 (推荐)
@ConfigurationProperties(prefix = "order")
public class OrderProperties {

    private String timeout;

    private String autoConfirm;

}

配置监听

Nacos 中的 NacosConfigManager 可以监听配置文件变化,并拿到变化值

案例: 项目一启动就监听配置文件变化,当配置文件变化时发送邮件

java
/**
     * 项目一启动就监听配置文件变化
     * 发生变化后拿到变化值
     * 发送邮件
     */
    @Bean
    ApplicationRunner applicationRunner(NacosConfigManager nacosConfigManager) {
        return args -> {
            ConfigService configService = nacosConfigManager.getConfigService();
            configService.addListener("service-order.properties", "DEFAULT_GROUP", new Listener() {
                @Override
                public Executor getExecutor() {
                    //随便创建一个固定大小的线程池
                    return Executors.newFixedThreadPool(4);
                }

                @Override
                public void receiveConfigInfo(String s) {
                    System.out.println("变化的配置信息: " + s);
                    System.out.println("发送邮件");
                }
            });
            System.out.println("监听配置文件变化");
        };
    }

面试题:配置中心的配置文件和微服务的 application.yml 配置文件的优先级谁高?

配置中心的目的是为了统一管理微服务的环境变量,如果配置中心与微服务中的配置冲突,以微服务的配置为准的话,那么配置中心将无法统一管理 各微服务的配置信息,因此配置中心配置文件优先级高

数据隔离

根据环境不同,指定不同的配置文件

yaml
spring:
  application:
    name: service-order
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
---

spring:
  config:
    import:
      - nacos:common.properties?group=order
      - nacos:database.properties?group=order
    activate:
      on-profile: dev
---

spring:
  config:
    import:
      - nacos:common.properties?group=order
      - nacos:database.properties?group=order
      - nacos:haha.properties?group=order
    activate:
      on-profile: test
---

spring:
  config:
    import:
      - nacos:common.properties?group=order
      - nacos:database.properties?group=order
      - nacos:haha.properties?group=order
    activate:
      on-profile: prod