說一個人是武林高手:十八般武藝,樣樣精通!如今,后端技術層出不窮,讓人眼花繚亂,如果看官不能達到樣樣精通,至少
拿起方天畫戟能耍幾下才行,比如削個蘋果。言歸正傳,配置中心屬於基礎設施,當然必須玩得溜,不論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:
- src/main/resources/application.yml 中修改 server:port: 8085
- src/main/resources/configservice.properties 中修改server.port= 8085
- src/main/scripts/startup.sh 中修改SERVER_PORT:=8085
- 數據庫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配置變化時自動更新注入的值,需要配合使用EnvironmentChangeEvent
或RefreshScope
。相關代碼實現,可以參考官方apollo-use-cases
項目;
后記:在以上各個部署的每個步驟中,幾乎都有多種實現方式,我只是使用了其中的1-2種,其他可參考官方說明!
全文完!
我近期其他文章:
- 1 聊聊算法——二分查找算法深度分析
- 2 DevOps系列——Jenkins/Gitlab自動打包部署
- 3 DevOps系列——Jenkins私服
- 4 DevOps系列——Gitlab私服
- 5 聊聊算法——滑動窗口
只寫原創,敬請關注