前言
公司的分布式架构是基于Alibaba Dubbo
实现的,微服务的相关治理也是基于Alibaba Dubbo
做的,随着公司系统规模的增长服务发布
,服务的治理
,注册中心的压力
,硬件成本
等问题逐渐的凸显出来,尤其是服务发布
和服务治理
,比如多版本的发布,金丝雀发布,精细化的流量控制等,用Alibaba Dubbo
实现这些分布式语义很麻烦,在Service Mesh
架构下,解决这些问题都会变得容易,基于此,本篇介绍一下公司Dubbo项目
的Service Mesh
改造方案。
其他基础架构
除了Alibaba Dubbo
,公司使用Zookeeper
作为配置中心,分布式消息队列rocketmq
,分布式缓存redis
,阿里云分库分表产品DRDS
,阿里云关系型数据库RDS
,这些基础组件在整个Service Mesh
架构的改造中是不变的。
Dubbo项目Service Mesh改造具体实现
基础技术方案
将Dubbo
架构改造成Service Mesh
架构就要Dubbo
框架从项目中移除,我们这里将Dubbo
框架移除,并且将服务端接口全部改造成rest
接口,这就需要服务端使用新的容器发布rest
接口,所以干脆就将项目改造成Sping Boot Web
应用,协议问题没有了,但是如何尽可能改动小的能够让消费端快速的接入Sping Boot Web
应用的服务端就成为了难点,由于Dubbo
项目API
模块的作用与Spring Cloud Feign
模块的作用十分相似,模块内都是一些interface
,需要服务端定义xxxServiceImpl
去实现各个interface
,然后消费者通过Spring
依赖注入将interface
注入并使用,这就使得Dubbo
最复杂的服务间调用方式有了解决的方案。
基础组件改造
- 根pom.xml引入SpringBoot parent,增加 spring-cloud-dependencies import引用
- 删除所有
dubbo
相关引用。
这一步骤简单来说就是将pom.xml中Dubbo的所有jar依赖全部删除,并且引入SpringBoot parent和 spring-cloud-dependencies的引用,例如:
改造前:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.8.4</version>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
</exclusion>
<exclusion>
<artifactId>curator-framework</artifactId>
<groupId>org.apache.curator</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
<exclusions>
<exclusion>
<artifactId>zookeeper</artifactId>
<groupId>org.apache.zookeeper</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
<!-- 移除log4j的打印框架,否则,log 4j日志不生效 -->
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
改造后:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.3.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>feign-hystrix</artifactId>
<groupId>io.github.openfeign</groupId>
</exclusion>
<exclusion>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<groupId>org.springframework.cloud</groupId>
</exclusion>
<exclusion>
<artifactId>spring-cloud-starter-netflix-archaius</artifactId>
<groupId>org.springframework.cloud</groupId>
</exclusion>
<exclusion>
<artifactId>spring-cloud-netflix-ribbon</artifactId>
<groupId>org.springframework.cloud</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
Dubbo门面API改造
-
pom.xml
增加spring-cloud-starter-openfeign
引用。 -
删除所有
Dubbo
相关引用Dubbo
相关配置文件。 -
Dubbo
原有API
接口是标准的JAVA
接口定义,与Feign Restful
接口定义十分类似。这里可以在原有的API
接口基础上增加@FeignClient
、@RequestMapping
等注解,将一个普通的API
接口改造成一个Feign Restful
接口,后续会使用Feign
这个Restful
框架来处理服务间调用等问题。由于Feign
本身是自带了Ribbon
负载均衡,服务访问者经过负载均衡后会找到服务提供者的一个 IP+Port进行调用,这与K8S Service
要求的服务名调用的方式相冲突,所以必须想办法去掉Feign
自带的负载均衡。好在@FeignClient
可以手工指定一个固定的调用地址,这里可以把这个地址设置成K8S Service
的name
名称,从而实现了通过Feign
对K8S Service
服务名调用的能力。此部分需要每个API
接口增加注解一次,改造工作量相对可控。 -
由于
Feign
要求接口使用Restful
格式,所以接口中的每个抽象方法都必须添加@RequestMapping
,@GetMapping
,@PostMapping
等注解暴露成一个Restful
资源地址。此部分改造涉及到每个API
接口的每个抽象方法,是整个方案里改动量最大的一部分。 -
此部分整体改造工作量取决于原有的
Dubbo
项目包含多少个API
接口,以及每个API
包含多少个抽象方法。
改造前示例:
public interface IOdaApi {
ModelsReturn<ModelsCommonResponse<ConsumptionODAResponse>> consumptionODA(ConsumptionODARequest consumptionODARequest);
ModelsReturn<ModelsCommonResponse<RefundODAResponse>> refundODA(RefundODARequest refundODARequest);
ModelsReturn<ModelsCommonResponse<ConsumptionODAResponse>> consumptionQueryODA( ConsumptionQueryODARequest consumptionQueryODARequest);
ModelsReturn<ModelsCommonResponse<String>> repayODA(RepayODARequest repayODARequest);
}
改造后示例:
@FeignClient(name = "miss-xxx-svc", url = "http://miss-xxx-svc:8080")
public interface IOdaApi {
@PostMapping(value = "/xxxService/consumptionODA")
ModelsReturn<ModelsCommonResponse<ConsumptionODAResponse>> consumptionODA(@RequestBody ConsumptionODARequest consumptionODARequest);
}
Dubbo Provider端改造
-
pom.xml
增加spring-boot-starter-web
、spring-cloud-starter-openfeign
等引用,同时增加SpringBoot
mainClass 标准启动项配置。删除所有Dubbo
相关引用、Dubbo
相关配置文件。 -
增加
SpringBoot
启动类,增加@SpringBootApplication
、@EnableFeignClients
两个注解,配置dubbo provider服务端口号。 -
xxxServiceImpl服务实现类上增加
@RestController
注解,提供 consumer Restful 访问的能力。 这个需要每个服务实现类都加上@RestController
注解,不要遗漏。 -
此部分大都属于一次性改动,改造工作量相对可控。
改造后示例:
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
...
</parent>
<artifactId>MISS.xxxService</artifactId>
<dependencies>
<dependency>
<groupId>com.xxx.xxx</groupId>
<artifactId>MISS.xxxService.API</artifactId>
<version>2.0.5-RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<build>
...
</build>
</project>
改造后的 OdaImpl.java 代码示例(改造前只是缺少 @RestController 注解,其他代码完全一致):
@Slf4j
@RestController
public class OdaImpl implements IOdaApi {
@Resource
private OdaService odaService;
@Override
public ModelsReturn<ModelsCommonResponse<ConsumptionODAResponse>> consumptionODA(ConsumptionODARequest consumptionODARequest) {
try {
Validation.validate(consumptionODARequest);
...
return ErrorCode.returnMsg(ErrorCode.OPSERATE_SUCCESS, odaService.consumptionODA(consumptionODARequest.getMultiIndustryInfo(),consumptionODA));
} catch (Exception e) {
log.error(LogUtil.exceptionMarker(), "ODA交易异常",e);
return ErrorCode.returnMsgByErrorMessage(ErrorCode.PARA_ERROR, "ODA交易异常");
}
}
}
Dubbo Comsumer端改造
-
pom.xml
增加spring-boot-starter-web
、spring-cloud-starter-openfeign
等引用,同时增加SpringBoot
mainClass 标准启动项配置。 -
删除所有
Dubbo
相关引用、Dubbo
相关配置文件。 -
增加
SpringBoot
启动类,增加@SpringBootApplication
、@EnableFeignClients
(需要配置 basePackages 扫描包路径) 两个注解,并配置dubbo consumer服务端口号。 -
此部分大都属于一次性改动,改造工作量相对可控。
由于dubbo consumer项目改造与dubbo provider改造极其相似,这里不再贴出代码示例。
总结
至此Dubbo项目的Service Mesh改造就已经完成了,k8s + Istio的部署这里就不做介绍了,此方案是在我司成功落地的技术方案,并且使用次方案进行改造的服务也具有一定规模,目前生产环境还没有遇到任何问题。
此种改造方案改动量相对于完全的去掉dubbo而不使用feign改造的方式来说改动较小,如有更好的改造方案欢迎交流。