Nacos源码分析002-Nacos中服务管理(01)

/ 默认分类 / 0 条评论 / 975浏览

官方文档地址

一。理解Nacos中的服务管理模型

Nacos中的服务管理模型主要设计三个bo类,下面我画了一幅图
下面是Nacos官方文档中的结构图

ps: 并且在Nacos中数据模型如下图所示定位数据data

其实也就是命名空间下面再指定组名来定位到一组数据

二. Nacos中的openapi案例介绍

nacos为我们提供了很多openapi,这样大大的提高了nacos的应用范围,任何一门语言的服务,只要可以支持http协议即可调用openapi来实现和注册中心的交互,关于nacos的openapi官方文档中 有很详细的列表,这里以注册服务实例为例子来重点熟悉下nacos中实现服务管理的原理.

提醒一点: 要想理解Nacos的服务管理原理,首先理解第一节中的那个图,理解后,下面这些就是一些crud而已,但是源码中还是有很多地方值得学习的

首先,提供服务实例管理的为InstanceController
com.alibaba.nacos.naming.controllers.InstanceController

@RestController
@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance")
public class InstanceController {
    //注册一个新的服务
    @CanDistro
    @PostMapping
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
    public String register(HttpServletRequest request) throws Exception {

        //工具类获取服务相关信息
        final String namespaceId = WebUtils
                .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
        final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        NamingUtils.checkServiceNameFormat(serviceName);
        //按照给定信息生成服务实例对象,方法内逻辑较为简单,就是从请求参数中获取实例的ip,端口,权重,是否上线等属性并且赋值给创建的服务实例(Instance)对象
        final Instance instance = parseInstance(request);
        //注册服务实例
        serviceManager.registerInstance(namespaceId, serviceName, instance);
        return "ok";
    }
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {

        //按照当前服名和其他参数创建一个空服务(如果已经存在那么就不会创建了),这里会将新创建的服务(Service放入ServiceManager中)
        createEmptyService(namespaceId, serviceName, instance.isEphemeral());

        //获取这个服务(Service)
        Service service = getService(namespaceId, serviceName);

        if (service == null) {
            throw new NacosException(NacosException.INVALID_PARAM,
                    "service not found, namespace: " + namespaceId + ", service: " + serviceName);
        }
        //将实例(Instance)添加进去
        addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
    }
    public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)
            throws NacosException {
        //先查询这个服务是否已经存在
        Service service = getService(namespaceId, serviceName);
        //如果不存在,则创建
        if (service == null) {

            Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
            service = new Service();
            service.setName(serviceName);
            service.setNamespaceId(namespaceId);
            service.setGroupName(NamingUtils.getGroupName(serviceName));
            // now validate the service. if failed, exception will be thrown
            service.setLastModifiedMillis(System.currentTimeMillis());
            //计算当前服务(service)的md5sum
            service.recalculateChecksum();
            //注册这个新的服务实例的时候,如果指定了需要加入的集群
            if (cluster != null) {
                cluster.setService(service);
                service.getClusterMap().put(cluster.getName(), cluster);
            }

            //判断服务名和集群名是否合法
            service.validate();

            //将服务放入serviceManager中
            putServiceAndInit(service);
            if (!local) {
                addOrReplaceService(service);
            }
        }
    }

所以其中的一些openapi就是对服务或者配置或者其他模型对象的的一些crud操作
另外一个小点就是,在nacos中用到很多实用计算md5sum来判断数据是否变化(理念都是一样的,linux中的文件传输完整性判断),所以在添加服务的时候,也需要重新计算服务管理器的md5sum

//返回当前服务所有实例的ip字符串拼接的md5
    public synchronized void recalculateChecksum() {
        //获取当前服务的所有实例集合
        List<Instance> ips = allIPs();

        StringBuilder ipsString = new StringBuilder();
        ipsString.append(getServiceString());

        if (Loggers.SRV_LOG.isDebugEnabled()) {
            Loggers.SRV_LOG.debug("service to json: " + getServiceString());
        }

        if (CollectionUtils.isNotEmpty(ips)) {
            Collections.sort(ips);
        }

        for (Instance ip : ips) {
            String string = ip.getIp() + ":" + ip.getPort() + "_" + ip.getWeight() + "_" + ip.isHealthy() + "_" + ip
                    .getClusterName();
            ipsString.append(string);
            ipsString.append(",");
        }

        checksum = MD5Utils.md5Hex(ipsString.toString(), Constants.ENCODE);
    }