Dubbo3.0入门教程与新特性介绍 包括改装为dubbo以及跨语言调用
这里以go语言为例

如何将⼀个应用改造为⼀个Dubbo应用

⾸先,新建两个SpringBoot项⽬,⼀个叫consumer,⼀个叫provider

provider项目

项⽬结构

java 跨dubbo 服务调用 dubbo可以跨语言调用吗_dubbo


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项⽬

项⽬结构

java 跨dubbo 服务调用 dubbo可以跨语言调用吗_java 跨dubbo 服务调用_02


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为:

java 跨dubbo 服务调用 dubbo可以跨语言调用吗_dubbo_03


其实UserController也可以去掉,去掉之后provider就更加简单了

java 跨dubbo 服务调用 dubbo可以跨语言调用吗_开发语言_04


此时就可以启动该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 跨dubbo 服务调用 dubbo可以跨语言调用吗_ide_05


并且会编译出对应的接⼝等信息,编译完成后,会⽣成⼀些类:

java 跨dubbo 服务调用 dubbo可以跨语言调用吗_ide_06


如果Java没有蓝⾊的,就

java 跨dubbo 服务调用 dubbo可以跨语言调用吗_开发语言_07


其中就包括了⼀个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模块:

java 跨dubbo 服务调用 dubbo可以跨语言调用吗_ide_08


然后把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中提示要安装插件,就安装⼀下:

java 跨dubbo 服务调用 dubbo可以跨语言调用吗_开发语言_09


安装完之后,代码可能会报错,可以在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:

java 跨dubbo 服务调用 dubbo可以跨语言调用吗_java_10