为了做服务注册迁移,我提前准备了这些东西,来看看对你有没有用!(下)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 为了做服务注册迁移,我提前准备了这些东西,来看看对你有没有用!(下)

derby数据库的介绍


官网地址:http://db.apache.org/derby/ 一 款java语言编写的内嵌于jvm的数据库,可以支持sql查询,以及jdbc协议,关于其持久化,大概推断是存储到了指定的目录文件下边:


image.png


服务列表源码分析

服务注册原理跟踪


根据debug会发现,在com.alibaba.nacos.naming.core.ServiceManager 类里面包含了相关的服务列表存储信息:


image.png


在源码里面会发现存储这些服务列表的本质就是一个ConcurrentHashMap数据结构:


(采用了ConcurrentHashMap来解决并发冲突问题,1.8之前是采用了分段锁,但是这种方式的锁粒度过大,所以后边改为了采用cas+synchronized的方式来进行加锁,通过使用无所插入头结点,如果插入失败,说明同一时刻有其他线程进行头插入,再次循坏插入)


private Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();


有点奇怪,这个map的数据是存储在内存里面的,那么服务在重启的时候应该是有进行初始化操作。并且当我们将provider的服务下架之后nacos依旧会有服务信息,在服务关闭之后的三十秒后nacos就查询不出任何信息了。


借此推测会有一个调度去专门维护这些数据信息。(猜测是心跳机制)


注册服务信息到nacos的接口:


/nacos/v1/ns/instance
com.alibaba.nacos.naming.controllers.InstanceController#register
->
com.alibaba.nacos.naming.core.ServiceManager#registerInstance


那么,假设我们通过启动dubbo工程,注册dubbo服务到nacos服务中心之后会看到哪些情况呢?


发现循环调用某些接口


  • 【DistroFilter request url】/nacos/v1/ns/instance/beat


  • 【DistroFilter request url】/nacos/v1/ns/instance/list


通过日志过滤发现会循环调用这两个接口,后来查询文档估计是某些调度在维护两端的数据。


客户端会重复发送心跳包到nacos这边,这份心跳包包含的数据还挺多的。关于心跳模块涉及到的类为:


com.alibaba.nacos.client.naming.beat.BeatReactor


发送的心跳数据基本格式通过BeatInfo格式进行数据传输。


关于循环发送心跳数据包的核心是借助了jdk内部的


ScheduledExecutorService


这个api来实现的,相关模板代码:


image.png


这样就能实现每个三秒发送一次心跳的功能。


同理,在nacos的服务端和客户端之间也存在心跳协调的代码:


class BeatTask implements Runnable {
        BeatInfo beatInfo;
        public BeatTask(BeatInfo beatInfo) {
            this.beatInfo = beatInfo;
        }
        @Override
        public void run() {
            if (beatInfo.isStopped()) {
                return;
            }
            long nextTime = beatInfo.getPeriod();
            try {
            //发送心跳包
                JSONObject result = serverProxy.sendBeat(beatInfo, BeatReactor.this.lightBeatEnabled);
                long interval = result.getIntValue("clientBeatInterval");
                boolean lightBeatEnabled = false;
                if (result.containsKey(CommonParams.LIGHT_BEAT_ENABLED)) {
                    lightBeatEnabled = result.getBooleanValue(CommonParams.LIGHT_BEAT_ENABLED);
                }
                BeatReactor.this.lightBeatEnabled = lightBeatEnabled;
                if (interval > 0) {
                    nextTime = interval;
                }
                int code = NamingResponseCode.OK;
                if (result.containsKey(CommonParams.CODE)) {
                    code = result.getIntValue(CommonParams.CODE);
                }
                if (code == NamingResponseCode.RESOURCE_NOT_FOUND) {
                //如果服务实例消失或者不存在,则注册一个服务实例
                    Instance instance = new Instance();
                    instance.setPort(beatInfo.getPort());
                    instance.setIp(beatInfo.getIp());
                    instance.setWeight(beatInfo.getWeight());
                    instance.setMetadata(beatInfo.getMetadata());
                    instance.setClusterName(beatInfo.getCluster());
                    instance.setServiceName(beatInfo.getServiceName());
                    instance.setInstanceId(instance.getInstanceId());
                    instance.setEphemeral(true);
                    try {
                        serverProxy.registerService(beatInfo.getServiceName(),
                            NamingUtils.getGroupName(beatInfo.getServiceName()), instance);
                    } catch (Exception ignore) {
                    }
                }
            } catch (NacosException ne) {
                NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}",
                    JSON.toJSONString(beatInfo), ne.getErrCode(), ne.getErrMsg());
            }
            //每隔5秒重新发送一次心跳包
            executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
        }
    }


其实我们深入sendbeat函数可以看到最底层就是请求nacos服务端的心跳接口


public JSONObject sendBeat(BeatInfo beatInfo, boolean lightBeatEnabled) throws NacosException {
        if (NAMING_LOGGER.isDebugEnabled()) {
            NAMING_LOGGER.debug("[BEAT] {} sending beat to server: {}", namespaceId, beatInfo.toString());
        }
        Map<String, String> params = new HashMap<String, String>(8);
        String body = StringUtils.EMPTY;
        if (!lightBeatEnabled) {
            try {
                body = "beat=" + URLEncoder.encode(JSON.toJSONString(beatInfo), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new NacosException(NacosException.SERVER_ERROR, "encode beatInfo error", e);
            }
        }
        params.put(CommonParams.NAMESPACE_ID, namespaceId);
        params.put(CommonParams.SERVICE_NAME, beatInfo.getServiceName());
        params.put(CommonParams.CLUSTER_NAME, beatInfo.getCluster());
        params.put("ip", beatInfo.getIp());
        params.put("port", String.valueOf(beatInfo.getPort()));
        String result = reqAPI(UtilAndComs.NACOS_URL_BASE + "/instance/beat", params, body, HttpMethod.PUT);
        return JSON.parseObject(result);
    }


结合springboot的starter如何做服务发现


首先你可能会有思路推断,加入了一个starter就能生效,估计是有什么springboot的自动化配置在生效吧。


springboot也有自己的一套spi机制,将spirng.factories配置文件下的类进行实例化操作。


image.png


然后根据这些配置的类进行初始化操作。


这里面有个 NacosServiceRegistryAutoConfiguration


参考源代码:


com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration
com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration
org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration


这个类里面继承类spring的事件,ApplicationListener,当spring容器启动的时候会去触发onApplicationEvent函数的。


bind(event)-->start --> register--> com.alibaba.nacos.api.naming.NamingService#registerInstance(java.lang.String, java.lang.String, com.alibaba.nacos.api.naming.pojo.Instance)


其实本质就是在这里调用类nacos的一个远程方法,关于nacos的远程方法看看源码包就了解了,这个不难。


注册的参数


private Instance getNacosInstanceFromRegistration(Registration registration) {
   Instance instance = new Instance();
   instance.setIp(registration.getHost());
   instance.setPort(registration.getPort());
   instance.setWeight(nacosDiscoveryProperties.getWeight());
   instance.setClusterName(nacosDiscoveryProperties.getClusterName());
   instance.setMetadata(registration.getMetadata());
   return instance;
}


整体的注册源码其实可以浓缩为下边这张图


image.png


nacos的集群化


基本配置条件:


一般集群需要至少3个节点。我们先准备3台机器,我这里选择了三台机器作为集群搭建基础:


192.168.11.200:8748
192.168.11.196:8748
192.168.11.126:8748


首先需要有三台基本的服务器用于运行多个nacos服务端程序。


然后修改conf配置文件:


[root@localhost conf]# ls
application.properties  application.properties.example  cluster.conf  cluster.conf.example.bak  nacos-logback.xml  nacos-mysql.sql  schema.sql
[root@localhost conf]# cat cluster.conf
#it is ip
#example
192.168.164.131:8848
192.168.164.132:8848
192.168.164.133:8848


最后再配置一下数据库连接部分:


### Count of DB:
 db.num=1
### Connect URL of DB:
 db.url.0=jdbc:mysql://10.11.9.243:3306/linhao_test?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
 db.user=crm
 db.password=USszJ497whda


启动之后日志会有明显说明提示nacos的集群已经部署成功。


如果需要方便操作可以借助使用nginx来做页面的转发。


upstream nacos_server {
  server 192.168.11.200:8748;
  server 192.168.11.196:8748;
  server 192.168.11.126:8748;
}
server {
  listen 80;
  server_name localhost;
  #charset koi8-r;
  #access_log logs/host.access.log main;
  location / {
    proxy_pass http://nacos_server;
    index index.html index.htm;
  }
}


初始化登录账号


登录账号可以从源码里面翻查,然后根据这里的加密方式在数据库里面设置账号信息:


package com.alibaba.nacos.console.utils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
 * Password encoder tool
 *
 * @author nacos
 */
public class PasswordEncoderUtil {
    public static void main(String[] args) {
        System.out.println(new BCryptPasswordEncoder().encode("nacos"));
    }
    public static Boolean matches(String raw, String encoded) {
        return new BCryptPasswordEncoder().matches(raw, encoded);
    }
    public static String encode(String raw) {
        return new BCryptPasswordEncoder().encode(raw);
    }
}


下边这段是nacos初始化时候给定的账号密码:


INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');


经过检测,不同账号登录nacos看到的基础配置信息大多都是相似的。


image.png


nacos里面的日志输出在nacos-logback.xml 配置了日志输出位置和等级,如果需要跟踪或者调整可以进去进行修改。


END




相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
6月前
|
存储 运维 监控
NACOS 配置中心和注册中心是分两个集群部署还是放在一个集群中
【2月更文挑战第33天】NACOS 配置中心和注册中心是分两个集群部署还是放在一个集群中
250 3
|
6月前
|
安全 Linux 网络安全
其他云平台的业务迁移到阿里云最新优惠,使用服务器迁移中心SMC快速迁移简介
越来越多的个人和企业希望将部署在其他云平台或者IDC企业的业务迁移到阿里云上,对于这部分用户来说,最为关注的问题就是迁移到阿里云有没有什么优惠,迁移过程是否麻烦,针对这部分用户的需求,阿里云推出了5亿算力补贴,针对迁移用户做出补贴优惠,在迁移过程中,用户可以自行做数据传输迁移,也可以使用阿里云的服务器迁移中心,快速完成迁移。本文为大家介绍其他云服务商的业务迁移到阿里云最新优惠政策,以及使用服务器迁移中心实现快速迁移的方法。
其他云平台的业务迁移到阿里云最新优惠,使用服务器迁移中心SMC快速迁移简介
|
6月前
|
Dubbo Java 应用服务中间件
双活工作下的数据迁移:Nacos注册中心实战解析
这篇内容介绍了如何使用NacosSync组件进行双活项目中的注册中心数据迁移。首先,准备包括64位OS、JDK 1.8+、Maven 3.2+和MySQL 5.6+的环境。接着,获取并解压NacosSync安装包,配置数据库连接,启动服务,并通过访问特定URL检查系统状态。然后,通过NacosSync控制台进行集群配置,添加Zookeeper和Nacos集群,并设置同步任务。当数据同步完成后,Dubbo客户端(Consumer和Provider)更新配置以连接Nacos注册中心。最后,迁移完成后,原有的Zookeeper集群可下线,整个过程确保了服务的平滑迁移。
191 1
|
6月前
|
监控 NoSQL 关系型数据库
Serverless 应用引擎常见问题之注册中心业务模块掉线了如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
|
6月前
|
微服务
注册中心机制
【2月更文挑战第16天】注册中心机制
56 5
|
数据安全/隐私保护
在服务器迁移中心中导入迁移源
在服务器迁移中心中导入迁移源
84 3
|
6月前
|
数据采集 分布式计算 DataWorks
DataWorks常见问题之跨账号失败设置依赖关系如何解决
DataWorks是阿里云提供的一站式大数据开发与管理平台,支持数据集成、数据开发、数据治理等功能;在本汇总中,我们梳理了DataWorks产品在使用过程中经常遇到的问题及解答,以助用户在数据处理和分析工作中提高效率,降低难度。
|
弹性计算 数据安全/隐私保护
阿里云注册流程详解
很多小白用户不知道怎么注册阿里云,下面小编就和大家系统讲解一下
|
数据安全/隐私保护
服务器迁移中心
服务器迁移中心
106 0
|
存储 负载均衡 Cloud Native
Nacos注册中心概述、服务注册、分级存储模型及环境隔离
Nacos注册中心概述、服务注册、分级存储模型及环境隔离
344 0