聊聊如何將資料同步到apollo配置中心

linyb極客之路發表於2022-02-22

前言

落地過微服務專案的朋友,對配置中心應該都不會陌生。利用配置中心可以集中化管理配置,還可以做到配置熱更新等。目前市面常見的配置中心有QConf、spring-cloud-config、diamond、disconf、apollo、nacos等。而微服務專案最常用應該是spring-cloud-config、apollo、nacos。

我們可能會有這樣的應用場景,將一些配置資料先落到資料庫,然後再將這些資料持久化到配置中心。這邊可以分成2步走,第一步將資料落庫,第二步再手動通過配置中心提供的皮膚,將資料寫到配置中心。不過可能我們會更傾向,將資料落庫後,直接將資料同步到配置中心。今天就以apollo為例,聊聊如何將資料同步到apollo配置中心

實現思路

利用apollo提供的開放API進行操作

實現步驟

1、將我們的應用接入Apollo開放平臺

Apollo管理員在 http://{portal_address}/open/manage.html 建立第三方應用,建立之前最好先查詢此AppId是否已經建立。建立成功之後會生成一個token,如下圖所示:

在這裡插入圖片描述

2、給已註冊的應用授權

Apollo管理員在 http://{portal_address}/open/manage.html 頁面給token賦權。賦權之後,應用就可以通過Apollo提供的Http REST介面來管理已授權的Namespace的配置了

3、應用呼叫Apollo Open API

示例演示

以將API閘道器路由資訊同步到apollo為例

1、建立第三方應用


建立後提示token

2、根據token給第三方應用授權操作的appId


我們授權可以操作API閘道器上的所有配置,授權型別為APP

3、通過apollo-openapi呼叫Apollo Open API

專案中pom匯入apollo-openapi座標

<dependency>
    <groupId>com.ctrip.framework.apollo</groupId>
    <artifactId>apollo-openapi</artifactId>
    <version>1.7.0</version>
</dependency>

引入後,我們就可以直接操作apollo open api了

a、查詢配置項

  public long getMaxRouteRuleIndex(){
        OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appInfoProperties.getAppId(),appInfoProperties.getEnv(),appInfoProperties.getClusterName(),appInfoProperties.getNameSpaceName());
        List<OpenItemDTO> items = openNamespaceDTO.getItems();
        if(CollectionUtils.isEmpty(items)){
            return 0;
        }
        return items.stream().filter(item -> item.getKey().matches(ID_PATTERN)).count();
    }

執行單元測試

  @Test
    public void testGetMaxRouteRuleIndex(){
        long index = routeService.getMaxRouteRuleIndex();
        Assert.assertTrue(index >= 0);
    }


此時閘道器上的apollo皮膚

b、建立併發布配置項

注: apollo的建立和釋出是兩個不同的API

  public boolean createRouteRule(RouteRule routeRule){
        try {
            long curRouteRuleIndex = getMaxRouteRuleIndex();
            buildOpenItemDTO(ROUTE_ID_KEY,curRouteRuleIndex,routeRule.getRouteId(),true);
            buildOpenItemDTO(ROUTE_URI_KEY,curRouteRuleIndex,routeRule.getUri(),true);
            buildOpenItemDTO(ROUTE_PREDICATES_KEY,curRouteRuleIndex,routeRule.getPredicate(),true);
            buildOpenItemDTO(ROUTE_FILTERS_KEY,curRouteRuleIndex,routeRule.getFilter(),true);
            return publish("新增閘道器路由","新增閘道器路由");
        } catch (Exception e) {
           log.error("{}",e.getMessage());
        }
        return false;
    }

執行單元測試

  @Test
    public void testCreateRouteRule(){
        RouteRule routeRule = RouteRule.builder().routeId(appName)
                .uri("http://localhost:8082")
                .predicate("Path=/dashboard/**")
                .filter("StripPrefix=1").build();

        boolean isSuccess = routeService.createRouteRule(routeRule);
        Assert.assertTrue(isSuccess);
    }


檢視api閘道器在apollo portal上的皮膚


發現出現一條路由配置。因為api閘道器做了動態路由,因此從api閘道器的控制檯可以發現如下輸出

訪問一下瀏覽器

動態路由生效

b、更新併發布配置項

 public boolean updateRouteRule(RouteRule routeRule){
        long ruleIndex = getRouteRuleIndex(routeRule.getRouteId());
        if(ruleIndex != -1){
            try {
                buildOpenItemDTO(ROUTE_URI_KEY,ruleIndex,routeRule.getUri(),false);
                buildOpenItemDTO(ROUTE_PREDICATES_KEY,ruleIndex,routeRule.getPredicate(),false);
                buildOpenItemDTO(ROUTE_FILTERS_KEY,ruleIndex,routeRule.getFilter(),false);
                return publish("更新閘道器路由","更新閘道器路由");
            } catch (Exception e) {
                log.error("{}",e.getMessage());
            }
        }

        return false;
    }

執行單元測試

    @Test
    public void testUpdateRouteRule(){
        RouteRule routeRule = RouteRule.builder().routeId(appName)
                .uri("http://localhost:8082")
                .predicate("Path=/xxx/**")
                .filter("StripPrefix=1").build();
        boolean isSuccess = routeService.updateRouteRule(routeRule);
        Assert.assertTrue(isSuccess);
    }


檢視api閘道器在apollo portal上的皮膚


可以發現此時predicate的Path已經改為xxx

檢視API閘道器控制檯

訪問一下瀏覽器,原先訪問http://localhost:8000/dashboa...會出現

改訪問http://localhost:8000/xxx/ops...

說明路由已經成功發生變更

b、刪除併發布配置項

 public boolean deleteRouteRule(String routeId){
        long ruleIndex = getRouteRuleIndex(routeId);

        if(ruleIndex != -1){
            try {
//                removeRouteItem(ROUTE_URI_KEY,ruleIndex);
//                removeRouteItem(ROUTE_PREDICATES_KEY,ruleIndex);
//                removeRouteItem(ROUTE_FILTERS_KEY,ruleIndex);
                buildOpenItemDTO(ROUTE_URI_KEY,ruleIndex,"http://null",false);
                buildOpenItemDTO(ROUTE_PREDICATES_KEY,ruleIndex,"Path=/-9999",false);
                return publish("刪除閘道器路由","刪除閘道器路由");
            } catch (Exception e) {
                log.error("{}",e.getMessage());
            }
        }

        return false;
    }
private void removeRouteItem(String key,long index){
        if(key.equalsIgnoreCase(ROUTE_PREDICATES_KEY) || key.equalsIgnoreCase(ROUTE_FILTERS_KEY)){
            key = String.format(key,index,0);
        }else{
            key = String.format(key,index);
        }
        apolloOpenApiClient.removeItem(appInfoProperties.getAppId(),appInfoProperties.getEnv(),appInfoProperties.getClusterName(),appInfoProperties.getNameSpaceName(),key,appInfoProperties.getAuthUser());
    }

注: 因為閘道器刪除相對複雜點,涉及到路由集合重算,這邊取巧採用更新成無法訪問的路由。如果是物理刪除直接,呼叫apollo的removeItem即可

總結

apollo開放平臺提供的api其實就是http restful操作,提供一系列的增刪改查操作。這邊有個小細節就是apollo的增刪改和釋出是分開操作。如果只呼叫增刪改,則需要在portal上點發布,或者利用釋出介面進行操作。更多細節可以檢視apollo的開放平臺連結

https://www.apolloconfig.com/#/zh/usage/apollo-open-api-platform?id=_3210-%e6%96%b0%e5%a2%9e%e9%85%8d%e7%bd%ae%e6%8e%a5%e5%8f%a3

本文提供的示例,僅做參考,不可直接用於生產環境。如果有朋友的配置中心是用nacos,也是可以實現類似的操作。因為nacos也有提供open api介面,感興趣朋友可以檢視如下連結

https://nacos.io/zh-cn/docs/open-api.html

demo連結

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-sync-apollo

相關文章