反应式编程(Reactive Programming)
反应式编程(Reactive Programming)这种新的编程范式越来越受到开发人员的欢迎。
在传统的编程范式中,我们一般通过迭代器(Iterator)模式来遍历一个序列。这种遍历方式是由调用者来控制节奏的,采用的是拉的方式。每次由调用者通过 next()方法来获取序列中的下一个值。
使用反应式流时采用的则是推的方式,即常见的发布者-订阅者模式。当发布者有新的数据产生时,这些数据会被推送到订阅者来进行处理。在反应式流上可以添加各种不同的操作来对数据进行处理,形成数据处理链。这个以声明式的方式添加的处理链只在订阅者进行订阅操作时才会真正执行.
Reactive由一系列事件以及发布和订阅这些事件的2个参与方组成的一个序列.
特点:
事件驱动;
异步非阻塞,无需引入线程概念。
推拉机制:数据提供方push,数据处理方拉去pull .
核心概念:
异步响应数据流 Reactive Stream:
功能类似:集合(Collections) ,表象:Java8 Stream.
单向流动;
时间+序列
数据流操作 Stream operators:
功能:操作数据 表象:过滤/合并/映射
产生新的流
观察者模式:
定义了一种1对多的依赖关系,让多个观察者对象同时监听某一主题对象(被观察者)。
这个主题对象的状态发生改变是会同时通知所有的观察者,使他们能够自动更新自己。
HttpClient
compile ‘io.projectreactor:reactor-core:3.3.4.RELEASE’
compile ‘io.projectreactor.netty:reactor-netty:0.9.6.RELEASE’
reactor.netty.http.client.HttpClient;
HttpClient.create()
.wiretap(true) //打印消息
.headers(h -> {
h.set(HttpHeaderNames.CONTENT_LENGTH, xml.length());
h.set(HttpHeaderNames.ACCEPT, "application/soap+xml, text/html") ;
h.set(HttpHeaderNames.CONTENT_TYPE, "application/soap+xml;charset=utf-8");
}) //设置消息头属性
.post() //post方法
.uri("http://127.0.0.1:8080/xxx")
.send(ByteBufFlux.fromString(Mono.just(body)))
.responseContent()
.aggregate()
.asString()
.map(str -> {
LOGGER.info("received {}", str); //收到的响应body
return str;
})
.timeout(Duration.ofMillis(10000), Mono.error(new Exception(sessionId+ " is timeout")))
.retryWhen(Retry.fixedDelay(10, Duration.ofSeconds(1))
.filter(throwable -> {
if (finished) {
return false;
}
return true;
})
.onRetryExhaustedThrow((retryBackoffSpec, ex) -> {
throw new RuntimeException("Failed retry Max times!");
})
)
.subscribe(body -> handleResponseBody);
;
HttpServer
DisposableServer server =
HttpServer.create()
.tcpConfiguration(tcpServer -> tcpServer.doOnConnection(connection ->
connection.addHandler("aggregator", new HttpObjectAggregator(10*1024*1024))
/*当我们用POST方式请求服务器的时候,对应的参数信息是保存在message body中的,如果只是单纯的用HttpServerCodec是无法完全的解析Http POST请求的,因为HttpServerCodec只能获取uri中参数,所以需要加上HttpObjectAggregator*/
)
)
.writeap(true)
.host("127.0.0.1")
.port(8080)
.route(routes -> //设置路由。 类似Springboot中的 controller
routes.post("/anat",(request, response) -> {
final UnicastProcessor<Object> processor = UnicastProcessor.create();//异步消息响应流
final FluxSink<Object> sink = processor.sink();
final Flux<String> buf = request.receiveContent()
.flatMap(MyCodec::decode)//解码请求消息
.flatMap(msg -> handle(msg, sink))//异步处理请求
.mergerWith(processor)
.flatMap(MyCodec::encode);//编码响应消息
return response.status(HttpResponseStatus.OK)
.header(HttpHeaderNames,ACCEPT, "application/soap+xml, text/html")
.header(HttpHeaderNames.CONTENT_TYPE, "application/soap+xml;charset=utf-8")
.chunkedTransfer(false) //禁止使用分块传输编码
.sendString(Mono.from(buf)) //发送响应
.then();
})
)
.bindNow(Duration.ofDays(9999));
server.onDispose()
.block();
class Handler {
public Publisher<Object> handle(MyMessage msg, FluxSink<Object> sink)
{
....
//异步处理业务
sink.next(response); //发布结果
}
}
TcpClient
TcpClient.create()
.host('127.0.0.1")
.port(9999)
.booststrap(booststrap -> bootstrap.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.SO_RCVBUF, 81920)
.option(ChannelOption.SO_SNDBUF, 81920)
)
.observe((conn, state) -> {
if (state == ConnectionObserver.State.DISCONNECTING) {
...
} else if(state == ConnectionObserver.State.CONNECTED){
...
}
})
.doOnConnected(conn -> conn.addHandler(new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 2, -2, 0)))
.handle((in, out) -> {
final UnicastProcessor<ByteBuf> processor = UnicastProcessor.create();
final FluxSink<ByteBuf> sink = processor.sink();
final Flux<ByteBuf> buf = in.receive()
.asInputStream()
.map(MyCodec::decode)
.flatMap(msg-> handle(msg, sink))
.mergerWith(processor)
.map(MyCodec::encode);
ChannelOperations<NettyInbound, NettyOutbound> conn = ( ChannelOperations<NettyInbound, NettyOutbound>)out;
return out.option(o -> o.flushOnEach())
.send(buf)
.then();
})
.writeap(true);
TcpServer
TcpServer.create()
.host("127.0.0.1")
.port(9999)
.bootstrap(b -> b.option(ChannelOption.SO_BACKLOG, 10)
.option(ChannelOption.SO_REUSEADDR, true)
.childOption(ChannelOption.SO_RCVBUF, 1024)
.childOption(ChannelOption.SO_SNDBUF, 1024)
)
.doOnConnection(c -> {
c.addHandler(new LengthFieldBasedFrameDecoder(1024, 0, 4, -4,0));
})
.observe((conn, state) -> {
if(state == ConnectionObserver.State.DISCONNECTING) {
LOGGER.info("connection{} lost session:{}", conn.address(), session);
}
})
.handle((in, out) -> {
final UnicastProcessor<ByteBuf> processor = UnicastProcessor.create();
final FluxSink<ByteBuf> sink = processor.sink();
final Flux<ByteBuf> buf = in.receive();
.asInputStream()
.map(MyCodec::decode)
.flatMap(msg-> handle(msg, sink))
.mergerWith(processor)
.onErrorResume(throwable -> {
LOGGER.info("Unexpected error:{} when process message ",throwable);
return Mono.empty();
})
.map(MyCodec::encode);
return out.option(o -> o.flushOnEach())
.send(buf)
.then();
} )
.bindNow(Duration.ofDays(300));