apollo
作者:互联网
架构图:
上图有四个核心模块:
ConfigService 服务对象是 Apollo 客户端。
提供配置获取接口 提供配置推送接口 服务于Apollo客户端
AdminService 服务对象是Apollo Portal(管理界面)。
Config Service 和admin service 读取的是相同的ApolloPortalDB数据库。因为Admin service因为需要不断开发API给
Portal ,所以回不定期重启。
提供配置管理接口 提供配置修改发布接口 服务于管理界面Portal
Client
为应用获取配置,支持实时更新 通过MetaServer获取ConfigService的服务列表 使用客户端软负载SLB方式调用ConfigService
Portal 服务对象是开发者和 开放平台 API。
Admin Service 和portal分离的原因:不同的环境中,回部署在不同的网络环境下进行隔离,
每套环境都部署一套Admin Service +Config Service ,而portal是管理界面,通过调用不同环境部署的Admin Service提供
的API接口,进行不同环境的配置管理
配置管理界面 通过MetaServer获取AdminService的服务列表 使用客户端软负载SLB方式调用AdminService
辅助服务发现模块
Eureka
用于服务发现和注册 Config/AdminService注册实例并定期报心跳 和ConfigService住在一起部署
MetaServer
Portal通过域名访问MetaServer获取AdminService的地址列表 Client通过域名访问MetaServer获取ConfigService的地址列表 相当于一个Eureka Proxy 逻辑角色,和ConfigService住在一起部署
NginxLB
和域名系统配合,协助Portal访问MetaServer获取AdminService地址列表 和域名系统配合,协助Client访问MetaServer获取ConfigService地址列表 和域名系统配合,协助用户访问Portal进行配置管理
实现原理:
1客户端和服务端保持一个长连接,从而能够第一时间获得配置更新的推送(通过HTTP LONG POlling实现)
2客户端还会定时从Apollo配置中心服务端拉取pull,最新配置
@这是一个fallback机制,为了防止推送机制失效导致【配置不跟新
@客户端定时拉去回上报本地版本,服务端一般都会返回304-Not Modified
@定时频率为5分钟拉一次,也可以指定,apollo.refreshInterval设置
3客户端拉去到最新配置后,会保存到内存当中
4获取到的配置在本地文件系统缓存一份。。遇到服务不可用或者网络不通的时候,依然能够从本地恢复配置
5应用程序可以从apollo客户端获取最新的配置,订阅配置更新通知
最简的架构图:
ConfigService是一个独立的微服务,服务于Client进行配置获取。 Client和ConfigService保持长连接,通过一种拖拉结合(push & pull)的模式,实现配置实时更新的同时,保证配置更新不丢失。 AdminService是一个独立的微服务,服务于Portal进行配置管理。Portal通过调用AdminService进行配置管理和发布。 ConfigService和AdminService共享ConfigDB,ConfigDB中存放项目在某个环境的配置信息。ConfigService/AdminService/ConfigDB三者在每个环境(DEV/FAT/UAT/PRO)中都要部署一份。 Protal有一个独立的PortalDB,存放用户权限、项目和配置的元数据信息。Protal只需部署一份,它可以管理多套环境。
为了保证高可用,ConfigService和AdminService都是无状态以集群方式部署的,这个时候就存在一个服务发现问题:
引入Eureka
Config/AdminService启动后都会注册到Eureka服务注册中心,并定期发送保活心跳。
最终的架构:
如果Apollo只支持Java客户端接入,不支持其它语言客户端接入的话,那么Client和Portal只需要引入Eureka的Java客户
端,就可以实现服务发现功能。发现目标服务后,通过客户端软负载(SLB,例如Ribbon)就可以路由到目标服务实例。 客户应用是非Java的。但是Eureka(包括Ribbon软负载)原生仅支持Java客户端,如果要为多语言开发Eureka/Ribbon客户
端,这个工作量很大也不可控。为此,Apollo的作者引入了MetaServer这个角色,它其实是一个Eureka的Proxy,将
Eureka的服务发现接口以更简单明确的HTTP接口的形式暴露出来,方便Client/Protal通过简单的HTTPClient就可以查询
到Config/AdminService的地址列表。获取到服务实例地址列表之后,再以简单的客户端软负载(Client SLB)策略路由
定位到目标实例,并发起调用。 MetaServer本身也是无状态以集群方式部署的,那么Client/Protal该如何发现MetaServer呢?一种传统的做法是借助
硬件或者软件负载均衡器,例如在携程采用的是扩展后的NginxLB(也称Software Load Balancer),由运维为
MetaServer集群配置一个域名,指向NginxLB集群,NginxLB再对MetaServer进行负载均衡和流量转发。
Client/Portal通过域名+NginxLB间接访问MetaServer集群。 Portal也是无状态以集群方式部署的,用户通过域名+NginxLB间接访问MetaServer集群
官网quickStart,
spring-boot-User-Apollo
apollo的web地址:http://localhost:8070/
apollo的Eurka地址:http://localhost:8080/
先在apollo里面配置一个项目:
环境为:DEV
Appid:springbootTest
两个值:server.port = 8686 timeout = 23112
spring-boot项目:
添加apollo客户端的依赖:
<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.1.0</version> </dependency>
配置文件:
apollo.boostrap.enabled = true 在应用的启动阶段,向spring容器注入被托管的application.properties app.id = springbootTest APPID 是配置中心获取配置的一个重要信息 apollo.bootstrap.eagerLoad.enabled=true 将Apollo配置家在提到初始化日志系统之前,要不然可能没有关于apollo的日志输出 apollo.meta = http://127.0.0.1:8080 apolloService的地址 server.port= 8989 timeout= logging.level.com.quan.apollo.controller = debug 选
注意:
http://127.0.0.1:8080这个地址并不是我们访问的apollo WEB端的地址
@RestController public class Aptest { @Value("${server.port}") String port; @Value("${timeout}") String time; @RequestMapping("done") public void apTest(){ System.out.println(port); System.out.println(time); } }
java-User-apolloAPI-Test:
普通的java项目,通过加入系统参数进行配置apollo:
-Dapp.id=apolloTestID -Dapollo.meta=http://localhost:8080 -Dapollo.configService=http://localhost:8080
-Denv=DEV
添加依赖:
<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.1.0</version> </dependency>
如果运行时报错:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defa
再添加一个以来:
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.2</version> </dependency>
使用apolloAPI:
public class ApolloT { public static void main(String[] args) { Config config = ConfigService.getConfig("application"); System.out.println(config); String somekey = "timeout"; String va = config.getProperty(somekey,"de"); System.out.println(va); } }
re:
监听配置变化事件:
public static void main(String[] args) { while (true){ try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } Config config = ConfigService.getConfig("application"); config.addChangeListener(new ConfigChangeListener() { @Override public void onChange(ConfigChangeEvent configChangeEvent) { System.out.println("Changes for namespace" + configChangeEvent.getNamespace()); for (String key :configChangeEvent.changedKeys()){ ConfigChange change = configChangeEvent.getChange(key); System.out.println(change.getOldValue()+"###"+change.getNewValue()+"###"+change.getChangeType()); } } }); System.out.println(config); String somekey = "timeout"; String va = config.getProperty(somekey,"de"); System.out.println(va); } } }
运行之后,修改apollo上的配置:
com.ctrip.framework.apollo.internals.DefaultConfig@358c99f5 98984444 Changes for namespaceapplication 98984444###98984444000###MODIFIED com.ctrip.framework.apollo.internals.DefaultConfig@358c99f5 98984444000
namespace;
java API 访问:
public class ApolloT { public static void main(String[] args) { while (true){ try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } Config config1 = ConfigService.getConfig("application"); config1.addChangeListener(new ConfigChangeListener() { @Override public void onChange(ConfigChangeEvent configChangeEvent) { System.out.println("Changes for namespace" + configChangeEvent.getNamespace()); for (String key :configChangeEvent.changedKeys()){ ConfigChange change = configChangeEvent.getChange(key); System.out.println(change.getOldValue()+"###"+change.getNewValue()+"###"+change.getChangeType()); } } }); System.out.println(config1); String somekey = "timeout"; String va = config1.getProperty(somekey,"de"); System.out.println(va); Config config2 = ConfigService.getConfig("publiceTest"); System.out.println(config2.getProperty("name","Q")); } } }
com.ctrip.framework.apollo.internals.DefaultConfig@358c99f5 98984444000 quanQQ com.ctrip.framework.apollo.internals.DefaultConfig@358c99f5 98984444000 quanQQ
namespace的格式:
配置文件有多种格式,例如:properties、xml、yml、yaml、json等
在Portal UI中可以看到“application”的Namespace上有一个“properties”标签,表明“application”是properties格式的
namespace的类型;
私有类型,共有类型,关联类型
公共类型,publice,相当于不受控制,不接受应该的约束。且通过namspace去
唯一表示公共的namespace ,所以!!!!公共namespaces必须全局唯一
如果不唯一,很有可能直接得到的配置是私有类型或者关联类型的配置。
使用场景:部门级别共享的配置,小组的配置
私有类型;只能当前应用拉去。
关联类型,就是建立一个私有类型的namespace去继承共有类型,可以对共有类型的所有属性进行覆盖修改。
提供一份公司默认的配置且可调整
同时每个用户也是可以自定义修改的
简单来讲,就是,一个是default,一个是owner,其中owner没有进行设置的就使用default
标签:ConfigService,配置,println,apollo,MetaServer,客户端 来源: https://www.cnblogs.com/java-quan/p/13424976.html