Skip to content

Nacos 服务注册与配置中心

字数: 0 字 时长: 0 分钟

简介

Nacos 是一个易于构建云原生应用的动态服务发现、配置管理和服务管理平台

安装

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

nacos本地单机启动.webp

服务注册中心

引入依赖

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服务注册成功.webp

服务发现

在代码里面可以通过 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());
            }
        }
    }
    
}

远程调用

版本1:直接调用

  • 向容器注册 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 远程调用

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

版本2:负载均衡调用

第一种使用 RestTemplate 指定 IP端口号 发送 HTTP 请求的方式,如果远程宕机,则调用失败

所以考虑远程服务启动多个节点,采用负载均衡策略进行访问,保证服务高可用

  • 引入 负载均衡 依赖
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);
}

版本3:基于注解的负载均衡

  • 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);
}

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

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

注册中心调用.webp

配置中心

1. 依赖引入

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

2. 配置整合

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

nacos编写配置文件.webp

  • 3. 编写代码进行测试
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 配置中心验证.webp

使用 ConfigurationProperties 自动绑定

springboot 的自动绑定功能可以批量读取 nacos 的配置中心配置,而且支持自动刷新

java
@Data
@Component
@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("发送邮件");
                    }
        });
    };
}

配置中心的配置文件和微服务的 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