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}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章