作者:用友汽车信息科技(上海)有限公司 刘亚洲 Java研发工程师
一、sermant架构
Sermant整体架构包含Sermant Agent、Sermant Backend、Sermant Injector、动静配置核心等组件。其中Sermant Agent是提供字节码加强根底能力及各类服务治理能力的外围组件,Sermant Backend、Sermant Injector、动静配置核心为Sermant提供其余能力的配套组件。
二、java agent和bytebuddy组合应用场景
比拟典型的就是skywalking、sermant、arthas、mockito。如果说java agent开了一扇门,那么bytebuddy在开的这扇门中关上了一片新的天地。
三、Sermant的入口
后面咱们说AgentLauncher是java agent的入口,为什么这么说呢?
<manifestEntries> <Premain-Class>com.huaweicloud.sermant.premain.AgentLauncher</Premain-Class> <Agent-Class>com.huaweicloud.sermant.premain.AgentLauncher</Agent-Class> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries>
答案能够从pom.xml中找到答案,这里能够看到基于Premain-Class和Agent-Class的两个类都指向了AgentLauncher这个类。因而咱们能够十分确认的必定它就是javaagent入口类。相似于java程序有一个main的执行入口,而java agent有一个本人的入口类premain。
因而能够看到它的入口执行main:
/** * premain * * @param agentArgs premain启动时携带的参数 * @param instrumentation 本次启动应用的instrumentation */public static void premain(String agentArgs, Instrumentation instrumentation) { launchAgent(agentArgs, instrumentation, false);}/** * agentmain * * @param agentArgs agentmain启动时携带的参数 * @param instrumentation 本次启动应用的instrumentation */public static void agentmain(String agentArgs, Instrumentation instrumentation) { launchAgent(agentArgs, instrumentation, true);}
基于premain模式的和基于agent模式,区别在于是否为isDynamic。从这里咱们能够看到这里提出了两个类值得咱们去关注:
AgentCoreEntrance、CommandProcessor,也即sermant这个我的项目的两个重点类。
更多须要理解的,能够参考byte-buddy这个开源我的项目。
四、入口办法执行的全流程
五、spi的加载过程
启动外围服务的过程是spi的加载过程,此时会初始化所有的服务。也即咱们看到的所有服务会在此时会做一个启动的操作,同时还会启动事件:
service.start();collectServiceStartEvent(startServiceArray.toString());
其实这个两个办法也做了很多事件。
启动服务做的事件:
collectServiceStartEvent则调用netty客户端向netty服务端发送数据。到服务端后,服务端进行数据处理,其收集的信息提供给backend模块不便后盾展现查看。
六、以标签路由为例PluginService中扩大插件初始化
除此之外,还有一批实现了BaseService接口的,也即PluginService扩大插件服务基类,以标签路由为例,能够看你的其初始化的整个过程。
七、install的过程
同时咱们能够看到install对应的process办法也是执行它的办法:
public ResettableClassFileTransformer install(Instrumentation instrumentation) { AgentBuilder builder = new Default().disableClassFormatChanges(); // 遍历actions for (BuilderAction action : actions) { builder = action.process(builder); } // 执行安装操作,此时交给bytebuddy return builder.installOn(instrumentation); }
从入参中的Instrumentation,咱们往回看:ByteEnhanceManager.init(instrumentation)
这个办法外面定义了action的程序。
public static void init(Instrumentation instrumentation) { instrumentationCache = instrumentation; builder = BufferedAgentBuilder.build(); // 初始化实现后,新增Action用于增加框架间接引入的字节码加强 enhanceForFramework(); }
执行上面的过程:
咱们依据下面的增加程序,来看初始化插件的程序:
public static void enhanceDynamicPlugin(Plugin plugin) { if (!plugin.isDynamic()) { return; } // 获取形容信息 List<PluginDescription> plugins = PluginCollector.getDescriptions(plugin); // 增加插件,而后执行安装操作 ResettableClassFileTransformer resettableClassFileTransformer = BufferedAgentBuilder.build() .addPlugins(plugins).install(instrumentationCache); plugin.setClassFileTransformer(resettableClassFileTransformer); }
从援用上看,PluginSystemEntrance.initialize(isDynamic)中援用了这个办法。
能够看到这里的增加插件,能够了解为自定义的插件。
从sermant官网,咱们能够晓得:定义自定义插件,须要实现PluginDeclarer这个接口。也即从这里能够看到也即自定义的插件:
/** * 从插件收集器中获取所有插件申明器 * * @param classLoader 类加载器 * @return 插件申明器集 */ private static List<? extends PluginDeclarer> getDeclarers(ClassLoader classLoader) { final List<PluginDeclarer> declares = new ArrayList<>(); for (PluginDeclarer declarer : loadDeclarers(classLoader)) { if (declarer.isEnabled()) { declares.add(declarer); } } return declares; }
有了插件,就能够进行安装操作。
依照这个程序,能够看到对应的action.process(builder)外面也执行了对应的构建办法。实现构建后,执行installOn办法。
实现装置工作后,依据装置前spi的加强实现,而后执行上游服务拦挡加强,从而实现精准筛选工作。
八、以标签路由上游拦挡解决为例
能够看到标签路由对应的几个代表性的Declarer:
NopInstanceFilterDeclarer、LoadBalancerDeclarer、BaseLoadBalancerDeclarer、ServiceInstanceListSupplierDeclarer等。
对应的拦截器Interceptor:
NopInstanceFilterInterceptor、LoadBalancerInterceptor、BaseLoadBalancerInterceptor、ServiceInstanceListSupplierInterceptor。
两者互相呼应。
LaneServiceImpl和LoadBalancerServiceImpl是基于sermant框架的插件服务spi做的实现。
LaneServiceImpl和RouteRequestTagHandler是和路由能力相干的,LaneServiceImpl和LaneRequestTagHandler是和染色能力相干的。RouteRequestTagHandler用来拦挡并存储调用过程中的标签,FlowRouteHandler和TagRouteHandler是在路由抉择上游实例时做的筛选过程。
上游拦挡办法会通过BaseLoadBalancerInterceptor到loadBalancerService.getTargetInstances(serviceId, instances, requestData),最终到 HandlerChainEntry.INSTANCE.process(targetName, instances, requestData),基于责任链模式执行解决。目前次要有两种形式:FlowRouteHandler和TagRouteHandler。
这外面只是简略的介绍了整体的流程,具体细节的内容,还须要本人多实际。同时sermant大量应用了java agent的内容。
因为自己的局限性,有不妥的中央,还望批评指正!
参考:
sermant官网: https://sermant.io/zh/
sermant开源地址:https://github.com/huaweicloud/Sermant
byte-buddy开源地址:https://github.com/raphw/byte-buddy