Nacos指南-服务发现:注册实例

x33g5p2x  于2021-12-20 转载在 其他  
字(4.3k)|赞(0)|评价(0)|浏览(373)

Nacos服务注册的流程图

API

描述

注册一个实例到服务。

请求类型

POST

请求路径

/nacos/v1/ns/instance

请求参数

名称类型是否必选描述
ip字符串服务实例IP
portint服务实例port
serviceName字符串服务名
namespaceId字符串命名空间ID
weightdouble权重
enabledboolean是否上线
healthyboolean是否健康
metadata字符串扩展信息
clusterName字符串集群名
groupName字符串分组名
ephemeralboolean是否临时实例

错误编码

错误代码描述语义
400Bad Request客户端请求中的语法错误
403Forbidden没有权限
404Not Found无法找到资源
500Internal Server Error服务器内部错误
200OK正常

示例请求

curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?port=8848&healthy=true&ip=11.11.11.11&weight=1.0&serviceName=nacos.test.3&encoding=GBK&namespaceId=n1'

示例返回

ok

关键流程源码解析

InstanceController

实例注册HTTP接口:

/** * Register new instance. * 通过HTTP请求注册一个实例 * @param request http request * @return 'ok' if success * @throws Exception any error during register */
    @CanDistro
    @PostMapping
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
    public String register(HttpServletRequest request) throws Exception {
		//获取参数中携带的命名空间id,未填则默认public
        final String namespaceId = WebUtils
                .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
        //获取实例注册的服务名,必填项 
        final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        //校验服务名合法性,Nacos设计的规则:groupName@@serviceName
        NamingUtils.checkServiceNameFormat(serviceName);
		//将请求中携带的参数信息包装成为Instance实例
        final Instance instance = parseInstance(request);
		//将该服务名节点的实例信息注册进命名空间中
        serviceManager.registerInstance(namespaceId, serviceName, instance);
        return "ok";
    }

ServiceManager

服务管理类

/** * 服务管理类中以一个Map持有了集群中所有的服务 * Map(namespace, Map(group::serviceName, Service)). */
    private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();

    /** * Register an instance to a service in AP mode. * 使用AP模式(舍弃强一致性而保证系统的分区容错性和可用性的场景)注册实例 * <p>This method creates service or cluster silently if they don't exist. * * @param namespaceId id of namespace * @param serviceName service name * @param instance instance to register * @throws Exception any error occurred in the process */
    public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
        //先判断服务是否存在,如果不存在则创建一个新的
        createEmptyService(namespaceId, serviceName, instance.isEphemeral());
		//根据命名空间及服务名获取服务
        Service service = getService(namespaceId, serviceName);
        if (service == null) {
            throw new NacosException(NacosException.INVALID_PARAM,
                    "service not found, namespace: " + namespaceId + ", service: " + serviceName);
        }
		//把当前实例添加进服务列表
        addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
    }

    /** * Add instance to service. * * @param namespaceId namespace * @param serviceName service name * @param ephemeral whether instance is ephemeral * @param ips instances * @throws NacosException nacos exception */
    public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)
            throws NacosException {
		//根据节点属性构建服务的key
		//临时节点 com.alibaba.nacos.naming.iplist.ephemeral.${namespaceId}##${serviceName}
		//持久化节点 com.alibaba.nacos.naming.iplist.${namespaceId}##${serviceName}
        String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);
        Service service = getService(namespaceId, serviceName);
        synchronized (service) {
			//将当前实例加入到所属服务名的实例列表中
            List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);
            Instances instances = new Instances();
            instances.setInstanceList(instanceList);
			//将最新的实例列表通过一致性服务更新所属服务(该一致性服务只是一个代理服务),
			//consistencyService会根据key的命名规则去选取实际服务
			//临时节点的一致性服务(Distro-AP模式),持久化节点的一致性服务(Raft-CP模式)
            consistencyService.put(key, instances);
        }
    }

DistroConsistencyServiceImpl

@Override
    public void put(String key, Record value) throws NacosException {
        //保存服务
        onPut(key, value);
        //同步事件
        distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX),DataOperation.CHANGE,globalConfig.getTaskDispatchPeriod() / 2);
    }
    /** * Put a new record. * * @param key key of record * @param value record */
    public void onPut(String key, Record value) {
        //临时节点,存储服务到DataStore的dataMap中
        if (KeyBuilder.matchEphemeralInstanceListKey(key)) {
            Datum<Instances> datum = new Datum<>();
            datum.value = (Instances) value;
            datum.key = key;
            datum.timestamp.incrementAndGet();
            dataStore.put(key, datum);
        }
        
        if (!listeners.containsKey(key)) {
            return;
        }
        //发送实例变更通知
        notifier.addTask(key, DataOperation.CHANGE);
    }

DataStore

//持有一个map
	private Map<String, Datum> dataMap = new ConcurrentHashMap<>(1024);
    //map结构保存服务在内存中
    public void put(String key, Datum value) {
        dataMap.put(key, value);
    }

思考与总结

调用该HTTP接口是否就完成了实例的注册?(默认实例类型:临时实例)
答:是也不是。说是因为此接口的确完成了服务的创建与实例的注册。说不是又因为该实例昙花一现,因为任何一个服务注册中心都具有检活功能,如果单单调用这个接口我们会发现这个实例在控制台上15秒钟之后状态会变为不健康,30秒之后不可用实例自动下线。它来了,但是30秒之后他又走了!

相关文章