1. 引言
1.1 項目的背景及意義
在當今的微服務架構中,應用程序通常被拆分成多個獨立的服務,這些服務通過網(wǎng)絡進行通信。這種架構的優(yōu)勢在于可以提高系統(tǒng)的可擴展性和靈活性,但也帶來了新的挑戰(zhàn),比如:
服務間通信的復雜性:不同服務之間需要進行可靠的通信,處理失敗重試、負載均衡等問題。
故障的容錯處理:系統(tǒng)的復雜性給與運維及故障處理帶來更大的挑戰(zhàn),如何快速處理故障解決線上問題,這是考驗一個企業(yè)基礎設施建設的重要關卡。
最初,開發(fā)者使用SDK來解決這些問題,通過在代碼中集成各種庫和工具來實現(xiàn)服務治理。然而,隨著微服務架構的規(guī)模不斷擴大,這種方法逐漸顯現(xiàn)出局限性:
代碼侵入性:需要在每個服務的代碼中集成和配置各種庫,增加了代碼的復雜性和維護成本。
一致性問題:不同服務可能使用不同版本的庫,導致治理邏輯不一致,SDK的升級難度凸顯。
為了解決這些問題,服務網(wǎng)格(Service Mesh)應運而生。服務網(wǎng)格通過在服務間引入一個代理層(通常稱為Sidecar),將服務治理的邏輯從應用代碼中分離出來,實現(xiàn)了更好的治理和管理。然而,服務網(wǎng)格的引入也帶來了額外的復雜性和性能開銷。
在這樣的背景下,我們通過Java字節(jié)碼增強技術,在服務治理領域提供了一種創(chuàng)新的解決方案。它結合了SDK和服務網(wǎng)格的優(yōu)點,提供了無侵入性的治理邏輯注入、靈活的擴展性和高效的性能優(yōu)化。這種方法不僅簡化了微服務架構中的服務治理,還提升了系統(tǒng)的可觀測性和安全性,對于現(xiàn)代微服務環(huán)境具有重要意義。
1.2 項目概述
Joylive Agent 是一個基于字節(jié)碼增強的框架,專注于多活和單元化場景下的流量治理。它提供了以下功能:多活流量調度、全鏈路灰度發(fā)布、QPS和并發(fā)限制、標簽路由、負載均衡,熔斷降級,鑒權等流量治理策略。其特性包括微內核架構、強類隔離、業(yè)務零侵入等,使其在保持高性能的同時對業(yè)務代碼影響最小,是面向Java領域的新一代Proxyless Service Mesh探索實現(xiàn)。
項目地址:
https://github.com/jd-opensource/joylive-agent
重要的事情說三遍:求Star,求Star,求Star。請Star完畢后繼續(xù)閱讀后文。
2. 微服務架構演進及優(yōu)缺點
2.1 單體架構階段
最初,大多數(shù)應用都是作為單體應用開發(fā)的。所有功能都集中在一個代碼庫中,部署也是作為一個整體。這種形式也是我們學習編程之初,最原始的模樣。確切的說,這種形態(tài)并不屬于微服務。如下圖所示:

優(yōu)點: 簡單、易于開發(fā)和測試,適合小團隊和小規(guī)模應用。
缺點: 這種架構隨著應用規(guī)模增大,可能會面臨維護困難、擴展性差等問題。
2.2 垂直拆分階段
隨著應用規(guī)模的成長,此時會考慮將每個功能模塊(服務)拆分為獨立的應用,也就是垂直拆分,擁有自己的代碼庫、數(shù)據(jù)庫和部署生命周期。服務之間通過輕量級協(xié)議(如HTTP、gRPC)通信。也就是正式開啟了面向服務的架構(SOA)。這種形態(tài)體現(xiàn)為:服務發(fā)現(xiàn)通過DNS解析,Consumer與Provider之間會有LB進行流量治理。服務間通過API進行通信。如下圖所示:

優(yōu)點: 獨立部署和擴展,每個服務可以由獨立的團隊開發(fā)和維護,提高了敏捷性。
缺點: 增加了分布式系統(tǒng)的復雜性,需要處理服務間通信、數(shù)據(jù)一致性、服務發(fā)現(xiàn)、負載均衡等問題,也因為中間引入LB而降低了性能。
2.3 微服務成熟階段
這個階段引入更多的微服務治理和管理工具,使用專業(yè)的微服務框架或中間件,通過專門定制的微服務通訊協(xié)議,讓應用取得更高的吞吐性能。如API網(wǎng)關、注冊中心、分布式追蹤等。DevOps和持續(xù)集成/持續(xù)部署(CI/CD)流程成熟。代表產物如Spring Cloud,Dubbo等。此時典型的微服務場景還都是具體的微服務SDK提供的治理能力。通訊流程為:SDK負責向注冊中心注冊當前服務信息,當需要進行服務消費時,同樣向注冊中心請求服務提供者信息,然后直連服務提供者IP及端口并發(fā)送請求。如下圖所示:

優(yōu)點: 高度可擴展、彈性和靈活性,支持高頻率的發(fā)布和更新。
缺點: 系統(tǒng)復雜性和運維成本較高,需要成熟的技術棧和團隊能力。微服務治理能力依賴SDK,升級更新成本高,需要綁定業(yè)務應用更新。
2.4 服務網(wǎng)格架構
隨著云原生容器化時代的到來,服務網(wǎng)格是一種專門用于管理微服務之間通信的基礎設施層。它通常包含一組輕量級的網(wǎng)絡代理(通常稱為 sidecar),這些代理與每個服務實例一起部署,這利用了K8s中Pod的基礎能力。服務網(wǎng)格負責處理服務間的通信、流量管理、安全性、監(jiān)控和彈性等功能。這種微服務治理方式也可以稱之為Proxy模式,其中SideCar即作為服務之間的Proxy。如下圖所示:

優(yōu)點: 主要優(yōu)點是解耦業(yè)務邏輯與服務治理的能力,通過集中控制平面(control plane)簡化了運維管理。
缺點: 增加了資源消耗,更高的運維挑戰(zhàn)。
3. 項目架構設計
有沒有一種微服務治理方案,既要有SDK架構的高性能、多功能的好處,又要有邊車架構的零侵入優(yōu)勢, 還要方便好用?這就是項目設計的初衷。項目的設計充分考慮了上面微服務的架構歷史,結合多活流量治理模型,進行了重新設計。其中項目設計到的主要技術點如下,并進行詳細解析。如下圖所示:

3.1 Proxyless模式
Proxyless模式(無代理模式)是為了優(yōu)化性能和減少資源消耗而引入的。傳統(tǒng)的微服務網(wǎng)格通常使用邊車代理(Sidecar Proxy)來處理服務之間的通信、安全、流量管理等功能。
我們選擇通過Java Agent模式實現(xiàn)Proxyless模式是一種將服務網(wǎng)格的功能(如服務發(fā)現(xiàn)、負載均衡、流量管理和安全性)直接集成到Java應用程序中的方法。這種方式可以利用Java Agent在運行時對應用程序進行字節(jié)碼操作,從而無縫地將服務網(wǎng)格功能注入到應用程序中,而無需顯式修改應用代碼。Java Agent模式實現(xiàn)Proxyless的優(yōu)點如下:
性能優(yōu)化:
減少網(wǎng)絡延遲:傳統(tǒng)的邊車代理模式會引入額外的網(wǎng)絡跳數(shù),因為每個請求都需要通過邊車代理進行處理。通過Java Agent直接將服務網(wǎng)格功能注入到應用程序中,可以減少這些額外的網(wǎng)絡開銷,從而降低延遲。
降低資源消耗:不再需要運行額外的邊車代理,從而減少了CPU、內存和網(wǎng)絡資源的占用。這對需要高效利用資源的應用非常重要。
簡化運維:
統(tǒng)一管理:通過Java Agent實現(xiàn)Proxyless模式,所有服務網(wǎng)格相關的配置和管理可以集中在控制平面進行,而無需在每個服務實例中單獨配置邊車代理。這簡化了運維工作,特別是在大型分布式系統(tǒng)中。
減少環(huán)境復雜性:通過消除邊車代理的配置和部署,環(huán)境的復雜性降低,減少了可能出現(xiàn)的配置錯誤或版本不兼容問題。
數(shù)據(jù)局面升級:Java Agent作為服務治理數(shù)據(jù)面,天然與應用程序解耦,這點是相對于SDK的最大優(yōu)點。當數(shù)據(jù)面面臨版本升級迭代時,可以統(tǒng)一管控而不依賴于用戶應用的重新打包構建。
靈活性:
無需修改源代碼與現(xiàn)有生態(tài)系統(tǒng)兼容:Java Agent可以在運行時對應用程序進行字節(jié)碼操作,直接在字節(jié)碼層面插入服務網(wǎng)格相關的邏輯,而無需開發(fā)者修改應用程序的源代碼。這使得現(xiàn)有應用能夠輕松集成Proxyless模式。
動態(tài)加載和卸載:Java Agent可以在應用程序啟動時或運行時動態(tài)加載和卸載。這意味著服務網(wǎng)格功能可以靈活地添加或移除,適應不同的運行時需求。
適用性廣:
支持遺留系統(tǒng):對于無法修改源代碼的遺留系統(tǒng),Java Agent是一種理想的方式,能夠將現(xiàn)代化的服務網(wǎng)格功能集成到老舊系統(tǒng)中,提升其功能和性能。
通過Java Agent實現(xiàn)Proxyless模式,能夠在保持現(xiàn)有系統(tǒng)穩(wěn)定性的同時,享受服務網(wǎng)格帶來的強大功能,是一種高效且靈活的解決方案。
3.2 微內核架構概述
微內核架構是一種軟件設計模式,主要分為核心功能(微內核)和一系列的插件或服務模塊。微內核負責處理系統(tǒng)的基礎功能,而其他功能則通過獨立的插件或模塊實現(xiàn)。這種架構的主要優(yōu)點是模塊化、可擴展性強,并且系統(tǒng)的核心部分保持輕量級。
核心組件:框架的核心組件更多的定義核心功能接口的抽象設計,模型的定義以及agent加載與類隔離等核心功能,為達到最小化依賴,很多核心功能都是基于自研代碼實現(xiàn)。具體可參見joylive-core代碼模塊。
插件化設計:使用了模塊化和插件化的設計,分別抽象了像保護插件,注冊插件,路由插件,透傳插件等豐富的插件生態(tài),極大的豐富了框架的可擴展性,為適配多樣化的開源生態(tài)奠定了基礎。具體可參見joylive-plugin代碼模塊。
3.3 插件擴展體系
項目基于Java的SPI機制實現(xiàn)了插件化的擴展方式,這也是Java生態(tài)的主流方式。
3.3.1 定義擴展
定義擴展接口,并使用@Extensible注解來進行擴展的聲明。下面是個負載均衡擴展示例:
@Extensible("LoadBalancer")public interface LoadBalancer {
int ORDER_RANDOM_WEIGHT = 0;
int ORDER_ROUND_ROBIN = ORDER_RANDOM_WEIGHT + 1;
default T choose(List endpoints, Invocation??> invocation) {
Candidate candidate = elect(endpoints, invocation);
return candidate == null ? null : candidate.getTarget();
}
Candidate elect(List endpoints, Invocation??> invocation);}
3.3.2 實現(xiàn)擴展
實現(xiàn)擴展接口,并使用@Extension注解來進行擴展實現(xiàn)的聲明。如下是實現(xiàn)了LoadBalancer接口的實現(xiàn)類:
@Extension(value = RoundRobinLoadBalancer.LOAD_BALANCER_NAME, order = LoadBalancer.ORDER_ROUND_ROBIN)@ConditionalOnProperties(value = {
@ConditionalOnProperty(value = GovernanceConfig.CONFIG_LIVE_ENABLED, matchIfMissing = true),
@ConditionalOnProperty(value = GovernanceConfig.CONFIG_LANE_ENABLED, matchIfMissing = true),
@ConditionalOnProperty(value = GovernanceConfig.CONFIG_FLOW_CONTROL_ENABLED, matchIfMissing = true)}, relation = ConditionalRelation.OR)public class RoundRobinLoadBalancer extends AbstractLoadBalancer {
public static final String LOAD_BALANCER_NAME = "ROUND_ROBIN";
private static final Function COUNTER_FUNC = s -> new AtomicLong(0L);
private final Map counters = new ConcurrentHashMap?>();
private final AtomicLong global = new AtomicLong(0);
@Override
public Candidate doElect(List endpoints, Invocation??> invocation) {
AtomicLong counter = global;
ServicePolicy servicePolicy = invocation.getServiceMetadata().getServicePolicy();
LoadBalancePolicy loadBalancePolicy = servicePolicy == null ? null : servicePolicy.getLoadBalancePolicy();
if (loadBalancePolicy != null) {
counter = counters.computeIfAbsent(loadBalancePolicy.getId(), COUNTER_FUNC);
}
long count = counter.getAndIncrement();
if (count < 0) {
counter.set(0);
count = counter.getAndIncrement();
}
// Ensure the index is within the bounds of the endpoints list.
int index = (int) (count % endpoints.size());
return new Candidate?>(endpoints.get(index), index);
}}
該類上的注解如下:
@Extension注解聲明擴展實現(xiàn),并提供了名稱
@ConditionalOnProperty注解聲明啟用的條件,可以組合多個條件
3.3.3 啟用擴展
在SPI文件
META-INF/services/com.jd.live.agent.governance.invoke.loadbalance.LoadBalancer中配置擴展全路徑名
com.jd.live.agent.governance.invoke.loadbalance.roundrobin.RoundRobinLoadBalancer即達到啟用效果。
更多詳情可查閱:
https://github.com/jd-opensource/joylive-agent/blob/main/docs/cn/extension.md
3.4 依賴注入設計
說到依賴注入估計大家會立馬想到Spring,的確這是Spring的看家本領。在復雜的工程中,自動化的依賴注入確實會簡化工程的實現(xiàn)復雜度。讓開發(fā)人員從復雜的依賴構建中脫離出來,專注于功能點設計開發(fā)。依賴注入的實現(xiàn)是基于上面插件擴展體系,是插件擴展的功能增強。并且依賴注入支持了兩類場景:注入對象與注入配置。該功能主要有4個注解類與3個接口構成。
3.4.1 @Injectable
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Injectable {
boolean enable() default true;}
這是一個非常簡潔的可以應用于類、接口(包括注解類型)或枚舉注解。其目的為了標識哪些類開啟了自動注入對象的要求。這點不同于Spring的控制范圍,而是按需注入。實例構建完成后,在自動注入的邏輯過程中會針對添加@Injectable注解的實例進行依賴對象注入。
3.4.2 @Inject
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Inject {
String value() default "";
boolean nullable() default false;
ResourcerType loader() default ResourcerType.CORE_IMPL;}
該注解用于自動注入值到字段。它用于指定一個字段在運行時應該注入一個值,支持基于配置的注入。還可以指示被注入的值是否可以為 null,如果注入過程中無注入實例或注入實例為null,而nullable配置為false,則會拋出異常。loader定義了指定要為注釋字段加載的資源或類實現(xiàn)的類型。ResourcerType為枚舉類型,分別是:CORE,CORE_IMPL,PLUGIN。劃分依據(jù)是工程打包后的jar文件分布目錄。因為不同目錄的類加載器是不同的(類隔離的原因,后面會講到)。所以可以簡單理解,這個配置是用于指定加載該對象所對應的類加載器。
3.4.3 @Configurable
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Configurable {
String prefix() default "";
boolean auto() default false;}
這個注解類似于@Injectable,用于指定哪些類啟用自動注入配置文件的支持。prefix指定用于配置鍵的前綴。這通常意味著前綴將來自類名或基于某種約定。auto指示配置值是否應自動注入到注解類的所有合規(guī)字段中。默認值為 false,這意味著默認情況下未啟用自動注入。
3.4.4 @Config
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Config {
String value() default "";
boolean nullable() default true;}
該注解用于指定字段配置詳細信息。它定義了字段的配置鍵以及配置是否為可選。此注釋可用于在運行時自動將配置值加載到字段中,并支持指定缺少配置(無配置)是否被允許。nullable指示字段的配置是否是可選的。如果為真,則系統(tǒng)將允許配置缺失而不會導致錯誤。
下面是具體的使用示例:
@Injectable@Extension(value = "CircuitBreakerFilter", order = OutboundFilter.ORDER_CIRCUIT_BREAKER)public class CircuitBreakerFilter implements OutboundFilter, ExtensionInitializer { @Inject private Map factories; @Inject(nullable = true) private CircuitBreakerFactory defaultFactory; @Inject(GovernanceConfig.COMPONENT_GOVERNANCE_CONFIG) private GovernanceConfig governanceConfig; ...}@Configurable(prefix = "app")public class Application { @Setter @Config("name") private String name; @Setter @Config("service") private AppService service; ...}
更多細節(jié),因為篇幅原因不再展開。詳情可以了解:
https://github.com/jd-opensource/joylive-agent/blob/main/docs/cn/extension.md
3.5 字節(jié)碼增強機制
Java 的字節(jié)碼增強(Bytecode Enhancement)是一種動態(tài)改變 Java 字節(jié)碼的技術,允許開發(fā)者在類加載之前或加載過程中修改類的字節(jié)碼。這種機制在 AOP(面向切面編程)、框架增強、性能監(jiān)控、日志記錄等領域廣泛應用。目前看此技術在APM產品使用較多,而針對流量治理方向的開源實現(xiàn)還是比較少的。
Java字節(jié)碼增強的主要方法有:
運行時增強:使用Java Agent在類加載時修改字節(jié)碼。
加載時增強:在類加載到JVM之前修改字節(jié)碼。
編譯時增強:在編譯階段修改或生成字節(jié)碼。
進行字節(jié)碼增強的框架有很多,例如:JavaAssist、ASM、ByteBuddy、ByteKit等。我們針對字節(jié)碼增強的過程及重要對象進行了接口抽象,并以插件化方式適配了ByteBuddy,開發(fā)了一種默認實現(xiàn)。當然你也可以使用其他的框架實現(xiàn)相應的接口,作為擴展的其他實現(xiàn)方式。下面以ByteBuddy為例,展示一個入門實例:
import net.bytebuddy.ByteBuddy;import net.bytebuddy.implementation.MethodDelegation;import net.bytebuddy.implementation.SuperMethodCall;import net.bytebuddy.matcher.ElementMatchers;import java.lang.reflect.Method;// 原始類class SimpleClass { public void sayHello() { System.out.println("Hello, World!"); }}// 攔截器class SimpleInterceptor { public static void beforeMethod() { System.out.println("Before saying hello"); } public static void afterMethod() { System.out.println("After saying hello"); }}public class ByteBuddyExample { public static void main(String[] args) throws Exception { // 使用ByteBuddy創(chuàng)建增強類 Class??> dynamicType = new ByteBuddy() .subclass(SimpleClass.class) .method(ElementMatchers.named("sayHello")) .intercept(MethodDelegation.to(SimpleInterceptor.class) .andThen(SuperMethodCall.INSTANCE)) .make() .load(ByteBuddyExample.class.getClassLoader()) .getLoaded(); // 創(chuàng)建增強類的實例 Object enhancedInstance = dynamicType.getDeclaredConstructor().newInstance(); // 調用增強后的方法 Method sayHelloMethod = enhancedInstance.getClass().getMethod("sayHello"); sayHelloMethod.invoke(enhancedInstance); }}
這個例子展示了如何使用ByteBuddy來增強SimpleClass的sayHello方法。讓我解釋一下這個過程:
我們定義了一個簡單的SimpleClass,它有一個sayHello方法。
我們創(chuàng)建了一個SimpleInterceptor類,包含了我們想要在原方法執(zhí)行前后添加的邏輯。
在ByteBuddyExample類的main方法中,我們使用ByteBuddy來創(chuàng)建一個增強的類:
我們創(chuàng)建了SimpleClass的一個子類。
我們攔截了名為"sayHello"的方法。
我們使用MethodDelegation.to(SimpleInterceptor.class)來添加前置和后置邏輯。
我們使用SuperMethodCall.INSTANCE來確保原始方法被調用。
我們創(chuàng)建了增強類的實例,并通過反射調用了sayHello方法。
當你運行這個程序時,輸出將會是:
Before saying hello Hello, World!After saying hello
當然,工程級別的實現(xiàn)是遠比上面Demo的組織形式復雜的。插件是基于擴展實現(xiàn)的,有多個擴展組成,對某個框架進行特定增強,實現(xiàn)了多活流量治理等等業(yè)務邏輯。一個插件打包成一個目錄,如下圖所示:
.└── plugin
├── dubbo
│ ├── joylive-registry-dubbo2.6-1.0.0.jar
│ ├── joylive-registry-dubbo2.7-1.0.0.jar
│ ├── joylive-registry-dubbo3-1.0.0.jar
│ ├── joylive-router-dubbo2.6-1.0.0.jar
│ ├── joylive-router-dubbo2.7-1.0.0.jar
│ ├── joylive-router-dubbo3-1.0.0.jar
│ ├── joylive-transmission-dubbo2.6-1.0.0.jar
│ ├── joylive-transmission-dubbo2.7-1.0.0.jar
│ └── joylive-transmission-dubbo3-1.0.0.jar
該dubbo插件,支持了3個版本,增強了注冊中心,路由和鏈路透傳的能力。下面介紹一下在joylive-agent中如何實現(xiàn)一個字節(jié)碼增強插件。
3.5.1 增強插件定義接口
增強插件(功能實現(xiàn)層面的插件)接口的定義采用了插件(系統(tǒng)架構層面的插件)擴展機制。如下代碼所示,定義的增強插件名稱為PluginDefinition。
@Extensible("PluginDefinition")public interface PluginDefinition {
ElementMatcher getMatcher();
InterceptorDefinition[] getInterceptors();}
接口共定義了兩個方法:getMatcher用于獲取匹配要增強類的匹配器,getInterceptors是返回要增強目標類的攔截器定義對象。
3.5.2 攔截器定義接口
攔截器定義接口主要是用來確定攔截增強位置(也就是方法),定位到具體方法也就找到了具體增強邏輯執(zhí)行的位置。攔截器定義接口并沒有采用擴展機制,這是因為具體到某個增強目標類后,要增強的方法與增強邏輯已經是確定行為,不再需要通過擴展機制實例化對象,在具體的增強插件定義接口實現(xiàn)里會直接通過new的方式構造蘭機器定義實現(xiàn)。攔截器定義接口如下:
public interface InterceptorDefinition {
ElementMatcher getMatcher();
Interceptor getInterceptor();}
該接口同樣抽象了兩個方法:getMatcher用于獲取匹配要增強方法的匹配器,getInterceptor是用于返回具體的增強邏輯實現(xiàn),我們稱之為攔截器(Interceptor)。
3.5.3 攔截器接口
攔截器的實現(xiàn)類是具體增強邏輯的載體,當我們要增強某個類的某個方法時,與AOP機制同理,我們抽象了幾處攔截位置。分別是:方法執(zhí)行前(剛進入方法執(zhí)行邏輯);方法執(zhí)行結束時;方法執(zhí)行成功時(無異常);方法執(zhí)行出錯時(有異常)。接口定義如下:
public interface Interceptor {
void onEnter(ExecutableContext ctx);
void onSuccess(ExecutableContext ctx);
void onError(ExecutableContext ctx);
void onExit(ExecutableContext ctx);}
增強邏輯的實現(xiàn)可以針對不同的功能目標,選擇合適的增強點。同樣,攔截點的入?yún)xecutableContext也是非常重要的組成部分,它承載了運行時的上下文信息,并且針對不同的增強目標我們做了不同的實現(xiàn)。如下圖所示:

更多詳情可查看:
https://github.com/jd-opensource/joylive-agent/blob/main/docs/cn/plugin.md
3.6 類加載與類隔離
類加載的原理比較容易理解,因為在Java Agent模式下,應用啟動時agent需要加載它所依賴的jar包。然而,如果這些類在加載后被用戶應用的類加載器感知到,就可能導致類沖突甚至不兼容的風險。因此,引入類隔離機制是為了解決這個問題。類隔離的實現(xiàn)原理并不復雜。首先,需要實現(xiàn)自定義的類加載器;其次,需要打破默認的雙親委派機制。通過這兩步,類隔離可以實現(xiàn)多層次的隔離,從而避免類沖突和不兼容問題。如下圖所示:

3.7 面向請求的抽象
整個框架的核心行為就是治理請求,而站在一個應用的視角我們可以大體把請求抽象為兩類:InboundRequest與OutboundRequest。InboundRequest是外部進入當前應用的請求,OutboundRequest是當前應用發(fā)往外部資源的請求。同樣的處理這些請求的過濾器也同樣分為InboundFilter與OutboundFilter。
請求接口

請求抽象實現(xiàn)

具體框架的請求適配,如Dubbo

如上圖所示,展現(xiàn)了適配Dubbo的請求對象的DubboOutboundRequest與DubboInboundRequest,體現(xiàn)了一個OutboundRequest與InboundRequest的實現(xiàn)與繼承關系。整體看起來確實比較復雜。這是因為在請求抽象時,不僅要考慮請求是Inbound還是Outbound,還要適配不同的協(xié)議框架。例如,像Dubbo和JSF這樣的私有通訊協(xié)議框架需要統(tǒng)一為RpcRequest接口的實現(xiàn),而SpringCloud這樣的HTTP通訊協(xié)議則統(tǒng)一為HttpRequest。再加上Inbound和Outbound的分類維度,整體的抽象在追求高擴展性的同時也增加了復雜性。
4. 核心功能
下面提供的流量治理功能,以API網(wǎng)關作為東西向流量第一入口進行流量識別染色。在很大程度上,API網(wǎng)關作為東西向流量識別的第一入口發(fā)揮了重要作用。API網(wǎng)關在接收到南北向流量后,后續(xù)將全部基于東西向流量治理。
4.1 多活模型及流量調度
應用多活通常包括同城多活和異地多活,異地多活可采用單元化技術來實現(xiàn)。下面描述整個多活模型涉及到的概念及嵌套關系。具體實現(xiàn)原理如下圖所示:

4.1.1 多活空間
在模型和權限設計方面,我們支持多租戶模式。一個租戶可以有多個多活空間,多活空間構成如下所示:
.└── 多活空間
├── 單元路由變量(*)
├── 單元(*)
│ ├── 分區(qū)(*)
├── 單元規(guī)則(*)
├── 多活域名(*)
│ ├── 單元子域名(*)
│ ├── 路徑(*)
│ │ ├── 業(yè)務參數(shù)(*)
4.1.2 單元
單元是邏輯上的概念,一般對應一個地域。常用于異地多活場景,通過用戶維度拆分業(yè)務和數(shù)據(jù),每個單元獨立運作,降低單元故障對整體業(yè)務的影響。
單元的屬性包括單元代碼、名稱、類型(中心單元或普通單元)、讀寫權限、標簽(如地域和可用區(qū))、以及單元下的分區(qū)。
單元分區(qū)是單元的組成部分,單元內的邏輯分區(qū),對應云上的可用區(qū)或物理數(shù)據(jù)中心,屬性類似單元。
4.1.3 路由變量
路由變量是決定流量路由到哪個單元的依據(jù),通常是用戶賬號。每個變量可以通過不同的取值方式(如Cookie、請求頭等)獲取,且可以定義轉換函數(shù)來獲取實際用戶標識。
變量取值方式則描述如何從請求參數(shù)、請求頭或Cookie中獲取路由變量。
4.1.4 單元規(guī)則
單元規(guī)則定義了單元和分區(qū)之間的流量調度規(guī)則。根據(jù)路由變量計算出的值,通過取模判斷流量應路由到哪個單元。它的屬性包括多活類型、變量、變量取值方式、計算函數(shù)、變量缺失時的操作、以及具體的單元路由規(guī)則。
單元路由規(guī)則定義了單元內的流量路由規(guī)則,包括允許的路由變量白名單、前綴、值區(qū)間等。
分區(qū)路由規(guī)則定義了單元內各個分區(qū)的流量路由規(guī)則,包括允許的變量白名單、前綴及權重。
4.1.5 多活域名
多活域名描述啟用多活的域名,用于在網(wǎng)關層進行流量攔截和路由。支持跨地域和同城多活的流量管理,配置路徑規(guī)則以匹配請求路徑并執(zhí)行相應的路由規(guī)則。
單元子域名描述各個單元的子域名,通常用于在HTTP請求或回調時閉環(huán)在單元內進行路由。
路徑規(guī)則定義了根據(jù)請求路徑匹配的路由規(guī)則,取最長匹配路徑來選擇適用的路由規(guī)則。
業(yè)務參數(shù)規(guī)則基于請求參數(shù)值進一步精細化路由,選擇特定的單元規(guī)則。
4.1.6 模型骨架
以下是多活治理模型的基本配置樣例,包括API版本、空間名稱、單元、域名、規(guī)則、變量等。
[
{
"apiVersion": "apaas.cos.com/v2alpha1",
"kind": "MultiLiveSpace",
"metadata": {
"name": "mls-abcdefg1",
"namespace": "apaas-livespace"
},
"spec": {
"id": "v4bEh4kd6Jvu5QBX09qYq-qlbcs",
"code": "7Jei1Q5nlDbx0dRB4ZKd",
"name": "TestLiveSpace",
"version": "2023120609580935201",
"tenantId": "tenant1",
"units": [
],
"domains": [
],
"unitRules": [
],
"variables": [
]
}
}]
以上概念會比較晦澀難懂,更多詳情可以訪問:
https://github.com/jd-opensource/joylive-agent/blob/main/docs/cn/livespace.md
4.2 全鏈路灰度(泳道)
泳道是一種隔離和劃分系統(tǒng)中不同服務或組件的方式,類似于游泳池中的泳道劃分,確保每個服務或組件在自己的“泳道”中獨立運作。泳道概念主要用于以下幾種場景:
多租戶架構中的隔離
在多租戶系統(tǒng)中,泳道通常用于隔離不同租戶的資源和服務。每個租戶有自己的獨立“泳道”,以確保數(shù)據(jù)和流量的隔離,防止不同租戶之間的相互影響。
流量隔離與管理
泳道可以用于根據(jù)特定規(guī)則(例如用戶屬性、地理位置、業(yè)務特性等)將流量分配到不同的微服務實例或集群中。這種方式允許團隊在某些條件下測試新版本、進行藍綠部署、金絲雀發(fā)布等,而不會影響到其他泳道中的流量。
業(yè)務邏輯劃分
在某些場景下,泳道也可以代表業(yè)務邏輯的劃分。例如,一個電商平臺可能會針對不同的用戶群體(如普通用戶、VIP用戶)提供不同的服務路徑和處理邏輯,形成不同的泳道。
版本管理
泳道可以用來管理微服務的不同版本,使得新版本和舊版本可以在不同的泳道中并行運行,從而降低升級時的風險。
開發(fā)和測試
在開發(fā)和測試過程中,不同的泳道可以用于隔離開發(fā)中的新功能、測試環(huán)境、甚至是不同開發(fā)團隊的工作,從而減少互相干擾。
泳道的核心目的是通過隔離服務或資源,提供獨立性和靈活性,確保系統(tǒng)的穩(wěn)定性和可擴展性。這種隔離機制幫助組織更好地管理復雜系統(tǒng)中的多樣性,尤其是在處理高并發(fā)、多租戶、或者需要快速迭代的場景下。功能流程如下圖所示:

模型定義及更多詳情可以訪問:
https://github.com/jd-opensource/joylive-agent/blob/main/docs/cn/lane.md
4.3 微服務治理策略
微服務治理策略是指在微服務架構中,為確保服務的穩(wěn)定性、可靠性、安全性以及高效運作而制定的一系列管理和控制措施。這些策略幫助企業(yè)有效地管理、監(jiān)控、協(xié)調和優(yōu)化成百上千個微服務的運行,以應對分布式系統(tǒng)的復雜性。目前我們實現(xiàn)了部分主流的微服務策略例如:負載均衡,重試,限流,熔斷降級,標簽路由,訪問鑒權等,更多的實用策略也在陸續(xù)補充中。微服務框架方面已經支持主流框架,如:Spring Cloud,Dubbo2/3,JSF,SofaRpc等。
由于篇幅原因,具體的治理策略與模型就不詳盡展開介紹了,下圖概括了服務治理的全貌。

值得一提有兩點:
策略的實現(xiàn)屏蔽了底層框架的差異性,這得益于上面提到的面向請求的抽象。
統(tǒng)一治理層級的劃分,多層級的策略掛載框架允許治理策略可以靈活的控制策略生效的影響半徑。
統(tǒng)一HTTP和傳統(tǒng)RPC的治理策略配置層級具體細節(jié)如下:
.└── 服務
├── 分組*
│ ├── 路徑*
│ │ ├── 方法*
服務治理策略放在分組、路徑和方法上,可以逐級設置,下級默認繼承上級的配置。服務的默認策略設置到默認分組default上。
| 類型 | 服務 | 分組 | 路徑 | 方法 |
| HTTP | 域名 | 分組 | URL路徑 | HTTP方法 |
| RPC 應用級注冊 | 應用名 | 分組 | 接口名 | 方法名 |
| RPC 接口級注冊 | 接口名 | 分組 | / | 方法名 |
模型定義及更多詳情可以訪問:
https://github.com/jd-opensource/joylive-agent/blob/main/docs/cn/governance.md
5. 功能實現(xiàn)示例
5.1 服務注冊
5.1.1 服務注冊
在應用啟動過程中,注冊插件會攔截獲取到消費者和服務提供者的初始化方法,會修改其元數(shù)據(jù),增加多活和泳道的標簽。后續(xù)往注冊中心注冊的時候就會帶有相關的標簽了。這是框架所有治理功能的前提基礎。以下是Dubbo服務提供者注冊樣例:
@Injectable@Extension(value = "ServiceConfigDefinition_v3", order = PluginDefinition.ORDER_REGISTRY)@ConditionalOnProperties(value = {
@ConditionalOnProperty(value = GovernanceConfig.CONFIG_LIVE_ENABLED, matchIfMissing = true),
@ConditionalOnProperty(value = GovernanceConfig.CONFIG_LANE_ENABLED, matchIfMissing = true),
@ConditionalOnProperty(value = GovernanceConfig.CONFIG_FLOW_CONTROL_ENABLED, matchIfMissing = true)}, relation = ConditionalRelation.OR) @ConditionalOnClass(ServiceConfigDefinition.TYPE_CONSUMER_CONTEXT_FILTER)@ConditionalOnClass(ServiceConfigDefinition.TYPE_SERVICE_CONFIG)public class ServiceConfigDefinition extends PluginDefinitionAdapter {
protected static final String TYPE_SERVICE_CONFIG = "org.apache.dubbo.config.ServiceConfig";
private static final String METHOD_BUILD_ATTRIBUTES = "buildAttributes";
private static final String[] ARGUMENT_BUILD_ATTRIBUTES = new String[]{
"org.apache.dubbo.config.ProtocolConfig"
};
// ......
public ServiceConfigDefinition() {
this.matcher = () -> MatcherBuilder.named(TYPE_SERVICE_CONFIG);
this.interceptors = new InterceptorDefinition[]{
new InterceptorDefinitionAdapter(
MatcherBuilder.named(METHOD_BUILD_ATTRIBUTES).
and(MatcherBuilder.arguments(ARGUMENT_BUILD_ATTRIBUTES)),
() -> new ServiceConfigInterceptor(application, policySupplier))
};
}}
public class ServiceConfigInterceptor extends InterceptorAdaptor {
// ......
@Override
public void onSuccess(ExecutableContext ctx) {
MethodContext methodContext = (MethodContext) ctx;
Map map = (Map) methodContext.getResult();
application.label(map::putIfAbsent);
// ......
}}
上面例子所呈現(xiàn)的效果是,當dubbo應用啟動時,增強插件攔截dubbo框架
org.apache.dubbo.config.ServiceConfig中的buildAttributes方法進行增強處理。從ServiceConfigInterceptor的實現(xiàn)中可以看出,當buildAttributes方法執(zhí)行成功后,對該方法的返回的Map對象繼續(xù)增加了框架額外的元數(shù)據(jù)標簽。
5.1.2 服務策略訂閱
如果注意ServiceConfigInterceptor的增強會發(fā)現(xiàn),在給注冊示例打標之后,還有一部分邏輯,如下:
public class ServiceConfigInterceptor extends InterceptorAdaptor {
@Override
public void onSuccess(ExecutableContext ctx) {
MethodContext methodContext = (MethodContext) ctx;
// ......
AbstractInterfaceConfig config = (AbstractInterfaceConfig) ctx.getTarget();
ApplicationConfig application = config.getApplication();
String registerMode = application.getRegisterMode();
if (DEFAULT_REGISTER_MODE_INSTANCE.equals(registerMode)) {
policySupplier.subscribe(application.getName());
} else if (DEFAULT_REGISTER_MODE_INTERFACE.equals(registerMode)) {
policySupplier.subscribe(config.getInterface());
} else {
policySupplier.subscribe(application.getName());
policySupplier.subscribe(config.getInterface());
}
}}
policySupplier.subscribe所執(zhí)行的是策略訂閱邏輯。因為策略是支持熱更新并實時生效的,策略訂閱邏輯便是開啟了訂閱當前服務在控制臺所配置策略的邏輯。
5.2 流量控制
5.2.1 入流量攔截點
入流量攔截也就是攔截Inbound請求,攔截相關框架的入流量處理鏈的入口或靠前的處理器的相關邏輯并予以增強。下面以Dubbo3的攔截點為例。
@Injectable@Extension(value = "ClassLoaderFilterDefinition_v3")@ConditionalOnProperty(value = GovernanceConfig.CONFIG_LIVE_ENABLED, matchIfMissing = true)@ConditionalOnProperty(value = GovernanceConfig.CONFIG_LIVE_DUBBO_ENABLED, matchIfMissing = true)@ConditionalOnProperty(value = GovernanceConfig.CONFIG_REGISTRY_ENABLED, matchIfMissing = true)@ConditionalOnProperty(value = GovernanceConfig.CONFIG_TRANSMISSION_ENABLED, matchIfMissing = true)@ConditionalOnClass(ClassLoaderFilterDefinition.TYPE_CLASSLOADER_FILTER)public class ClassLoaderFilterDefinition extends PluginDefinitionAdapter {
protected static final String TYPE_CLASSLOADER_FILTER = "org.apache.dubbo.rpc.filter.ClassLoaderFilter";
private static final String METHOD_INVOKE = "invoke";
protected static final String[] ARGUMENT_INVOKE = new String[]{
"org.apache.dubbo.rpc.Invoker",
"org.apache.dubbo.rpc.Invocation"
};
// ......
public ClassLoaderFilterDefinition() {
this.matcher = () -> MatcherBuilder.named(TYPE_CLASSLOADER_FILTER);
this.interceptors = new InterceptorDefinition[]{
new InterceptorDefinitionAdapter(
MatcherBuilder.named(METHOD_INVOKE).
and(MatcherBuilder.arguments(ARGUMENT_INVOKE)),
() -> new ClassLoaderFilterInterceptor(context)
)
};
}}
public class ClassLoaderFilterInterceptor extends InterceptorAdaptor {
private final InvocationContext context;
public ClassLoaderFilterInterceptor(InvocationContext context) {
this.context = context;
}
@Override
public void onEnter(ExecutableContext ctx) {
MethodContext mc = (MethodContext) ctx;
Object[] arguments = mc.getArguments();
Invocation invocation = (Invocation) arguments[1];
try {
context.inbound(new DubboInboundInvocation(new DubboInboundRequest(invocation), context));
} catch (RejectException e) {
Result result = new AppResponse(new RpcException(RpcException.FORBIDDEN_EXCEPTION, e.getMessage()));
mc.setResult(result);
mc.setSkip(true);
}
}}
public interface InvocationContext {
// ......
default void inbound(InboundInvocation invocation) {
InboundFilterChain.Chain chain = new InboundFilterChain.Chain(getInboundFilters());
chain.filter(invocation);
}
}
上面展示了針對Dubbo框架的增強處理是選擇了
org.apache.dubbo.rpc.filter.ClassLoaderFilter的invoke方法作為攔截點,織入我們統(tǒng)一的InboundFilterChain對象作為入流量處理鏈。我們可以根據(jù)需求實現(xiàn)不同的InboundFilter即可,我們內置了一部分實現(xiàn)。如下所示:

| 過濾器 | 名稱 | 說明 |
| RateLimitInboundFilter | 限流過濾器 | 根據(jù)當前服務的限流策略來進行限流 |
| ConcurrencyLimitInboundFilter | 并發(fā)過濾器 | 根據(jù)當前服務的并發(fā)策略來進行限流 |
| ReadyInboundFilter | 治理就緒過濾器 | 判斷治理狀態(tài),只有就緒狀態(tài)才能進入流量 |
| UnitInboundFilter | 單元過濾器 | 判斷當前請求是否匹配當前單元,以及當前單元是否可以訪問 |
| CellInboundFilter | 分區(qū)過濾器 | 判斷當前分區(qū)是否可以訪問 |
| FailoverInboundFilter | 糾錯過濾器 | 目前對錯誤流量只實現(xiàn)了拒絕 |
5.2.2 出流量攔截點
出流量攔截也就是攔截Outbound請求,攔截相關框架的出流量處理鏈的入口并予以增強。下面以Dubbo2的攔截點為例。
如果只開啟了多活或泳道治理,則只需對后端實例進行過濾,可以攔截負載均衡或服務實例提供者相關方法
@Injectable@Extension(value = "LoadBalanceDefinition_v2.7")@ConditionalOnProperties(value = {
@ConditionalOnProperty(name = {
GovernanceConfig.CONFIG_LIVE_ENABLED,
GovernanceConfig.CONFIG_LANE_ENABLED
}, matchIfMissing = true, relation = ConditionalRelation.OR),
@ConditionalOnProperty(name = GovernanceConfig.CONFIG_FLOW_CONTROL_ENABLED, value = "false"),
@ConditionalOnProperty(name = GovernanceConfig.CONFIG_LIVE_DUBBO_ENABLED, matchIfMissing = true)}, relation = ConditionalRelation.AND)@ConditionalOnClass(LoadBalanceDefinition.TYPE_ABSTRACT_CLUSTER)@ConditionalOnClass(ClassLoaderFilterDefinition.TYPE_CONSUMER_CLASSLOADER_FILTER)public class LoadBalanceDefinition extends PluginDefinitionAdapter {
protected static final String TYPE_ABSTRACT_CLUSTER = "com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker";
private static final String METHOD_SELECT = "select";
private static final String[] ARGUMENT_SELECT = new String[]{
"org.apache.dubbo.rpc.cluster.LoadBalance",
"org.apache.dubbo.rpc.Invocation",
"java.util.List",
"java.util.List"
};
// ......
public LoadBalanceDefinition() {
this.matcher = () -> MatcherBuilder.isSubTypeOf(TYPE_ABSTRACT_CLUSTER)
.and(MatcherBuilder.not(MatcherBuilder.isAbstract()));
this.interceptors = new InterceptorDefinition[]{
new InterceptorDefinitionAdapter(
MatcherBuilder.named(METHOD_SELECT)
.and(MatcherBuilder.arguments(ARGUMENT_SELECT)),
() -> new LoadBalanceInterceptor(context)
)
};
}}
攔截器里面調用上下文的路由方法
public class LoadBalanceInterceptor extends InterceptorAdaptor {
// ......
@Override
public void onEnter(ExecutableContext ctx) {
MethodContext mc = (MethodContext) ctx;
Object[] arguments = ctx.getArguments();
List> invokers = (List>) arguments[2];
List> invoked = (List>) arguments[3];
DubboOutboundRequest request = new DubboOutboundRequest((Invocation) arguments[1]);
DubboOutboundInvocation invocation = new DubboOutboundInvocation(request, context);
DubboCluster3 cluster = clusters.computeIfAbsent((AbstractClusterInvoker??>) ctx.getTarget(), DubboCluster3::new);
try {
List> instances = invokers.stream().map(DubboEndpoint::of).collect(Collectors.toList());
if (invoked != null) {
invoked.forEach(p -> request.addAttempt(new DubboEndpoint?>(p).getId()));
}
List? extends Endpoint?> endpoints = context.route(invocation, instances);
if (endpoints != null && !endpoints.isEmpty()) {
mc.setResult(((DubboEndpoint??>) endpoints.get(0)).getInvoker());
} else {
mc.setThrowable(cluster.createNoProviderException(request));
}
} catch (RejectException e) {
mc.setThrowable(cluster.createRejectException(e, request));
}
mc.setSkip(true);
}}
如果開啟了微服務治理,則設計到重試,需要對集群調用進行攔截
@Injectable@Extension(value = "ClusterDefinition_v2.7")@ConditionalOnProperty(name = GovernanceConfig.CONFIG_FLOW_CONTROL_ENABLED, matchIfMissing = true)@ConditionalOnProperty(name = GovernanceConfig.CONFIG_LIVE_DUBBO_ENABLED, matchIfMissing = true)@ConditionalOnClass(ClusterDefinition.TYPE_ABSTRACT_CLUSTER)@ConditionalOnClass(ClassLoaderFilterDefinition.TYPE_CONSUMER_CLASSLOADER_FILTER)public class ClusterDefinition extends PluginDefinitionAdapter {
protected static final String TYPE_ABSTRACT_CLUSTER = "org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker";
private static final String METHOD_DO_INVOKE = "doInvoke";
private static final String[] ARGUMENT_DO_INVOKE = new String[]{
"org.apache.dubbo.rpc.Invocation",
"java.util.List",
"org.apache.dubbo.rpc.cluster.LoadBalance"
};
// ......
public ClusterDefinition() {
this.matcher = () -> MatcherBuilder.isSubTypeOf(TYPE_ABSTRACT_CLUSTER)
.and(MatcherBuilder.not(MatcherBuilder.isAbstract()));
this.interceptors = new InterceptorDefinition[]{
new InterceptorDefinitionAdapter(
MatcherBuilder.named(METHOD_DO_INVOKE)
.and(MatcherBuilder.arguments(ARGUMENT_DO_INVOKE)),
() -> new ClusterInterceptor(context)
)
};
}}
攔截器里面構造集群對象進行同步或異步調用
public class ClusterInterceptor extends InterceptorAdaptor {
// ......
@Override
public void onEnter(ExecutableContext ctx) {
MethodContext mc = (MethodContext) ctx;
Object[] arguments = ctx.getArguments();
DubboCluster3 cluster = clusters.computeIfAbsent((AbstractClusterInvoker??>) ctx.getTarget(), DubboCluster3::new);
List> invokers = (List>) arguments[1];
List> instances = invokers.stream().map(DubboEndpoint::of).collect(Collectors.toList());
DubboOutboundRequest request = new DubboOutboundRequest((Invocation) arguments[0]);
DubboOutboundInvocation invocation = new DubboOutboundInvocation(request, context);
DubboOutboundResponse response = cluster.request(context, invocation, instances);
if (response.getThrowable() != null) {
mc.setThrowable(response.getThrowable());
} else {
mc.setResult(response.getResponse());
}
mc.setSkip(true);
}}
同樣,出流量攔截也是采用了責任鏈模式設計了OutboundFilterChain,用戶可以根據(jù)自己的需求擴展實現(xiàn)OutboundFilter,目前針對已支持功能內置了部分實現(xiàn),如下所示:

| 過濾器 | 名稱 | 說明 |
| StickyFilter | 粘連過濾器 | 根據(jù)服務的粘連策略進行過濾 |
| LocalhostFilter | 本機過濾器 | 本地開發(fā)調試插件 |
| HealthyFilter | 健康過濾器 | 根據(jù)后端實例的健康狀態(tài)進行過濾 |
| VirtualFilter | 虛擬節(jié)點過濾器 | 復制出指定數(shù)量的節(jié)點,用于開發(fā)測試 |
| UnitRouteFilter | 單元路由過濾器 | 根據(jù)多活路由規(guī)則及微服務的多活策略,根據(jù)請求的目標單元進行過濾 |
| TagRouteFilter | 標簽路由過濾器 | 根據(jù)服務配置的標簽路由策略進行過濾 |
| LaneFilter | 泳道過濾器 | 根據(jù)泳道策略進行過濾 |
| CellRouteFilter | 分區(qū)路由過濾器 | 根據(jù)多活路由規(guī)則及微服務的多活策略,根據(jù)請求的目標分區(qū)進行過濾 |
| RetryFilter | 重試過濾器 | 嘗試過濾掉已經重試過的節(jié)點 |
| LoadBalanceFilter | 負載均衡過濾器 | 根據(jù)服務配置的負載均衡策略進行路由 |
6. 部署實踐
6.1 基于Kubernates實踐場景
我們開源的另一個項目joylive-injector是針對K8s場景打造的自動注入組件。joylive-injector是基于kubernetes的動態(tài)準入控制webhook,它可以用于修改kubernete資源。它會監(jiān)視工作負載(如deployments)的CREATE、UPDATE、DELETE事件和pods的CREATE事件,并為POD添加initContainer、默認增加環(huán)境變量JAVA_TOOL_OPTIONS、掛載configmap、修改主容器的卷裝載等操作。目前已支持的特性如下:
支持自動將joylive-agent注入應用的Pod。
支持多版本joylive-agent與對應配置管理。
支持注入指定版本joylive-agent及對應配置。
所以,針對采用K8s進行應用發(fā)布管理的場景中,集成joylive-agent變得非常簡單,在安裝joylive-injector組件后,只需要在對應的deployment文件中加入標簽x-live-enabled: "true"即可,如下所示:
apiVersion: apps/v1kind: Deploymentmetadata:
labels:
app: joylive-demo-springcloud2021-provider x-live-enabled: "true"
name: joylive-demo-springcloud2021-providerspec:
replicas: 1
selector:
matchLabels:
app: joylive-demo-springcloud2021-provider template:
metadata:
labels:
app: joylive-demo-springcloud2021-provider x-live-enabled: "true"
spec:
containers:
- env:
- name: CONFIG_LIVE_SPACE_API_TYPE value: multilive - name: CONFIG_LIVE_SPACE_API_URL value: http://api.live.local/v1 - name: CONFIG_LIVE_SPACE_API_HEADERS value: pin=demo - name: CONFIG_SERVICE_API_TYPE value: jmsf - name: CONFIG_SERVICE_API_URL value: http://api.jmsf.local/v1 - name: LIVE_LOG_LEVEL value: info - name: CONFIG_LANE_ENABLED value: "false"
- name: NACOS_ADDR value: nacos-server.nacos.svc:8848
- name: NACOS_USERNAME value: nacos - name: NACOS_PASSWORD value: nacos - name: APPLICATION_NAME value: springcloud2021-provider - name: APPLICATION_SERVICE_NAME value: service-provider - name: APPLICATION_SERVICE_NAMESPACE value: default - name: SERVER_PORT value: "18081"
- name: APPLICATION_LOCATION_REGION value: region1 - name: APPLICATION_LOCATION_ZONE value: zone1 - name: APPLICATION_LOCATION_LIVESPACE_ID value: v4bEh4kd6Jvu5QBX09qYq-qlbcs - name: APPLICATION_LOCATION_UNIT value: unit1 - name: APPLICATION_LOCATION_CELL value: cell1 - name: APPLICATION_LOCATION_LANESPACE_ID value: "1"
- name: APPLICATION_LOCATION_LANE value: production image: hub-vpc.jdcloud.com/jmsf/joylive-demo-springcloud2021-provider:1.1.0-5aab82b3-AMD64 imagePullPolicy: Always name: joylive-demo-springcloud2021-provider ports:
- containerPort: 18081
name: http protocol: TCP resources:
requests:
cpu: "4"
memory: "8Gi"
limits:
cpu: "4"
memory: "8Gi"
terminationMessagePath: /dev/termination-log terminationMessagePolicy: File dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler securityContext: { }
terminationGracePeriodSeconds: 30
啟動后Pod如下圖所示即代表注入成功,隨后觀察應用日志及功能測試即可。

我們有更高的目標和方向,希望更多有志于打造開源優(yōu)品的朋友加入進來,一起為開源事業(yè)貢獻自己的光與熱。
下一篇:KubeCon China 2024全球大會在香港舉行,京東云受邀參加探討云原生、開源及 AI
審核編輯 黃宇
-
SDK
+關注
關注
3文章
1101瀏覽量
51713 -
微服務
+關注
關注
0文章
150瀏覽量
8102 -
微服務架構
+關注
關注
0文章
26瀏覽量
3179
發(fā)布評論請先 登錄
微服務架構和CQRS架構基本概念介紹
微服務網(wǎng)關gateway的相關資料推薦
什么是微服務_微服務知識點全面總結
java微服務架構有哪些
什么是微服務和容器?微服務和容器的作用是什么
Dubbo 如何成為連接異構微服務體系的最佳服務開發(fā)框架
Proxyless的多活流量和微服務治理
評論