目录
Netty UDP服务端(UdpServer)
Reactor Netty 提供了易于使用和易于配置的UdpServer. 它隐藏了创建UDP服务器所需的大部分Netty功能并增加了Reactive Streams背压(Reactive Streams是具有无阻塞背压的异步流处理的标准)
启动和停止
要启动UDP服务器,必须创建和配置UdpServer实例
默认情况下:主机:localhost、端口:12012
创建和启动UDP服务器:
返回的Connection提供了一个简单的服务器API,服务器主机localhost、端口8080;包括disposeNow(),它以阻塞方式关闭服务器。
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;
public class Application {
public static void main(String[] args) {
Connection server =
UdpServer.create() // 创建一个UdpServer准备好配置的实例
.host("localhost") // 配置UDP服务器主机
.port(8080) // 配置UDP服务器端口
.bindNow(Duration.ofSeconds(30)); // 以阻塞方式启动服务器并等待它完成初始化
server.onDispose()
.block();
}
}
急切初始化
默认情况下,UdpServer资源的初始化按需进行。这意味着绑定操作会占用初始化和加载所需的额外时间:
(1)事件循环组
(2)本机传输库(使用本机传输时)
当需要预加载这些资源时,可以进行UdpServer如下配置:
import io.netty.channel.socket.DatagramPacket;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;
public class Application {
public static void main(String[] args) {
UdpServer udpServer =
UdpServer.create()
.handle((in, out) ->
out.sendObject(
in.receiveObject()
.map(o -> {
if (o instanceof DatagramPacket) {
DatagramPacket p = (DatagramPacket) o;
return new DatagramPacket(p.content().retain(), p.sender());
}
else {
return Mono.error(new Exception("Unexpected type of the message: " + o));
}
})));
udpServer.warmup() // 初始化并加载事件循环组和本机传输库
.block();
Connection server = udpServer.bindNow(Duration.ofSeconds(30));
server.onDispose()
.block();
}
}
写入数据
要将数据发送到远程对等方,必须附加一个I/O处理程序。I/O处理程序有权访问UdpOutbound,以便能够写入数据:
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;
public class Application {
public static void main(String[] args) {
Connection server =
UdpServer.create()
.handle((in, out) ->
out.sendObject(
in.receiveObject()
.map(o -> {
if (o instanceof DatagramPacket) {
DatagramPacket p = (DatagramPacket) o;
ByteBuf buf = Unpooled.copiedBuffer("hello", CharsetUtil.UTF_8);
// hello向远程对等方发送字符串
return new DatagramPacket(buf, p.sender());
}
else {
return Mono.error(new Exception("Unexpected type of the message: " + o));
}
})))
.bindNow(Duration.ofSeconds(30));
server.onDispose()
.block();
}
}
消费数据
要从远程对等方接收数据,必须附加一个I/O处理程序。I/O处理程序有权访问UdpInboun,以便能够读取数据:
import io.netty.channel.socket.DatagramPacket;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;
public class Application {
public static void main(String[] args) {
Connection server =
UdpServer.create()
.handle((in, out) ->
out.sendObject(
in.receiveObject()
.map(o -> { // 从远程对等方接收数据
if (o instanceof DatagramPacket) {
DatagramPacket p = (DatagramPacket) o;
return new DatagramPacket(p.content().retain(), p.sender());
}
else {
return Mono.error(new Exception("Unexpected type of the message: " + o));
}
})))
.bindNow(Duration.ofSeconds(30));
server.onDispose()
.block();
}
}
生命周期回调
提供了以下生命周期回调以让扩展UdpServer:
回调函数 | 描述 |
---|---|
doOnBind | 在服务器通道即将绑定时调用 |
doOnBound | 在绑定服务器通道时调用 |
doOnChannelInit | 初始化通道时调用 |
doOnUnbound | 当服务器通道未绑定时调用 |
使用doOnBound和doOnChannelInit回调:
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.logging.LoggingHandler;
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;
public class Application {
public static void main(String[] args) {
Connection server =
UdpServer.create()
// Netty管道LineBasedFrameDecoder在绑定服务器通道时扩展
.doOnBound(conn -> conn.addHandler(new LineBasedFrameDecoder(8192)))
.doOnChannelInit((observer, channel, remoteAddress) ->
// Netty管道LoggingHandler在初始化通道时扩展
channel.pipeline()
.addFirst(new LoggingHandler("reactor.netty.examples")))
.bindNow(Duration.ofSeconds(30));
server.onDispose()
.block();
}
}
Option和childOption参数设置
连接配置(三种配置)
(1)Channel Options:通道配置
默认情况下,UDP服务器配置有以下选项:
UdpServerBind() {
this.config = new UdpServerConfig(
Collections.singletonMap(ChannelOption.AUTO_READ, false),
() -> new InetSocketAddress(NetUtil.LOCALHOST, DEFAULT_PORT));
}
如果需要附加选项或需要更改当前选项,可以应用以下配置:
import io.netty.channel.ChannelOption;
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;
public class Application {
public static void main(String[] args) {
Connection server =
UdpServer.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.bindNow(Duration.ofSeconds(30));
server.onDispose()
.block();
}
}
(2)Wire Logger:连线日志记录
Reactor Netty提供线路日志记录,用于何时需要检查对等点之间的流量。默认情况下,线路日志记录处于禁用状态。要启用它,必须将记录器reactor.netty.udp.UdpServer级别设置为DEBUG并应用以下配置:
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;
public class Application {
public static void main(String[] args) {
Connection server =
UdpServer.create()
.wiretap(true) // 启用连线记录
.bindNow(Duration.ofSeconds(30));
server.onDispose()
.block();
}
}
Wire Logger格式化程序
Reactor Netty支持3种不同的格式化程序:
连线日志格式化 | 描述 |
---|---|
AdvancedByteBufFormat#HEX_DUM | 同时记录事件和内容。内容将采用十六进制格式(默认) |
AdvancedByteBufFormat#SIMPLE | 使用此格式启用连线记录时,仅记录事件 |
AdvancedByteBufFormat#TEXTUAL | 同时记录事件和内容。内容将采用纯文本格式 |
(3)Event Loop Group:事件循环组
默认情况下,UDP服务器使用“事件循环组”,其中工作线程的数量等于初始化时运行时可用的处理器数量(但最小值为4)。当需要不同的配置时,可以使用LoopResource#create()方法之一
“事件循环组”的默认配置:
/**
* 默认工作线程数,回退到可用处理器(但最小值为4)
*/
public static final String IO_WORKER_COUNT = "reactor.netty.ioWorkerCount";
/**
* 默认选择器线程计数,回退到-1(无选择器线程)
*/
public static final String IO_SELECT_COUNT = "reactor.netty.ioSelectCount";
/**
*UDP的默认工作线程数,回退到可用处理器(但最小值为4)
*/
public static final String UDP_IO_THREAD_COUNT = "reactor.netty.udp.ioThreadCount";
/**
* 默认的静默期,保证不会发生对底层循环资源的处置,回退到2秒
*/
public static final String SHUTDOWN_QUIET_PERIOD = "reactor.netty.ioShutdownQuietPeriod";
/**
* 默认情况下,无论任务是否在静默期内提交,在处理底层资源之前等待的最长时间为15秒。
*/
public static final String SHUTDOWN_TIMEOUT = "reactor.netty.ioShutdownTimeout";
/**
* 默认值是否首选本机传输(epoll、kqueue),回退在可用时是否首选
*/
public static final String NATIVE = "reactor.netty.native";
需要更改这些设置,可以应用以下配置:
import reactor.netty.Connection;
import reactor.netty.resources.LoopResources;
import reactor.netty.udp.UdpServer;
import java.time.Duration;
public class Application {
public static void main(String[] args) {
LoopResources loop = LoopResources.create("event-loop", 1, 4, true);
Connection server =
UdpServer.create()
.runOn(loop)
.bindNow(Duration.ofSeconds(30));
server.onDispose()
.block();
}
}
Metrics(指标监控)
UDP服务器支持与Micrometer,它公开了前缀为“reactor.netty.udp.server”的所有指标
启用该集成:
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;
public class Application {
public static void main(String[] args) {
Connection server =
UdpServer.create()
.metrics(true) // 启用与Micrometer的内置集成
.bindNow(Duration.ofSeconds(30));
server.onDispose()
.block();
}
}
当需要UDP服务器指标与系统集成时,Micrometer或者想提供自己的集成Micrometer,可以提供自己的指标记录器:
import reactor.netty.Connection;
import reactor.netty.channel.ChannelMetricsRecorder;
import reactor.netty.udp.UdpServer;
import java.net.SocketAddress;
import java.time.Duration;
public class Application {
public static void main(String[] args) {
Connection server =
UdpServer.create()
// 启用UDP服务器指标并提供ChannelMetricsRecorder实现
.metrics(true, CustomChannelMetricsRecorder::new)
.bindNow(Duration.ofSeconds(30));
server.onDispose()
.block();
}
}
(1)UDP服务器指标的信息
metric名称 | 类型 | 描述 |
---|---|---|
reactor.netty.udp.server.data.received | DistributionSummary | 接收的数据量,以字节为单位 |
reactor.netty.udp.server.data.sent | DistributionSummary | 发送的数据量,以字节为单位 |
reactor.netty.udp.server.errors | Counter | 发生的错误数 |
(2)ByteBufAllocator指标
metric名称 | 类型 | 描述 |
---|---|---|
reactor.netty.bytebuf.allocator.used.heap.memory | Gauge | 堆内存的字节数 |
reactor.netty.bytebuf.allocator.used.direct.memory | Gauge | 直接内存的字节数 |
reactor.netty.bytebuf.allocator.used.heap.arenas | Gauge | 堆区域的数量(当PooledByteBufAllocator) |
reactor.netty.bytebuf.allocator.used.direct.arenas | Gauge | 直接竞技场的数量(当PooledByteBufAllocator) |
reactor.netty.bytebuf.allocator.used.threadlocal.caches | Gauge | 直接竞技场的数量(当PooledByteBufAllocator) |
reactor.netty.bytebuf.allocator.used.small.cache.size | Gauge | 小缓存的大小(当PooledByteBufAllocator) |
reactor.netty.bytebuf.allocator.used.normal.cache.size | Gauge | 正常缓存的大小(当PooledByteBufAllocator) |
reactor.netty.bytebuf.allocator.used.chunk.size | Gauge | 竞技场的块大小(当PooledByteBufAllocator) |
(3)EventLoop指标
metric名称 | 类型 | 描述 |
---|---|---|
reactor.netty.eventloop.pending.tasks | Gauge | 事件循环中待处理的任务数 |