前面我们已经知道,AgentCoreEntrance主要做了这些事情
我们来看主要的这些步骤的套路,其一个核心,将相关信息进行上报
EventSender.sendEvent(new EventMessage(BootArgsIndexer.getInstanceId(), events))
上报的过程是通过事件完成的。最终通过netty来完成,发送即时数据到服务端:
nettyClient.sendInstantData(
JSON.toJSONString(object, SerializerFeature.WriteMapNullValue).getBytes(StandardCharsets.UTF_8),
type))
在发送的过程中,首先会将消息进行压缩,然后构建服务的数据,然后进行发送。主要分为两种,一种是即时发送,一种是放入队列中。
public boolean sendInstantData(byte[] msg, Message.ServiceData.DataType dataType) {
if (!this.connectionAvailable) {
LOGGER.warning("Netty connection is not available.");
return false;
}
byte[] compressMsg = GzipUtils.compress(msg);
Message.ServiceData serviceData =
Message.ServiceData.newBuilder().setDataType(dataType).setData(ByteString.copyFrom(compressMsg))
.build();
// 设置消息信息,然后进行发送,可以理解为就是写入和刷新
Message.NettyMessage message = Message.NettyMessage.newBuilder()
.setMessageType(Message.NettyMessage.MessageType.SERVICE_DATA).addServiceData(serviceData).build();
if (channel == null) {
LOGGER.warning("Netty channel is null, send instant data failure.");
return false;
} else {
channel.writeAndFlush(message);
LOGGER.info("Sent instant data successfully by netty.");
return true;
}
}
NettyServer会对压缩的业务数据进行解压,然后对数据类型进行判断。数据类型主要分为下面几类:
@Override
protected void handlerData(ChannelHandlerContext ctx, Message.NettyMessage msg) {
List<Message.ServiceData> serviceDataList = msg.getServiceDataList();
for (Message.ServiceData serviceData : serviceDataList) {
ByteString data = serviceData.getData();
// 解压数据,处理数据业务逻辑
byte[] message = GzipUtils.decompress(data.toByteArray());
int dataType = serviceData.getDataTypeValue();
switch (dataType) {
case Message.ServiceData.DataType.HEARTBEAT_DATA_VALUE:
handleHeartBeat(message);
break;
case Message.ServiceData.DataType.EVENT_DATA_VALUE:
handleEvent(message);
break;
// 保存契约信息
case Message.ServiceData.DataType.VISIBILITY_DATA_VALUE:
handleServiceVisibility(message);
break;
default:
LOGGER.warn("Can not find the corresponding data type {}.", dataType);
}
}
}
也即:
HEARTBEAT_DATA_VALUE 心跳数据
EVENT_DATA_VALUE 事件数据
VISIBILITY_DATA_VALUE 可视化数据
然后进行对应的逻辑处理。
对应事件数据的处理是,首先发布事件publishEvent,然后执行webHookClient.doNotify(needPushWebHook)。
webhook事件推送的主要分为三种方式:
钉钉
飞书
welink
处理服务可见性信息
保存契约信息SERVER_MAP => 服务器名称、服务信息
保存血缘关系信息
或者移除服务。
获取命令执行器 => 执行对应的命令,然后execute
commandExecutor.execute(commandArgs)
以安装方法为例:
private static void doInitPlugin(Plugin plugin) {
loadPluginLibs(plugin);
loadServiceLibs(plugin);
PluginConfigManager.loadPluginConfigs(plugin);
PluginServiceManager.initPluginServices(plugin);
// 适配逻辑,类加载器需要在字节码增强前加入到插件类检索器中,否则可能会在字节码增强时,找不到拦截器
ClassLoaderManager.getPluginClassFinder().addPluginClassLoader(plugin);
// 根据插件类型选择不同的字节码增强安装方式
if (plugin.isDynamic()) {
ByteEnhanceManager.enhanceDynamicPlugin(plugin);
} else {
ByteEnhanceManager.enhanceStaticPlugin(plugin);
}
// 插件成功加载后步骤
PLUGIN_MAP.put(plugin.getName(), plugin);
PluginSchemaValidator.setDefaultVersion(plugin.getName());
FrameworkEventCollector.getInstance().collectPluginsLoadEvent(plugin.getName());
LOGGER.log(Level.INFO, "Load plugin:{0} successful.", plugin.getName());
}
上面可以看的完成安装后会执行收集插件加载事件,同时进行上报,方便在backend中可以查看到。
其主要是这个方法,而增强则是 ByteEnhanceManager.enhanceDynamicPlugin(plugin)和ByteEnhanceManager.enhanceStaticPlugin(plugin)这个方法。
可以看的这个重要工作installOn交给了bytebuddy。而sermant做的重要工作在process上。
public ResettableClassFileTransformer install(Instrumentation instrumentation) {
AgentBuilder builder = new Default().disableClassFormatChanges();
for (BuilderAction action : actions) {
builder = action.process(builder);
}
return builder.installOn(instrumentation);
}
创建BufferedAgentBuilder并依据配置设置基础操作:
1.设置启动类加载器相关的增强策略,见{@link #setBootStrapStrategy}
2.设置增强扫描过滤规则,见{@link #setIgnoredRule}
3.设置增强时的扫描日志监听器,见{@link #setLogListener}
4.设置输出增强后字节码的监听器,见{@link #setOutputListener}
最终基于{@link com.huaweicloud.sermant.core.plugin.agent.declarer.PluginDeclarer}添加字节码增强
也即我们声明的插件增强。
public void addEnhance(AbstractPluginDeclarer pluginDeclarer) {
addAction(builder -> {
PluginDescription pluginDescription = new AbstractPluginDescription() {
final AbstractPluginDeclarer abstractPluginDeclarer = pluginDeclarer;
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule module, ProtectionDomain protectionDomain) {
return new ReentrantTransformer(abstractPluginDeclarer.getInterceptDeclarers(classLoader),
virtualPlugin)
.transform(builder, typeDescription, classLoader, module, protectionDomain);
}
@Override
public boolean matches(TypeDescription target) {
return abstractPluginDeclarer.getClassMatcher().matches(target);
}
};
return builder.type(pluginDescription).transform(pluginDescription);
});
}
也即构建完成后,bytebuddy帮sermant完成了最终的install的工作。
参考:
开源项目地址:https://github.com/huaweicloud/Sermant
开源项目官网:https://sermant.io/zh/document/user-guide