1分钟了解响应式编程 | dubbo框架下响应式官例

作者:Mars酱

声明:本文章由Mars酱编写,部分内容来源于网络,如有疑问请联系本人。

转载:欢迎转载,转载前先请联系我!

前言

上篇搜了一堆概念,拼凑了一些例子,现今我们的应用已经不是单例架构,一般都是分布式微服务架构,微服务架构情况下框架也很多,SpringCloud、Dubbo等等

我的工程是dubbo的,那我们看看Dubbo官方示例给出的响应式编程的例子,毕竟我看Dubbo官文说天然支持流式编程,好了,继续。

Dubbo对流式处理的支持

协议 性能 网关友好 流式通信 多语言支持 编程API 说明
triple 支持,客户端流、服务端流、双向流 支持(Java、Go、Node.js、JavaScript、Rust) Java Interface、Protobuf(IDL) 在多语言兼容、性能、网关、Streaming、gRPC 等方面最均衡的协议实现,官方推荐。
dubbo 不支持 支持(Java、Go) Java Interface 性能最高的私有协议,但前端流量接入、多语言支持等成本较高
rest 不支持 支持 Java Interface rest 协议在前端接入、互通等方面具备最高的灵活性,但对比 rpc 存在性能、弱类型等缺点。注意,rest 在 dubbo3 中仅是 triple 协议的一种发布形式

从官方的文档中找到,Dubbo是支持流式编程的,但是需要使用triple协议,而不是其他协议。

从git上下载Dubbo Samples

bash 复制代码
# 从git上下载 dubbo的官方示例,这是示例的git地址
https://github.com/apache/dubbo-samples.git

下载之后,导入到你的idea中,知道了triple协议支持流式通信,那么从导入好的idea中找到triple的stream相关的代码,如下图:

Dubbo服务端支持的两种流式处理

Dubbo通过triple协议支持了两种流式处理,一种是服务端流,另一种是双向流。

服务端流

首先我们看服务端流的示例代码,代码位置在dubbo-samples-triple-streaming工程中的GreeterImpl这个文件,我抽取了服务端流的方式,如下:

java 复制代码
@Override
public void serverStream(GreeterRequest request, StreamObserver<GreeterReply> responseObserver) {
	LOGGER.info("receive request: {}", request.getName());
	for (int i = 0; i < 10; i++) {
		GreeterReply reply = GreeterReply.newBuilder().setMessage("reply from serverStream. " + i).build();
		responseObserver.onNext(reply);
	}
	responseObserver.onCompleted();
}

可以看到serversStream方法是没有void,没有返回值的,它通过GreeterRequest对象传递参数,然后把业务逻辑数据依次循环写入到StreamObserver对像中。

GreeterRequest对象可以是我们自己定义的业务请求对象,StreamObserver对象是dubbo框架对于流式处理的接口定义,如图:

响应结果被封装在这个接口对象中,实现了流式数据写入到消费者端的业务场景。

双向流

这是双向流的官方示例:

java 复制代码
@Override
public StreamObserver<GreeterRequest> biStream(StreamObserver<GreeterReply> responseObserver) {
	return new StreamObserver<GreeterRequest>() {
		@Override
		public void onNext(GreeterRequest data) {
			GreeterReply resp = GreeterReply.newBuilder().setMessage("reply from biStream " + data.getName()).build();
			responseObserver.onNext(resp);
		}

		@Override
		public void onError(Throwable throwable) {

		}

		@Override
		public void onCompleted() {

		}
	};
}

可以看到,双向流方法 binStream 的参数和返回值都是 StreamObserver 类型。

但需要注意的是,它与我们传统方法定义中参数是请求值、返回值是响应的理解是反过来的,在这里,函数的入参 StreamObserver responseObserver 是响应,跟之前一样,是在业务逻辑中不停的写数据到 回响应中。

这个函数也通过new了一个 StreamObserver 不断的在onNext函数中写入了请求流数据,而且这里 请求流 与 响应流 是独立的,我们在写回响应流数据的过程时,随时可能有请求流到达,对于每个流而言,值都是有序的。

Dubbo客户端的调用示例

dubbo对于两种流的处理方式是不同的调用实现

服务端流的客户端调用

调用 serverStream() 传入能够处理流式响应的 SampleStreamObserver 对象,调用发起后即快速返回,之后流式响应会不停的发送到 SampleStreamObserver 。

java 复制代码
private static void serverStream(Greeter greeter) {
	GreeterRequest request = GreeterRequest.newBuilder().setName("server stream request.").build();
	greeter.serverStream(request, new SampleStreamObserver());
}

// 声名上面调用类的SampleStreamObserver实现
private static class SampleStreamObserver implements StreamObserver<GreeterReply> {

	@Override
	public void onNext(GreeterReply data) {
		LOGGER.info("stream <- reply:{}", data);
	}

	@Override
	public void onError(Throwable throwable) {
		LOGGER.error("stream onError", throwable);
		throwable.printStackTrace();
	}

	@Override
	public void onCompleted() {
		LOGGER.info("stream completed");
	}
}

双向流的客户端调用

java 复制代码
private static void biStream(Greeter greeter) {
	// SampleStreamObserver的定义在 '服务端流的客户端调用示例' 中已经给出
	StreamObserver<GreeterRequest> requestStreamObserver = greeter.biStream(new SampleStreamObserver());
	for (int i = 0; i < 10; i++) {
		GreeterRequest request = GreeterRequest.newBuilder().setName("name-" + i).build();
		requestStreamObserver.onNext(request);
	}
	requestStreamObserver.onCompleted();
}

调用 greeter.biStream() 方法会立即返回一个请求流 requestStreamObserver 对象,同时,需要为方法传入一个能处理响应的 observer 对象new SampleStreamObserver()。

接下来,我们就可以用才刚才返回值中得到的 requestStreamObserver 持续发送请求 requestStreamObserver .onNext(reqeust), 此时,如果有响应返回,则会由 SampleStreamObserver 对象接收处理,

官例好像有问题

其实没有任何问题,只是dubbo支持的是observer的流式处理,这是一种无背压方式的流式处理。背压是一种流控机制 ,dubbo 的 StreamObserver 不提供内置背压支持

  • 它的 onNext 方法是通过官方的示例就知道它属于阅后即焚风格;
  • 调用 onNext 会立即把数据写入网络缓冲区(或本地队列),不会等待消费者处理完成
  • 如果消费者消费得太慢,而 producer 持续高速发送,有没有可能 导致 OOM 或者其他问题?没试过,存疑

这与 Reactive Streams 规范 貌似不同,并没有背压特征,但是RxJava框架不也是支持背压、无背压方式的吗?所以,应该根据自己的场景来选择是否需要背压方式,超时了,下个1分钟看怎么用dubbo处理背压特征的响应式编程吧。

相关推荐
涡能增压发动积1 天前
同样的代码循环 10次正常 循环 100次就抛异常?自定义 Comparator 的 bug 让我丢尽颜面
后端
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Wenweno0o1 天前
0基础Go语言Eino框架智能体实战-chatModel
开发语言·后端·golang
于慨1 天前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
swg3213211 天前
Spring Boot 3.X Oauth2 认证服务与资源服务
java·spring boot·后端
tyung1 天前
一个 main.go 搞定协作白板:你画一笔,全世界都看见
后端·go
gelald1 天前
SpringBoot - 自动配置原理
java·spring boot·后端
殷紫川1 天前
深入理解 AQS:从架构到实现,解锁 Java 并发编程的核心密钥
java
一轮弯弯的明月1 天前
贝尔数求集合划分方案总数
java·笔记·蓝桥杯·学习心得
chenjingming6661 天前
jmeter线程组设置以及串行和并行设置
java·开发语言·jmeter