Dubbo學習系列之十九(Apollo分布式部署)


 

說一個人是武林高手:十八般武藝,樣樣精通!如今,后端技術層出不窮,讓人眼花繚亂,如果看官不能達到樣樣精通,至少

拿起方天畫戟能耍幾下才行,比如削個蘋果。言歸正傳,配置中心屬於基礎設施,當然必須玩得溜,不論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種,其他可參考官方說明!

 

全文完!


我近期其他文章:

       只寫原創,敬請關注 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM