介绍

使用 Dubbo Spring Cloud 实现分布式调用

简介

在《Spring Cloud Alibaba 服务注册与发现》篇中曾提到,Spring Cloud Alibaba Nacos Discovery 能无缝整合 Spring Cloud OpenFeign。换言之,Spring Cloud Alibaba 延续了 Spring Cloud 分布式服务调用的特性。除此之外,Spring Cloud Alibaba 引入了 Dubbo Spring Cloud,扩展了分布式服务调用能力,不仅能使 Apache Dubbo 和 OpenFeign 共存,还允许 Spring Cloud 标准调用底层通过 Dubbo 支持的通讯协议传输。无论开发人员是 Dubbo 用户还是 Spring Cloud 用户,都能轻松地驾驭,并以接近“零”成本的代价使应用向上迁移。Dubbo Spring Cloud 致力于简化 Cloud Native 开发成本,提高研发效能以及提升应用性能等目的。

学习目标

  • 使用 Dubbo Spring Cloud 实现 Spring Cloud 分布式服务调用
  • 使用 Dubbo Spring Cloud 替换 Spring Cloud 分布式服务调用底层协议
  • 理解 Dubbo Spring Cloud 高级特性:服务订阅、元数据、Actuator

详细内容

  • 快速上手:使用 Apache Dubbo
  • 适配整合:使用注解 @DubboTransported 适配 Spring Cloud OpenFeign 和 @LoadBalanced RestTemplate
  • 运维特性:演示服务订阅、元信息(服务、REST)以及 Actuator Endpoints

功能特性介绍

本节没有操作内容 由于 Dubbo Spring Cloud 构建在原生的 Spring Cloud 之上,其服务治理方面的能力可认为是 Spring Cloud Plus,不仅完全覆盖 Spring Cloud 原生特性,而且提供更为稳定和成熟的实现,特性比对如下表所示:

功能组件

Spring Cloud

Dubbo Spring Cloud

分布式配置(Distributed configuration)

Git、Zookeeper、Consul、JDBC

Spring Cloud 分布式配置 + Dubbo 配置中心

服务注册与发现(Service registration and discovery)

Eureka、Zookeeper、Consul

Spring Cloud 原生注册中心+ Dubbo 原生注册中心

负载均衡(Load balancing)

Ribbon(随机、轮询等算法)

Dubbo 内建实现(随机、轮询等算法 + 权重等特性)

服务熔断(Circuit Breakers)

Spring Cloud Hystrix

Spring Cloud Hystrix + Alibaba Sentinel17 等

服务调用(Service-to-service calls)

Open Feign、RestTemplate

Spring Cloud 服务调用 + Dubbo@Reference

链路跟踪(Tracing)

Spring Cloud Sleuth+ Zipkin

Zipkin、opentracing 等

高亮特性

Dubbo 使用 Spring Cloud 服务注册与发现

Dubbo Spring Cloud 基于 Spring Cloud Commons 抽象实现 Dubbo 服务注册与发现,无需添加任何外部化配置,就能轻松地桥接到所有原生 Spring Cloud 注册中心,包括:

  • Nacos
  • Eureka
  • Zookeeper
  • Consul

注:Dubbo Spring Cloud 将在下个版本支持 Spring Cloud 注册中心与 Dubbo 注册中心并存,提供双注册机制,实现无缝迁移。

Dubbo 作为 Spring Cloud 服务调用

默认情况,Spring Cloud Open Feign 以及@LoadBalancedRestTemplate作为 Spring Cloud 的两种服务调用方式。Dubbo Spring Cloud 为其提供了第三种选择,即 Dubbo 服务将作为 Spring Cloud 服务调用的同等公民出现,应用可通过 Apache Dubbo 注解@Service和@Reference暴露和引用 Dubbo 服务,实现服务间多种协议的通讯。同时,也可以利用 Dubbo 泛化接口轻松实现服务网关。

Dubbo 服务自省

Dubbo Spring Cloud 引入了全新的服务治理特性 - 服务自省(Service Introspection),其设计目的在于最大化减轻注册中心负载,去 Dubbo 注册元信息中心化。假设一个 Spring Cloud 应用引入 Dubbo Spring Boot Starter,并暴露 N 个 Dubbo 服务,以Dubbo Nacos 注册中心为例,当前应用将注册 N+1 个 Nacos 应用,除 Spring Cloud 应用本身之前,其余 N 个应用均来自于 Dubbo 服务,当 N 越大时,注册中心负载越重。因此,Dubbo Spring Cloud 应用对注册中心的负载相当于传统 Dubbo 的 N 分之一,在不增加基础设施投入的前提下,理论上,使其集群规模扩大 N 倍。当然,未来的 Dubbo 也将提供服务自省的能力。

Dubbo 迁移 Spring Cloud 服务调用

尽管 Dubbo Spring Cloud 完全地保留了原生 Spring Cloud 服务调用特性,不过 Dubbo 服务治理的能力是 Spring Cloud Open Feign 所不及的,如高性能、高可用以及负载均衡稳定性等方面。因此,建议开发人员将 Spring Cloud Open Feign 或者@LoadBalancedRestTemplate迁移为 Dubbo 服务。考虑到迁移过程并非一蹴而就,因此,Dubbo Spring Cloud 提供了方案,即@DubboTransported注解。该注解能够帮助服务消费端的 Spring Cloud Open Feign 接口以及@LoadBalancedRestTemplateBean 底层走 Dubbo 调用(可切换 Dubbo 支持的协议),而服务提供方则只需在原有@RestController类上追加 Dubbo@Servce注解(需要抽取接口)即可,换言之,在不调整 Feign 接口以及RestTemplateURL 的前提下,实现无缝迁移。如果迁移时间充分的话,建议使用 Dubbo 服务重构系统中的原生 Spring Cloud 服务的定义。

准备工作

启动nacos、获取代码

通过脚手架来获取初始代码

这里没有按照官方的demo来,使用的是gradle项目,使用的组件为:

Spring Cloud Alibaba Dubbo
Nacos Service Discovery - 服务注册与发现组件
Spring Web - Spring Web MVC 组件
Spring Boot Actuator - Spring Boot Actator 组件
  • Group :“com.example”
  • Artifact:“dubbo-provider-sample“
    最终配置如下:
plugins {
  id 'org.springframework.boot' version '2.3.7.RELEASE'
  id 'io.spring.dependency-management' version '1.0.10.RELEASE'
  id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
  mavenLocal()
  maven { url 'https://maven.aliyun.com/repository/public/' }
  mavenCentral()
}

ext {
  set('springCloudAlibabaVersion', "2.2.2.RELEASE")
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-actuator'
  implementation 'org.springframework.boot:spring-boot-starter-web'
  implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery'
  implementation 'com.alibaba.cloud:spring-cloud-starter-dubbo'
  testImplementation('org.springframework.boot:spring-boot-starter-test') {
      exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
  }
}

dependencyManagement {
  imports {
      mavenBom "com.alibaba.cloud:spring-cloud-alibaba-dependencies:${springCloudAlibabaVersion}"
  }
}

test {
  useJUnitPlatform()
}

创建服务接口

创建 api 工程 - dubbo-sample-api

这里采用的是gradle的依赖方式,官方的是maven的方式,直接在idea里建立一个空的gradle项目,

  • Group :“com.example.dubbo”
  • Artifact:“dubbo-sample-api“

配置如下,gradle生成的setting文件可以不用,因为这个只是用于接口服务的包:

plugins {
    id 'java'
}

group 'com.example.dubbo'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenLocal()
    maven { url 'https://maven.aliyun.com/repository/public/' }
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

定义 Dubbo 服务接口

Dubbo 服务接口是服务提供方与消费方的远程通讯契约,通常由普通的 Java 接口(interface)来声明,如EchoService接口

package com.example.dubbo.api;

public interface EchoService {
	String echo(String message);
}

部署 api 工程 - dubbo-sample-api

本次测试采用的是gradle依赖,修改dubbo-provider-sampl的gradle的setting.gradle文件如下:

rootProject.name = 'dubbo-provider-sample'

include 'dubbo-sample-api'

在build.gradle中增加编译依赖

dependencies {
    compile(project(":dubbo-sample-api"))
}

服务端开发

前面的准备工作,已经创建了服务端工程,本节主要完成了针对服务接口的实现工作。上面已经配置好了接口的依赖,现在来实现服务的方法

实现 Dubbo 服务

这里新建了一个service包用来单独存接口方法

package com.example.dubboprovidersample.service;


import com.example.dubbo.api.EchoService;
import org.apache.dubbo.config.annotation.DubboService;

@DubboService
public class SimpleEchoService implements EchoService {

	@Override
	public String echo(String message) {
		return "[ECHO] " + message;
	}
}

其中,@DubboService是 Dubbo 服务注解,仅声明该 Java 服务(本地)实现为 Dubbo 服务,原@org.apache.dubbo.config.annotation.Service已经弃用。 因此,下一步需要将其配置 Dubbo 服务(远程)。

配置 Dubbo 服务提供方

打开 dubbo-provider-sample 的 application.properties 文件,主要修改内容如下:

# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

# 应用服务 WEB 访问端口
server.port=61000

# dubbo 服务扫描基准包
dubbo.scan.base-packages=com.example.dubboprovidersample.service

application.properties 的配置项说明:

  • dubbo.scan.base-packages: 指定 Dubbo 服务实现类的扫描基准包
  • dubbo.protocol: Dubbo 服务暴露的协议配置,其中子属性name为协议名称,port为协议端口( -1 表示自增端口,从 20880 开始)
  • spring.application.name: Spring 应用名称,用于 Spring Cloud 服务注册和发现。

该值在 Dubbo Spring Cloud 加持下被视作dubbo.application.name,因此,无需再显示地配置dubbo.application.name

  • spring.cloud.nacos.discovery: Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口

客户端开发

同样以脚手架方式生成客户端基础代码,如下

Spring Cloud Alibaba Dubbo
Nacos Service Discovery - 服务注册与发现组件
Spring Web - Spring Web MVC 组件
Spring Boot Actuator - Spring Boot Actator 组件
  • Group :“com.example”
  • Artifact:“dubbo-consumer-sample“

增加 api 依赖 - dubbo-sample-api

增加客户端对api的依赖,方法和服务端相同

修改消费端配置

Dubbo 服务消费方配置与服务提供方类似,当前应用 dubbo-consumer-sample 属于纯服务消费方,只有很少的地方需要修改:

# 这里订阅"自己",会被忽略掉,请根据实际情况添加
dubbo.cloud.subscribed-services=dubbo-consumer-sample,dubbo-provider-sample

dubbo.cloud.subscribed-services: 用于服务消费方订阅服务提供方的应用名称的列表,若需订阅多应用,请使用 "," 分割。不推荐使用默认值为 "*",它将订阅所有应用。

继续修改端口等,和服务端类似,最终配置如下:

plugins {
    id 'org.springframework.boot' version '2.3.7.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenLocal()
    maven { url 'https://maven.aliyun.com/repository/public/' }
    mavenCentral()
}

ext {
    set('springCloudAlibabaVersion', "2.2.2.RELEASE")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery'
    implementation 'com.alibaba.cloud:spring-cloud-starter-dubbo'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    compile(project(":dubbo-sample-api"))
}

dependencyManagement {
    imports {
        mavenBom "com.alibaba.cloud:spring-cloud-alibaba-dependencies:${springCloudAlibabaVersion}"
    }
}

test {
    useJUnitPlatform()
}

增加web访问控制器

这里新建了一个controller的包。dubbo-consumer-sample 增加一个控制器,以支持处理web请求,并调用服务端的EchoService。这里的原官方例子中注解过期,使用新的注解。

package com.example.dubboconsumersample.controller;

import com.example.dubbo.api.EchoService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DubboConsumerSampleController {

	@DubboReference
	private EchoService echoService;

	// http://127.0.0.1:8080/echo?message=somemessage
	@GetMapping("/echo")
	public String echo(@RequestParam(name = "message", defaultValue = "no message") String message) {
		return echoService.echo(message);
	}
}

编译、启动项目

http://127.0.0.1:62000/echo?message=hello%20dubbo

返回结果

[ECHO] hello dubbo

服务端服务提供者日志如下:

INFO 9612 --- [erverWorker-3-1] o.a.d.r.t.netty4.NettyServerHandler      :  [DUBBO] The connection of /169.254.232.209:14995 -> /169.254.232.209:20880 is established., dubbo version: 2.7.8, current host: 192.168.1.2