1.调研目标
可以说明一下对 Dubbo 和 Skywalking 的理解,以及这次调研的目的。
目前在我们的系统中大量使用了 OpenFeign 作为主要的服务调用方式,少部分有高性能需求的接口(如 Data Core)使用了 gRPC 协议提供接口。并且由于没有使用 Nacos Discovery 注册中心,在配置文件中大量充斥着服务地址的配置信息。
supibd:datacore:server-addr: data-core:8080resource-permission:server-addr: resource-permission.obp-dev:8080ETL:server-addr: data-warehouse.obp-dev:8080task:addHours: 3server-addr: schedule-manager.obp-dev:8080gettree:server-addr: app-manager.bigdata-dev:8080
还有一点,那就是在微服务环境下的链路追踪问题。目前在开发框架中,我们为 Log4j2 添加了一堆栈信息用来帮助开发者进行观察、调试。但实际体验下来,效果并不是很好。特别是对于 requestId
,还需要借助于小平台的日志页面完成全链路追踪。
[system-manager-dev@192.168.182.29:10005] [DEV-1.2.0-SNAPSHOT] [2022-12-13 14:03:44.665] [INFO ] [sup-log-exec-1 ] mbase.logapi.service.impl.LogServiceImpl [LogServiceImpl.java:149] [requestId-APP_d0f391661f] [requestOrder-0] [requestChain# Chrome > system-manager-dev] [moduleName-服务资源注册]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
综合考虑,Dubbo 3 的中文文档齐全、社区比较活跃;原生支持 gRPC、HTTP/2 等常用协议;官方提供与 Skywalking 集成方案,可以实现更全面与优化的链路追踪与监控。因此进行提出本次调研目标。
2.环境搭建
介绍 Dubbo 和 Skywalking 的安装配置过程,遇到的主要问题及解决方式。
2.1.SkyWalking
利用无侵入性的 SkyWalking 在微服务之间架起数据链路监控的桥梁。更多内容参考之前的调研记录。
elasticsearch:image: docker.elastic.co/elasticsearch/elasticsearch:7.14.2container_name: elasticsearchports:- "9200:9200"healthcheck:test: [ "CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1" ]interval: 30stimeout: 10sretries: 3start_period: 10senvironment:- discovery.type=single-node- bootstrap.memory_lock=true- TZ=Asia/Shanghai- "ES_JAVA_OPTS=-Xms512m -Xmx512m"ulimits:memlock:soft: -1hard: -1oap:image: apache/skywalking-oap-server:8.9.1container_name: oapdepends_on:elasticsearch:condition: service_healthylinks:- elasticsearchports:- "11800:11800"- "12800:12800"healthcheck:test: [ "CMD-SHELL", "/skywalking/bin/swctl ch" ]interval: 30stimeout: 10sretries: 3start_period: 10senvironment:SW_STORAGE: elasticsearchSW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200SW_HEALTH_CHECKER: defaultSW_TELEMETRY: prometheusJAVA_OPTS: "-Xms2048m -Xmx2048m"TZ: Asia/Shanghaiui:image: apache/skywalking-ui:8.9.1container_name: uidepends_on:oap:condition: service_healthylinks:- oapports:- "13800:8080"environment:SW_OAP_ADDRESS: http://oap:12800TZ: Asia/Shanghai
2.2.NACOS
NACOS 版本与公司、现场保持一致,使用 v2.1.2
版本。根据一些现场反馈的安全问题,目前开启了 NACOS_AUTH_ENABLE
授权认证,因此客户端在进行连接时需要提供账号密码。
本次测试中,使用到 NACOS 的配置中心与注册中心功能。
nacos:image: harbor.supcon5t.com/devops/nacos/nacos-server:v2.1.2environment:MODE: standaloneNACOS_AUTH_ENABLE: trueports:- "8848:8848"- "9848:9848"- "9555:9555"
2.3.Duboo Admin
Dubbo Admin 是 Dubbo 的可视化管理后台,提供用户从浏览器管理 Dubbo 服务的能力。由于我们使用 NACOS 作为注册中心,因此需要配置 NACOS 的地址信息。注意密码认证。
docker run -d \--name dubbo-admin \-p 8900:8080 \-e admin.registry.address="nacos://192.168.182.18:8848?username=nacos&password=Supcon@1304" \-e admin.config-center=nacos://192.168.182.18:8848 \-e admin.metadata-report.address=nacos://192.168.182.18:8848 \
apache/dubbo-admin:0.5.0
部署完成后通过 http://localhost:8900 在浏览器登录,账号密码都是 root。
2.4.应用程序
上层应用的代码参考 Dubbo 的官方示例编写,使用 OBP 开发框架构建。
首先,为多模块项目创建一个 pom.xml
文件,定义工程结构。下面的结构描述中,包含了公共部分(app-common)、功能提供者(app-provider)和功能消费者(app-cunsumer)三个部分。
<parent><groupId>com.supcon</groupId><artifactId>supcon-parent</artifactId><version>1.2.1</version>
</parent><artifactId>app</artifactId>
<version>2023.6.5</version>
<name>app</name>
<packaging>pom</packaging><properties><java.version>11</java.version><dubbo.version>3.1.7</dubbo.version>
</properties><modules><module>app-common</module><module>app-provider</module><module>app-consumer</module>
</modules>
2.4.1.app-common
公共部分(common)定义了功能接口和数据模型,提供公共部分让功能提供者和消费者同时使用。
因为仅是公共部分,所以 pom.xml
中除了本身的定义外无需其他内容。
<parent><groupId>com.supcon</groupId><artifactId>app</artifactId><version>2023.6.5</version>
</parent><artifactId>app-common</artifactId><packaging>jar</packaging>
接着需要定义接口和数据模型。需要特别注意的是,数据模型必须实现 Serializable
序列化接口。
@Data
public class ChatQuestion implements Serializable {private String text;
}@Data
public class ChatAnswer implements Serializable {private String text;private LocalDateTime datetime;
}
接着我们定义一个公共接口,不需要添加任何注解,仅作为服务提供者和消费者的公共部分。
public interface ChatService {ChatAnswer conversation(ChatQuestion question);
}
2.4.2.app-provider
服务提供者(provider)提供了 ChatService
接口的实现,它会注册到 NACOS 上,供其他服务通过通过 Dubbo 进行调用。
<parent><groupId>com.supcon</groupId><artifactId>app</artifactId><version>2023.6.5</version>
</parent><artifactId>app-provider</artifactId><packaging>jar</packaging><dependencies><dependency><groupId>com.supcon</groupId><artifactId>common</artifactId><version>2023.6.5</version></dependency><!-- dubbo --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>${dubbo.version}</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-nacos</artifactId><version>${dubbo.version}</version></dependency><!-- nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
</dependencies>
由于使用了 Dubbo 和 NACOS,因此我们需要在配置文件中添加相关的配置。
server:port: 8081spring:application:name: app-providercloud:nacos:username: nacospassword: Supcon@1304server-addr: chenshoufeng:8848config:file-extension: yamlenabled: truediscovery:enabled: truedubbo:registry:address: nacos://chenshoufeng:8848?username=nacos&password=Supcon@1304protocol:port: -1host: chenshoufeng
还要注意打开 Dubbo 和 NACOS Discovery 注解。
@EnableDiscoveryClient
@EnableDubbo
public class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}
}
接着我们为公共接口 ChatService
添加实现。这里的 @DubboService
可以替代 @Service
的功能,比如在控制器中注入这个 ChatServiceImpl
实现。
@DubboService
public class ChatServiceImpl implements ChatService {@Overridepublic ChatAnswer conversation(ChatQuestion question) {return new ChatAnswer(LangUtil.cheat());}
}
public class LangUtil {public static String cheat() {return "我,秦始皇,打钱";}
}
2.4.3.app-consumer
服务消费者(consumer)其实就是我们的上层业务服务,通过公共接口(common)经过 Dubbo 远程调用服务提供者(provider)的接口实现。
<parent><groupId>com.supcon</groupId><artifactId>app</artifactId><version>2023.6.5</version>
</parent><artifactId>app-consumer</artifactId><packaging>jar</packaging><dependencies><dependency><groupId>com.supcon</groupId><artifactId>common</artifactId><version>2023.6.5</version></dependency><dependency><groupId>com.supcon</groupId><artifactId>system-base</artifactId></dependency><!-- dubbo --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>${dubbo.version}</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-nacos</artifactId><version>${dubbo.version}</version></dependency><!-- nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
</dependencies>
同样的,需要添加 Dubbo 和 NACOS 的相关配置。
server:port: 8080spring:application:name: chatter-consumercloud:nacos:username: nacospassword: Supcon@1304server-addr: chenshoufeng:8848config:file-extension: yamldubbo:registry:address: nacos://chenshoufeng:8848?username=nacos&password=Supcon@1304protocol:port: -1host: chenshoufeng
还要注意打开 Dubbo 和 NACOS Discovery 注解。
@EnableDiscoveryClient
@EnableDubbo
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}
}
然后我们只需要编写业务控制器,通过 Dubbo 调用服务提供者的实现即可!
@RestController
@RequestMapping("/api/chat")
public class ChatController extends BasicController {@DubboReferenceChatService chatService;@PostMapping("dubbo")public ResponseEntity<ChatAnswer> dubbo(ChatQuestion question) {return ResponseEntity.ok(chatService.conversation(question));}
}
3.优势分析与性能对比
3.1.服务调用 - Dubbo Vs. Feign
分析 Dubbo 和 Feign 在服务调用方面的优势,如性能、功能等方面对比。
提供者编写差异
无论是 Dubbo 还是 Feign,都需要为功能接口编写实现。差别无非是使用 @Service
还是 @DubboService
而已,但实际上标注了 @DubboService
的实现类也能替代 @Service
注解。
@DubboService
//@Service
public class ChatServiceImpl implements ChatService {@Overridepublic ChatAnswer conversation(ChatQuestion question) {return new ChatAnswer(LangUtil.random());}
}
而 Feign 还需要编写控制器实现,毕竟只能通过 HTTP/S 进行交互。
@RestController
@RequestMapping("/api/chat")
public class ChatController extends BasicController {@ResourceChatService chatService;@PostMapping("conversation")public ResponseEntity<ChatAnswer> conversation(ChatQuestion question) {return ResponseEntity.ok(chatService.conversation(question));}
}
消费者使用差异
Feign 首先需要在启动类上标注 @EnableFeignClients
,然后通过 @Resource
注入客户端。
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ChatterConsumerApplication.class, args);}
}@RestController
@RequestMapping("/api/chat")
public class ChatController extends BasicController {@ResourceChatFeign chatFeign;@PostMapping("feign")public ResponseEntity<ChatAnswer> feign(ChatQuestion question) {return ResponseEntity.ok(chatFeign.conversation(question));}
}
Dubbo 类似,无非是注解不同。而且因为 Dubbo 通常会配合注册中心一起使用,所以也要加上 @EnableDiscoveryClient
注解。
@EnableDubbo
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ChatterConsumerApplication.class, args);}
}@RestController
@RequestMapping("/api/chat")
public class ChatController extends BasicController {@DubboReferenceChatService chatService;@PostMapping("dubbo")public ResponseEntity<ChatAnswer> dubbo(ChatQuestion question) {return ResponseEntity.ok(chatService.conversation(question));}
}
3.2.链路追踪 - Skywalking Vs. Log4j2
分析 Skywalking 和 Log4j2 在链路追踪方面的优势,如支持的链路追踪协议、性能对比、功能差异等。
/ | SkyWalking | Log4j2 |
---|---|---|
链路追踪 | 支持多种链路追踪协议,包括 Zipkin、Jaeger 等,可以与不同的链路追踪系统进行集成 | 并不支持链路追踪。 但是开发框架通过在请求头中置入 requestId 来主动保证请求是被串联起来的(前提是使用开发框架的请求方案),Log4j2 仅负责在日志中打印这个标识 |
性能对比 | 经过针对性优化,具备较高的性能。它使用 Agent 进行数据采集,通过异步非阻塞的方式发送数据到 Collector,减少了对应用程序的性能影响 | 性能损耗仅在于复制请求头,所以性能很高。 |
功能差异 | SkyWalking 是一套专门用于分布式系统的性能监控和故障诊断工具。 除了链路追踪外,它还提供了全面的监控功能,包括性能指标收集、拓扑图展示、报警等,以帮助开发人员全面了解和优化系统性能 | 存粹的日志记录工具 |
目前,我们通过在请求头中置入 requestId
的方式使得微服务调用能够串联起来,但这种方式既不能捕获 MariaDB/Redis 等中间件,也无法保证链路的连续性(如果链路中有不经过开发框架请求方案的请求发生的话)。
而 SkyWalking 作为专门设计用于分布式系统的性能监控和链路追踪工具,它提供广泛的功能和协议支持,显然更适合在此场景进行使用。
4.结论
总结 Dubbo 与 Skywalking 集成的主要收获与体会,对微服务框架选型有所帮助。
如果从问题排查、调试的角度进行考虑,那么无论是 Dubbo 还是 SkyWalking 都会在我们当前的微服务体系下带来更好的体验,这是 Feign 和 Log4j2 做不到的。
如果从性能的角度进行考量,那么 Dubbo 的网络通信、序列化设计会让它更有优势。而 SkyWalking 通过代理的方式虽然在应用的核心业务逻辑上没有影响,但是通过 Agent 代理仍然会产生一些额外的性能消耗。
Agent 会拦截应用程序的网络请求、方法调用等操作,并将这些数据发送到 SkyWalking Collector 进行处理和分析。
如果从研发成本的角度进行考量,那么引入 SkyWalking 仅有 Dockerfile 文件需要调整。而使用 Dubbo 则会产生不小的改造成本,对服务提供方还好;对于服务使用方来说,则有着巨大的改造成本。并且,在某些特定的场景——如和 Runtime Python 服务进行交互时,依然只能使用 Feign/OkHttp 的方式。
5.参考资料
- https://cn.dubbo.apache.org/zh-cn/
- https://www.cnblogs.com/cjsblog/p/14075486.html
- https://segmentfault.com/a/1190000039836624