• Spring Cloud(二):Spring Cloud Alibaba Nacos


    在这里插入图片描述

    Nacos注册中心

    Nacos注册中心(Nacos Server)核心功能

    • 服务注册:Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务,提供自身的元数据,比如ip地址、端口等信息。Nacos Server接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中。
    • 服务心跳:在服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server,说明服务一直处于可用状态,防止被剔除。默认5s发送一次心跳
    • 服务同步:Nacos Server集群之间会互相同步服务实例,用来保证服务信息的一致性。
    • 服务发现:服务消费者(Nacos Client)在调用服务提供者的服务时,会发送一个REST请求给Nacos Server,获取上面注册的服务清单,并且缓存在Nacos Client本地,同时会在Nacos Client本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存
    • 服务健康检查:Nacos Server会开启一个定时任务用来检查注册服务实例的健康情况,对于超过15s没有收到客户端心跳的实例会将它的healthy属性置为false(客户端服务发现时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复发送心跳则会重新注册)

    Nacos注册中心(Nacos Server)环境搭建

    官方文档: https://nacos.io/zh-cn/docs/deployment.html

    下载安装包:https://github.com/alibaba/nacos/releases/

    解压,进入nacos目录

    在这里插入图片描述

    配置数据库

    单机下:不配置Naocs默认使用内存数据库
    Mysql数据库:使用conf/nacos-mysql.sql初始化表
    application.properties Mysql配置: conf/application.properties
    在这里插入图片描述

    启动Nacos

    单机模式:

    ./bin/startup.sh -m standalone

    集群模式:

    ./bin/startup.sh or ./bin/startup.sh -m cluster

    conf/cluster.conf.example

    #example
    192.168.16.101:8848
    192.168.16.102:8848
    192.168.16.103:8848
    
    • 1
    • 2
    • 3
    • 4

    如果是一台集群需要区分nacos.home目录配置 nacos.home=/nacos-cluster/nacos-8847 需要创建目录并在目录下新建/nacos-cluster/nacos-8847/conf/cluster.conf

    Nacos2.x版本相比1.X新增了gRPC的通信方式,因此需要增加2个端口
    新增端口是在配置的主端口(server.port 8848)基础上,进行一定偏移量自动生成

    端口与主端口的偏移量描述
    98481000客户端gRPC请求服务端端口,用于客户端向服务端发起连接和请求
    98491001服务端gRPC请求服务端端口,用于服务间同步等

    如果碰到mysql-connector-java不兼容添加
    对应jar包到nacos下plugins/mysql文件夹里面

    Spring Cloud Alibaba Nacos快速开始

    1. 依赖

    pom.xml

    
    <dependency>
    	<groupId>com.alibaba.cloudgroupId>
    	<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    application.yml

    spring:
      application:
        name: open-api-global-quartz
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
            namespace: 2cd251e2-5fb4-491a-955e-67c43be601f4
            group: open-api
          config:
            server-addr: localhost:8848
            file-extension: yaml
            group: open-api
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    更多配置:https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-discovery

    在这里插入图片描述

    2. 启动服务,nacos管理端界面查看是否成功注册

    在这里插入图片描述

    测试,通过Open API查询实例列表

    open-api 目前还是1.x.x

    在这里插入图片描述

    http://localhost:8848/nacos/v1/ns/instance/list?serviceName=open-api-service-product&groupName=open-api&namespaceId=2cd251e2-5fb4-491a-955e-67c43be601f4

    在这里插入图片描述

    注册示例代码

    /* Refer to document: https://github.com/alibaba/nacos/blob/master/example/src/main/java/com/alibaba/nacos/example
    *  pom.xml
        
            com.alibaba.nacos
            nacos-client
            ${latest.version}
        
    */
    package com.alibaba.nacos.example;
    
    import java.util.Properties;
    
    import com.alibaba.nacos.api.exception.NacosException;
    import com.alibaba.nacos.api.naming.NamingFactory;
    import com.alibaba.nacos.api.naming.NamingService;
    import com.alibaba.nacos.api.naming.listener.Event;
    import com.alibaba.nacos.api.naming.listener.EventListener;
    import com.alibaba.nacos.api.naming.listener.NamingEvent;
    
    /**
     * @author nkorange
     */
    public class NamingExample {
    
        public static void main(String[] args) throws NacosException {
    
            Properties properties = new Properties();
            properties.setProperty("serverAddr", System.getProperty("serverAddr"));
            properties.setProperty("namespace", System.getProperty("namespace"));
    
            NamingService naming = NamingFactory.createNamingService(properties);
    
            naming.registerInstance("open-api-service-product", "11.11.11.11", 8888, "TEST1");
    
            naming.registerInstance("open-api-service-product", "2.2.2.2", 9999, "DEFAULT");
    
            System.out.println(naming.getAllInstances("open-api-service-product"));
    
            naming.deregisterInstance("open-api-service-product", "2.2.2.2", 9999, "DEFAULT");
    
            System.out.println(naming.getAllInstances("open-api-service-product"));
    
            naming.subscribe("open-api-service-product", new EventListener() {
                @Override
                public void onEvent(Event event) {
                    System.out.println(((NamingEvent)event).getServiceName());
                    System.out.println(((NamingEvent)event).getInstances());
                }
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    在这里插入图片描述

    Ribbon 提供的LoadBalanced

    在这里插入图片描述

    @Configuration
    public class RestConfig {
    
        @Bean
        @LoadBalanced //open-api-service-product => ip:port
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    @Slf4j
    @RestController
    @RequestMapping("/demo")
    public class RestTemplateController {
    
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping("/product")
        public String productIndex() {
            log.info("demo product start");
            String url = "http://open-api-service-product/check";
            String result = restTemplate.getForObject(url, String.class);
            log.info("demo product result = " + result);
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Nacos注册中心常见配置

    服务分级存储模型

    服务-集群-实例 三层模型
    在这里插入图片描述

    服务逻辑隔离

    在这里插入图片描述

    • Namespace 隔离设计: 命名空间(Namespace)用于进行租户(用户)粒度的隔离,Namespace 的常用场景之一是不同环境的隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
    • group服务分组:不同的服务可以归类到同一分组,group也可以起到服务隔离的作用

    临时实例和持久化实例

    在定义上区分临时实例和持久化实例的关键是健康检查的方式

    • 临时实例使用客户端上报模式:临时实例需要能够自动摘除不健康实例,而且无需持久化存储实例
    • 持久化实例使用服务端反向探测模式:持久化实例使用服务端探测的健康检查方式,因为客户端不会上报心跳, 所以不能自动摘除下线的实例

    在大中型的公司里往往:

    • 持久化实例注册—基础组件: 例如数据库、缓存等,这些往往不能上报心跳
    • 动态服务的注册—上层业务: 例如微服务或者 Dubbo 服务,服务的 Provider 端支持添加汇报心跳的逻辑

    Nacos 1.x 中持久化及非持久化的属性是作为实例的⼀个元数据进行存储和识别。
    Nacos 2.x 中继续沿用了持久化及非持久化的设定,但是有了⼀些调整。在 Nacos2.0 中将是否持久化的数据抽象至服务级别, 且不再允许⼀个服务同时存在持久化实例和非持久化实例,实例的持久化属性继承自服务的持久化属性。

    # 持久化实例
    spring.cloud.nacos.discovery.ephemeral: false
    
    • 1
    • 2

    临时实例变更为持久实例:删除 nacos/data/protocol/raft/naming_persistent_service_v2

    NacosServiceRegistry implements ServiceRegistry 源码入口

    org.springframework.cloud.client.serviceregistry.ServiceRegistry

    com.alibaba.cloud.nacos.registry.NacosServiceRegistry implements ServiceRegistry

    public abstract class AbstractAutoServiceRegistration<R extends Registration>
    		implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
    	...
    	public void onApplicationEvent(WebServerInitializedEvent event) {
    		bind(event);
    	}
    	...
    }		
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    public class NacosServiceRegistry implements ServiceRegistry<Registration> {
    	...
    	@Override
    	public void register(Registration registration) {
    
    		if (StringUtils.isEmpty(registration.getServiceId())) {
    			log.warn("No service to register for nacos client...");
    			return;
    		}
    
    		String serviceId = registration.getServiceId();
    		String group = nacosDiscoveryProperties.getGroup();
    
    		Instance instance = getNacosInstanceFromRegistration(registration);
    
    		try {
    			// Nacos namingService 注册
    			namingService.registerInstance(serviceId, group, instance);
    			log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
    					instance.getIp(), instance.getPort());
    		}
    		catch (Exception e) {
    			log.error("nacos registry, {} register failed...{},", serviceId,
    					registration.toString(), e);
    			// rethrow a RuntimeException if the registration is failed.
    			// issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132
    			rethrowRuntimeException(e);
    		}
    	}
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    Nacos 配置中心

    demo

    1. pom.xml
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    1. bootstrap.yml
    spring:
      application:
        name: stock-service
      cloud:
        nacos:
          config:
            server-addr: localhost:8848
            cluster-name: Beijing
            namespace: dev
            file-extension: yaml
            shared-configs[0]:
              dataId: nacos-share.yaml
              refresh: true
            extension-configs[0]:
              dataId: nacos-ext.yaml
              refresh: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 优先级从高到低:
    1. nacos-config-product.yaml 精准配置
    2. nacos-config.yaml 同工程不同环境的通用配置
    3. ext-config: 不同工程 扩展配置
    4. shared-dataids 不同工程通用配置
    1. @Value注解可以获取到配置中心的值,但是无法动态感知修改后的值,需要利用@RefreshScope注解
    @RestController
    @RequestMapping("/nacos")
    @RefreshScope
    public class NacosConfigController {
    
        @Value("${mxnacos.nacos-ext.name}")
        private String nacosExtName;
    
        @Value("${mxnacos.nacos-share.name}")
        private String nacosShareName;
    
        @Value("${mxnacos.stock-service.name}")
        private String stockServiceName;
    
        @GetMapping("/config")
        public String deductStock() {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("nacosShareName: " + nacosShareName);
            stringBuilder.append(" < ");
            stringBuilder.append("nacosExtName: " + nacosExtName);
            stringBuilder.append(" < ");
            stringBuilder.append("stockServiceName: " + stockServiceName);
            return stringBuilder.toString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    Nacos nacos-examples

    https://github.com/nacos-group/nacos-examples

  • 相关阅读:
    Chrome插件 — ReRes
    leetcode 76. 最小覆盖子串
    一个来自内蒙 正式工作两年的攻城狮的独白以及总结
    美国国家安全实验室员工详细数据在网上泄露
    windows服务器自带IIS搭建网站并发布公网访问
    taro全局配置页面路由和tabBar页面跳转
    [hive面试真题]-基础理论篇
    【开题报告】基于SpringBoot的中小企业设备管理系统的设计与实现
    关于numpy库的一些函数使用的记录
    I²C通信协议
  • 原文地址:https://blog.csdn.net/menxu_work/article/details/126721803