简介我之前写过一篇文章《分布式配置中心apollo是如何实时感知配置被修改》,就是客户端如何知道配置被修改了。很多读者私信我。既然你说了client是怎么感知的,那server是怎么感知的呢?知道修改了配置。今天我们就来看看Apollo是如何修改Portal中的配置文件,以及如何通知configService的。什么是portal和configService?我建议你阅读这篇《分布式配置中心apollo是如何实时感知配置被修改》文章,其中对这些模块进行了简单的介绍。如果实在不想看,那我直接截图看看服务器是怎么感知更新的。官网提供的一张图1.用户在Portal上操作并发布配置2.Portal调用AdminService的接口操作发布3.AdminService发布配置后,向各个Config发送ReleaseMessageService4.ConfigService收到ReleaseMessage后,通知对应的customer端的流程是Portal到ConfigService的主流程。让我们来看看具体的细节。要想知道细节,还得自己调试源码。我们可以根据官网的文档,自己在本地运行项目。文档还是很详细的,只要按照步骤操作就可以运行了。我们新建一个项目,编辑key,然后打开浏览器的F12。当我们点击提交按钮时,我们就会知道她调用了哪些接口。有了界面,我们就知道入口了。剩下的就是破题了。调试。Portal是如何获取AdminService的根据这个方法,我们可以定位到Portal模块后端代码的controller。找到对应的controller打开可以看到基本没有业务逻辑,然后portal立马调用adminService。根据上图,我们可以找到对应的adminService,传送门怎么找到对应的adminService服务,因为adminService可以部署多台机器,这里需要用到服务注册,发现adminService只能注册到服务上去center,门户可以通过服务注册中心获取对应的adminService服务。默认情况下,Apollo使用eureka进行服务注册和发现。它还提供nacos和consul用于服务注册和发现。它还提供了一种不使用第三方进行服务注册和发现的kubernetes,直接在数据库中配置服务的地址。如果有多个地址,可以在数据库中用逗号分隔。它提供了四种获取服务列表的实现方法。如果我们使用的注册中心是eureka,是否需要通过eurekaapi获取服务列表?如果我们的服务发现使用nacos,是否需要使用nacosAPI?获取服务列表。..因此,Apollo提供了一个MetaService层来封装服务发现的细节。对于Portal和Client来说,它们始终是通过Http接口获取AdminService和ConfigService的服务信息,而不关心其背后实际的服务注册和发现组件。就像我们平时搬砖一样,没有什么问题是加个中间层解决不了的。如果一个失败,则添加另一个。所以MetaService提供了两个接口services/admin和services/config分别获取AdminService和ConfigService的服务信息。那么Portal是如何调用services/admin接口的呢?在apollo-portal项目中类com.ctrip.framework.apollo.portal.component#AdminServiceAddressLocator中,加载该类时会通过MetaService提供的services/admin接口获取adminService的服务地址进行缓存。@PostConstructpublicvoidinit(){allEnvs=portalSettings.getAllEnvs();//初始化restTemplaterestTemplate=restTemplateFactory.getObject();refreshServiceAddressService=Executors.newScheduledThreadPool(1,ApolloThreadFactory.create("ServiceLocator",true));//创建延时任务,1秒后开始执行获取AdminService服务地址refreshServiceAddressService.schedule(newRefreshAdminServerAddressTask(),1,TimeUnit.MILLISECONDS);}上面是去MetaService请求地址,那么MetaService是什么地址?如何得到这个?com.ctrip.framework.apollo.portal.environment#DefaultPortalMetaServerProvider这个类。portal模块说完了,我们回到adminService。通过入口调用adminService的接口地址,我们可以很快找到它的入口AdminService实现也很简单@PreAcquireNamespaceLock@PostMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items")publicItemDTOcreate(@PathVariable("appId")StringappId,@PathVariable("clusterName")StringclusterName,@PathVariable("namespaceName")StringnamespaceName,@RequestBodyItemDTOdto){Item实体=BeanUtils.transform(Item.class,dto);ConfigChangeContentBuilder构建器=newConfigChangeContentBuilder();ItemmanagedEntity=itemService.findOne(appId,clusterName,namespaceName,entity.getKey());if(managedEntity!=null){thrownewBadRequestException("项目已经存在");}entity=itemService.save(entity);builder.createItem(实体);dto=BeanUtils.transform(ItemDTO.class,entity);提交commit=newCommit();commit.setAppId(appId);commit.setClusterName(clusterName);commit.setNamespaceName(namespaceName);commit.setChangeSets(builder.build());commit.setDataChangeCreatedBy(dto.getDataChangeLastModifiedBy());commit.setDataChangeLastModifiedBy(dto.getDataChangeLastModifiedBy());(犯罪);返回数据;}PreAcquireNamespaceLock注解首先,方法上有一个@PreAcquireNamespaceLock注解。根据名字大家应该能猜到一个,大概就是获取NameSpace的分布式锁。现在分布式锁比较常见的方式是使用redis和zookeeper,但是这里Apollo是使用数据库来实现的。你可以去查看源代码来了解细节。无非就是在DB中插入一条数据加锁,释放锁,然后删除数据。有点不同的是,如果获取锁失败,则直接返回失败,不会继续自旋或休眠再次获取锁。因为获取锁失败说明别人在你之前修改了配置,只有发布或删除此人的新配置后,其他人才能继续添加配置。在这种情况下,一个NameSpace只能同时被一个人使用。修订。这个限制默认是关闭的,我们需要在数据库中配置(ApolloConfigDb的ServiceConfig表)。一般来说,我们应用的配置修改应该是比较少的。如果是多人同时修改,那就比较少见了。此外,一些公司开发并提交配置。测试发布配置,提交和修改不能是同一个人,这样新的配置冲突会少一些,应该不需要配置namespace.lock.switch=true。命名空间只能由一个人修改。下面的代码非常简单明了,就是一个简单的参数判断然后执行入库操作,将数据插入到Item表中。这是我们新的配置数据已经保存。效果如下:此时新添加的配置将不起作用,不会推送到客户端。只是一个类似于草稿的状态。发布配置接下来,我们需要让上面的新配置生效,并推送到客户端。同样的,我们点击发布按钮,就可以知道对应的后端方法入口了。通过这个接口,我们可以直接找到adminService的方法入口publicReleaseDTOpublish(@PathVariable("appId")StringappId,@PathVariable("clusterName")StringclusterName,@PathVariable("namespaceName")StringnamespaceName,@RequestParam("name")StringreleaseName,@RequestParam(name="comment",required=false)StringreleaseComment,@RequestParam("operator")字符串运算符,@RequestParam(name="isEmergencyPublish",defaultValue="false")booleanisEmergencyPublish){命名空间namespace=namespaceService.findOne(appId,clusterName,namespaceName);if(namespace==null){thrownewNotFoundException(String.format("找不到%s%s%s的命名空间",appId,集群名称、命名空间名称));}Releaserelease=releaseService.publish(namespace,releaseName,releaseComment,operator,isEmergencyPublish);//发送释放消息NamespaceparentNamespace=namespaceService.findParentNamespace(namespace);字符串消息集群;if(parentNamespace!=null){messageCluster=parentNamespace.getClusterName();}else{messageCluster=clusterName;}messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId,messageCluster,namespaceName),Topics.APOLLO_RELEASE_TOPIC);returnBeanUtils.transform(ReleaseDTO.class,}上面的代码就不详细分析了,有兴趣的可以自行debug,我们重点关注releaseService.publish方法,里面有一些灰度发布相关的逻辑,不过这不是本文的重点,以后主要用到这个方法,向release表插入数据后面是messageSender.sendMessage方法,主要是往ReleaseMessage表中插入一条记录,保存ReleaseMessage后表,就会得到对应的主键ID,然后把这个ID放入队列中。然后在加载DatabaseMessageSender的时候,会默认设置一个定时任务,获取上面队列中放入的消息ID,然后找出小于这些ID的消息删除。发布过程结束,并没有提到服务器如何感知有配置修改。配置服务通知配置更改。服务启动时,ReleaseMessageScanner会每隔1s启动一个定时任务,检查ReleaseMessage中是否有最新消息。如果有,会通知所有的消息监听器,如NotificationControllerV2、ConfigFileController等,这个消息监听器注册在ConfigServiceAutoConfiguration中注册。NotificationControllerV2拿到配置释放的AppId+Cluster+Namespace后,会通知对应的client,这样整个从portal到configService再到client的消息通知变化就串起来了。服务端如何通知客户端的细节可以参考《分布式配置中心apollo是如何实时感知配置被修改》总结至此,关于如何更新服务端配置的过程就结束了。1.用户在Portal上操作并发布配置。2.Portal调用AdminService接口操作发布。3.AdminService发布配置后,向各个ConfigService发送ReleaseMessage。4、ConfigService收到ReleaseMessage后,通知对应的client,apollo的源码是其他中间件,比较简单,比较适合想研究中间件源码,但不知道如何实现的同学开始。最后,由于自己的无知和知识的匮乏,难免有错误。如果您发现错误,请留言指出给我,我会改正。如果您觉得文章还不错,您的转发、分享、欣赏、点赞、评论就是对我最大的鼓励。感谢您的阅读,非常欢迎并感谢您的关注。站在巨人的肩膀上https://www.apolloconfig.com/...https://www.iocoder.cn/Apollo...
