dubbo 实践

dubbo 实践

什么是 dubbo

根据官方文档的说法,dubbo是一款 RPC 服务开发框架。其用于微服务之间的服务治理和通信。

在实践中,除了用于微服务开发,也可以用于多个业务实例中某一业务过程的同步调用。

demo 示例

方便起见,创建一个 Spring Boot 项目,使用 Gradle 作为依赖管理和构建工具。

文件 settings.gradle

1
2
3
4
rootProject.name = 'nacos-dubbo-test'
// 引入子项目
include 'api'
include 'service'

文件 build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
plugins {
id 'java'
}

group 'top.kanchitsu'
version '1.0-SNAPSHOT'

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

dependencies {
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter
implementation 'org.springframework.boot:spring-boot-starter:2.7.18'
// https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-spring-boot-starter
implementation 'org.apache.dubbo:dubbo-spring-boot-starter:3.3.4'
// https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-nacos-spring-boot-starter
implementation 'org.apache.dubbo:dubbo-nacos-spring-boot-starter:3.3.4'
// https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-registry-redis
implementation project(':api')
}

test {
useJUnitPlatform()
}
subprojects {
apply plugin: 'java'

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

}

创建 api

api 子项目中用于定义 RPC 接口

文件 build.gradle

1
2
3
4
5
6
plugins {
id 'java'
}

group 'top.kanchitsu'
version '1.0-SNAPSHOT'

文件 DemoService.java

1
2
3
4
5
package top.kanchitsu.api;

public interface DemoService {
String sayHello(String name);
}

创建 api 的实现(provider)

service 子项目用于实现已定义的 RPC 接口

文件 build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
plugins {
id 'java'
}

group 'top.kanchitsu'
version '1.0-SNAPSHOT'

dependencies {
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter
implementation 'org.springframework.boot:spring-boot-starter:2.7.18'
// https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-spring-boot-starter
implementation 'org.apache.dubbo:dubbo-spring-boot-starter:3.3.4'
// https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-nacos-spring-boot-starter
implementation 'org.apache.dubbo:dubbo-nacos-spring-boot-starter:3.3.4'

implementation project(':api')
}

test {
useJUnitPlatform()
}

文件 DemoServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
package top.kanchitsu.service;

import org.apache.dubbo.config.annotation.DubboService;
import top.kanchitsu.api.DemoService;

@DubboService
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello " + name;
}
}

文件 application.yml

1
2
3
4
5
6
7
8
dubbo:
registry:
address: nacos://${nacos.address:127.0.0.1}:8848?username=nacos&password=VOLtOugsgy
register-mode: instance
application:
name: demo01-consumer
logger: slf4j
qos-port: 22223

使用 api(customer)

在根项目中使用api
文件Consumer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package top.kanchitsu.Consumer;

import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import top.kanchitsu.api.DemoService;


@Component
public class Consumer implements CommandLineRunner {
@DubboReference(scope = "remote")
private DemoService demoService;

@Override
public void run(String... args) throws Exception {
String result = demoService.sayHello("world");
System.out.println("Receive result ======> " + result);
}
}

文件Main.java

1
2
3
4
5
6
7
8
9
10
11
package top.kanchitsu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}

文件 application.yml

1
2
3
4
5
6
7
8
dubbo:
registry:
address: nacos://${nacos.address:127.0.0.1}:8848?username=nacos&password=VOLtOugsgy
register-mode: instance
application:
name: demo01-consumer
logger: slf4j
qos-port: 22223

启动

使用 dubbo 的项目需要一个注册中心,注册中心提供服务注册、服务发现等功能。这里直接使用已构建好的 nacos 服务端作为注册中心

Windows:

1
./shartup.cmd

*unix:

1
./shartup.sh

然后依次启动 provider、customer,观察输出,出现以下内容视为成功

1
Hello world

在 AOP 中使用 dubbo

dubbo 支持泛化调用,允许在没有提供 API 定义的情况下调用目标服务,这为改造现有业务代码提供了极大的便利。

考虑以下需求,同一单体应用部署了多了实例在不同服务器上,其中部分业务过程在执行后需要在其他实例上执行一次以同步操作,并视需要统一提交或回滚事务操作。

在 AOP 中使用 dubbo 可大幅度降低对现有业务代码的侵入性。

创建一个注解@BusinessProcessSync

文件BusinsessProcessSync.java

1
2
3
4
5
6
7
8
import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface BusinessProcessSync {

}

创建 AOP 切面

文件BusinsessProcessSyncAspect.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@Aspect
@Component
public class BusinsessProcessSyncAspect {
@Resource
private ApplicationContext applicationContext;

@Resource
private ServiceDiscovery serviceDiscovery; // Dubbo服务发现组件
@Around(value="@annotation(BusinessProcessSync)")
public Object around(ProceedingJobPoint pjp){
// 获取当前的服务类和方法

// 构造调用参数

// 调用本地业务

// 开启泛化调用
ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
referenceConfig.setGeneric("true");
// 获取所有远程实例
// 但是需要排除当前节点
List<ServiceInstance> instances = serviceDiscovery.getInstances(serviceName)
.stream()
.filter(instance -> !isCurrentInstance(instance)) // 过滤逻辑
.collect(Collectors.toList());
// 执行远程业务
ExecutorService executor = Executors.newCachedThreadPool();
for (ServiceInstance instance : instances) {
executor.execute(() -> {
try {
Object serviceProxy = createServiceProxy(instance, serviceName);
Method method = findMethod(serviceProxy.getClass(), methodName, params);
method.invoke(serviceProxy, params);
} catch (Exception e) {
// 处理异常,并视情况决定是否重试
}
});
}
}
}

按照此方案,dubbo 将在 AOP 中以泛化调用的形式,执行其他实例中相同的业务过程