擴展Zuul實現敏捷開發的小小技巧

分析下目前遇到的痛點

你在開發工做的是否遇到這個問題,微服務模塊劃分過細,基礎模塊依賴的比較多?
好比你要進行微服務開發則須要啓動如下基礎模塊java

  • 註冊中心(eureka)
  • 配置中心(spring cloud config)
  • 網關(zuul)
  • 認證中心(oauth)
  • ...
    如上圖紅色標註的服務模塊,而你須要編碼或者須要的只有那麼一個業務微服務模塊,本地啓動這麼微服務模塊對開發機器的要求性能較高,而且影響開發效率。
    爲了解決這種問題,團隊通常都會把通用的基礎模塊部,提供統一的開發環境,方便你們開發,如上圖 只須要考慮你的業務模塊(serviceA、serviceB) 便可,提升開發效率。
    這種統一開發基礎環境問題存在小小的問題,好比當開發A維護serviceA,開發B維護serviceB 不會出現衝突;若是開發A、B同時維護一個模塊時候,就會出現衝突以下圖所示:A的本地請求會被路由到B正在開發的biz-service,而不是目標的A的biz-service,由於zuul 是根據服務名稱進行路由的

解決原理

重寫網關的轉發規則,其實就是重寫ribbon的路由規則,根據客戶端不一樣的用戶請求(能夠根據入參不一樣區分)路由到對應的微服務上,因此對應的微服務上要打上標籤。biz-service A開發版本,biz-service B開發版本
ios

代碼實現

1. 在微服務的eureka客戶端聲明版本所屬

eureka:
	  instance:
		metadata-map: 
      	version: v1.0 # A的開發版本

2. 自定義ribbon 的斷言

@Slf4j
public class MetadataCanaryRuleHandler extends ZoneAvoidanceRule {

    @Override
    public AbstractServerPredicate getPredicate() {
        return new AbstractServerPredicate() {
            @Override
            public boolean apply(PredicateKey predicateKey) {
                String targetVersion = RibbonVersionHolder.getContext();
                RibbonVersionHolder.clearContext();
                if (StrUtil.isBlank(targetVersion)) {
                    log.debug("客戶端未配置目標版本直接路由");
                    return true;
                }

                DiscoveryEnabledServer server = (DiscoveryEnabledServer) predicateKey.getServer();
                final Map<String, String> metadata = server.getInstanceInfo().getMetadata();
                if (StrUtil.isBlank(metadata.get(SecurityConstants.VERSION))) {
                    log.debug("當前微服務{} 未配置版本直接路由");
                    return true;
                }

                if (metadata.get(SecurityConstants.VERSION).equals(targetVersion)) {
                    return true;
                } else {
                    log.debug("當前微服務{} 版本爲{},目標版本{} 匹配失敗", server.getInstanceInfo().getAppName()
                            , metadata.get(SecurityConstants.VERSION), targetVersion);
                    return false;
                }
            }
        };
    }
}

3. 版本上下文TTL

public class RibbonVersionHolder {
    private static final ThreadLocal<String> context = new TransmittableThreadLocal<>();

    public static String getContext() {
        return context.get();
    }

    public static void setContext(String value) {
        context.set(value);
    }

    public static void clearContext() {
        context.remove();
    }
}

4. 初始化ribbon 路由配置

@Configuration
@ConditionalOnClass(DiscoveryEnabledNIWSServerList.class)
@AutoConfigureBefore(RibbonClientConfiguration.class)
@ConditionalOnProperty(value = "zuul.ribbon.metadata.enabled")
public class RibbonMetaFilterAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public ZoneAvoidanceRule metadataAwareRule() {
        return new MetadataCanaryRuleHandler();
    }
}

5. 客戶端調用時 header 傳入版本,以 axios 爲例

axios.interceptors.request.use(config => {
  NProgress.start() // start progress bar
  if (store.getters.access_token) {
    config.headers['Authorization'] = 'Bearer ' + token
    config.headers['version'] = 'v1.0'  // 開發人員本身的版本標誌,對應eureka metadata 配置
  }
  return config
}

總結

  1. 擴展ribbon 的路由規則,根據客戶端來去不一樣版本的服務,也能夠理解爲灰度發佈。git

  2. 生產環境能夠藉助Kong、Traefik 集合zuul 來實現灰度發佈spring

  3. 代碼請參考微服務權限框架pig的灰度發佈功能,已經所有開源axios

  4. 關於pig:
    基於Spring Cloud、oAuth2.0開發基於Vue先後分離的開發平臺,支持帳號、短信、SSO等多種登陸,提供配套視頻開發教程。
    https://gitee.com/log4j/pigapp

相關文章
相關標籤/搜索