Nacos如何實現服務自動註冊

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1. 背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接着上次的說,Nacos問題的分析。這次就來分享一下自己的分析方法。以及如何利用Spring事件完成服務註冊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2. 分析一下"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1 問題分析過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"問題是這樣的,我以爲是Nacos服務註冊不上嗎? 那就肯定是註冊有問題了, 那就去找是服務是如何註冊的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關鍵是從哪找呢?具體步驟:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"知道服務啓動方式。如Nacos,就是通過properties,那就找哪有用到啓動的屬性文件"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"項目用SpringBoot集成,那核心類的加載就在Spring.factories中。(這個文件會被springSPI加載到)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"找到關鍵類"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很湊巧,我們在spring.factories中找到了名字很像啓動註冊類的類。如下圖:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d7/d7f6b9803f8040ac572a7a4c82f8d7c6.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 在找類的時候不要怕錯,大膽猜,只要驗證了自己的想法即可,不對就在接着找。 "}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration"}]},{"type":"text","text":"類我們找到了,接下來就是去看這個類中的代碼去驗證自己的想法了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2 Nacos是如何利用Spring事件來實現服務註冊的?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration"}]},{"type":"text","text":"代碼如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class NacosServiceRegistryAutoConfiguration {\n\t// 註冊類實現,驗證我們的想法,需要用到NacosDiscoverProperties\n\t@Bean\n\tpublic NacosServiceRegistry nacosServiceRegistry(\n\t\t\tNacosDiscoveryProperties nacosDiscoveryProperties) {\n\t\treturn new NacosServiceRegistry(nacosDiscoveryProperties);\n\t}\n\t// 構建註冊事例,依然要用到NacosDiscoverProperties\n\t@Bean\n\t@ConditionalOnBean(AutoServiceRegistrationProperties.class)\n\tpublic NacosRegistration nacosRegistration(\n\t\t\tNacosDiscoveryProperties nacosDiscoveryProperties,\n\t\t\tApplicationContext context) {\n\t\treturn new NacosRegistration(nacosDiscoveryProperties, context);\n\t}\n\t// 服務自動註冊,通過上面實例化的兩個類\n\t@Bean\n\t@ConditionalOnBean(AutoServiceRegistrationProperties.class)\n\tpublic NacosAutoServiceRegistration nacosAutoServiceRegistration(\n\t\t\tNacosServiceRegistry registry,\n\t\t\tAutoServiceRegistrationProperties autoServiceRegistrationProperties,\n\t\t\tNacosRegistration registration) {\n\t\treturn new NacosAutoServiceRegistration(registry,\n\t\t\t\tautoServiceRegistrationProperties, registration);\n\t}\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過查看代碼,發現和我們猜想沒有出入,是通過"},{"type":"codeinline","content":[{"type":"text","text":"NacosDiscoverProperties"}]},{"type":"text","text":"來進行初始化,和官方給的Demo也相似(只是沒繼承SpringBoot,名字不叫這個),前面兩個類我們先不用關注(好奇的可以先點進去看看),我們直接進入第三個方法"},{"type":"codeinline","content":[{"type":"text","text":"NacosAutoServiceRegistration"}]},{"type":"text","text":"中,通過名字我們也可以看出,這個是自動註冊的,參數中包含了Registry和Registration。我們進入類中接着查看。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class NacosAutoServiceRegistration\n\t\textends AbstractAutoServiceRegistration {\n\n\tprivate NacosRegistration registration;\n\t// 構造參數,關鍵\n\tpublic NacosAutoServiceRegistration(ServiceRegistry serviceRegistry,\n\t\t\tAutoServiceRegistrationProperties autoServiceRegistrationProperties,\n\t\t\tNacosRegistration registration) {\n\t\tsuper(serviceRegistry, autoServiceRegistrationProperties);\n\t\tthis.registration = registration;\n\t}\n // 註冊方法\n @Override\n\tprotected void register() {\n\t\tif (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {\n\t\t\tlog.debug(\"Registration disabled.\");\n\t\t\treturn;\n\t\t}\n\t\tif (this.registration.getPort() < 0) {\n\t\t\tthis.registration.setPort(getPort().get());\n\t\t}\n // 調用了父類的註冊方法\n\t\tsuper.register();\n\t}\n} "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進入類中,我們發現了register()方法,更進一步驗證我們想法,是通過這個類來進行服務註冊,但是在register()方法中,調用了父類的register()方法, 這點就要引起我們的好奇,父類都沒都啥東西,調用父類的方法有啥用呢? 這個時候就想到了肯定有構造參數,或者方法初始化父類的東西啦!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們觀察構造參數,發現把serviceRegistry傳入到了父類構造參數中,我們直接查看父類代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 類\npublic abstract class AbstractAutoServiceRegistration\n\t\timplements AutoServiceRegistration, ApplicationContextAware,\n\t\tApplicationListener {\n \n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"AbstractAutoServiceRegistration"}]},{"type":"text","text":"是繼承了"},{"type":"codeinline","content":[{"type":"text","text":"ApplicationListener"}]},{"type":"text","text":",看到這,不就是我們熟悉的領域了嗎?繼承了"},{"type":"codeinline","content":[{"type":"text","text":"ApplicationListener"}]},{"type":"text","text":"那就不多BB,直接看"},{"type":"codeinline","content":[{"type":"text","text":"onApplicationEvent()"}]},{"type":"text","text":"方法就好啦。(這個事件是WebServerInitializedEvent,這點還是要知道的哈)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\t@Override\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void onApplicationEvent(WebServerInitializedEvent event) {\n // 調用了bind方法\n\t\tbind(event);\n\t}\n\t\n\t@Deprecated\n\tpublic void bind(WebServerInitializedEvent event) {\n\t\tApplicationContext context = event.getApplicationContext();\n\t\tif (context instanceof ConfigurableWebServerApplicationContext) {\n\t\t\tif (\"management\".equals(((ConfigurableWebServerApplicationContext) context)\n\t\t\t\t\t.getServerNamespace())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthis.port.compareAndSet(0, event.getWebServer().getPort());\n // 調用了start()方法\n\t\tthis.start();\n\t}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們直接進入start()方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\tpublic void start() {\n\t\tif (!isEnabled()) {\n\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\tlogger.debug(\"Discovery Lifecycle disabled. Not starting\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t// only initialize if nonSecurePort is greater than 0 and it isn't already running\n\t\t// because of containerPortInitializer below\n\t\tif (!this.running.get()) {\n // 時間註冊之前事件\n\t\t\tthis.context.publishEvent(\n\t\t\t\t\tnew InstancePreRegisteredEvent(this, getRegistration()));\n // 調用了註冊方法\n\t\t\tregister();\n // 服務註冊事件\n\t\t\tthis.context.publishEvent(\n\t\t\t\t\tnew InstanceRegisteredEvent<>(this, getConfiguration()));\n\t\t\tthis.running.compareAndSet(false, true);\n\t\t}\n\n\t}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"start()方法裏面還發布了兩個事件,可見Spring中,服務的註冊前後都有一些監聽器來處理服務信息。先跳過這些,我們直接關注我們的核心——註冊,直接進入register()方法中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\tprotected void register() {\n // this.serviceRegistry 是我們傳遞進來的,\n // getRegistration() 是在NacosAutoServiceRegistration中實現的\n\t\tthis.serviceRegistry.register(getRegistration());\n\t}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到這我們基本知道是怎麼回事了,"},{"type":"codeinline","content":[{"type":"text","text":"this.serviceRegistry"}]},{"type":"text","text":"是傳遞進來的,那調用的"},{"type":"codeinline","content":[{"type":"text","text":"register()"}]},{"type":"text","text":"方法則是Nacos自己實現的註冊,"},{"type":"codeinline","content":[{"type":"text","text":"getRegistration()"}]},{"type":"text","text":"是"},{"type":"codeinline","content":[{"type":"text","text":"NacosServiceRegistryAutoConfiguration"}]},{"type":"text","text":"中通過"},{"type":"codeinline","content":[{"type":"text","text":"nacosDiscoveryProperties"}]},{"type":"text","text":"生成的。那我們就看一下"},{"type":"codeinline","content":[{"type":"text","text":"register()"}]},{"type":"text","text":"方法就好啦。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\t@Override\n\tpublic void register(Registration registration) {\n\n\t\tif (StringUtils.isEmpty(registration.getServiceId())) {\n\t\t\tlog.warn(\"No service to register for nacos client...\");\n\t\t\treturn;\n\t\t}\n\n\t\tString serviceId = registration.getServiceId();\n\t\tString group = nacosDiscoveryProperties.getGroup();\n\t\t// 通過registration構建Instance實例\n\t\tInstance instance = getNacosInstanceFromRegistration(registration);\n\n\t\ttry {\n // 向服務端發送註冊請求\n\t\t\tnamingService.registerInstance(serviceId, group, instance);\n\t\t\tlog.info(\"nacos registry, {} {} {}:{} register finished\", group, serviceId,\n\t\t\t\t\tinstance.getIp(), instance.getPort());\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tlog.error(\"nacos registry, {} register failed...{},\", serviceId,\n\t\t\t\t\tregistration.toString(), e);\n\t\t\t// rethrow a RuntimeException if the registration is failed.\n\t\t\t// issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132\n\t\t\trethrowRuntimeException(e);\n\t\t}\n\t}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到這我們就看懂了,也知道大概流程了。流程圖如下"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d2/d20ceedc971c13fe5e03083165910f32.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3. 吸星大法:吸收代碼經驗"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring 在實現的時候,通過設計模式(裝飾),實現流程,又把關鍵部分丟給開發自己實現,提高了拓展性,然後巧妙的結合了觀察者模式(變種),在合適的時間註冊服務,妙啊~~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Nacos在實現的時候,深知Spring各種屬性,各種用法,一定是對Spring有個深入瞭解的人(或者正好看過? 這個解釋有點牽強),結合AutoRegistry和事件(不知道,肯定不知道怎麼調用的),妙啊~~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到這實現的代碼,簡直舒服,妙啊~~ "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"xua~~"},{"type":"text","marks":[{"type":"del"}],"text":"(), 爽啊 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"4. 總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總的看下來,這個啓動,只能有Web容器的時候才能註冊, 我猜主要是和namingServer註冊有關,所以也不能算是一個bug了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章