前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何将一个 Dubbo 项目改造成一个 Service Mesh 项目?

如何将一个 Dubbo 项目改造成一个 Service Mesh 项目?

原创
作者头像
axlyzhang
修改2020-07-01 17:03:20
4K0
修改2020-07-01 17:03:20
举报
文章被收录于专栏:泛互云原生泛互云原生

本篇介绍如何将一个 Dubbo 项目改造成一个 SpringBoot + K8S + Istio 项目的全过程,实现了在不改变 Dubbo 项目整体代码结构的基础上,向 Service Mesh 云原生项目的蜕变。

一. 痛点问题:

  • 服务名调用问题:Istio 通过对 K8S 服务名调用的拦截,实现了无侵入式的流量治理功能,因此 Isito 要求不同服务间的调用必须以服务名的方式进行。现有项目是否为服务名调用,成了不同类型的项目向 Istio 改造的最大障碍之一,例如 Dubbo 项目就不是服务名调用,而是 Interface 调用,这是第一个痛点问题。
  • 注册中心问题:由于 Istio 目前只支持 K8S etcd、Consul 两种服务注册中心,其他注册中心(例如:Zookeeper)的对接以及跟 Istio 配置文件的集成及 xDS 协议数据的下发,成为了第二个痛点问题。
  • 私有协议问题:由于 Istio 目前只支持 http、gRPC、tcp 三种协议,私有协议适配难度较高,即使在新版 Envoy 已经支持了 Dubbo 协议的情况下,还是需要通过 EnvoyFilter 下发专属 xDS 协议数据来支持 Dubbo 的服务调用及流量治理,这是第三个痛点问题。

二. 改造思路:

  • 由于改造 Dubbo SDK、Isito控制面、Envoy 数据面,让 Dubbo 去适配 Service Mesh 的技术难度较大,而且即使改造成功也需要通过 EnvoyFilter 下发专属 xDS 协议数据的形式来支持 Dubbo 服务间调用的流量治理,使得这种方式与原生 Istio 的使用方式差距较大。
  • 所以我们选择了一条将 Dubbo 项目改造成 SpringBoot + K8S + Istio 项目的更简单的路,充分利用现有 Dubbo 项目的代码结构,将代码修改量降到一个可控的范围内。
  • 由于 Dubbo 项目 facade 模块的作用与 Spring Cloud Feign 模块的作用十分相似(模块内都是一些 interface,需要服务端 xxxServiceImpl 去实现各个 interface,消费端通过 @Resource 注解的方式引入 interface 并直接调用),使得 Dubbo 最复杂的服务间调用方式有了解决的方案。
  • 此次改造只是利用了 Dubbo 项目的代码结构,Dubbo 原有的注册中心、Dubbo 协议等功能全部都会被去掉,也就是改造后的项目跟 Dubbo 已经没有任何关系了,所以注册中心、Dubbo私有协议这两个痛点问题也就不存在了。
  • K8S 会接管服务注册发现、服务编排等工作,Istio 会接管服务治理、调用链监控、服务安全等工作,改造后的项目是一个标准的 Service Mesh 项目。

三. Dubbo 项目结构:

现有 Dubbo 项目 xyz-dubbo,包括以下三个模块:

  • dubbo-facade: dubbo 接口定义
  • dubbo-provider: 服务提供方
  • dubbo-consumer: 服务调用方

四. 具体改造步骤:

4.1 Dubbo 项目根 POM 改造:

  • 根 pom.xml 引入 SpringBoot parent,增加 spring-cloud-dependencies import 引用。
  • 删除所有 dubbo 相关引用。
  • 虽然 pom 文件改动很大,但属于一次性改动,改造工作量较小。

改造前的 Dubbo 项目根 pom.xml:

代码语言:txt
复制
<?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>

    <groupId>io.xyzdemo.dubbo</groupId>
    <artifactId>xyz-dubbo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>xyz-dubbo</name>

    <modules>
        <module>dubbo-consumer</module>
        <module>dubbo-provider</module>
        <module>dubbo-facade</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.xyzdemo.dubbo</groupId>
                <artifactId>dubbo-facade</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>4.2.5.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>2.7.6</version>
            </dependency>
            <dependency>
                <groupId>com.ecwid.consul</groupId>
                <artifactId>consul-api</artifactId>
                <version>1.4.5</version>
            </dependency>
            <dependency>
                <groupId>com.orbitz.consul</groupId>
                <artifactId>consul-client</artifactId>
                <version>1.4.2</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

改造后的 SpringBoot 项目根 pom.xml:

代码语言:txt
复制
<?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>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>io.xyzdemo.dubbo</groupId>
    <artifactId>xyz-dubbo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>xyz-dubbo</name>

    <modules>
        <module>dubbo-consumer</module>
        <module>dubbo-provider</module>
        <module>dubbo-facade</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>io.xyzdemo.dubbo</groupId>
                <artifactId>dubbo-facade</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

4.2 dubbo-facade 项目改造:

  • pom.xml 增加 spring-cloud-starter-openfeign 引用。
  • 删除所有 Dubbo 相关引用、Dubbo 相关配置文件。
  • Dubbo 原有 facade 接口是标准的 JAVA 接口定义,与 Feign Restful 接口定义十分类似。这里可以在原有的 facade 接口基础上增加 @FeignClient、@RequestMapping 等注解,将一个普通的 facade 接口改造成一个 Feign Restful 接口,后续会使用 Feign 这个 Restful 框架来处理服务间调用等问题。
  • 由于 Feign 本身是自带了 Ribbon 负载均衡,服务访问者经过负载均衡后会找到服务提供者的一个 IP+Port 进行调用,这与 K8S Service 要求的服务名调用的方式相冲突,所以必须想办法去掉 Feign 自带的负载均衡。好在 @FeignClient 可以手工指定一个固定的调用地址,这里可以把这个地址设置成 K8S Service 的 name 名称,从而实现了通过 Feign 对 K8S Service 服务名调用的能力。此部分需要每个 facade 接口增加注解一次,改造工作量相对可控。
  • 由于 Feign 要求接口使用 Restful 格式,所以接口中的每个抽象方法都必须添加 @RequestMapping、@GetMapping、@PostMapping 等注解暴露成一个 Restful 资源地址。此部分改造涉及到每个 facade 接口的每个抽象方法,是整个方案里改动量最大的一部分。
  • 此部分整体改造工作量取决于原有的 Dubbo 项目包含多少个 facade 接口,以及每个 facade 包含多少个抽象方法。

改造前的 Dubbo facade 接口示例:

代码语言:txt
复制
public interface HelloService {
	String sayHello(String name);
}

改造后的 Feign Restful 接口示例:

代码语言:txt
复制
@FeignClient(name = "dubbo-provider", url = "http://dubbo-provider:8001")
public interface HelloService {
	@GetMapping(value = "/apis/hello/{name}")
	String sayHello(@PathVariable String name);
}

4.3 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 注解,不要遗漏。
  • 此部分大都属于一次性改动,改造工作量相对可控。

改造前的 dubbo-provider 模块 pom.xml 配置:

代码语言:txt
复制
<?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>
        <artifactId>xyz-dubbo</artifactId>
        <groupId>io.xyzdemo.dubbo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>dubbo-provider</artifactId>
    <packaging>war</packaging>
    <name>dubbo-provider</name>

    <dependencies>
        <dependency>
            <groupId>io.xyzdemo.dubbo</groupId>
            <artifactId>dubbo-facade</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ecwid.consul</groupId>
            <artifactId>consul-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.orbitz.consul</groupId>
            <artifactId>consul-client</artifactId>
        </dependency>
    </dependencies>
</project>

改造后的 dubbo-provider 模块 pom.xml 配置:

代码语言:txt
复制
<?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>
        <artifactId>xyz-dubbo</artifactId>
        <groupId>io.xyzdemo.dubbo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>dubbo-provider</artifactId>
    <name>dubbo-provider</name>

    <dependencies>
        <dependency>
            <groupId>io.xyzdemo.dubbo</groupId>
            <artifactId>dubbo-facade</artifactId>
        </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>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>io.xyzdemo.dubbo.provider.ProviderApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

改造后的 HelloServiceImpl.java 代码示例(改造前只是缺少 @RestController 注解,其他代码完全一致):

代码语言:txt
复制
@RestController
public class HelloServiceImpl implements HelloService {
	public String sayHello(String name) {
		return String.format("hello %s! podIP is %s!", name, CommonUtils.getLocalIP());
	}
}

4.4 dubbo-consumer 项目改造

  • pom.xml 增加 spring-boot-starter-web、spring-cloud-starter-openfeign 等引用,同时增加 SpringBoot mainClass 标准启动项配置。
  • 删除所有 Dubbo 相关引用、Dubbo 相关配置文件。
  • 增加 SpringBoot 启动类,增加 @SpringBootApplication、@EnableFeignClients(需要配置 basePackages 扫描包路径) 两个注解,并配置 dubbo-consumer 服务端口号。
  • 此部分大都属于一次性改动,改造工作量相对可控。

由于dubbo-consumer 项目改造与dubbo-provider 改造极其相似,这里不再贴出代码示例。

五. 将改造后的项目部署到 K8S + Istio:

将下面三个配置文件通过 kubectl 在 K8S+Istio 的集群中执行,即可完成改造后的项目在 K8S+Istio 集群中的部署。

5.1 创建 dubbo-provider K8S Deployment、K8S Service(ClusterIP),提供集群内访问服务

dubbo-provider-service.yml 配置文件示例:

代码语言:txt
复制
apiVersion: v1
kind: Service
metadata:
  name: dubbo-provider
spec:
  type: ClusterIP
  ports:
    - name: http
      port: 8001
  selector:
    k8s-app: dubbo-provider
    qcloud-app: dubbo-provider

---

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: dubbo-provider-deployment
  labels:
    k8s-app: dubbo-provider
    qcloud-app: dubbo-provider
spec:
  replicas: 2 # 副本数量
  selector:
    matchLabels:
      k8s-app: dubbo-provider
      qcloud-app: dubbo-provider
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        k8s-app: dubbo-provider
        qcloud-app: dubbo-provider
    spec:
      containers:
        - name: dubbo-provider
          image: ccr.ccs.tencentyun.com/axlyzhang-images/dubbo-provider:v1.1 # 镜像地址
          env:
            - name: PATH
              value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
            - name: LANG
              value: C.UTF-8
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-8-openjdk-amd64
            - name: JAVA_VERSION
              value: 8u111
          resources:
            limits:
              cpu: 500m
              memory: 1Gi
            requests:
              cpu: 250m
              memory: 256Mi
          securityContext:
            privileged: false
            procMount: Default
      imagePullSecrets:
        - name: qcloudregistrykey
        - name: tencenthubkey

5.2 创建 dubbo-consumer K8S Deployment、K8S Service(ClusterIP),提供集群内访问服务:

dubbo-consumer-service.yml 配置文件示例:

代码语言:txt
复制
apiVersion: v1
kind: Service
metadata:
  name: dubbo-consumer
spec:
  type: ClusterIP
  ports:
    - name: http
      port: 8002
  selector:
    k8s-app: dubbo-consumer
    qcloud-app: dubbo-consumer

---

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: dubbo-consumer-deployment
  labels:
    k8s-app: dubbo-consumer
    qcloud-app: dubbo-consumer
spec:
  replicas: 2 # 副本数量
  selector:
    matchLabels:
      k8s-app: dubbo-consumer
      qcloud-app: dubbo-consumer
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        k8s-app: dubbo-consumer
        qcloud-app: dubbo-consumer
    spec:
      containers:
        - name: dubbo-consumer
          image: ccr.ccs.tencentyun.com/axlyzhang-images/dubbo-consumer:v1.0 # 镜像地址
          env:
            - name: PATH
              value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
            - name: LANG
              value: C.UTF-8
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-8-openjdk-amd64
            - name: JAVA_VERSION
              value: 8u111
          resources:
            limits:
              cpu: 500m
              memory: 1Gi
            requests:
              cpu: 250m
              memory: 256Mi
          securityContext:
            privileged: false
            procMount: Default
      imagePullSecrets:
        - name: qcloudregistrykey
        - name: tencenthubkey

5.3 创建 istio ingressgateway、VirtualService,提供公网访问入口,并进行流量治理测试:

代码语言:txt
复制
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: dubbo-consumer-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*"

---

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: consumer-vs
spec:
  hosts:
    - "*"
  gateways:
    - dubbo-consumer-gateway
  http:
    - match:
        - uri:
            prefix: /hello/error
      route:
        - destination:
            host: dubbo-consumer
            port:
              number: 8003   -- 错误的服务端口
    - route:
        - destination:
            host: dubbo-consumer
            port:
              number: 8002   -- 正确的服务端口

六. 改造成果验证及总结:

  • http://111.230.187.114/hello/world (virtualservice 配置路由到一个正确的服务端口 8002,正常访问)
  • http://111.230.187.114/hello/error (virtualservice 配置路由到一个错误的服务端口 8003,访问出错,证明 VirtualService 已经起到了服务治理的作用,项目改造验证成功)

经过上面几步操作,我们成功的将一个 Dubbo 项目改造成了一个 Service Mesh 项目,并在 K8S + Istio 集群中部署成功、测试通过。

当然,真实业务系统中的架构复杂度是远高于这个 Demo 的,实际改造的难度要比改造这个 Demo 大得多。这篇文章只是抱砖引玉,希望可以跟大家继续探讨 Dubbo 向 Service Mesh 改造的更好方式。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 痛点问题:
  • 二. 改造思路:
  • 三. Dubbo 项目结构:
  • 四. 具体改造步骤:
  • 4.1 Dubbo 项目根 POM 改造:
  • 4.2 dubbo-facade 项目改造:
  • 4.3 dubbo-provider 项目改造:
  • 4.4 dubbo-consumer 项目改造:
  • 五. 将改造后的项目部署到 K8S + Istio:
  • 5.1 创建 dubbo-provider K8S Deployment、K8S Service(ClusterIP),提供集群内访问服务:
  • 5.2 创建 dubbo-consumer K8S Deployment、K8S Service(ClusterIP),提供集群内访问服务:
  • 5.3 创建 istio ingressgateway、VirtualService,提供公网访问入口,并进行流量治理测试:
  • 六. 改造成果验证及总结:
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档