Nacos

nacos是一个服务注册于发现的平台 它可以做到将我们的服务展示出来 只需要去访问我们的nacos就可以浏览所有的服务,那它和我们的网关有什么关系,网关主要是提供给用户一个访问的接口,所有的用户通过网关访问,而nacos主要是提供给我们的所有服务其他服务的信息,包括给网关提供我们需要到达服务的信息。
在这里插入图片描述
nacos的简单使用:官网地址

https://nacos.io/zh-cn/docs/quick-start.html

官网推荐下载稳定的2.03在这里插入图片描述

下载好我们的nacos包过后 将他上传到linux服务器上
执行命令解压tar -xvf nacos-server-$version.tar.gz在这里插入图片描述
进入解压好的nacos的bin目录下在这里插入图片描述
启动:
在这里插入图片描述
启动成功:可以到最下面那一行的推荐目录下观察它的日志输出:
在这里插入图片描述根据网址进入即可:
在这里插入图片描述
版本对应文档:
https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
同时如果是使用的nacos是2.0以上版本 还要打开9848 9849端口
版本一定要用好
在这里插入图片描述
我使用的是2.0.3版本的nacos,父类的在pom文件中添加这个:

<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>
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${spring-boot.version}</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5</version>
        </dependency>
</dependencies>

接下来是服务提供者的pom文件: 记得把服务提供者配置为父类的子文件

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
</dependencies>

接下来是服务的yml文件的,其实服务消费者和服务提供者 在我们的nacos中并没有什么不同,都是同样的一组组件 ,只是当我们的某个组件需要调用分布式中其他项目的时候 我们会把被调用的项目称作服务的提供者,另一个叫做服务消费者

server:
  port: 9001
spring:
  application:
    name: nacos-provider
  cloud:
    nacos:
      server-addr: 47.108.140.135:8848
management:
  endpoint:
    web:
      exposure:
        include: '*'
==================================服务消费者的========================================
server:
  port: 8080
spring:
  application:
    name: nacos-consumer
  cloud:
    nacos:
      server-addr: 47.108.140.135:8848

#消费者将要去访问的微服务名称 这个没有什么大用 就是方便用@Value拿而已 不用写直接字符串也可
#就是配置文件和代码的分离
#我们的服务注册中心会把 应用程序名和它的ip联系起来
service-url:
  nacos-user-service: http://nacos-provider

设计到服务的消费就需要设计到远程方法调用:
在消费者的主程序中添加这一个 好方便我们调用其他服务 ribbon为我们提供了负载均衡

	@Bean
    @LoadBalanced //开启负载均衡(利用hash码的均匀分布 达成负载均衡)
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

远程方法调用:

提供者的
@Value("${server.port}")
    private String port;

    @GetMapping("/serverport")
    public String getServerPort(){
        return "hello nacos discovery "+port;
    }
============================================================================
消费者的
@Value("${service-url.nacos-user-service}")
    private String url;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/consumer/nacos")
    public String getDiscovery(){
        //第一个参数是访问的地址 第二个是返回值的类型 如果返回值类型是 list 要写成arr数组类型
        //第三个参数是传给url的动态参数 使用该参数需要 在url中加上 {1}{2}{3}占位符
        return restTemplate.getForObject(url+"/serverport",String.class);
    }

接下来可以执行方法 可以看出 我们的调用结果是不同的,证明我们的ribbon确实办到了负载均衡(复杂的我没有测试,就是权重 每个服务器性能不同我们设置不同权重 让性能差的分的请求少一些)
在这里插入图片描述
在这里插入图片描述
nacos的核心功能:在这里插入图片描述
接下来我们来了解一下nacos服务是如何运行起来的,下载源码的过程网上都可以找到:
我们来看一看nacos给我们留下的naming测试类:

@Test
    public void testServiceList() throws Exception {

        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, "www.gsxa.top:8848");//nacos的连接信息
        properties.put(PropertyKeyConst.USERNAME, "nacos");
        properties.put(PropertyKeyConst.PASSWORD, "nacos");
        properties.put(CommonParams.NAMING_REQUEST_TIMEOUT,20);

        Instance instance = new Instance();//实例信息 将自身的信息发送给nacos服务器
        instance.setIp("1.1.1.1");
        instance.setPort(800);
        instance.setWeight(2);
        Map<String, String> map = new HashMap<String, String>();
        map.put("netType", "external");
        map.put("version", "2.0");
        instance.setMetadata(map);

        NamingService namingService = NacosFactory.createNamingService(properties);
        namingService.registerInstance("nacos.test.1", instance);

        ThreadUtils.sleep(5000L);
        List<Instance> list = namingService.getAllInstances("nacos.test.1");
        System.out.println(list);

    }

首先 它创建了两种信息 1. 连接信息 即properties属性 里面主要存储了 与nacos服务端连接需要什么信息。 2. Instance 实例信息 就是自生服务的信息元数据传送给服务器 然他了解 我们直接看 registerInstance方法。

@Override
    public void registerInstance(String serviceName, Instance instance) throws NacosException {
        registerInstance(serviceName, Constants.DEFAULT_GROUP, instance);
    }

    @Override
    public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
        NamingUtils.checkInstanceIsLegal(instance);//检查心跳时间是否设置有问题
        clientProxy.registerService(serviceName, groupName, instance);
    }
========================================================================================
 public static void checkInstanceIsLegal(Instance instance) throws NacosException {
 //这几个属性都是Instance的默认设置的值 比如发送心跳时间5s 每5s向nacos服务端发送心跳信息 证明自己还活着   15s心跳异常时间 如果15s没有发送信息 那么该实例将被标记为异常 30s删除服务 如果30s没有发送信息 nacos服务端会直接将这个服务从他的列表中删除
        //查看一下心跳的间隔发送时间 是否大于了心跳的销毁时间 或者 心跳发生异常的时间  如果超过了 证明你设置出了问题
        if (instance.getInstanceHeartBeatTimeOut() < instance.getInstanceHeartBeatInterval()
                || instance.getIpDeleteTimeout() < instance.getInstanceHeartBeatInterval()) {
            throw new NacosException(NacosException.INVALID_PARAM,
                    "Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'.");
        }
}
=======================================================================================
上面检测完过后调用这个方法 去注册服务 先查看 getExecuteClientProxy
@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
        getExecuteClientProxy(instance).registerService(serviceName, groupName, instance);
}
//使用那种请求去发送 http 还是 grpc
private NamingClientProxy getExecuteClientProxy(Instance instance) {
        //用grpc 还是 http协议来发送请求 如果是瞬时对象就会用grpc
        //grpc 是rpc同学的一个实现 2.0版本默认使用grpc
        return instance.isEphemeral() ? grpcClientProxy : httpClientProxy;
}
instance中的属性  默认为true  我们在nacos的官方文档中也可以看到 默认用grpc发送连接请求等信息 上面有图片
/**
     * If instance is ephemeral.
     *
     * @since 1.0.0
     */
private boolean ephemeral = true;
========================================================================================
所以我们直接调用grpcClientProxy的
@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
        NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName,
                instance);//日志信息
        InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName,
                NamingRemoteConstants.REGISTER_INSTANCE, instance);//创建实例请求对象
        requestToServer(request, Response.class);//向服务端发送请求
        namingGrpcConnectionEventListener.cacheInstanceForRedo(serviceName, groupName, instance);//缓存 为了下次再次发送
}
=========================================================================================
对象创建没有什么好看的 直接去看 requestToServer查看发送请求到服务端的
private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass)   throws NacosException {
        try {
            request.putAllHeader(getSecurityHeaders());//往请求头里面放数据
            request.putAllHeader(getSpasHeaders(
                    NamingUtils.getGroupedNameOptional(request.getServiceName(), request.getGroupName())));
            Response response =
                    requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout);
            //如果请求超时时间为小于0 就使用默认的3s 如果不是 就使用我们自己规定的
            //其实求实发送请求 获取响应对象信息
            if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) {//请求失败 抛异常
                throw new NacosException(response.getErrorCode(), response.getMessage());
            }
            if (responseClass.isAssignableFrom(response.getClass())) {
                return (T) response;
            }
            NAMING_LOGGER.error("Server return unexpected response '{}', expected response should be '{}'",
                    response.getClass().getName(), responseClass.getName());
        } catch (Exception e) {
            throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e);
        }
        throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response");
}
======================================================================================
没有什么好看的 其实就是发送请求 获取响应 我删除了一些 太多trycache了
public Response request(Request request, long timeoutMills) throws NacosException {
        int retryTimes = 0;//重试次数  默认为3次
        Response response = null;
        Exception exceptionThrow = null;
        long start = System.currentTimeMillis();//计时器 防止超时
        while (retryTimes < RETRY_TIMES && System.currentTimeMillis() < timeoutMills + start) {//如果没有超时 那么就反复重试
            boolean waitReconnect = false;
            try {
                if (this.currentConnection == null || !isRunning()) {
                    waitReconnect = true;
                    throw new NacosException(NacosException.CLIENT_DISCONNECT,
                            "Client not connected,current status:" + rpcClientStatus.get());
                }
                response = this.currentConnection.request(request, timeoutMills);//通过当前连接发送请求
                if (response == null) {
                    throw new NacosException(SERVER_ERROR, "Unknown Exception.");
                }
                if (response instanceof ErrorResponse) {//如果响应是错误的响应
                    if (response.getErrorCode() == NacosException.UN_REGISTER) {
                        synchronized (this) {
                            waitReconnect = true;
                            if (rpcClientStatus.compareAndSet(RpcClientStatus.RUNNING, RpcClientStatus.UNHEALTHY)) {
                                LoggerUtils.printIfErrorEnabled(LOGGER,
                                        "Connection is unregistered, switch server,connectionId={},request={}",
                                        currentConnection.getConnectionId(), request.getClass().getSimpleName());
                                switchServerAsync();
                            }
                        }

                    }
                    throw new NacosException(response.getErrorCode(), response.getMessage());
                }
                // return response.
                lastActiveTimeStamp = System.currentTimeMillis();
                return response;//返回响应

            } catch (Exception e) {
                if (waitReconnect) {
                    try {
                        //wait client to re connect.
                        Thread.sleep(Math.min(100, timeoutMills / 3));
                    } catch (Exception exception) {
                        //Do nothing.
                    }
                }

                LoggerUtils.printIfErrorEnabled(LOGGER, "Send request fail, request={}, retryTimes={},errorMessage={}",
                        request, retryTimes, e.getMessage());

                exceptionThrow = e;

            }
            retryTimes++;

        }
}

大致就是这么一个流程
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值