服务器是如何感知更新的我们看一张官网提供的图:1.用户在Portal操作中配置和发布。2.Portal调用AdminService的接口进行操作和发布。3.AdminService发布配置后,向各个ConfigService发送ReleaseMessage。4.ConfigService收到ReleaseMessage后通知对应的client。以上流程是Portal到ConfigService的主要流程。让我们来看看具体的细节。要想知道细节,还得自己调试源码。我们可以根据官网的文档,自己在本地运行项目。文档还是很详细的,只要按照步骤操作就可以运行了。我们新建一个项目,编辑key,然后打开浏览器的F12。当我们点击提交按钮时,我们就会知道她调用了哪些接口。有了界面,我们就知道入口了。剩下的就是破题了。调试。Portal是如何获取AdminService的根据这个方法,我们可以定位到Portal模块后端代码的controller。找到对应的controller,打开可以看到基本没有业务逻辑。然后门户立即调用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(bu年长者.build());commit.setDataChangeCreatedBy(dto.getDataChangeLastModifiedBy());commit.setDataChangeLastModifiedBy(dto.getDataChangeLastModifiedBy());commitService.save(commit);返回数据;注意,根据名字大家应该能猜到,大概是获取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”)Stringoperator,@RequestParam(name=“isEmergencyPublish”,defaultValue=“false”)booleanisEmergencyPublish){Namespacenamespace=namespaceService.findOne(appId,clusterName,命名空间名称);if(namespace==null){thrownewNotFoundException(String.format("找不到%s%s%s的命名空间",appId,clusterName,namespaceName));}Releaserelease=releaseService.publish(namespace,releaseName,releaseComment,operator,isEmergencyPublish);//发送释放消息NamespaceparentNamespace=namespaceService.findParentNamespace(namespace);字符串消息集群;if(parentNamespace!=null){messageCluster=parentNamespace.getClusterName();}else{messageCluster=clusterName;}messageSender.send(ReleaseMessageKeyGenerator.generate(appId,messageCluster,namespaceName),Topics.APOLLO_RELEASE_TOPIC);返回BeanUtils.transform(ReleaseDTO.class,release);上面的代码就不详细分析了。有兴趣的可以自己调试我们的重点看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的消息通知变化就串起来了。综上所述,如何更新服务器配置的过程就结束了。1、用户配置并发布Portal操作。2.Portal调用AdminService的接口进行操作和发布。3.AdminService发布配置后,向各个ConfigService发送ReleaseMessage。4、ConfigService收到ReleaseMessage后,通知对应的client,apollo的源码相对于其他中间件来说比较简单,更适合想研究中间件源码但不知道的同学如何开始。
