说一个人是武林高手:十八般武艺,样样精通!如今,后端技术层出不穷,让人眼花缭乱,如果看官不能达到样样精通,至少

拿起方天画戟能耍几下才行,比如削个苹果。言归正传,配置中心属于基础设施,当然必须玩得溜,不论Nacos还是Config,

今天我们来耍下Apollo,看好玩否。

作者原创文章,谢绝一切转载,违者必究!

本文只发表在"公众号"和"博客园",其他均属复制粘贴!如果觉得排版不清晰,请查看公众号文章。 

准备:

Idea2019.03/Gradle6.0.1/Maven3.6.3/JDK11.0.4/SpringBoot2.3.0RELEASE/Mysql8.0.11/Apollo1.7.0/RHEL8.0/ VMwareWorkstation15Pro

难度: 新手--战士--老兵--大师

目标:

1.完成Apollo分布式部署和简单应用

说明: 为了遇见各种问题,同时保持时效性,我尽量使用最新的软件版本,源码地址,其中的day31:https://github.com/xiexiaobiao/dubbo-project

1 部署图(单服务)

Apollo基础知识,略!请先安装好Maven和JDK环境!Linux内存建议4G以上!

注:Apollo自带Eureka组件,可以自动组成集群,当然,如果希望使用外部Eureka组件,需修改配置即可!

2 步骤

我使用最纯粹的源码编译部署方式,其他如Docker、QuickStart,略!

2.1 Git clone 项目源码文件到本地,Window下构建修改文件apollo\scripts\build.bat(Linux下则修改apollo\scripts\build.sh):

2.2 然后直接双击运行build.bat:

2.3 复制\apollo-adminservice,\apollo-configservice,\apollo-portal三个模块的 target 文件夹下的 zip 文件到Linux下,我这里分别放三个目

录admin-service, config-service, portal-service下:

解压:

[root@server226 config-service]# unzip apollo-configservice-1.7.0-SNAPSHOT-github.zip
[root@server226 admin-service]# unzip apollo-adminservice-1.7.0-SNAPSHOT-github.zip
[root@server226 portal-service]# unzip apollo-portal-1.7.0-SNAPSHOT-github.zip

启动服务,注意顺序:

[root@server226 portal-service]# bash /usr/apollo1.7.0/config-service/scripts/startup.sh
[root@server226 portal-service]# bash /usr/apollo1.7.0/admin-service/scripts/startup.sh
[root@server226 portal-service]# bash /usr/apollo1.7.0/portal-service/scripts/startup.sh

(常见启动异常问题,请见文末部分)

注:如果 ApolloConfigDB.ServerConfig 的 eureka.service.url 只配了当前正在启动的机器的话,在启动apollo-configservice的过程中会在日志

中输出eureka注册失败的信息,如com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused

需要注意的是,这个是预期的情况,因为apollo-configservice需要向Meta Server(它自己)注册服务,但是因为在启动过程中,自己还没起来,

所以会报这个错。后面会进行重试的动作,所以等自己服务起来后就会注册正常了。

2.4 在启动config-service成功后,即可打开http://192.168.2.226:8080/,(修改端口见后文),可以查看eureka界面:

如果src/main/config/application-github.properties中,开启以下两项:

apollo.eureka.server.enabled=true
apollo.eureka.client.enabled=true

2.5 验证是否全部启动成功:http://192.168.2.226:8070/ 账号/密码:apollo/admin 登录:

2.6 优雅关闭服务,注意顺序:

[root@server226 portal-service]# bash /usr/apollo1.7.0/portal-service/scripts/shutdown.sh
[root@server226 portal-service]# bash /usr/apollo1.7.0/admin-service/scripts/shutdown.sh
[root@server226 portal-service]# bash /usr/apollo1.7.0/config-service/scripts/shutdown.sh

2.7 修改config-service默认端口8080为8085:

  1. src/main/resources/application.yml 中修改 server:port: 8085
  2. src/main/resources/configservice.properties 中修改server.port= 8085
  3. src/main/scripts/startup.sh 中修改SERVER_PORT:=8085
  4. 数据库apolloconfigdb.serverconfig 中eureka.service.url 字段修改http://192.168.2.226:8085/eureka/

3 部署图(高可用)

架构部署图:

注:这里未画出client,client会先访问metaService,获取configService的列表,再从configService获取配置数据,可以使用SLB(Software Load Balancer)对client访问多个metaService做负载均衡。

同上方法,我修改config-service端口为8086,需修改(共5处) src/main/resources/application.yml,src/main/resources/configservice.properties,src/main/scripts/startup.sh,src/main/resources/configservice.properties, 数据库apolloconfigdb.serverconfig 中eureka.service.url 字段添加http://192.168.2.224:8086/eureka/,并使用同上apollo\scripts\build.bat脚本进行构建打包,放到另一Linux(192.168.2.224)上运行:

然后,可以访问:http://192.168.2.224:8086/

访问:http://192.168.2.226:8085/

由以上两图,可见eureka实例已组成集群,互为备份!

打开:http://192.168.2.226:8070/system_info.html,可以看到 portal 能管理所有的 configService和adminService实例:

4 客户端应用

apollo客户端,即在应用中访问apollo的配置数据,引入相关依赖包即可。具体依赖,略!

4.1 API方式(此方式不依赖spring框架)

配置appID和 metaService, src/main/resources/META-INF/app.properties

配置metaService,如果是多environment,通过src/main/resources/apollo-env.properties

指定env,Windows文件位置为 C:\opt\settings\server.properties (对于Mac/Linux,文件位置为/opt/settings/server.properties) ,内容为:

env=DEV

通过portal UI创建一个配置,最后效果如下:

测试代码(java):

public class ApiMain {

    private static final Logger logger = LoggerFactory.getLogger(ApiMain.class);
private String DEFAULT_VALUE = "undefined";
// config instance is singleton for each namespace and is never null
private Config config; ConfigChangeListener changeListener = configChangeEvent -> {
for (String key: configChangeEvent.changedKeys()
) {
ConfigChange change = configChangeEvent.getChange(key);
logger.info("Config Change >>>>> key: {}, oldValue: {}, newValue: {}, changeType: {}",
change.getPropertyName(), change.getOldValue(), change.getNewValue(),
change.getChangeType());
}
}; public ApiMain() {
this.config = ConfigService.getAppConfig();
this.config.addChangeListener(changeListener);
} public static void main(String[] args) throws IOException {
ApiMain apiMain = new ApiMain();
// getConfig("key"); key为配置数据中的key
apiMain.getConfig("name");
} private String getConfig(String key){
String result = config.getProperty(key,DEFAULT_VALUE);
logger.info(String.format("Loading key >>>> %s with value: %s", key, result));
return result;
}
}
 

以上代码解析: 通过 Config config 成员变量注入,并添加一个 ConfigChangeListener 做配置变化的监听器,可以在配置变化时得到通知,如数据库连接串变化后需要重建连接等。

运行输出:

读取到的配置信息:

在portal界面,进行“修改”->“发布”,监听到的配置变化:

4.2 Springboot方式

修改src/main/resources/config/application.yml:

注:多namespace的情况下,应用会默认读取非‘application’的配置;

写一个测试用bean,使用SPEL表达式就可以实现自动属性注入,冒号后数值为超时时间

public class ConfigBean {

    private String school;

    @Value("${timeValue:100}")
private String timeValue; public String getSchool() {
return school;
} @Value("${school:200}")
public void setSchool(String school) {
this.school = school;
}
...
}
 

另外我还写了一个注解方式的:

@ConfigurationProperties(prefix = "redis.cache")
public class SampleRedisConfig {
private int expireSeconds;
private int commandTimeout;
...
}
 

配合spring机制实现bean注入:

@Configuration
//@EnableApolloConfig(value = {"FX.apollo", "application.yml"}, order = 1)
public class SomeConfig { @Bean
public ConfigBean getConfigBean(){
return new ConfigBean();
} @Bean
public SampleRedisConfig sampleRedisConfig() {
return new SampleRedisConfig();
}
}
 

最后给个应用的入口:

@SpringBootApplication
@RestController
public class AppMain {
private ConfigBean configBean;
private SampleRedisConfig redisConfig; @Autowired
public AppMain(ConfigBean configBean,SampleRedisConfig redisConfig){
this.configBean = configBean;
this.redisConfig = redisConfig;
} public static void main(String[] args) {
SpringApplication.run(AppMain.class,args);
System.out.println("AppMain app started >>>>>>>>>>>>");
}
@RequestMapping("/config")
public String apollo(){
System.out.println("config 1 >>>> " + configBean.getSchool() +"/"+ configBean.getTime());
System.out.println("config 2 >>>> " + redisConfig.getCommandTimeout()+"/"+ redisConfig.getExpireSeconds());
return "config >>>> " + configBean.getSchool() +"/"+ configBean.getTime();
}
}
 

在portal界面配置KV值:

运行应用并URL访问:

控制台输出:

5 常见问题

5.1 查看config-service启动日志,发现mysql连接异常:

[root@server226 admin-service]# less /opt/logs/100003171/apollo-configservice.log

另一种查看mysql连接是否成功方法是启动apollo服务前后分别运行,比较连接数:

问题解决,注意window机器防火墙规则,可以直接关闭,或添加出入规则。

5.2 查看config-service启动日志,发现端口冲突异常:

解决问题,先查看端口占用情况:

[root@server226 config-service]# lsof -i:8080

直接kill 命令关闭,或者找到对应的应用进行关闭即可!

5.3 查看config-service启动日志,发现Eureka连接异常:

问题解决:Linux使用了hostname,导致localhost解析异常,修改 src/main/resources/bootstrap.yml :

注意需同步修改 apollo\apollo-adminservice 模块下的 src/main/resources/bootstrap.yml,内容同上。

遗留问题

1."访问密钥"开启的情况下,一直访问失败;

2.使用@ConfigurationProperties如果需要在Apollo配置变化时自动更新注入的值,需要配合使用EnvironmentChangeEventRefreshScope。相关代码实现,可以参考官方apollo-use-cases项目;

后记:在以上各个部署的每个步骤中,几乎都有多种实现方式,我只是使用了其中的1-2种,其他可参考官方说明!

全文完!


我近期其他文章:

只写原创,敬请关注

Dubbo学习系列之十九(Apollo分布式部署)的更多相关文章

  1. Dubbo学习系列之十五(Seata分布式事务方案TCC模式)

    上篇的续集. 工具: Idea201902/JDK11/Gradle5.6.2/Mysql8.0.11/Lombok0.27/Postman7.5.0/SpringBoot2.1.9/Nacos1.1 ...

  2. Dubbo学习系列之十六(ELK海量日志分析框架)

    外卖公司如何匹配骑手和订单?淘宝如何进行商品推荐?或者读者兴趣匹配?还有海量数据存储搜索.实时日志分析.应用程序监控等场景,Elasticsearch或许可以提供一些思路,作为业界最具影响力的海量搜索 ...

  3. Dubbo学习系列之十二(Quartz任务调度)

    Quartz词义为"石英"水晶,然后聪明的人类利用它发明了石英手表,因石英晶体在受到电流影响时,它会产生规律的振动,于是,这种时间上的规律,也被应用到了软件界,来命名了一款任务调度 ...

  4. Dubbo学习系列之十(Sentinel之限流与降级)

    各位看官,先提个问题,如果让你设计一套秒杀系统,核心要点是啥???我认为有三点:缓存.限流和分离.想当年12306大面积崩溃,还有如今的微博整体宕机情况,感觉就是限流降级没做好,"用有限的资 ...

  5. Dubbo学习系列之十四(Seata分布式事务方案AT模式)

    一直说写有关最新技术的文章,但前面似乎都有点偏了,只能说算主流技术,今天这个主题,我觉得应该名副其实.分布式微服务的深水区并不是单个微服务的设计,而是服务间的数据一致性问题!解决了这个问题,才算是把分 ...

  6. Dubbo学习系列之十八(Skywalking服务跟踪)

    我们知道,微服务不是独立的存在,否则就不需要微服务这个架构了,那么当发起一次请求,如何知道这次请求的轨迹,或者说遇到响应缓慢. 请求出错的情况,我们该如何定位呢?这就涉及到APM(Applicatio ...

  7. WP8.1学习系列(第十九章)——事件和路由事件概述

    我们将介绍在使用 C#.Visual Basic 或 Visual C++ 组件扩展 (C++/CX) 作为编程语言并使用 XAML 进行 UI 定义时,针对 Windows 运行时应用的事件的编程概 ...

  8. Dubbo学习系列之十一(Dashboard+Nacos规则推送)

    中国武术,门派林立,都是号称多少代的XXX传人,结果在面对现代武术时,经常被KO秒杀,为啥,光靠宣传和口号撑门面,终究是靠不住,必须得有真货 ,得经得住考验,所以不能只说Sentinel有多好,也得给 ...

  9. Dubbo学习系列之十三(Mycat数据库代理)

    软件界有只猫,不用我说,各位看官肯定知道是哪只,那就是大名鼎鼎的Tomcat,现在又来了一只猫,据说是位东方萌妹子,暂且认作Tom猫的表妹,本来叫OpencloudDB,后又改名为Mycat,或许Ca ...

  10. Dubbo学习系列之八(分布式事务之MQ方案)

    自从小王玩起了微服务,发现微服务果然很强大,好处真是太多,心中暗喜,然而,却也遇到了分布式中最棘手的问题:分布式事务.小王遍访各路神仙,也无个完美开源解决方案,当然,也有些实际可行的手法,虽不算完美, ...

随机推荐

  1. 从源码解析golang Timer定时器体系的来龙去脉

    大家好,我是思无邪,某go中厂开发工程师,也是OSPP2024的学生参与者! 如果你觉得我的文章有帮助,记得三连支持一下哦! 目前正在深入研究源码,与你们一起进步,共同攻克编程难关! 欢迎关注我的公众 ...

  2. FunPapers[1]: GBDT和DNN强强联手,表格预测新突破!

    Team up GBDTs and DNNs: Advancing Efficient and Effective Tabular Prediction with Tree-hybrid MLPs h ...

  3. Air实现Go程序的热重载(热加载)

    简介: air是Go的热加载工具,它可以监听文件或者目录的变化,自动编译,重启程序,提高开发的工作效率. 场景: 在代码修改后需要通过ctrl+c来停止项目,go run的方式来再次重启项目,在开发进 ...

  4. 鸿蒙开发 - 数据持久化 Preferences (内存存储) (封装)

    这篇文章介绍鸿蒙中的 Preferences,它是一种轻量级存储方式,数据存储在内存中,用于存储少量的数据. 可以执行 flush() 方法将内存中的数据写入到磁盘文件,保证下次重启后数据可以继续使用 ...

  5. mongoDb 的启动方式

    参考地址:https://www.cnblogs.com/LLBFWH/articles/11013791.html 一. 启动 1. 最简单的启动方式,前台启动,仅指定数据目录,并且使用默认的271 ...

  6. Scrapy 入门基础

    原文学习参考链接:https://blog.csdn.net/u011054333/article/details/70165401 问题解决参考链接:https://blog.csdn.net/du ...

  7. 最新版go-cqhttp的sign 签名服务器搭建教程

    安装go-cqhttp 传送门 自建sign签名服务器容器: 拉取镜像(只支持amd64) docker pull hansaes/unidbg-fetch-qsign:latest 启动容器 doc ...

  8. 【渗透测试】Vulnhub DarkHole

    渗透环境 攻击机:   IP: 192.168.216.129(Kali) 靶机:     IP:192.168.216.130 靶机下载地址:https://www.vulnhub.com/entr ...

  9. RedHat8密码复杂度策略配置

    1.密码复杂度策略概念 在Linux系统中,确保用户密码的复杂度是提高系统安全性的重要措施之一.通过配置密码策略,可以强制用户使用强密码,从而降低被破解的风险.本文将详细介绍如何在 Linux 系统中 ...

  10. CF2029C New Rating

    思路(二分 + 数据结构优化DP) 大致题意为:一个值 \(x\) 初始为 \(0\),然后有一个数组 \(a\),遍历一次数组. 如果 \(a_i > x\),则 \(x + 1\). 如果 ...