Dubbo(一):Dubbo 3.0
Dubbo 3.0
- 注册模型的改变
- 新一代RPC协议-Triple协议
Dubbo3.0新特性介绍
注册模型的改变
在服务注册领域,市面上有两种模型,一种是应用级注册(),一种是接口级注册,在Spring Cloud中,一个应用是一个微服务,而在Dubbo2.7中,一个接口是一个微服务。
- Spring Cloud在进行服务注册时,是把
应用名
以及应用所在服务器的IP地址
和应用所绑定的端口
注册到注册中心,相当于key是应用名
,value是ip+port
, - Dubbo2.7中,是把
接口名
以及对应应用的IP地址
和所绑定的端口
注册到注册中心,相当于key是接口名
,value是ip+port
。
所以在Dubbo2.7中,一个应用如果提供了10个Dubbo服务,那么注册中心中就会存储10对keyvalue,而Spring Cloud就只会存一对keyvalue,所以以Spring Cloud为首的应用级注册是更加适合的。
所以Dubbo3.0中将注册模型也改为了应用级注册,提升效率节省资源的同时,通过统一注册模型,也为各个微服务框架的互通打下了基础。
Http1.X、Dubbo、Triple 协议
服务消费者是通过发送一个Invocation对象来表示要调用的是哪个接口中的哪个方法,我们是直接把Invocation对象进行JDK序列化得到字节流后然后发送出去的,那如果现在不用JDK序列化呢,比如还有很多序列化的方式,比如JSON、Hessian等等。
此时服务消费者最好能在发送的请求中,能标记所使用的序列化方式,这个标记是不能放在Invocation对象中的,因为这个标记指的就是Invocation对象的序列化方法,服务端接收到字节流后,首先得能拿到序列化标记,看采用的是什么序列化方法,再解析反序列化。
Http1.X 协议
-
如果我们通过HTTP协议(特指HTTP1.x,HTTP2后面分析),那实现起来就比较方便,把序列化标记放在请求头,Invocation对象序列化之后的结果放在请求体,服务端收到HTTP请求后,就先解析请求头得到序列化标记,再取请求体进行反序列化。
-
比如HTTP协议就描述了,从第一个字节开始,遇到第一个空格符时,那就是表示前面每个字节对应的字符组合成的字符串就表示请求方法(字符编码为ascii,一个字符对应一个字节),紧接着继续解析字节,将会按HTTP协议格式解析出请求行、请求头,解析完请求头之后,取出content-length对应的value,该value表示请求体的字节数,所以直接再获取content-length个字节,就表示获取到了请求体(Invocation对象序列化之后的字节),从而一个HTTP请求就获取出来,下一个字节就是另外一个HTTP请求了。
- HTTP1.x协议性能太低了,原因在于:
- HTTP1.x协议中,多余无用的字符太多了,比如回车符、换行符,这每一个字符都会占用一个字节,这些字节占用了网络带宽,降低了网络IO的效率
- HTTP1.x协议中,一条Socket连接,一次只能发送一个HTTP请求,因为如果连续发送两个HTTP请求,然后收到了一个响应,那怎么知道这个响应对应的是哪个请求呢,这样导致Socket连接的利用低,并发、吞吐量低。
Dubbo 协议
-
dubbo协议为了解决上面两个问题,协议,描述的就是一份数据长什么样子,HTTP协议也是一样,描述的就是一个HTTP请求长什么样子,以什么开始,到哪里结束。
-
dubbo协议在Dubbo框架内使用还是比较舒服的,并且dubbo协议相比于http1.x协议,性能会更好,因为请求中没有多余的无用的字节,都是必要的字节,并且每个Dubbo请求和响应中都有一个请求ID,这样可以基于一个Socket连接同时发送多个Dubbo请求,不用担心请求和响应对不上,所以dubbo协议成为了Dubbo框架中的默认协议。
而dubbo协议也有自己的格式,比如:
Triple 协议
-
dubbo协议一旦涉及到跨RPC框架,比如一个Dubbo服务要调用gPRC服务,就比较麻烦了,因为发一个dubbo协议的请求给一个gPRC服务,gPRC服务只会按照gRPC的格式来解析字节流,最终肯定会解析不成功的。
-
dubbo协议虽好,但是不够通用,所以这就出现了Triple协议,Triple协议是基于HTTP2,没有性能问题,另外HTTP协议非常通用,全世界都认它,兼容起来也比较简单,而且还有很多额外的功能,比如流式调用。
-
HTTP2是HTTP1的升级版,完全兼容HTTP1,而且HTTP2协议从设计层面就解决了HTTP1性能低的问题,具体看.html
-
Google公司开发的gRPC,也基于的HTTP2,目前gRPC是云原生事实上协议标准,包括k8s/etcd等都支持gRPC协议
-
Dubbo3.0为了能够更方便的和k8s进行通信,在实现Triple的时候也兼容了gRPC,也就是可以用gPRC的客户端调用Dubbo3.0所提供的triple服务,也可以用triple服务调用gRPC的服务
Http2.0
- 帧长度,用三个字节来存一个数字,这个数字表示当前帧的实际传输的数据的大小,3个字节表示的最大数字是2的24次方(16M),所以一个帧最大为9字节+16M。
- 帧类型,占一个字节,可以分为数据帧和控制帧
- 数据帧又分为:HEADERS 帧和 DATA 帧,用来传输请求头、请求体的
- 控制帧又分为:SETTINGS、PING、PRIORITY,用来进行管理的
- 标志位,占一个字节,可以用来表示当前帧是整个请求里的最后一帧,方便服务端解析
- 流标识符,占4个字节,在Java中也就是一个int,不过最高位保留不用,表示Stream ID,这也是HTTP2的一个重要设计
- 实际传输的数据Payload,如果帧类型是HEADERS,那么这里存的就是请求头,如果帧类型是DATA ,那么这里存的就是请求体
利用HTTP2发送一个完整的请求响应
-
利用HTTP2发送一个请求:
- 新建一个TCP连接(三次握手)
- 新建一个Stream,生成一个新的StreamID,生成一个控制帧,帧里记录了前面生成出来的StreamID,通过TCP连接发送出去
- 生成一个要发送的请求对应的HEADERS 帧,用来发送请求头,也是key:value的格式,先利用ascii进行编码,然后利用
HPACK
算法进行压缩,最终把压缩之后的字节存在帧中的Payload区域,记录好StreamID,最后通过TCP连接把这个HEADERS 帧发送出去 - 最后把要发送的请求体数据按指定的压缩算法(请求中所指定的压缩算法,比如gzip)进行压缩,把压缩之后的字节生成DATA 帧,记录好StreamID,通过TCP连接把DATA 帧发送出去。
-
对于服务端而言:
- 会不断的从TCP连接接收到某些帧
- 当接收到一个控制帧时,表示客户端要和服务端新建一个Stream,在服务端记录一下StreamID,比如在Dubbo3.0的源码中会生成一个ServerStreamObserver的对象
- 当接收到一个HEADERS 帧,取出StreamID,找到对应的ServerStreamObserver对象,并解压得到请求头,把请求头信息保存在ServerStreamObserver对象中
- 当接收到一个DATA 帧时,取出StreamID,找到对应的ServerStreamObserver对象,根据请求头的信息看如何解压请求体,解压之后就得到了原生了请求体数据,然后按业务逻辑处理请求体
- 处理完了之后,就把结果也生成HEADERS 帧和DATA 帧时发送客户端,客户端此时就变成了服务端,来处理响应结果。
- 客户端接收到响应结果的HEADERS 帧,是也先解压得到响应头,记录响应体的解压方式
- 然后继续接收到响应结果的DATA 帧,解压响应体,得到原生的响应体,处理响应体
对于Triple协议而言,我们主要理解HTTP2中的Stream、HEADERS 帧、DATA 帧就可以了。
UDP 升级至HTTP2.0 – QUIC
QUIC
QUIC代表”快速UDP Internet连接”,基于UDP的传输层协议,它本身就是Google尝试将TCP协议重写为一种结合了HTTP/2、TCP、UDP和TLS(用于加密)等多种技术的改进技术。
谷歌希望QUIC通信技术逐渐取代TCP和UDP,作为在Internet上移动二进制数据的新选择协议,QUIC 协议的主要目的,是为了整合 TCP 协议的可靠性和 UDP 协议的速度和效率。
由于 TCP 是在操作系统内核和中间件固件中实现的,因此对 TCP 进行重大更改几乎是不可能的(TCP 协议栈通常由操作系统实现,如 Linux、Windows 内核或者其他移动设备操作系统。修改 TCP 协议是一项浩大的工程,因为每种设备、系统的实现都需要更新)。但是,由于 QUIC 建立在 UDP 之上,因此没有这种限制。
QUIC 的优势在于:
-
采用多路复用 思想,一个连接可以同时承载多个流 ( stream ),同时发起多个请求。 请求间完全独立 ,某个请求阻塞甚至报文出错均不影响其他请求。
-
QUIC只需要1RTT(Round-Trip Time)的延迟就可以建立可靠安全的连接,相对于TCP+TLS的3次RTT要更加快捷。之后客户端可以在本地缓存加密的认证信息,再次与服务器建立连接时可以实现0-RTT的连接建立延迟。
-
TCP 采用 重传 机制,而 QUIC 采用 纠错 机制。
TCP 发生丢包时,需要一个等待延时判断发生了丢包,然后再启动重传机制,这个过程会造成一定的阻塞,影响传输时间。
而 QUIC 则采用一种更主动的方案,有点类似 RAID5 ,每 n 个包额外发一个 校验和包 。 如果这 n 个包中丢了一个包,可以通过其他包和校验和恢复出来,完全不需要重传。 -
QUIC 直接基于客户端(应用进程)实现,而非基于内核,可以快速迭代更新,不需要操作系统层面的改造,部署灵活。
-
连接保持
QUIC 在客户端保存连接标识,当客户端 IP 或者端口发生变化时,可以快速恢复连接 —— 客户端以标识请求服务端,服务端验证标识后感知客户端新地址端口并重新关联,继续通讯。 这对于改善移动端应用连接体验意义重大(从 WiFi 切换到流量)。
对比 triple、dubbo、rest这三个协议
- triple协议基于的是HTTP2,rest协议目前基于的是HTTP1,都可以做到跨语言。
- triple协议兼容了gPRC(Triple服务可以直接调用gRPC服务,反过来也可以),rest协议不行
- triple协议支持流式调用,rest协议不行
- rest协议更方便浏览器、客户端直接调用,triple协议不行(原理上支持,当得对triple协议的底层实现比较熟悉才行,得知道具体的请求头、请求体是怎么生成的)
- dubbo协议是Dubbo3.0之前的默认协议,triple协议是Dubbo3.0之后的默认协议,优先用Triple协议
- dubbo协议不是基于的HTTP,不够通用,triple协议底层基于HTTP所以更通用(比如跨语言、跨异构系统实现起来比较方便)
- dubbo协议不支持流式调用
服务导出与导入
导出
导入
Dubbo应用
- 添加dubbo核心依赖
- 配置dubbo应用名、协议、注册中心
- 定义服务接口和实现类
- 使用@DubboService来定义一个Dubbo服务
- 使用@DubboReference来使用一个Dubbo服务
- 使用@EnableDubbo开启Dubbo
Provider
pom.xml
<dependency><groupId>com.demo</groupId><artifactId>common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><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-rpc-triple</artifactId><version>3.0.7</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>3.0.7</version></dependency>
application.properties
server.port=7070dubbo.application.name=provider-application#dubbo.protocol.name=dubbo
#dubbo.protocol.port=20880dubbo.protocol.name=tri
dubbo.protocol.port=50051
#
##dubbo.protocol.p1.name=dubbo
##dubbo.protocol.p1.port=20880
##
##dubbo.protocol.p2.name=rest
##dubbo.protocol.p2.port=8082
#
dubbo.registry.address=zookeeper://zookeeper.localhost.com:2181
log4j.properties
###set log levels###
log4j.rootLogger=info, stdout
###output to the console###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n
ProviderApplication.java
UserServiceImpl.java
@SpringBootApplication
@EnableDubbo(scanBasePackages = "com.demo.provider.service")
public class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}
}@DubboService
public class UserServiceImpl implements UserService {public User getUser(String uid) {User user = new User(uid, "men-dd");return user;}
}
Consumer
pom.xml
<dependency><groupId>com.demo</groupId><artifactId>common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><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-rpc-triple</artifactId><version>3.0.7</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>3.0.7</version></dependency>
application.properties
server.port=7100dubbo.application.name=consumer-application
dubbo.registry.address=zookeeper://zookeeper.localhost.com:2181
ConsumerApplication.java
OrderService.java
@SpringBootApplication
public class ConsumerApplication {@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}
}@Service
public class OrderService {@Resourceprivate RestTemplate restTemplate;@DubboReferenceprivate UserService userService;public String createOrder() {
// User user = restTemplate.getForObject("http://localhost:7070/user/1", User.class);User user = userService.getUser("1");System.out.println("用户创建订单" + user);return user.toString() + " created success";}
}
Triple 应用
common
<dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-common</artifactId><version>3.0.7</version></dependency>
public interface UserService {public User getUser(String uid);// UNARY 正常使用public User getUserTriple(String uid);// SERVER_STREAM 系统default void getUserServerStream(String name, StreamObserver<String> response) {}// CLIENT_STREAM / BI_STREAM 双端default StreamObserver<String> getUserStream(StreamObserver<String> response) {return response;}
}
provider
UserServiceImpl.java
@DubboService
public class UserServiceImpl implements UserService {public User getUser(String uid) {User user = new User(uid, "men-dd");return user;}@Overridepublic User getUserTriple(String uid) {User user = new User(uid, "triple-men-dd");return user;}@Overridepublic void getUserServerStream(String name, StreamObserver<String> response) {System.out.println("triple-server-处理请求1 :" + name);response.onNext("triple-server-返回结果1");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("triple-server-处理请求2 :" + name);response.onNext("triple-server-返回结果2");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("triple-server-处理请求3 :" + name);response.onNext("triple-server-返回结果3");response.onCompleted();//所有的响应结果返回完毕}@Overridepublic StreamObserver<String> getUserStream(final StreamObserver<String> response) {return new StreamObserver<String>() {@Overridepublic void onNext(String data) {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream-接收到客户端请求数据: " + data);response.onNext(DateUtil.nowDate() + " --- " + "triple-stream result: " + data);}@Overridepublic void onError(Throwable throwable) {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream throwable " + throwable.getMessage());}@Overridepublic void onCompleted() {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream completed");}};}
}
consumer
OrderService.java
@Service
public class OrderService {@Resourceprivate RestTemplate restTemplate;@DubboReferenceprivate UserService userService;public String createOrder() {
// User user = restTemplate.getForObject("http://localhost:7070/user/1", User.class);User user = userService.getUserTriple("1");System.out.println("用户创建订单" + user);return user.toString() + " created success";}//正常的调用方法public String createOrderTriple() {User user = userService.getUserTriple("2");System.out.println("用户创建订单" + user);return user.toString() + " created success";}//服务端流public String createOrderTripleServer() {String uid = "3";final StringBuffer stringBuffer = new StringBuffer();System.out.println(DateUtil.nowDate() + " --- " + uid);stringBuffer.append(DateUtil.nowDate() + " --- " + uid + "\n");userService.getUserServerStream(uid, new StreamObserver<String>() {@Overridepublic void onNext(String data) {System.out.println(DateUtil.nowDate() + " --- " + "triple-server-接收到响应数据: " + data);stringBuffer.append(DateUtil.nowDate() + " --- " + data + "\n");}@Overridepublic void onError(Throwable throwable) {System.out.println(DateUtil.nowDate() + " --- " + "triple-server-接收到响应数据异常:" + throwable.getMessage());}@Overridepublic void onCompleted() {System.out.println(DateUtil.nowDate() + " --- " + "triple-server-接收到响应数据完毕" );}});return stringBuffer + "created success";}//双端流public String createOrderTripleStream() {String uid = "4";final StringBuffer stringBuffer = new StringBuffer();System.out.println(DateUtil.nowDate() + " --- " + uid);stringBuffer.append(DateUtil.nowDate() + " --- " + uid + "\n");StreamObserver<String> streamObserver = userService.getUserStream(new StreamObserver<String>() {@Overridepublic void onNext(String data) {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream-接收到服务端响应数据: " + data);stringBuffer.append(DateUtil.nowDate() + " --- " + data + "\n");}@Overridepublic void onError(Throwable throwable) {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream-接收到响应数据异常:" + throwable.getMessage());}@Overridepublic void onCompleted() {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream-接收到响应数据完毕" );}});streamObserver.onNext("request step 1");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}streamObserver.onNext("request step 2");try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}streamObserver.onNext("request step 3");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}streamObserver.onCompleted();return stringBuffer + "created success";}
}
-
http://localhost:7100/createOrderTripleServer
-
http://localhost:7100/createOrderTripleStream
grpc
grpc-provider
1. pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns=".0.0" xmlns:xsi=""xsi:schemaLocation=".0.0 .0.0.xsd"><parent><artifactId>spring-cloud-dubbo-demo</artifactId><groupId>com.demo</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>grpc-provider</artifactId><name>grpc-provider</name><!-- FIXME change it to the project's website --><url>;/url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><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-rpc-triple</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.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>3.3.0</version></dependency></dependencies><build><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><protocExecutable>protoc</protocExecutable><protoSourceRoot>../grpc-provider/src/proto</protoSourceRoot><protocArtifact>com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}</protocArtifact><outputDirectory>${project.basedir}/src/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></plugins></build>
</project>
2. src/proto/userservice.proto
syntax = "proto3";import "google/protobuf/wrappers.proto";package api;option go_package = "./;api";option java_multiple_files = true;
option java_package = "com.demo";
option java_outer_classname = "UserServiceProto";service UserService {rpc GetUser (UserRequest) returns (User) {}rpc ServerGetUser (UserRequest) returns (stream User) {}rpc ClientGetUser (stream UserRequest) returns (User) {}rpc BiGetUser (stream UserRequest) returns (stream 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;
}
3. mvn compile
生成
4. src/build/generated/source/proto/main/java Mark Sources Root
5. UserServiceImpl.java
@DubboService
public class UserServiceImpl implements UserService{@Overridepublic User getUser(UserRequest request) {User user = User.newBuilder().setUid(request.getUid()).setUsername("men-dd").build();return user;}@Overridepublic void serverGetUser(UserRequest request, StreamObserver<User> responseObserver) {}@Overridepublic StreamObserver<UserRequest> clientGetUser(StreamObserver<User> responseObserver) {return null;}@Overridepublic StreamObserver<UserRequest> biGetUser(StreamObserver<User> responseObserver) {return null;}
}
grpc-consumer
同grpc-provider 步骤1、2、3、4
@Service
public class OrderService {@DubboReferenceprivate UserService userService;public String createOrder() {User user = userService.getUser(UserRequest.newBuilder().setUid("1").build());return user.toString() + " success";}
}
dubbo-go
1. 安装执行
go env -w GO111MODULE=on
go env -w GOPROXY=,directgo 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-triplecp $GOPATH/bin/protoc-gen-go /usr/local/bin
cp $GOPATH/bin/protoc-gen-go-triple /usr/local/binprotoc -I. test.proto --go_out=../api --go-triple_out=../apigo mod tidy
2. proto
syntax = "proto3";package api;option go_package = "./;api";option java_multiple_files = true;
option java_package = "com.demo.service";
option java_outer_classname = "UserServiceProto";service UserService {rpc GetUser (UserRequest) returns (User) {}rpc ServerGetUser (UserRequest) returns (stream User) {}rpc ClientGetUser (stream UserRequest) returns (User) {}rpc BiGetUser (stream UserRequest) returns (stream 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;
}
3.conf
dubbo:registries:demoZk:protocol: zookeeperaddress: zookeeper.local.com:2181consumer:references:UserServiceClientImpl:protocol: triinterface: com.demo.service.UserService
4. main
consumer.go
package mainimport ("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)
}
Dubbo(一):Dubbo 3.0
Dubbo 3.0
- 注册模型的改变
- 新一代RPC协议-Triple协议
Dubbo3.0新特性介绍
注册模型的改变
在服务注册领域,市面上有两种模型,一种是应用级注册(),一种是接口级注册,在Spring Cloud中,一个应用是一个微服务,而在Dubbo2.7中,一个接口是一个微服务。
- Spring Cloud在进行服务注册时,是把
应用名
以及应用所在服务器的IP地址
和应用所绑定的端口
注册到注册中心,相当于key是应用名
,value是ip+port
, - Dubbo2.7中,是把
接口名
以及对应应用的IP地址
和所绑定的端口
注册到注册中心,相当于key是接口名
,value是ip+port
。
所以在Dubbo2.7中,一个应用如果提供了10个Dubbo服务,那么注册中心中就会存储10对keyvalue,而Spring Cloud就只会存一对keyvalue,所以以Spring Cloud为首的应用级注册是更加适合的。
所以Dubbo3.0中将注册模型也改为了应用级注册,提升效率节省资源的同时,通过统一注册模型,也为各个微服务框架的互通打下了基础。
Http1.X、Dubbo、Triple 协议
服务消费者是通过发送一个Invocation对象来表示要调用的是哪个接口中的哪个方法,我们是直接把Invocation对象进行JDK序列化得到字节流后然后发送出去的,那如果现在不用JDK序列化呢,比如还有很多序列化的方式,比如JSON、Hessian等等。
此时服务消费者最好能在发送的请求中,能标记所使用的序列化方式,这个标记是不能放在Invocation对象中的,因为这个标记指的就是Invocation对象的序列化方法,服务端接收到字节流后,首先得能拿到序列化标记,看采用的是什么序列化方法,再解析反序列化。
Http1.X 协议
-
如果我们通过HTTP协议(特指HTTP1.x,HTTP2后面分析),那实现起来就比较方便,把序列化标记放在请求头,Invocation对象序列化之后的结果放在请求体,服务端收到HTTP请求后,就先解析请求头得到序列化标记,再取请求体进行反序列化。
-
比如HTTP协议就描述了,从第一个字节开始,遇到第一个空格符时,那就是表示前面每个字节对应的字符组合成的字符串就表示请求方法(字符编码为ascii,一个字符对应一个字节),紧接着继续解析字节,将会按HTTP协议格式解析出请求行、请求头,解析完请求头之后,取出content-length对应的value,该value表示请求体的字节数,所以直接再获取content-length个字节,就表示获取到了请求体(Invocation对象序列化之后的字节),从而一个HTTP请求就获取出来,下一个字节就是另外一个HTTP请求了。
- HTTP1.x协议性能太低了,原因在于:
- HTTP1.x协议中,多余无用的字符太多了,比如回车符、换行符,这每一个字符都会占用一个字节,这些字节占用了网络带宽,降低了网络IO的效率
- HTTP1.x协议中,一条Socket连接,一次只能发送一个HTTP请求,因为如果连续发送两个HTTP请求,然后收到了一个响应,那怎么知道这个响应对应的是哪个请求呢,这样导致Socket连接的利用低,并发、吞吐量低。
Dubbo 协议
-
dubbo协议为了解决上面两个问题,协议,描述的就是一份数据长什么样子,HTTP协议也是一样,描述的就是一个HTTP请求长什么样子,以什么开始,到哪里结束。
-
dubbo协议在Dubbo框架内使用还是比较舒服的,并且dubbo协议相比于http1.x协议,性能会更好,因为请求中没有多余的无用的字节,都是必要的字节,并且每个Dubbo请求和响应中都有一个请求ID,这样可以基于一个Socket连接同时发送多个Dubbo请求,不用担心请求和响应对不上,所以dubbo协议成为了Dubbo框架中的默认协议。
而dubbo协议也有自己的格式,比如:
Triple 协议
-
dubbo协议一旦涉及到跨RPC框架,比如一个Dubbo服务要调用gPRC服务,就比较麻烦了,因为发一个dubbo协议的请求给一个gPRC服务,gPRC服务只会按照gRPC的格式来解析字节流,最终肯定会解析不成功的。
-
dubbo协议虽好,但是不够通用,所以这就出现了Triple协议,Triple协议是基于HTTP2,没有性能问题,另外HTTP协议非常通用,全世界都认它,兼容起来也比较简单,而且还有很多额外的功能,比如流式调用。
-
HTTP2是HTTP1的升级版,完全兼容HTTP1,而且HTTP2协议从设计层面就解决了HTTP1性能低的问题,具体看.html
-
Google公司开发的gRPC,也基于的HTTP2,目前gRPC是云原生事实上协议标准,包括k8s/etcd等都支持gRPC协议
-
Dubbo3.0为了能够更方便的和k8s进行通信,在实现Triple的时候也兼容了gRPC,也就是可以用gPRC的客户端调用Dubbo3.0所提供的triple服务,也可以用triple服务调用gRPC的服务
Http2.0
- 帧长度,用三个字节来存一个数字,这个数字表示当前帧的实际传输的数据的大小,3个字节表示的最大数字是2的24次方(16M),所以一个帧最大为9字节+16M。
- 帧类型,占一个字节,可以分为数据帧和控制帧
- 数据帧又分为:HEADERS 帧和 DATA 帧,用来传输请求头、请求体的
- 控制帧又分为:SETTINGS、PING、PRIORITY,用来进行管理的
- 标志位,占一个字节,可以用来表示当前帧是整个请求里的最后一帧,方便服务端解析
- 流标识符,占4个字节,在Java中也就是一个int,不过最高位保留不用,表示Stream ID,这也是HTTP2的一个重要设计
- 实际传输的数据Payload,如果帧类型是HEADERS,那么这里存的就是请求头,如果帧类型是DATA ,那么这里存的就是请求体
利用HTTP2发送一个完整的请求响应
-
利用HTTP2发送一个请求:
- 新建一个TCP连接(三次握手)
- 新建一个Stream,生成一个新的StreamID,生成一个控制帧,帧里记录了前面生成出来的StreamID,通过TCP连接发送出去
- 生成一个要发送的请求对应的HEADERS 帧,用来发送请求头,也是key:value的格式,先利用ascii进行编码,然后利用
HPACK
算法进行压缩,最终把压缩之后的字节存在帧中的Payload区域,记录好StreamID,最后通过TCP连接把这个HEADERS 帧发送出去 - 最后把要发送的请求体数据按指定的压缩算法(请求中所指定的压缩算法,比如gzip)进行压缩,把压缩之后的字节生成DATA 帧,记录好StreamID,通过TCP连接把DATA 帧发送出去。
-
对于服务端而言:
- 会不断的从TCP连接接收到某些帧
- 当接收到一个控制帧时,表示客户端要和服务端新建一个Stream,在服务端记录一下StreamID,比如在Dubbo3.0的源码中会生成一个ServerStreamObserver的对象
- 当接收到一个HEADERS 帧,取出StreamID,找到对应的ServerStreamObserver对象,并解压得到请求头,把请求头信息保存在ServerStreamObserver对象中
- 当接收到一个DATA 帧时,取出StreamID,找到对应的ServerStreamObserver对象,根据请求头的信息看如何解压请求体,解压之后就得到了原生了请求体数据,然后按业务逻辑处理请求体
- 处理完了之后,就把结果也生成HEADERS 帧和DATA 帧时发送客户端,客户端此时就变成了服务端,来处理响应结果。
- 客户端接收到响应结果的HEADERS 帧,是也先解压得到响应头,记录响应体的解压方式
- 然后继续接收到响应结果的DATA 帧,解压响应体,得到原生的响应体,处理响应体
对于Triple协议而言,我们主要理解HTTP2中的Stream、HEADERS 帧、DATA 帧就可以了。
UDP 升级至HTTP2.0 – QUIC
QUIC
QUIC代表”快速UDP Internet连接”,基于UDP的传输层协议,它本身就是Google尝试将TCP协议重写为一种结合了HTTP/2、TCP、UDP和TLS(用于加密)等多种技术的改进技术。
谷歌希望QUIC通信技术逐渐取代TCP和UDP,作为在Internet上移动二进制数据的新选择协议,QUIC 协议的主要目的,是为了整合 TCP 协议的可靠性和 UDP 协议的速度和效率。
由于 TCP 是在操作系统内核和中间件固件中实现的,因此对 TCP 进行重大更改几乎是不可能的(TCP 协议栈通常由操作系统实现,如 Linux、Windows 内核或者其他移动设备操作系统。修改 TCP 协议是一项浩大的工程,因为每种设备、系统的实现都需要更新)。但是,由于 QUIC 建立在 UDP 之上,因此没有这种限制。
QUIC 的优势在于:
-
采用多路复用 思想,一个连接可以同时承载多个流 ( stream ),同时发起多个请求。 请求间完全独立 ,某个请求阻塞甚至报文出错均不影响其他请求。
-
QUIC只需要1RTT(Round-Trip Time)的延迟就可以建立可靠安全的连接,相对于TCP+TLS的3次RTT要更加快捷。之后客户端可以在本地缓存加密的认证信息,再次与服务器建立连接时可以实现0-RTT的连接建立延迟。
-
TCP 采用 重传 机制,而 QUIC 采用 纠错 机制。
TCP 发生丢包时,需要一个等待延时判断发生了丢包,然后再启动重传机制,这个过程会造成一定的阻塞,影响传输时间。
而 QUIC 则采用一种更主动的方案,有点类似 RAID5 ,每 n 个包额外发一个 校验和包 。 如果这 n 个包中丢了一个包,可以通过其他包和校验和恢复出来,完全不需要重传。 -
QUIC 直接基于客户端(应用进程)实现,而非基于内核,可以快速迭代更新,不需要操作系统层面的改造,部署灵活。
-
连接保持
QUIC 在客户端保存连接标识,当客户端 IP 或者端口发生变化时,可以快速恢复连接 —— 客户端以标识请求服务端,服务端验证标识后感知客户端新地址端口并重新关联,继续通讯。 这对于改善移动端应用连接体验意义重大(从 WiFi 切换到流量)。
对比 triple、dubbo、rest这三个协议
- triple协议基于的是HTTP2,rest协议目前基于的是HTTP1,都可以做到跨语言。
- triple协议兼容了gPRC(Triple服务可以直接调用gRPC服务,反过来也可以),rest协议不行
- triple协议支持流式调用,rest协议不行
- rest协议更方便浏览器、客户端直接调用,triple协议不行(原理上支持,当得对triple协议的底层实现比较熟悉才行,得知道具体的请求头、请求体是怎么生成的)
- dubbo协议是Dubbo3.0之前的默认协议,triple协议是Dubbo3.0之后的默认协议,优先用Triple协议
- dubbo协议不是基于的HTTP,不够通用,triple协议底层基于HTTP所以更通用(比如跨语言、跨异构系统实现起来比较方便)
- dubbo协议不支持流式调用
服务导出与导入
导出
导入
Dubbo应用
- 添加dubbo核心依赖
- 配置dubbo应用名、协议、注册中心
- 定义服务接口和实现类
- 使用@DubboService来定义一个Dubbo服务
- 使用@DubboReference来使用一个Dubbo服务
- 使用@EnableDubbo开启Dubbo
Provider
pom.xml
<dependency><groupId>com.demo</groupId><artifactId>common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><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-rpc-triple</artifactId><version>3.0.7</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>3.0.7</version></dependency>
application.properties
server.port=7070dubbo.application.name=provider-application#dubbo.protocol.name=dubbo
#dubbo.protocol.port=20880dubbo.protocol.name=tri
dubbo.protocol.port=50051
#
##dubbo.protocol.p1.name=dubbo
##dubbo.protocol.p1.port=20880
##
##dubbo.protocol.p2.name=rest
##dubbo.protocol.p2.port=8082
#
dubbo.registry.address=zookeeper://zookeeper.localhost.com:2181
log4j.properties
###set log levels###
log4j.rootLogger=info, stdout
###output to the console###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n
ProviderApplication.java
UserServiceImpl.java
@SpringBootApplication
@EnableDubbo(scanBasePackages = "com.demo.provider.service")
public class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}
}@DubboService
public class UserServiceImpl implements UserService {public User getUser(String uid) {User user = new User(uid, "men-dd");return user;}
}
Consumer
pom.xml
<dependency><groupId>com.demo</groupId><artifactId>common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><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-rpc-triple</artifactId><version>3.0.7</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>3.0.7</version></dependency>
application.properties
server.port=7100dubbo.application.name=consumer-application
dubbo.registry.address=zookeeper://zookeeper.localhost.com:2181
ConsumerApplication.java
OrderService.java
@SpringBootApplication
public class ConsumerApplication {@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}
}@Service
public class OrderService {@Resourceprivate RestTemplate restTemplate;@DubboReferenceprivate UserService userService;public String createOrder() {
// User user = restTemplate.getForObject("http://localhost:7070/user/1", User.class);User user = userService.getUser("1");System.out.println("用户创建订单" + user);return user.toString() + " created success";}
}
Triple 应用
common
<dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-common</artifactId><version>3.0.7</version></dependency>
public interface UserService {public User getUser(String uid);// UNARY 正常使用public User getUserTriple(String uid);// SERVER_STREAM 系统default void getUserServerStream(String name, StreamObserver<String> response) {}// CLIENT_STREAM / BI_STREAM 双端default StreamObserver<String> getUserStream(StreamObserver<String> response) {return response;}
}
provider
UserServiceImpl.java
@DubboService
public class UserServiceImpl implements UserService {public User getUser(String uid) {User user = new User(uid, "men-dd");return user;}@Overridepublic User getUserTriple(String uid) {User user = new User(uid, "triple-men-dd");return user;}@Overridepublic void getUserServerStream(String name, StreamObserver<String> response) {System.out.println("triple-server-处理请求1 :" + name);response.onNext("triple-server-返回结果1");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("triple-server-处理请求2 :" + name);response.onNext("triple-server-返回结果2");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("triple-server-处理请求3 :" + name);response.onNext("triple-server-返回结果3");response.onCompleted();//所有的响应结果返回完毕}@Overridepublic StreamObserver<String> getUserStream(final StreamObserver<String> response) {return new StreamObserver<String>() {@Overridepublic void onNext(String data) {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream-接收到客户端请求数据: " + data);response.onNext(DateUtil.nowDate() + " --- " + "triple-stream result: " + data);}@Overridepublic void onError(Throwable throwable) {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream throwable " + throwable.getMessage());}@Overridepublic void onCompleted() {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream completed");}};}
}
consumer
OrderService.java
@Service
public class OrderService {@Resourceprivate RestTemplate restTemplate;@DubboReferenceprivate UserService userService;public String createOrder() {
// User user = restTemplate.getForObject("http://localhost:7070/user/1", User.class);User user = userService.getUserTriple("1");System.out.println("用户创建订单" + user);return user.toString() + " created success";}//正常的调用方法public String createOrderTriple() {User user = userService.getUserTriple("2");System.out.println("用户创建订单" + user);return user.toString() + " created success";}//服务端流public String createOrderTripleServer() {String uid = "3";final StringBuffer stringBuffer = new StringBuffer();System.out.println(DateUtil.nowDate() + " --- " + uid);stringBuffer.append(DateUtil.nowDate() + " --- " + uid + "\n");userService.getUserServerStream(uid, new StreamObserver<String>() {@Overridepublic void onNext(String data) {System.out.println(DateUtil.nowDate() + " --- " + "triple-server-接收到响应数据: " + data);stringBuffer.append(DateUtil.nowDate() + " --- " + data + "\n");}@Overridepublic void onError(Throwable throwable) {System.out.println(DateUtil.nowDate() + " --- " + "triple-server-接收到响应数据异常:" + throwable.getMessage());}@Overridepublic void onCompleted() {System.out.println(DateUtil.nowDate() + " --- " + "triple-server-接收到响应数据完毕" );}});return stringBuffer + "created success";}//双端流public String createOrderTripleStream() {String uid = "4";final StringBuffer stringBuffer = new StringBuffer();System.out.println(DateUtil.nowDate() + " --- " + uid);stringBuffer.append(DateUtil.nowDate() + " --- " + uid + "\n");StreamObserver<String> streamObserver = userService.getUserStream(new StreamObserver<String>() {@Overridepublic void onNext(String data) {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream-接收到服务端响应数据: " + data);stringBuffer.append(DateUtil.nowDate() + " --- " + data + "\n");}@Overridepublic void onError(Throwable throwable) {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream-接收到响应数据异常:" + throwable.getMessage());}@Overridepublic void onCompleted() {System.out.println(DateUtil.nowDate() + " --- " + "triple-stream-接收到响应数据完毕" );}});streamObserver.onNext("request step 1");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}streamObserver.onNext("request step 2");try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}streamObserver.onNext("request step 3");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}streamObserver.onCompleted();return stringBuffer + "created success";}
}
-
http://localhost:7100/createOrderTripleServer
-
http://localhost:7100/createOrderTripleStream
grpc
grpc-provider
1. pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns=".0.0" xmlns:xsi=""xsi:schemaLocation=".0.0 .0.0.xsd"><parent><artifactId>spring-cloud-dubbo-demo</artifactId><groupId>com.demo</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>grpc-provider</artifactId><name>grpc-provider</name><!-- FIXME change it to the project's website --><url>;/url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><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-rpc-triple</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.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>3.3.0</version></dependency></dependencies><build><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><protocExecutable>protoc</protocExecutable><protoSourceRoot>../grpc-provider/src/proto</protoSourceRoot><protocArtifact>com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}</protocArtifact><outputDirectory>${project.basedir}/src/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></plugins></build>
</project>
2. src/proto/userservice.proto
syntax = "proto3";import "google/protobuf/wrappers.proto";package api;option go_package = "./;api";option java_multiple_files = true;
option java_package = "com.demo";
option java_outer_classname = "UserServiceProto";service UserService {rpc GetUser (UserRequest) returns (User) {}rpc ServerGetUser (UserRequest) returns (stream User) {}rpc ClientGetUser (stream UserRequest) returns (User) {}rpc BiGetUser (stream UserRequest) returns (stream 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;
}
3. mvn compile
生成
4. src/build/generated/source/proto/main/java Mark Sources Root
5. UserServiceImpl.java
@DubboService
public class UserServiceImpl implements UserService{@Overridepublic User getUser(UserRequest request) {User user = User.newBuilder().setUid(request.getUid()).setUsername("men-dd").build();return user;}@Overridepublic void serverGetUser(UserRequest request, StreamObserver<User> responseObserver) {}@Overridepublic StreamObserver<UserRequest> clientGetUser(StreamObserver<User> responseObserver) {return null;}@Overridepublic StreamObserver<UserRequest> biGetUser(StreamObserver<User> responseObserver) {return null;}
}
grpc-consumer
同grpc-provider 步骤1、2、3、4
@Service
public class OrderService {@DubboReferenceprivate UserService userService;public String createOrder() {User user = userService.getUser(UserRequest.newBuilder().setUid("1").build());return user.toString() + " success";}
}
dubbo-go
1. 安装执行
go env -w GO111MODULE=on
go env -w GOPROXY=,directgo 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-triplecp $GOPATH/bin/protoc-gen-go /usr/local/bin
cp $GOPATH/bin/protoc-gen-go-triple /usr/local/binprotoc -I. test.proto --go_out=../api --go-triple_out=../apigo mod tidy
2. proto
syntax = "proto3";package api;option go_package = "./;api";option java_multiple_files = true;
option java_package = "com.demo.service";
option java_outer_classname = "UserServiceProto";service UserService {rpc GetUser (UserRequest) returns (User) {}rpc ServerGetUser (UserRequest) returns (stream User) {}rpc ClientGetUser (stream UserRequest) returns (User) {}rpc BiGetUser (stream UserRequest) returns (stream 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;
}
3.conf
dubbo:registries:demoZk:protocol: zookeeperaddress: zookeeper.local.com:2181consumer:references:UserServiceClientImpl:protocol: triinterface: com.demo.service.UserService
4. main
consumer.go
package mainimport ("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)
}
发布评论