SO_REUSEPORT学习笔记补遗
来源:互联网 发布:网游mac版的游戏有什么 编辑:程序博客网 时间:2024/05/22 06:43
前言
因为能力有限,还是有很多东西(SO_REUSEADDR和SO_REUSEPORT的区别等)没有能够在一篇文字中表达清楚,作为补遗,也方便以后自己回过头来复习。
SO_REUSADDR VS SO_REUSEPORT
两者不是一码事,没有可比性。有时也会被其搞晕,自己总结的不好,推荐StackOverflow的Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ?资料,总结的很全面。
简单来说:
- 设置了SO_REUSADDR的应用可以避免TCP 的 TIME_WAIT 状态 时间过长无法复用端口,尤其表现在应用程序关闭-重启交替的瞬间
- SO_REUSEPORT更强大,隶属于同一个用户(防止端口劫持)的多个进程/线程共享一个端口,同时在内核层面替上层应用做数据包进程/线程的处理均衡
若有困惑,推荐两者都设置,不会有冲突。
Netty多线程使用SO_REUSEPORT
上一篇讲到SO_REUSEPORT,多个程绑定同一个端口,可以根据需要控制进程的数量。这里讲讲基于Netty 4.0.25+Epoll navtie transport
在单个进程内多个线程绑定同一个端口的情况,也是比较实用的。
TCP服务器,同一个进程多线程绑定同一个端口
这是一个PING-PONG示范应用:
public void run() throws Exception { final EventLoopGroup bossGroup = new EpollEventLoopGroup(); final EventLoopGroup workerGroup = new EpollEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(EpollServerSocketChannel. class) .childHandler( new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( new StringDecoder(CharsetUtil.UTF_8 ), new StringEncoder(CharsetUtil.UTF_8 ), new PingPongServerHandler()); } }).option(ChannelOption. SO_REUSEADDR, true) .option(EpollChannelOption. SO_REUSEPORT, true) .childOption(ChannelOption. SO_KEEPALIVE, true); int workerThreads = Runtime.getRuntime().availableProcessors(); ChannelFuture future; for ( int i = 0; i < workerThreads; ++i) { future = b.bind( port).await(); if (!future.isSuccess()) throw new Exception(String. format("fail to bind on port = %d.", port), future.cause()); } Runtime. getRuntime().addShutdownHook (new Thread(){ @Override public void run(){ workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } }); }
打成jar包,在CentOS 7下面运行,检查同一个端口所打开的文件句柄。
# lsof -i:8000COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEjava 3515 root 42u IPv6 29040 0t0 TCP *:irdmi (LISTEN)java 3515 root 43u IPv6 29087 0t0 TCP *:irdmi (LISTEN)java 3515 root 44u IPv6 29088 0t0 TCP *:irdmi (LISTEN)java 3515 root 45u IPv6 29089 0t0 TCP *:irdmi (LISTEN)
同一进程,但打开的文件句柄是不一样的。
UDP服务器,多个线程绑同一个端口
/** * UDP谚语服务器,单进程多线程绑定同一端口示范 */public final class QuoteOfTheMomentServer { private static final int PORT = Integer.parseInt(System. getProperty("port" , "9000" )); public static void main(String[] args) throws Exception { final EventLoopGroup group = new EpollEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(group).channel(EpollDatagramChannel. class) .option(EpollChannelOption. SO_REUSEPORT, true ) .handler( new QuoteOfTheMomentServerHandler()); int workerThreads = Runtime.getRuntime().availableProcessors(); for (int i = 0; i < workerThreads; ++i) { ChannelFuture future = b.bind( PORT).await(); if (!future.isSuccess()) throw new Exception(String.format ("Fail to bind on port = %d.", PORT), future.cause()); } Runtime. getRuntime().addShutdownHook(new Thread() { @Override public void run() { group.shutdownGracefully(); } }); }}}@Sharableclass QuoteOfTheMomentServerHandler extends SimpleChannelInboundHandler<DatagramPacket> { private static final String[] quotes = { "Where there is love there is life." , "First they ignore you, then they laugh at you, then they fight you, then you win.", "Be the change you want to see in the world." , "The weak can never forgive. Forgiveness is the attribute of the strong.", }; private static String nextQuote() { int quoteId = ThreadLocalRandom.current().nextInt( quotes .length ); return quotes [quoteId]; } @Override public void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { if ("QOTM?" .equals(packet.content().toString(CharsetUtil. UTF_8))) { ctx.write( new DatagramPacket(Unpooled.copiedBuffer( "QOTM: " + nextQuote(), CharsetUtil. UTF_8), packet.sender())); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); }}
同样也要检测一下端口文件句柄打开情况:
# lsof -i:9000COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEjava 3181 root 26u IPv6 27188 0t0 UDP *:cslistenerjava 3181 root 27u IPv6 27217 0t0 UDP *:cslistenerjava 3181 root 28u IPv6 27218 0t0 UDP *:cslistenerjava 3181 root 29u IPv6 27219 0t0 UDP *:cslistener
小结
以上为Netty+SO_REUSEPORT多线程绑定同一端口的一些情况,是为记载。
0 0
- SO_REUSEPORT学习笔记补遗
- SO_REUSEPORT学习笔记
- SO_REUSEPORT学习笔记
- SO_REUSEPORT学习笔记
- SO_REUSEPORT学习笔记
- 记录学习SO_REUSEPORT
- TCP/IP学习笔记补遗
- SO_REUSEPORT
- c++/c学习笔记--补遗
- 数据库学习笔记2(补遗笔记1)
- Java学习笔记—基础知识点拾漏补遗
- VC 笔记补遗
- 廖雪峰教程笔记补遗
- linux SO_REUSEPORT
- so_reuseaddr & so_reuseport
- java基础知识补遗1(纯手打笔记)
- tiku网学习记录(补遗)
- Set SO_REUSEADDR & SO_REUSEPORT options
- Netty端口被占用问题
- opencv 轮廓的凸包,凸缺陷
- 阅读笔记-时间管理-哈佛商学院
- Photos(PHCachingImageManager)
- 2016-01-05 工作笔记5
- SO_REUSEPORT学习笔记补遗
- C#控制台应用程序的设计
- Android自定义dialog实现支付宝支付成功样式
- 【2016新年版】年度精品 XP,32/64位Win7,32/64位Win8,32/64位Win10系统
- jquery 判断checkbox 是否选中和如何动态选中一个checkbox
- 浅析数据仓库
- javascript技巧之——document.createElement()的用法
- 新建Android项目出现错误
- 【UML】UML类图