nacos-服務(wù)注冊(cè)發(fā)現(xiàn)源碼分析-注冊(cè)請(qǐng)求接口-03

nacos-服務(wù)注冊(cè)發(fā)現(xiàn)源碼分析-啟動(dòng)注冊(cè)-02分析了nacos在springboot 啟動(dòng)后攻谁,通過(guò)事件機(jī)制弯予,通知到啟動(dòng)事件監(jiān)聽(tīng)器熙涤,在事件監(jiān)聽(tīng)器里面完成了服務(wù)的注冊(cè)。現(xiàn)在我們來(lái)分析一下那槽,服務(wù)注冊(cè)的具體訪(fǎng)問(wèn)接口骚灸。

  • NacosServiceRegistry 服務(wù)注冊(cè)接口
/**
 * @author xiaojing
 * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
 */
public class NacosServiceRegistry implements ServiceRegistry<Registration> {

    private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class);

    private final NacosDiscoveryProperties nacosDiscoveryProperties;

    private final NamingService namingService;

    public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
        this.nacosDiscoveryProperties = nacosDiscoveryProperties;
        this.namingService = nacosDiscoveryProperties.namingServiceInstance(); // 依賴(lài)這個(gè)接口實(shí)現(xiàn)服務(wù)注冊(cè)請(qǐng)求
    }
        // 服務(wù)注冊(cè)
    @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 {
            namingService.registerInstance(serviceId, group, instance); // 注冊(cè)接口
            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);
        }
    }
  • this.namingService = nacosDiscoveryProperties.namingServiceInstance(); 實(shí)例化
public NamingService namingServiceInstance() {

        if (null != namingService) {
            return namingService;
        }

        try {  // 繼續(xù)看這個(gè)方法
            namingService = NacosFactory.createNamingService(getNacosProperties());
        }
        catch (Exception e) {
            log.error("create naming service error!properties={},e=,", this, e);
            return null;
        }
        return namingService;
    }
  • namingService = NacosFactory.createNamingService(getNacosProperties());
/**
     * Create naming service
     *
     * @param properties init param
     * @return Naming
     * @throws NacosException Exception
     */
    public static NamingService createNamingService(Properties properties) throws NacosException {
        return NamingFactory.createNamingService(properties);
    }

    public static NamingService createNamingService(Properties properties) throws NacosException {
        try { // 創(chuàng)建的對(duì)象
            Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
            Constructor constructor = driverImplClass.getConstructor(Properties.class);
            NamingService vendorImpl = (NamingService)constructor.newInstance(properties);
            return vendorImpl;
        } catch (Throwable e) {
            throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
        }
    }

com.alibaba.nacos.client.naming.NacosNamingService 從上面的方法我們可以看出 this.namingService = nacosDiscoveryProperties.namingServiceInstance(); 這里的namingService 就是com.alibaba.nacos.client.naming.NacosNamingService。ok 非驮,回國(guó)頭去看看這句話(huà)namingService.registerInstance(serviceId, group, instance); 也就是說(shuō)這個(gè)方法就是 com.alibaba.nacos.client.naming.NacosNamingService#registerInstance(serviceId, group, instance);

  • registerInstance(serviceId, group, instance);
.......
private NamingProxy serverProxy; //代理對(duì)象劫笙,這個(gè)對(duì)象就是發(fā)送底層http請(qǐng)求的

    public NacosNamingService(String serverList) { //構(gòu)造
        Properties properties = new Properties();
        properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList);

        init(properties);
    }

    public NacosNamingService(Properties properties) { // 構(gòu)造
        init(properties); // 調(diào)用下面的方法
    }

    private void init(Properties properties) {
        namespace = InitUtils.initNamespaceForNaming(properties);
        initServerAddr(properties);
        InitUtils.initWebRootContext();
        initCacheDir();
        initLogName(properties);

        eventDispatcher = new EventDispatcher();
        serverProxy = new NamingProxy(namespace, endpoint, serverList); // 初始化 代理對(duì)象`serverList` 注冊(cè)中心服務(wù)地址集合填大,也就是nacos 服務(wù)端集合
        serverProxy.setProperties(properties);
        beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));
        hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, isLoadCacheAtStart(properties), initPollingThreadCount(properties));
    }
// 負(fù)責(zé)服務(wù)注冊(cè)
@Override
    public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {

        if (instance.isEphemeral()) {
            BeatInfo beatInfo = new BeatInfo();
            beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
            beatInfo.setIp(instance.getIp());
            beatInfo.setPort(instance.getPort());
            beatInfo.setCluster(instance.getClusterName());
            beatInfo.setWeight(instance.getWeight());
            beatInfo.setMetadata(instance.getMetadata());
            beatInfo.setScheduled(false);
            long instanceInterval = instance.getInstanceHeartBeatInterval();
            beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval);
// 添加心跳
            beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
        }
// 我們看到這里,就知道注冊(cè)是繼續(xù)調(diào)用這句話(huà)
        serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
    }
  • NamingProxy http 服務(wù)代理 的registerService
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {

        NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}",
            namespaceId, serviceName, instance);
// 組裝請(qǐng)求參數(shù)
        final Map<String, String> params = new HashMap<String, String>(9);
        params.put(CommonParams.NAMESPACE_ID, namespaceId);
        params.put(CommonParams.SERVICE_NAME, serviceName);
        params.put(CommonParams.GROUP_NAME, groupName);
        params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
        params.put("ip", instance.getIp()); //服務(wù)的ip
        params.put("port", String.valueOf(instance.getPort())); // 端口
        params.put("weight", String.valueOf(instance.getWeight()));
        params.put("enable", String.valueOf(instance.isEnabled()));
        params.put("healthy", String.valueOf(instance.isHealthy()));
        params.put("ephemeral", String.valueOf(instance.isEphemeral()));
        params.put("metadata", JSON.toJSONString(instance.getMetadata()));
// 發(fā)送服務(wù)注冊(cè)請(qǐng)求 post 請(qǐng)求
        reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);

    }
  • reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);
public String reqAPI(String api, Map<String, String> params, String method) throws NacosException {

        List<String> snapshot = serversFromEndpoint;
        if (!CollectionUtils.isEmpty(serverList)) {
            snapshot = serverList;
        }

        return reqAPI(api, params, snapshot, method); // 發(fā)送請(qǐng)求
    }
  • reqAPI(api, params, snapshot, method);
public String reqAPI(String api, Map<String, String> params, List<String> servers, String method) {

        params.put(CommonParams.NAMESPACE_ID, getNamespaceId());

        if (CollectionUtils.isEmpty(servers) && StringUtils.isEmpty(nacosDomain)) {
            throw new IllegalArgumentException("no server available");
        }

        Exception exception = new Exception();

        if (servers != null && !servers.isEmpty()) {

            Random random = new Random(System.currentTimeMillis());
            int index = random.nextInt(servers.size()); // 隨機(jī)取一個(gè)服務(wù)地址進(jìn)行注冊(cè)

            for (int i = 0; i < servers.size(); i++) { 
                String server = servers.get(index);
                try {
                    return callServer(api, params, server, method); // 如果注冊(cè)失敗召耘,就執(zhí)行下面的邏輯,記錄日志收壕,并取 下一個(gè)服務(wù)地址轨蛤,直到注冊(cè)成功,return
                } catch (NacosException e) {
                    exception = e;
                    NAMING_LOGGER.error("request {} failed.", server, e);
                } catch (Exception e) {
                    exception = e;
                    NAMING_LOGGER.error("request {} failed.", server, e);
                }
// 如果 上面 注冊(cè)失敗圃验,就再取一個(gè)服務(wù)地址進(jìn)行注冊(cè)
                index = (index + 1) % servers.size();
            }

            throw new IllegalStateException("failed to req API:" + api + " after all servers(" + servers + ") tried: "
                + exception.getMessage());
        }

        for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) {
            try {
                return callServer(api, params, nacosDomain);
            } catch (Exception e) {
                exception = e;
                NAMING_LOGGER.error("[NA] req api:" + api + " failed, server(" + nacosDomain, e);
            }
        }

        throw new IllegalStateException("failed to req API:/api/" + api + " after all servers(" + servers + ") tried: "
            + exception.getMessage());

    }

上面代碼是服務(wù)注冊(cè)的邏輯,大致的意思是:注冊(cè)請(qǐng)求隨機(jī)發(fā)送的服務(wù)器供常,如果第一次就注冊(cè)成功栈暇,就return,否則繼續(xù)遍歷服務(wù)列表,繼續(xù)注冊(cè)煎源,直到注冊(cè)成功手销。下面看看callServer

  • callServer
 public String callServer(String api, Map<String, String> params, String curServer, String method)
        throws NacosException {
        long start = System.currentTimeMillis();
        long end = 0;
        checkSignature(params);
        List<String> headers = builderHeaders();

        String url;
        if (curServer.startsWith(UtilAndComs.HTTPS) || curServer.startsWith(UtilAndComs.HTTP)) {
            url = curServer + api; // 請(qǐng)求地址
        } else {
            if (!curServer.contains(UtilAndComs.SERVER_ADDR_IP_SPLITER)) {
                curServer = curServer + UtilAndComs.SERVER_ADDR_IP_SPLITER + serverPort;
            }
            url = HttpClient.getPrefix() + curServer + api;
        }
// 發(fā)送http 請(qǐng)求
        HttpClient.HttpResult result = HttpClient.request(url, headers, params, UtilAndComs.ENCODING, method);
        end = System.currentTimeMillis();

        MetricsMonitor.getNamingRequestMonitor(method, url, String.valueOf(result.code))
            .observe(end - start);

        if (HttpURLConnection.HTTP_OK == result.code) {
            return result.content; // 請(qǐng)求成功锋拖,返回?cái)?shù)據(jù)
        }

        if (HttpURLConnection.HTTP_NOT_MODIFIED == result.code) {
            return StringUtils.EMPTY;
        }

        throw new NacosException(NacosException.SERVER_ERROR, "failed to req API:"
            + curServer + api + ". code:"
            + result.code + " msg: " + result.content);
    }
  • 請(qǐng)求接口UtilAndComs.NACOS_URL_INSTANCE
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {

        NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}",
            namespaceId, serviceName, instance);

        final Map<String, String> params = new HashMap<String, String>(9);
        params.put(CommonParams.NAMESPACE_ID, namespaceId);
        params.put(CommonParams.SERVICE_NAME, serviceName);
        params.put(CommonParams.GROUP_NAME, groupName);
        params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
        params.put("ip", instance.getIp());
        params.put("port", String.valueOf(instance.getPort()));
        params.put("weight", String.valueOf(instance.getWeight()));
        params.put("enable", String.valueOf(instance.isEnabled()));
        params.put("healthy", String.valueOf(instance.isHealthy()));
        params.put("ephemeral", String.valueOf(instance.isEphemeral()));
        params.put("metadata", JSON.toJSONString(instance.getMetadata()));
// url = UtilAndComs.NACOS_URL_INSTANCE
        reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);

    }
  • UtilAndComs.NACOS_URL_INSTANCE
    public static String WEB_CONTEXT = "/nacos";

    public static String NACOS_URL_BASE = WEB_CONTEXT + "/v1/ns";

    public static String NACOS_URL_INSTANCE = NACOS_URL_BASE + "/instance";

/nacos/v1/ns/instance 接口地址馏慨,請(qǐng)求方式:POST。
ok姑隅,到這里我們就分析完了,最底層服務(wù)注冊(cè)請(qǐng)求邏輯倔撞。主要有:1.隨機(jī)將服務(wù)注冊(cè)到一個(gè)服務(wù)器讲仰,直到注冊(cè)成功。2. 請(qǐng)求接口是:/nacos/v1/ns/instance痪蝇,請(qǐng)求方式是post鄙陡,通信協(xié)議采用http 協(xié)議。

  • 總結(jié):
    我們使用nacos 的時(shí)候躏啰,發(fā)現(xiàn)我們服務(wù)一啟動(dòng)后,就會(huì)注冊(cè)服務(wù)到注冊(cè)中心给僵,于是我們就分析了一下毫捣,nacos 服務(wù)注冊(cè)的邏輯,主要有一下幾點(diǎn):
  1. 服務(wù)啟動(dòng)就注冊(cè)帝际,原因是 springboot 啟動(dòng)后發(fā)送啟動(dòng)事件蔓同,然后事件監(jiān)聽(tīng)器里發(fā)送服務(wù)注冊(cè)請(qǐng)求。
  2. 服務(wù)底層通信蹲诀,采用http 協(xié)議
  3. 服務(wù)注冊(cè)的時(shí)候斑粱,隨機(jī)注冊(cè)到一個(gè)nacos 服務(wù)器,如果第一次注冊(cè)失敗脯爪,就繼續(xù)取下一服務(wù)器地址则北,發(fā)送請(qǐng)求進(jìn)行注冊(cè),直到注冊(cè)成功痕慢,便返回尚揣。
    注意:有一段代碼沒(méi)有分析:
for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) {
            try {
                return callServer(api, params, nacosDomain);
            } catch (Exception e) {
                exception = e;
                NAMING_LOGGER.error("[NA] req api:" + api + " failed, server(" + nacosDomain, e);
            }
        }

        throw new IllegalStateException("failed to req API:/api/" + api + " after all servers(" + servers + ") tried: "
            + exception.getMessage());

如果有興趣的可以 nacosDomain 取得是啥值。上面的邏輯是沒(méi)有獲取到nacos 服務(wù)器地址時(shí)的執(zhí)行邏輯掖举。

public String reqAPI(String api, Map<String, String> params, List<String> servers, String method) {

        params.put(CommonParams.NAMESPACE_ID, getNamespaceId());

        if (CollectionUtils.isEmpty(servers) && StringUtils.isEmpty(nacosDomain)) {
            throw new IllegalArgumentException("no server available");
        }

        Exception exception = new Exception();

        if (servers != null && !servers.isEmpty()) { // 服務(wù)器地址不為空

            Random random = new Random(System.currentTimeMillis());
            int index = random.nextInt(servers.size());

            for (int i = 0; i < servers.size(); i++) {
                String server = servers.get(index);
                try {
                    return callServer(api, params, server, method);
                } catch (NacosException e) {
                    exception = e;
                    NAMING_LOGGER.error("request {} failed.", server, e);
                } catch (Exception e) {
                    exception = e;
                    NAMING_LOGGER.error("request {} failed.", server, e);
                }

                index = (index + 1) % servers.size();
            }

            throw new IllegalStateException("failed to req API:" + api + " after all servers(" + servers + ") tried: "
                + exception.getMessage());
        }
// 服務(wù)端地址為空的時(shí)候惑艇,執(zhí)行這段邏輯
        for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) { // 循環(huán)注冊(cè)3次
            try {
                return callServer(api, params, nacosDomain);
            } catch (Exception e) {
                exception = e;
                NAMING_LOGGER.error("[NA] req api:" + api + " failed, server(" + nacosDomain, e);
            }
        }

        throw new IllegalStateException("failed to req API:/api/" + api + " after all servers(" + servers + ") tried: "
            + exception.getMessage());

    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子滨巴,更是在濱河造成了極大的恐慌思灌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恭取,死亡現(xiàn)場(chǎng)離奇詭異泰偿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蜈垮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)耗跛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人攒发,你說(shuō)我怎么就攤上這事调塌。” “怎么了惠猿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵羔砾,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我偶妖,道長(zhǎng)姜凄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任趾访,我火速辦了婚禮态秧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扼鞋。我一直安慰自己申鱼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布云头。 她就那樣靜靜地躺著润讥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盘寡。 梳的紋絲不亂的頭發(fā)上楚殿,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音竿痰,去河邊找鬼脆粥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛影涉,可吹牛的內(nèi)容都是我干的变隔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蟹倾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼匣缘!你這毒婦竟也來(lái)了猖闪?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤肌厨,失蹤者是張志新(化名)和其女友劉穎培慌,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體柑爸,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吵护,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了表鳍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片馅而。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖譬圣,靈堂內(nèi)的尸體忽然破棺而出瓮恭,到底是詐尸還是另有隱情,我是刑警寧澤厘熟,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布屯蹦,位于F島的核電站,受9級(jí)特大地震影響盯漂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笨农,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一就缆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谒亦,春花似錦竭宰、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至锁摔,卻和暖如春廓旬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谐腰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工孕豹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人十气。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓励背,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親砸西。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叶眉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355