Netty 入门初体验
Netty简介
Netty是一款异步的事件驱动的网络应用程序框架,支持快速开发可维护的高性能的面向协议的服务器和客户端。Netty主要是对java 的 nio包进行的封装
为什么要使用 Netty
上面介绍到 Netty是一款 高性能的网络通讯框架,那么我们为什么要使用Netty,换句话说,Netty有哪些优点让我们值得使用它,为什么不使用原生的 Java Socket编程,或者使用 Java 1.4引入的 Java NIO。接下来分析分析 Java Socket编程和 Java NIO。
Java 网络编程
首先来看一个Java 网络编程的例子:
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
System.out.println("received: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
复制代码
上面展示了一个简单的Socket服务端例子,该代码只能同时处理一个连接,要管理多个并发客户端,需要为每个新的客户端Socket创建一个 新的Thread。这种并发方案对于中小数量的客户端来说还可以接受,如果是针对高并发,超过100000的并发连接来说该方案并不可取,它所需要的线程资源太多,而且任何时候都可能存在大量线程处于阻塞状态,等待输入或者输出数据就绪,整个方案性能太差。所以,高并发的场景,一般的Java 网络编程方案是不可取的。
Java NIO
还是先来看一个 Java NIO的例子:
public class ServerSocketChannelDemo {
private ServerSocketChannel serverSocketChannel;
private Selector selector;
public ServerSocketChannelDemo(int port) throws IOException {
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void listener() throws IOException {
while (true) {
int n = selector.select();
if (n == 0) {
continue;
}
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = (SelectionKey) iterator.next();
if (selectionKey.isAcceptable()) {
ServerSocketChannel server_channel = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = server_channel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (selectionKey.isReadable()) {
//如果通道处于读就绪的状态
//读操作
//TODO
}
}
}
}
}
复制代码
NIO的核心部分主要有:
- 通道 Channel
- 缓冲区 Buffer
- 多路复用器 Selector
选择器 Selector 是 Java 非阻塞 I/O实现的关键,将通道Channel注册在 Selector上,如果某个通道 Channel发送 读或写事件,这个Channel处于就绪状态,会被Selector轮询出来,进而进行后续I/O操作。这种I/O多路复用的方式相比上面的阻塞 I/O模型,提供了更好的资源管理:
- 使用较少的线程便可以处理很多连接,因此也减少了内存管理和上下文切换所带来的开销
- 当没有I/O操作需要处理的时候,线程也可以被用于其他任务。
尽管使用 Java NIO可以让我们使用较少的线程处理很多连接,但是在高负载下可靠和高效地处理和调度I/O操作是一项繁琐而且容易出错的任务,所以才引出了高性能网络编程专家——Netty
Netty特性
Netty有很多优秀的特性值得让我们去使用它(摘自《Netty实战》):
设计
- 统一的API,适用于不同的协议(阻塞和非阻塞)
- 基于灵活、可扩展的事件驱动模型
- 高度可定制的线程模型
- 可靠的无连接数据Socket支持(UDP)
性能
- 更好的吞吐量,低延迟
- 更低的资源消耗
- 最少的内存复制
健壮性
- 不再因过快、过慢或超负载连接导致OutOfMemoryError
- 不再有在高速网络环境下NIO读写频率不一致的问题
安全性:
- 完整的SSL/TLS和STARTTLS的支持
- 可用于受限环境下,如 Applet 和OSGI
易用:
- 详实的Javadoc和大量的示例集
- 不需要超过 JDK 1.6+的依赖
Netty示例代码
下面是server 和client的示例代码,先来看看Netty代码是怎么样的,后续文章会详细分析各个模块。
Server代码
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public static void main(String[] args) throws InterruptedException {
new EchoServer(8888).start();
}
public void start() throws InterruptedException {
final EchoServerHandler serverHandler = new EchoServerHandler();
//创建EventLoopGroup,处理事件
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(boss,worker)
//指定所使用的NIO传输 Channel
.channel(NioServerSocketChannel.class)
//使用指定的端口设置套接字地址
.localAddress(new InetSocketAddress(port))
//添加一个EchoServerHandler到子Channel的ChannelPipeline
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//EchoServerHandler标志为@Shareable,所以我们可以总是使用同样的实例
socketChannel.pipeline().addLast(serverHandler);
}
});
//异步的绑定服务器,调用sync()方法阻塞等待直到绑定完成
ChannelFuture future = b.bind().sync();
future.channel().closeFuture().sync();
} finally {
//关闭EventLoopGroup,释放所有的资源
group.shutdownGracefully().sync();
worker.shutdownGracefully().sync();
}
}
}
复制代码
EchoServerHandler
@ChannelHandler.Sharable //标识一个 ChannelHandler可以被多个Channel安全地共享
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buffer = (ByteBuf) msg;
//将消息记录到控制台
System.out.println("Server received: " + buffer.toString(CharsetUtil.UTF_8));
//将接受到消息回写给发送者
ctx.write(buffer);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//将未消息冲刷到远程节点,并且关闭该 Channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//打印异常栈跟踪
cause.printStackTrace();
//关闭该Channel
ctx.close();
}
}
复制代码
代码要点解读:
ServerBootStrap是引导类,帮助服务启动的辅助类,可以设置 Socket参数EventLoopGroup是处理I/O操作的线程池,用来分配 服务于Channel的I/O和事件的EventLoop,而NioEventLoopGroup是EventLoopGroup的一个实现类。这里实例化了两个NioEventLoopGroup,一个boss,主要用于处理客户端连接,一个worker用于处理客户端的数据读写工作EchoServerHandler实现了业务逻辑- 通过调用
ServerBootStrap.bind()方法以绑定服务器
Client 代码
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture channelFuture = b.connect().sync();
channelFuture.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoClient("127.0.0.1", 8888).start();
}
}
复制代码
EchoClientHandler
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("Client received: "+byteBuf.toString());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks",CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
复制代码
代码要点解读:
- 为初始化客户端,创建了一个BootStrap实例,与
ServerBootStrap一样,也是一个引导类,主要辅助客户端 - 分配了一个
NioEventLoopGroup实例,里面的EventLoop,处理连接的生命周期中所发生的事件 EchoClientHandler类负责处理业务逻辑,与服务端的EchoSeverHandler作用相似。
参考资料 & 鸣谢
作者:pjmike_pj
链接:https://juejin.im/post/5ba35a76f265da0a951ed4db
Netty 入门初体验的更多相关文章
- gulp快速入门&初体验
前言 一句话先 gulp 是一个可以简单和自动化"管理"前端文件的构建工具 先说我以前的主要工作,我主要是做游戏服务端的,用c++/python,所以我对东西的概念理解难免要套到自 ...
- Node.js入门初体验
今天有一个类似网络爬虫的需求,本来打算用我还算熟悉的asp或者asp.NET来做这个事情,但是写了这么长时间js,asp的语法实在不喜欢,VS又早被我卸掉了,思来想去打算用一下最近比较火的Node.j ...
- Spring入门初体验
Spring其实就是一个容器,让我们更方便的拿到想要的对象. 1.编写一个service // userService.java public interface userService { publ ...
- angularjs入门初体验
1. http://www.zouyesheng.com/angular.html#toc39
- 我的Go语言学习之旅二:入门初体验 Hello World
好吧,全部的程序猿们都已经习惯了.学习不论什么一门语言,我们都会以Hello World实例開始我们的学习,我也不例外.先来一个简单的样例 打开编辑器 (能够用记事本,我已经习惯 Notepad++了 ...
- json入门初体验
json是JavaScript对象表示法,也是轻量级的文本数据交互格式,独立于语言,能够自我描述 json文本格式在语法上与创建JavaScript对象代码相同,多以json不需要解析器,js程序能够 ...
- SpringBoot入门初体验
概述 Java项目开发中繁多的配置,复杂的部署流程和第三方技术集成让码农在开发项目中效率低下,因此springBoot应运而生. 环境 IntelliJ IDEA 2018.3 jkd1.8 开始(傻 ...
- Maya+VS编程入门初体验(HelloWorld)
Maya2018 + VS2017 环境搭建见 博客 1. 项目: VS 新建了一个 MEL Command类型的项目(MayaProject) 2. HelloWorld代码 #include< ...
- javaWeb快速入门+——初体验-HelloWorld
文章转载自 https://www.cnblogs.com/1906859953Lucas/p/10821840.html 练习成品下载 https://www.lanzous.com/i9fljkj ...
随机推荐
- css3 伪元素 ::before ::after
键代码分析: /*css代码*/ .effect::before, .effect::after{ content:""; position:absolute; z-index:- ...
- 安装DHCP 服务器 指的是由服务器控制一段IP地址范围,客户机登录服务器时就可以自动获得服务器分配的IP地址和子网掩码
DHCP服务详解 前言:动态主机配置协议,给局域网内的主机分配IP地址,子网掩码,网关,DNS ARP协议 arp: address resolveing protocol (地址解析协议) 实现:I ...
- Python——cmd调用(os.system阻塞处理)(多条命令执行)
os.system(返回值为0,1,2)方法 0:成功 1:失败 2:错误 os.system默认阻塞当前程序执行,在cmd命令前加入start可不阻塞当前程序执行. 例如: import os os ...
- CSUOJ 1141——第四届河南省程序设计大赛
题目的意思是给你一个机器人,初始的时候在某一个给定的路灯位置,机器人要把路边所有的路灯关掉,每个路灯都有一个距离和一个功率,求要把所有的路灯关掉最小的最终能耗是多少? 题目是一个很明显的区间DP.可以 ...
- Android四大组件之Intent
Intent不是android几大组件框架,但是确实是android 各大组件之间沟通的桥梁. 尤其Intent对于activity有很大的关系. 一下是我个人对task以及backstack的总结.
- 51nod 1317 相似字符串对(容斥原理+思维)
题意: 称一对字符串(A,B)是相似的,当且仅当满足以下条件: (1)字符串A和B都恰好包含N个字符: (2)A和B串中的每个字符都是小写字母的前k个字符,即A.B中只可能出现'a','b','c', ...
- 【刷题】BZOJ 4650 [Noi2016]优秀的拆分
Description 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆分是优秀的.例如,对于字符串 aabaabaa,如果令 A ...
- CF712E Memory and Casinos 期望概率
题意:\(n\)个赌场,每个赌场有\(p_{i}\)的胜率,如果赢了就走到下一个赌场,输了就退回上一个赌场,规定\(1\)号赌场的上一个是\(0\)号赌场,\(n\)号赌场的下一个是\(n + 1\) ...
- bzoj2734:[HNOI2012]集合选数(状压DP)
菜菜的喵喵题~ 化序列为矩阵!化腐朽为神奇!左上角为1,往右每次*3,往下每次*2,这样子就把问题转化成了在矩阵里选不相邻的数有几种可能. 举个矩阵的例子 1 3 9 27 2 6 18 54 4 1 ...
- 专题训练之区间DP
例题:以下例题部分的内容来自https://blog.csdn.net/my_sunshine26/article/details/77141398 一.石子合并问题 1.(NYOJ737)http: ...