写点什么

实例讲解基于 Sermant 快速开发服务治理插件

  • 2023-11-22
    广东
  • 本文字数:6302 字

    阅读完需:约 21 分钟

实例讲解基于Sermant快速开发服务治理插件

本文分享自华为云社区《Sermant框架下的服务治理插件快速开发及使用指南》,作者: 华为云开源 。


Sermant 是基于 Java 字节码增强技术的云原生无代理服务网格,它具有非侵入、插件化和高性能的特点。通过 Sermant 核心框架,可以很容易的开发用于各种服务治理用途的插件,包括负载均衡、流量控制、标签路由、标签透传等。在本文中,我们通过案例讲解,说明如何基于 Sermant 开发一个接口统计调用时长的插件,并用于生产环境的部署。

一、 插件开发


本章,我们将基于 Sermant 官方提供的插件开发模板,从零开始完整的展示使用 Sermant 框架开发服务治理插件的流程。


本模板插件需要实现的主要任务是拦截并增强宿主应用的 controller 接口方法,计算方法执行耗时。


接下来就让我们开始插件开发的代码之路吧!


Sermant 官方提供了插件开发的模板代码,执行以下 Maven 指令拉取:


$ mvn archetype:generate -DarchetypeGroupId=com.huaweicloud.sermant -DarchetypeArtifactId=sermant-template-archetype -DarchetypeVersion=1.2.0 -DgroupId=com.huaweicloud.sermant -Dversion=1.2.0 -Dpackage=com.huaweicloud -DartifactId=first-plugin
复制代码


具体的执行步骤和细节可以参考 Sermant 官网创建​​首个插件文档​​。该模板代码预先设计了插件开发的项目结构和 pom 文件配置,我们可以更好的专注于插件功能的实现。下面展示的代码在此模板的基础上进行开发。我们也在​​Sermant-example的first-plugin-demo​​中提供了我们开发后的示例代码。

1.1 插件主模块开发


插件主模块是插件的主要实现,开发者需要在该模块中声明该插件的增强逻辑。


插件主模块需要确定拦截宿主应用的哪些类。本篇文章,宿主应用被拦截的类如下所示:


package com.huaweicloud.template;


@RestController
public class Controller {
@RequestMapping("sayHello")
public void sayHello(String name) throws InterruptedException {
System.out.println("hello " + name);
Thread.sleep(5);
}
}
复制代码


插件主模块需要完成计算 sayHello 方法执行耗时的增强逻辑,因此我们首先需要拦截 Controller 类的 sayHello 方法,然后在拦截器中对该方法进行前置和后置增强计算方法耗时。


在 template-plugin 模块创建 com.huawei.sermant.template 包,本节创建的类均位于此包。


1)创建 TemplateDeclarer 类,该类继承自 com.huaweicloud.sermant.core.plugin.agent.declarer.AbstractPluginDeclarer 类,需重写父类的 getClassMatcher 方法和 getInterceptDeclarers 方法。用于声明字节码增强拦截的类名和方法名,以及相应的拦截器。代码实现细节如下:


public class TemplateDeclarer extends AbstractPluginDeclarer {
@Override
public ClassMatcher getClassMatcher() {
// 匹配需要拦截的类,具有多种匹配方式,本处使用类的全限定名匹配
return ClassMatcher.nameEquals("com.huaweicloud.template.Controller" );
}


@Override
public InterceptDeclarer[] getInterceptDeclarers(ClassLoader classLoader) {
// 返回InterceptDeclarer数组,每个InterceptDeclarer确定自己需要拦截的方法和Interceptor
return new InterceptDeclarer[]{
// 匹配需要拦截的方法,具有多种匹配方式,本处使用方法名称进行匹配
InterceptDeclarer.build(MethodMatcher.nameEquals("sayHello"), new TemplateInterceptor())
};
}
}
复制代码


ClassMatcher 类和 MethodMatcher 类具有多种匹配方法,可以查阅 Sermant 官网​​字节码增强文档​​。


2)创建 TemplateInterceptor 类,该类需实现 com.huaweicloud.sermant.core.plugin.agent.interceptor.Interceptor 接口,重写接口的 before、after 和 onThrow 方法,以分别实现对拦截点的前置增强、后置增强和异常处理代码逻辑。代码实现细节如下:


public class TemplateInterceptor implements Interceptor {


private static final String START_TIME = "startTime";


@Override
public ExecuteContext before(ExecuteContext context) {
context.setLocalFieldValue(START_TIME, System.currentTimeMillis());
System.out.println("已记录方法运行开始的时间");
return context;
}


@Override
public ExecuteContext after(ExecuteContext context) {
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - (long) context.getLocalFieldValue(START_TIME);
System.out.println("方法耗时:" + elapsedTime + "毫秒");
return context;
}


@Override
public ExecuteContext onThrow(ExecuteContext context) {
return context;
}
}
复制代码


我们在 before 方法记录被拦截方法执行开始的时间,在 after 方法计算被拦截方法执行耗时并打印到控制台。


3)添加增强声明的 SPI 配置,在工程中 template/template-plugin 下的资源目录 resources 中添加 META-INF/services 目录,并在其中创建名为 com.huaweicloud.sermant.core.plugin.agent.declarer.PluginDeclarer 的 SPI 文件,并向其中添加字节码增强声明类的类名:


com.huaweicloud.sermant.template.TemplateDeclarer
复制代码


到此,插件主模块的代码逻辑开发完成,我们实现了对目标类的拦截,并对被拦截类的方法进行了增强。

1.2 插件打包构建


在该模板目录下执行 maven 命令对模板插件进行打包构建:


mvn clean package
复制代码


构建完成后会生成 agent/目录,具体文件结构如下所示:


.
├── common
├── config Sermant配置文件
├── core
├── god
├── implemant
├── Application.jar 宿主应用,可以替换为自己开发的应用
├── sermant-agent.jar Sermant Agent产品,用于宿主应用挂载使用
└── pluginPackage
└── template
├── config 插件配置文件
├── plugin 插件主模块
└── service 插件服务模块
复制代码


现在我们已经完成了一个简单的插件,该插件可以计算被拦截方法的耗时,下面我们将开始展示该插件的效果。

二、 Sermant Agent 使用

2.1 启动 Sermant Agent


Sermant 默认开启动态配置服务,​​修改 config 目录下的 config.properties 文件关闭动态配置服务:​​


# 动态配置服务开关
agent.service.dynamic.config.enable=false
复制代码


进入到第一章 1.2 节构建的产物目录 agent/中,通过为宿主服务配置-javaagent 指令来通过 premain 方式启动 Sermant Agent:


java -javaagent:sermant-agent.jar -jar Application.jar
复制代码


控制台打印如下输出说明通过 premain 方式启动 Sermant Agent 成功。


[2023-1024T17:26:49.049] [INFO] Loading god library into BootstrapClassLoader.
[2023-10-24T17:26:49.153] [INFO] Building argument map by agent arguments.
[2023-10-24T17:26:49.161] [INFO] Loading core library into SermantClassLoader.
[2023-10-24T17:26:49.162] [INFO] Loading sermant agent, artifact is: default
[2023-10-24T17:26:49.740] [INFO] Load sermant done, artifact is: default
复制代码


Sermant Agent 还可以通过 agentmain 方式在宿主应用运行中执行挂载。关于 Sermant Agent 使用的更多细节请参考 Sermant 官网​​Sermant Agent使用手册文档​​。

2.2 验证


宿主应用已经成功挂载 Sermant Agent,本节我们将验证插件增强逻辑是否生效。


执行以下命令发起对宿主应用接口服务的 get 请求:


curl http://127.0.0.1:8080/sayHello?name=lihua
复制代码


宿主应用控制台打印的输出内容如下所示:


已记录方法运行开始的时间
hello lihua
方法耗时:20毫秒
ECHO: Best wish to you!
复制代码


可以看到,Sermant Agent 成功拦截到了宿主应用的类并执行了增强逻辑。

三、 Sermant 进阶功能

3.1 Sermant Backend


Sermant Backend 包含 Sermant 数据处理后端模块和前端信息展示模块,旨在为 Sermant 提供运行时的可观测能力,当前主要包括 Sermant Agent 心跳信息、上报事件的接收和展示、webhook 推送等功能。


Sermant Backend 与 Sermant Agent 配合使用。Sermant Agent 挂载在宿主应用中,其作为数据发送端可定时发送当前 Sermant Agent 的心跳数据(服务名、主机名、实例 ID、版本号、IP、时间戳、插件挂载信息)和事件数据(Sermant Agent 启停、核心服务启停、字节码增强、日志数据等)。


Backend 为非必要组件,用户可按需部署,通过​​下载Sermant-1.2.0​​的 release 包获取 Backend 组件。解压该 release 包,进入 sermant-agent/server/sermant 目录执行以下命令启动 Sermant Backend:


java -jar sermant-backend-1.2.0.jar
复制代码


通过浏览器访问地址​​http://127.0.0.1:8900/​​,如果看到如下页面,则说明 Sermant Backend 启动成功;



Sermant Agent 使用 Backend 需要对 Agent 的参数进行配置。修改 agent(第一章构建的产物目录)/config 目录下的 config.properties 文件。


配置项需修改为:


# 开启心跳服务开关
agent.service.heartbeat.enable=true
# 开启统一网关服务开关
agent.service.gateway.enable=true
复制代码


参考 2.1 节启动 Sermant Agent,在 Backend 前端页面可以看到启动的 Sermant Agent 和挂载的 template 插件信息:



关于 Backend 的更多细节请参考 Sermant 官网​​Sermant Backend使用手册文档​​。

3.2 日志功能


日志是在程序开发中不可或缺的能力,通过日志可以快速找出程序运行时的状态及遇到的问题。


JavaAgent 产品在使用日志类时容易出现和宿主应用类冲突和日志配置冲突的问题,Sermant 构建的日志系统解决了这一问题。Sermant 日志系统基于 JUL&logback 构建,为插件开发提供了完整的、配置灵活的、避免类冲突的日志工具,并且 Sermant 提供的日志系统可以和宿主微服务做到从配置到执行上的完全隔离。除此之外,结合 Sermant Backend 组件,Sermant 日志系统可以提供异常事件的统一采集和上报。


现在我们需要在日志中记录被拦截方法的执行时间,本节将演示如何使用 Sermant 提供的日志系统。

3.2.1 添加日志


我们在插件主模块的 TemplateInterceptor 类中定义一个私有静态常量 LOGGER,用于该类下的日志构造。


// Sermant日志类
private static final Logger LOGGER = LoggerFactory.getLogger();
复制代码


接下来我们在该类的 after 方法使用日志记录被拦截方法的运行时间,该条日志的级别为 INFO。


LOGGER.info("方法耗时: " + elapsedTime);
复制代码


Sermant 日志系统可以记录各级别(TRACE、DEBUG、INFO、WARN、ERROR)日志来达到对程序运行不同程度的监控。


更多细节请参考 Sermant 官网​​日志功能文档​​。

3.2.2 日志演示


按照第二章的方式启动 Sermant 并调用宿主应用的接口:


curl http://127.0.0.1:8080/sayHello?name=lihua
复制代码


前往 logs/sermant/core/app/xxxx-xx-xx 目录查看 Sermant 日志,“xxxx-xx-xx”为记录日志的时间。可以看到方法耗时成功记录在了日志文件中。


[INFO] [com.huaweicloud.sermant.template.TemplateInterceptor] [after:45] [http-nio-8080-exec-1] 方法耗时: 20
复制代码

3.2.3 异常日志上报


Sermant 日志系统统一采集到的 warn 和 error 级别的日志可上传至 Sermant Backend 进行观测。我们将人为打印异常日志,并通过 Backend 观测。


在插件主模块 TemplateInterceptor 类的 before 方法中打印一条 warn 级别的日志,在 after 方法中打印一条 error 级别的日志:


LOGGER.warning("warning message");
LOGGER.severe("error message");
复制代码


修改 config 目录下的 config.properties 文件开启事件上报相关配置:


# 事件系统开关
event.enable=true
# Warn级别日志事件上报开关
event.offerWarnLog=true
# Error级别日志事件上报开关
event.offerErrorLog=true
复制代码


按照第二章的方式启动 Sermant 并调用宿主应用的接口:


curl http://127.0.0.1:8080/sayHello?name=lihua
复制代码


前往 Sermant Backend 前端页面可以看到上报的异常信息:


3.3 动态配置

3.3.1 添加动态配置


Sermant 提供了动态配置功能,目前支持 Zookeeper、Nacos 和 Kie 作为配置中心。动态配置功能允许 Sermant 对动态配置中心下发的配置进行配置管理和监听等操作,以实现丰富多样的服务治理能力。本节以 Zookeeper 为例,演示如何使用 Sermant 的动态配置服务为此插件添加执行增强逻辑的开关。


首先在插件主模块 TemplateInterceptor 类中添加 Map 字段模拟开关状态,ENABLE_KEY 为开关的名称,并设置开关默认状态为 true:


private static final Map<String, Boolean> CONFIG_MAP = new HashMap<>();


private static final String ENABLE_KEY = "enable";
{
// 开关默认状态为true
CONFIG_MAP.put(ENABLE_KEY, true);
}
复制代码


然后在 before 方法和 after 方法中添加开关:


if (!CONFIG_MAP.get(ENABLE_KEY)) {
System.out.println("不执行增强逻辑");
return context;
}
复制代码


接下来,我们需要动态修改开关的状态,添加动态配置服务字段,获取动态配置服务类实例并注册监听器监听 Zookeeper 相应节点变化:


// 获取动态配置服务类实例
private final DynamicConfigService dynamicConfigService = ServiceManager.getService(DynamicConfigService.class);


// 获取Sermant框架提供的yaml解析器
private final YamlConverter converter = OperationManager.getOperation(YamlConverter.class);


{
dynamicConfigService.addConfigListener("template-config", "sermant/template-plugin",
new DynamicConfigListener() {
@Override
public void process(DynamicConfigEvent event) {
// 解析yaml文件为map
Optional<Map<String, Object>> convertOptional = converter.convert(event.getContent(), Map.class);
if (convertOptional.isPresent()) {
// 修改开关状态
CONFIG_MAP.put(ENABLE_KEY, (boolean) convertOptional.get().get(ENABLE_KEY));
}
System.out.println("插件配置项发生变化, 配置项值为: " + event.getContent());
}
});
}
复制代码


使用动态配置需要打开动态配置服务开关,修改 config 目录下的 config.properties 文件:


# 动态配置服务开关
agent.service.dynamic.config.enable=true
复制代码

3.3.2 动态配置演示


开启动态配置服务需在本地启动 zookeeper。启动宿主应用并挂载 Sermant Agent,对宿主应用接口服务发起调用:


curl http://127.0.0.1:8080/sayHello?name=lihua
复制代码


此时开关默认开启,控制台输出如下:


已记录方法运行开始的时间
hello lihua
方法耗时:23毫秒
复制代码


开始下发动态配置,创建/sermant/template-plugin/template-config 节点,设置节点的 value 为“enable: false”:


create /sermant
create /sermant/template-plugin
create /sermant/template-plugin/template-config enable: false
复制代码


宿主应用控制台打印如下输出:


插件配置项发生变化, 配置项值为: enable: false
复制代码


再次对宿主应用接口服务发起调用:


curl http://127.0.0.1:8080/sayHello?name=lihua
复制代码


可以看到控制台打印出了不执行增强逻辑的内容,说明动态配置下发成功:


不执行增强逻辑
hello lihua
不执行增强逻辑
复制代码


动态配置的演示到此就结束了,更多动态配置的细节请参考 Sermant 官网​​动态配置功能文档​​。

四、本章总结


本篇文章介绍了如何基于 Sermant 框架快速开发服务治理插件的流程以及使用 Sermant Agent 产品的方式。可以发现,通过 Sermant 底层框架提供的能力,我们可以非常容易的开发符合自身需求的插件,同时结合 Sermant 的日志系统、动态配置和 Backend 组件,更精细灵活的增强服务治理插件的功能。Sermant 是基于 Java 字节码增强技术的无代理服务网格,具有无侵入、插件化和高性能的特点,希望本篇文章能对有意愿使用 Sermant 产品的开发者们有所帮助!


Sermant 作为专注于服务治理领域的字节码增强框架,致力于提供高性能、可扩展、易接入、功能丰富的服务治理体验,并会在每个版本中做好性能、功能、体验的看护,广泛欢迎大家的加入。



点击关注,第一时间了解华为云新鲜技术~

发布于: 刚刚阅读数: 5
用户头像

提供全面深入的云计算技术干货 2020-07-14 加入

生于云,长于云,让开发者成为决定性力量

评论

发布
暂无评论
实例讲解基于Sermant快速开发服务治理插件_云原生_华为云开发者联盟_InfoQ写作社区