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. vim 简单用法

    vim 是一个纯文本编辑器 模式化的编辑器 1:编辑模式2:输入模式3:末行模式 : 具有命令的接口,在末行模式中可以直接的通过命令修改vim编辑器打开的文本文件 模式转换 1:编辑模式—>输入 ...

  2. 敏捷冲刺DAY5

    一. 每日会议 1. 照片 2. 昨日完成工作 发布和提供需求功能的实现 用户修改自己的信息 用户界面设计 管理员界面设计 3. 今日完成工作 4. 工作中遇到的困难 1.设置的背景无法显示. 2.一 ...

  3. Node.js系列——(3)连接DB

    背景 node.js,有人称之为运行在服务器端的JavaScript.以往我们使用JavaScript时,都是依赖后端查询数据库并返回数据,而JavaScript只需要展示即可.问题来了,就不能绕开后 ...

  4. VisualStudio2013 代码查看优化 对齐线

    http://jingyan.baidu.com/article/363872eccef5276e4ba16f91.html

  5. Mac & how to uninstall LANDesk

    Mac & how to uninstall LANDesk http://eddiejackson.net/wp/?p=9036 https://community.ivanti.com/d ...

  6. Vue.js 判断对象属性是否存,不存在添加

    Vue.set是可以对对象添加属性的,这里item对象添加一个checked属性 //if(typeof item.checked=='undefined'){if(!this.item.checke ...

  7. .netMVC Vue axios 获取数据

    网页 <link href="~/Content/css/bootstrap-theme.min.css" rel="stylesheet" /> ...

  8. bzoj4815[CQOI2017]小Q的格子

    题意 不简述题意了,简述题意之后这道题就做出来了.放个原题面. 小Q是个程序员. 作为一个年轻的程序员,小Q总是被老C欺负,老C经常把一些麻烦的任务交给小Q来处理. 每当小Q不知道如何解决时,就只好向 ...

  9. 《Linux Shell 脚本攻略》读书笔记

    本书主要讲解一些linux shell命令的用法,讲解一些shell的奇技淫巧. 第一章 小试牛刀 主要介绍一些基本shell指令 终端打印:echo.printf 别名:alias 终端处理工具:t ...

  10. TortoiseSVN 和 VisualSVN Server 使用教程

    TortoiseSVN 和 VisualSVN Server 使用教程 来源 https://blog.csdn.net/xgf415/article/details/75196360 目录: SVN ...