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,而NioEventLoopGroupEventLoopGroup的一个实现类。这里实例化了两个 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 入门初体验的更多相关文章

  1. gulp快速入门&初体验

    前言 一句话先 gulp 是一个可以简单和自动化"管理"前端文件的构建工具 先说我以前的主要工作,我主要是做游戏服务端的,用c++/python,所以我对东西的概念理解难免要套到自 ...

  2. Node.js入门初体验

    今天有一个类似网络爬虫的需求,本来打算用我还算熟悉的asp或者asp.NET来做这个事情,但是写了这么长时间js,asp的语法实在不喜欢,VS又早被我卸掉了,思来想去打算用一下最近比较火的Node.j ...

  3. Spring入门初体验

    Spring其实就是一个容器,让我们更方便的拿到想要的对象. 1.编写一个service // userService.java public interface userService { publ ...

  4. angularjs入门初体验

    1. http://www.zouyesheng.com/angular.html#toc39

  5. 我的Go语言学习之旅二:入门初体验 Hello World

    好吧,全部的程序猿们都已经习惯了.学习不论什么一门语言,我们都会以Hello World实例開始我们的学习,我也不例外.先来一个简单的样例 打开编辑器 (能够用记事本,我已经习惯 Notepad++了 ...

  6. json入门初体验

    json是JavaScript对象表示法,也是轻量级的文本数据交互格式,独立于语言,能够自我描述 json文本格式在语法上与创建JavaScript对象代码相同,多以json不需要解析器,js程序能够 ...

  7. SpringBoot入门初体验

    概述 Java项目开发中繁多的配置,复杂的部署流程和第三方技术集成让码农在开发项目中效率低下,因此springBoot应运而生. 环境 IntelliJ IDEA 2018.3 jkd1.8 开始(傻 ...

  8. Maya+VS编程入门初体验(HelloWorld)

    Maya2018 + VS2017 环境搭建见 博客 1. 项目: VS 新建了一个 MEL Command类型的项目(MayaProject) 2. HelloWorld代码 #include< ...

  9. javaWeb快速入门+——初体验-HelloWorld

    文章转载自 https://www.cnblogs.com/1906859953Lucas/p/10821840.html 练习成品下载 https://www.lanzous.com/i9fljkj ...

随机推荐

  1. css3 伪元素 ::before ::after

    键代码分析: /*css代码*/ .effect::before, .effect::after{ content:""; position:absolute; z-index:- ...

  2. 安装DHCP 服务器 指的是由服务器控制一段IP地址范围,客户机登录服务器时就可以自动获得服务器分配的IP地址和子网掩码

    DHCP服务详解 前言:动态主机配置协议,给局域网内的主机分配IP地址,子网掩码,网关,DNS ARP协议 arp: address resolveing protocol (地址解析协议) 实现:I ...

  3. Python——cmd调用(os.system阻塞处理)(多条命令执行)

    os.system(返回值为0,1,2)方法 0:成功 1:失败 2:错误 os.system默认阻塞当前程序执行,在cmd命令前加入start可不阻塞当前程序执行. 例如: import os os ...

  4. CSUOJ 1141——第四届河南省程序设计大赛

    题目的意思是给你一个机器人,初始的时候在某一个给定的路灯位置,机器人要把路边所有的路灯关掉,每个路灯都有一个距离和一个功率,求要把所有的路灯关掉最小的最终能耗是多少? 题目是一个很明显的区间DP.可以 ...

  5. Android四大组件之Intent

    Intent不是android几大组件框架,但是确实是android 各大组件之间沟通的桥梁. 尤其Intent对于activity有很大的关系. 一下是我个人对task以及backstack的总结.

  6. 51nod 1317 相似字符串对(容斥原理+思维)

    题意: 称一对字符串(A,B)是相似的,当且仅当满足以下条件: (1)字符串A和B都恰好包含N个字符: (2)A和B串中的每个字符都是小写字母的前k个字符,即A.B中只可能出现'a','b','c', ...

  7. 【刷题】BZOJ 4650 [Noi2016]优秀的拆分

    Description 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆分是优秀的.例如,对于字符串 aabaabaa,如果令 A ...

  8. CF712E Memory and Casinos 期望概率

    题意:\(n\)个赌场,每个赌场有\(p_{i}\)的胜率,如果赢了就走到下一个赌场,输了就退回上一个赌场,规定\(1\)号赌场的上一个是\(0\)号赌场,\(n\)号赌场的下一个是\(n + 1\) ...

  9. bzoj2734:[HNOI2012]集合选数(状压DP)

    菜菜的喵喵题~ 化序列为矩阵!化腐朽为神奇!左上角为1,往右每次*3,往下每次*2,这样子就把问题转化成了在矩阵里选不相邻的数有几种可能. 举个矩阵的例子 1 3 9 27 2 6 18 54 4 1 ...

  10. 专题训练之区间DP

    例题:以下例题部分的内容来自https://blog.csdn.net/my_sunshine26/article/details/77141398 一.石子合并问题 1.(NYOJ737)http: ...