Dubbo3.0入门教程与新特性介绍 包括改装为dubbo以及跨语言调用
这里以go语言为例
如何将⼀个应用改造为⼀个Dubbo应用
⾸先,新建两个SpringBoot项⽬,⼀个叫consumer,⼀个叫provider
provider项目
项⽬结构
pom⽂件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
ProviderApplication
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class);
}
}
User
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String uid;
private String username;
}
UserService
@Service
public class UserService {
public User getUser(String uid) {
User zhouyu = new User(uid, "zhouyu");
return zhouyu;
}
}
UserController
@RestController
public class UserController {
@Resource
private UserService userService;
@GetMapping("/user/{uid}")
public User getUser(@PathVariable("uid") String uid) {
return userService.getUser(uid);
}
}
application.properties
server.port=8080
consumer项⽬
项⽬结构
pom⽂件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
ConsumerApplication
@SpringBootApplication
public class ConsumerApplication {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class);
}
}
User
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String uid;
private String username;
}
UserService
@Service
public class UserService {
public User getUser(String uid) {
User zhouyu = new User(uid, "zhouyu");
return zhouyu;
}
}
OrderService
@Service
public class OrderService {
@Resource
private RestTemplate restTemplate;
public String createOrder(){
User user =
restTemplate.getForObject("http://localhost:8080/user/1", User.class);
System.out.println("创建订单");
return user.toString()+" succeeded in creating the order";
}
}
OrderController
@RestController
public class OrderController {
@Resource
private OrderService orderService;
@GetMapping("/createOrder")
public String createOrder() {
return orderService.createOrder();
}
}
application.properties
server.port=8080
consumer中的OrderService会通过RestTemplate调⽤provider中的UserService。
改造成Dubbo
改造成Dubbo项⽬,有⼏件事情要做:
1. 添加dubbo核⼼依赖
2. 添加要使⽤的注册中⼼依赖
3. 添加要使⽤的协议的依赖
4. 配置dubbo相关的基本信息
5. 配置注册中⼼地址
6. 配置所使⽤的协议
增加依赖
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>3.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>3.0.7</version>
</dependency>
配置properties
dubbo.application.name=provider-application
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.registry.address=zookeeper://127.0.0.1:2181
改造服务
consumer和provider中都⽤到了User类,所以可以单独新建⼀个maven项⽬⽤来存consumer和provider 公⽤的⼀些类,新增⼀个common模块,把User类转移到这个模块中
要改造成Dubbo,得先抽象出来服务,⽤接⼝表示。
像UserService就是⼀个服务,不过我们得额外定义⼀个接⼝,我们把之前的UserService改为 UserServiceImpl,然后新定义⼀个接⼝UserService,该接⼝表示⼀个服务,UserServiceImpl为该服务的具体实现。
public interface UserService {
public User getUser(String uid);
}
@DubboService
public class UserServiceImpl implements UserService {
public User getUser(String uid) {
User zhouyu = new User(uid, "zhouyu");
return zhouyu;
}
}
注意:要把Spring中的@Service注解替换成Dubbo中的@DubboService注解。
然后把UserService接⼝也转移到common模块中去,在provider中依赖common。
改造之后的provider为:
其实UserController也可以去掉,去掉之后provider就更加简单了
此时就可以启动该Provider了,注意先启动zookeeper(⾼版本的Zookeeper启动过程中不仅会占⽤ 2181,也会占⽤8080,所以可以把provider的端⼝改为8082)
开启Dubbo
在ProviderApplication上加上@EnableDubbo(scanBasePackages = “com.zhouyu.service”),表示 Dubbo会去扫描某个路径下@DubboService,从⽽对外提供该Dubbo服务。
@SpringBootApplication
@EnableDubbo(scanBasePackages = "com.zhouyu.service")
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class);
}
}
调⽤Dubbo服务
引⼊依赖
在consumer中如果想要调⽤Dubbo服务,也要引⼊相关的依赖:
1. 引⼊common,主要是引⼊要⽤调⽤的接⼝
2. 引⼊dubbo依赖
3. 引⼊需要使⽤的协议的依赖
4. 引⼊需要使⽤的注册中⼼的依赖
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>3.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>3.0.7</version>
</dependency>
<dependency>
<groupId>com.zhouyu</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
引⼊服务
通过@DubboReference注解来引⼊⼀个Dubbo服务。
@Service
public class OrderService {
@DubboReference
private UserService userService;
public String createOrder(){
User user = userService.getUser("1");
System.out.println("创建订单");
return user.toString()+" succeeded in creating the order";
}
}
这样就不需要⽤RestTemplate了。
配置properties
dubbo.application.name=consumer-application
dubbo.registry.address=zookeeper://127.0.0.1:2181
调⽤
如果User没有实现Serializable接⼝,则会报错。
总结
⾃此,Dubbo的改造就完成了,总结⼀下:
1. 添加pom依赖
2. 配置dubbo应⽤名、协议、注册中⼼
3. 定义服务接⼝和实现类
4. 使⽤@DubboService来定义⼀个Dubbo服务
5. 使⽤@DubboReference来使⽤⼀个Dubbo服务
6. 使⽤@EnableDubbo开启Dubbo
Dubbo3.0新特性介绍
注册模型的改变
在服务注册领域,市⾯上有两种模型,⼀种是应⽤级注册,⼀种是接⼝级注册,在Spring Cloud中,⼀个应⽤是⼀个微服务,⽽在Dubbo2.7中,⼀个接⼝是⼀个微服务。
所以,Spring Cloud在进⾏服务注册时,是把应⽤名以及应⽤所在服务器的IP地址和应⽤所绑定的端⼝注册到注册中⼼,相当于key是应⽤名valueip+port,⽽在Dubbo2.7中,是把接⼝名以及对应应⽤的IP地址和所绑定的端⼝注册到注册中⼼,相当于key是接⼝名,value是ip+port。
所以在Dubbo2.7中,⼀个应⽤如果提供了10个Dubbo服务,那么注册中⼼中就会存储10对keyvalue,⽽Spring Cloud就只会存⼀对keyvalue,所以以Spring Cloud为⾸的应⽤级注册是更加适合的。
所以Dubbo3.0中将注册模型也改为了应⽤级注册,提升效率节省资源的同时,通过统⼀注册模型,也为各个微服务框架的互通打下了基础。
新⼀代RPC协议
定义了全新的 RPC 通信协议 – Triple,⼀句话概括 Triple:它是基于 HTTP/2 上构建的 RPC 协议,完全兼容 gRPC,并在此基础上扩展出了更丰富的语义。使⽤ Triple 协议,⽤户将获得以下能⼒
- 更容易到适配⽹关、Mesh架构,Triple 协议让 Dubbo 更⽅便的与各种⽹关、Sidecar 组件配合⼯作。
- 多语⾔友好,推荐配合 Protobuf 使⽤ Triple 协议,使⽤ IDL 定义服务,使⽤ Protobuf 编码业务数据。
- 流式通信⽀持。Triple 协议⽀持 Request Stream、Response Stream、Bi-direction Stream当使⽤Triple协议进⾏RPC调⽤时,⽀持多种⽅式来调⽤服务,只不过在服务接⼝中要定义不同的⽅法,
⽐如:
public interface DemoService {
// UNARY
String sayHello(String name);
// SERVER_STREAM
default void sayHelloServerStream(String name, StreamObserver<String>response) {
}
// CLIENT_STREAM / BI_STREAM
default StreamObserver<String> sayHelloStream(StreamObserver<String>response) {
return response;
}
}
UNARY
unary,就是正常的调⽤⽅法
服务实现类对应的⽅法:
// UNARY
@Override
public String sayHello(String name) {
return "Hello " + name;
}
服务消费者调⽤⽅式:
String result = demoService.sayHello("mu");
SERVER_STREAM
服务实现类对应的⽅法:
// SERVER_STREAM
@Override
public void sayHelloServerStream(String name, StreamObserver<String>
response) {
response.onNext(name + " hello");
response.onNext(name + " world");
response.onCompleted();
}
服务消费者调⽤⽅式:
demoService.sayHelloServerStream("zhouyu", new StreamObserver<String>() {
@Override
public void onNext(String data) {
// 服务端返回的数据
System.out.println(data);
}
@Override
public void onError(Throwable throwable) {}
@Override
public void onCompleted() {
System.out.println("complete");
}
});
服务消费者调⽤⽅式:
StreamObserver<String> streamObserver = demoService.sayHelloStream(new
StreamObserver<String>() {
@Override
public void onNext(String data) {
System.out.println("接收到响应数据:"+ data);
}
@Override
public void onError(Throwable throwable) {}
@Override
public void onCompleted() {
System.out.println("接收到响应数据完毕");
}
});
// 发送数据
streamObserver.onNext("request zhouyu hello");
streamObserver.onNext("request zhouyu world");
streamObserver.onCompleted();
BI_STREAM
和CLIENT_STREAM⼀样
Dubbo3.0跨语⾔调⽤
在⼯作中,我们⽤Java语⾔通过Dubbo提供了⼀个服务,另外⼀个应⽤(也就是消费者)想要使⽤这个服务,如果消费者应⽤也是⽤Java语⾔开发的,那没什么好说的,直接在消费者应⽤引⼊Dubbo和服务接⼝相关的依赖即可。
但是,如果消费者应⽤不是⽤Java语⾔写的呢,⽐如是通过python或者go语⾔实现的,那就⾄少需要满⾜两个条件才能调⽤Java实现的Dubbo服务:
1. Dubbo⼀开始是⽤Java语⾔实现的,那现在就需要⼀个go语⾔实现的Dubbo框架,也就是现在的dubbo-go,然后在go项⽬中引⼊dubbo-go,从⽽可以在go项⽬中使⽤dubbo,⽐如使⽤go语⾔去暴露和使⽤Dubbo服务。
2. 我们在使⽤Java语⾔开发⼀个Dubbo服务时,会把服务接⼝和相关类,单独抽象成为⼀个Maven项 ⽬,实际上就相当于⼀个单独的jar包,这个jar能被Java项⽬所使⽤,但不能被go项⽬所使⽤,所以 go项⽬中该如何使⽤Java语⾔所定义的接⼝呢?直接⽤是不太可能的,只能通过间接的⽅式来解决这个问题,除开Java语⾔之外,那有没有其他技术也能定义接⼝呢?并且该技术也是Java和go都⽀持,这就是protobuf。
protobuf
我们可以通过protobuf来定义接⼝,然后通过protobuf的编译器将接⼝编译为特定语⾔的实现。
在provider项⽬中定义⼀个userservice.proto⽂件,路径为src/main/proto/userservice.proto:
syntax = "proto3";
package api;
option go_package = "./;api";
option java_multiple_files = true;
option java_package = "com.zhouyu";
option java_outer_classname = "UserServiceProto";
service UserService {
rpc GetUser (UserRequest) returns (User) {}
}
// The response message containing the greetings
message UserRequest {
string uid = 1;
}
// The response message containing the greetings
message User {
string uid = 1;
string username = 2;
}
相当于定义了⼀个HelloService服务,并且定义了⼀个getUser⽅法,接收UserRequest类型的参数,返 回User类型的对象。
编译成Java
在provider项⽬中的pom⽂件中添加相关maven插件:
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.1</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.class
ifier}</protocArtifact>
<outputDirectory>build/generated/source/proto/main/java</outputDirectory
>
<clearOutputDirectory>false</clearOutputDirectory>
<protocPlugins>
<protocPlugin>
<id>dubbo</id>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-compiler</artifactId>
<version>0.0.3</version>
<mainClass>org.apache.dubbo.gen.dubbo.Dubbo3Generator</mainClass>
</protocPlugin>
</protocPlugins>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>build/generated/source/proto/main/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
并且可以把common依赖去掉,然后运⾏provider中lifecycle的compile,就会进⾏编译了,
并且会编译出对应的接⼝等信息,编译完成后,会⽣成⼀些类:
如果Java没有蓝⾊的,就
其中就包括了⼀个UserService接⼝,所以我们的UserServiceImpl就可以实现这个接⼝了:
@DubboService
public class UserServiceImpl implements UserService {
public User getUser(UserRequest userRequest) {
User user =
User.newBuilder().setUid(userRequest.getUid()).setUsername("zhouyu").buil
d();
return user;
}
}
⽽对于想要调⽤UserService服务的消费者⽽⾔,其实也是⼀样的改造,只需要使⽤同⼀份userservice.proto进⾏编译就可以了,⽐如现在有⼀个go语⾔的消费者。
go消费者调⽤java服务
⾸先,在IDEA中新建⼀个go模块:
然后把userservice.proto复制到go-consumer/proto下,然后进⾏编译,编译成为go语⾔对应的服务代码,只不过go语⾔中没有maven这种东⻄可以帮助我们编译,我们只能⽤原⽣的protobuf的编译器进⾏编译。
这就需要⼤家在机器上下载、安装protobuf的编译器:protoc
1. 下载地址:https://github.com/protocolbuffers/protobuf/releases/download/v3.20.1/protoc-3.20.1-win64.zip
2. 解压之后,把protoc-3.20.1-win64\bin添加到环境变量中去
3. 在cmd中执⾏protoc --version,能正常看到版本号即表示安装成功
另外还需要安装go:
1. 下载地址:https://studygolang.com/dl/golang/go1.18.1.windows-amd64.msi
2. 然后直接下⼀步安装
3. 在cmd中(新开⼀个cmd窗⼝)执⾏go version,能正常看到版本号即表安装成功
然后在go-consumer下新建⽂件夹api,进⼊到go-consumer/proto下,运⾏:
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
go get -u github.com/dubbogo/tools/cmd/protoc-gen-go-triple
go install github.com/golang/protobuf/protoc-gen-go
go install github.com/dubbogo/tools/cmd/protoc-gen-go-triple
protoc -I. userservice.proto --go_out=../api --go-triple_out=../api
这样就会在go-consumer/api下⽣成⼀个userservice.pb.go⽂件和userservice_triple.pb.go⽂件
如果IDEA中提示要安装插件,就安装⼀下:
安装完之后,代码可能会报错,可以在go-consumer⽬录下执⾏命令下载依赖:
然后就可以写go语⾔的服务消费者了,新建⼀个consumer.go,内容为:
package main
import (
"context"
"dubbo.apache.org/dubbo-go/v3/common/logger"
"dubbo.apache.org/dubbo-go/v3/config"
_ "dubbo.apache.org/dubbo-go/v3/imports"
"go-consumer/api"
)
var userServiceImpl = new(api.UserServiceClientImpl)
// export DUBBO_GO_CONFIG_PATH=conf/dubbogo.yml
func main() {
config.SetConsumerService(userServiceImpl)
config.Load()
logger.Info("start to test dubbo")
req := &api.UserRequest{
Uid: "1",
}
user, err := userServiceImpl.GetUser(context.Background(), req)
if err != nil {
logger.Error(err)
}
logger.Infof("client response result: %v\n", user)
}
然后在go-consumer下新建conf/dubbogo.yml,⽤来配置注册中⼼:
dubbo:
registries:
demoZK:
protocol: zookeeper
address: 127.0.0.1:2181
consumer:
references:
UserServiceClientImpl:
protocol: tri
interface: com.zhouyu.UserService
注意这⾥配置的协议为tri,⽽不是dubbo,在provider端也得把协议改为tri: