技术标签: Netty
如果在网络上搜索它,你可以在官网上看到如下内容:
Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.
简单说,Netty 是一个基于NIO的客户、服务器端编程框架,它支持快速、简单地开发面向协议的服务器和客户端等网络应用程序,简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
对于早期的网络编程,Java API只支持由本地系统套接字库提供的所谓的阻塞函数,通过它们虽然能实现网络编程,但是问题也随之而来。在这种阻塞的I/O模型中,一个Thread只能同时处理一个连接,要想实现多个并发客户端,就需要为每个客户端创建一个新的Thread。这种并发方案对于支撑中小数量的客户端来说还算可以接受,但是为了支撑 100000 或者更多的并发连接所需要的资源使得它很不理想。
Java 对于非阻塞 I/O 的支持是在 2002 年引入的,位于 JDK 1.4 的 java.nio 包中。该模型使用了事件通知API来确定在一组非阻塞套接字中有哪些通道已经就绪能够进行I/O相关的操作,使得可以在任何的时间检查任意I/O操作的完成情况,然后再交由指定的Thread进行处理,这样就可以使用一个单一的线程处理多个并发的客户端连接。
相对阻塞 I/O 来说,这种模型为网络编程提供了更好的方案,使用较少的线程便可以处理多个连接,减少了内存管理和上下文切换所带来开销,避免了资源的浪费。
尽管我们已经可以通过NIO来进行应用程序的构建,但是NIO的类库和API繁杂,使用麻烦;且编写高质量的NIO程序,你还需要对多线程和网络编程等方面非常熟悉,对于大多数的程序员要做到如此正确和安全并不容易。特别是,在高负载下可靠和高效地处理和调度 I/O 操作是一项繁琐而且容易出错的任务。
于是,使用Netty进行网络编程也就顺理成章了,它封装了网络编程的复杂性,API使用简单,更容易上手,使网络编程和Web技术的最新进展能够被比以往更广泛的开发人员接触到。
在学习Netty之前,先仔细看看Netty的特点(优势),虽然现在可能对此理解不深,但在后面的学习和使用中,我们将不断验证这些特点,正是这些特点让Netty逐渐成为了Java NIO变成的首选框架。
分类 | Netty的特点 |
---|---|
设计 | 统一的API,支持多种传输类型,阻塞的和非阻塞的 简单而强大的线程模型 真正的无连接数据报套接字支持 链接逻辑组件以支持复用 |
易于使用 | 翔实的Javaodc和大量的代码实例 不需要超过JDK1.6+的依赖(部分特性可能需要Java1.7+的依赖) |
性能 | 拥有比Java核心API更高的吞吐量和更低的延迟 得益于池化和复用,拥有更低的资源消耗 最少的内存复制 |
健壮性 | 不会因为慢速、快速或者超载的连接而导致OutOfMemoryError 消除在高速网络中NIO应用程序常见的不公平读/写比 |
安全性 | 完整的SSL/TLS和StartTLS支持 可用于受限环境下,如Applet和OSGI |
社区驱动 | 发布快速而且频繁 |
在了解了Netty的相关知识后,我们通过一个 Echo客户端和服务器应用程序来了解Netty的工作方式(该Demo参考了 第二章的案例,稍微进行了修改)。
本文的Netty版本为4.0.33.Final,下面是maven的配置:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lincain</groupId>
<artifactId>maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<name>maven-parent</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.33.Final</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
</dependencies>
</project>
1 .Echo服务器
1.1引导服务器
package com.lincain.echo;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class EchoServer {
private final int port;
public EchoServer(int port){
this.port = port;
}
public void start() throws Exception{
// 创建EventLoopGroup的实现类NioEventLoopGroup对象
EventLoopGroup bossGroup = new NioEventLoopGroup();
try {
// 创建服务器引导ServerBootstrap的示例对象
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup);
// 指定使用NIO的传输Channel
bootstrap.channel(NioServerSocketChannel.class);
// 使用指定的端口设置套接字地址
bootstrap.localAddress(port);
// 通过ChannelInitializer添加一个EchoServerHandler到子Channel的ChannelPipeLine上
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoServerHandler());
}
});
// 异步地绑定服务器,调用sync()方法阻塞等待直到绑定完成
ChannelFuture future = bootstrap.bind().sync();
System.out.println(EchoServer.class.getSimpleName() +
" started and listen on " + future.channel().localAddress());
// 获取Channel的CloseFuture,并阻塞当前线程直至它完成
future.channel().closeFuture().sync();
}finally {
// 关闭EventLoopGroup,并释放所有的资源
bossGroup.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception{
// 设置端口号,并调用服务器的start()方法
new EchoServer(20000).start();
}
}
1.2 服务器业务处理逻辑
package com.lincain.echo;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
ByteBuf in = (ByteBuf)msg;
// 将入站的消息打印在控制台
System.out.println("Server received:" + in.toString(CharsetUtil.UTF_8));
// 将接收到的消息写给发送者,而不冲刷出站消息
ctx.write(in);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx)
throws Exception {
// 将未决消息冲刷到远程节点,并且关闭该Channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).
addListener(ChannelFutureListener.CLOSE);
}
// 打印异常栈跟踪,并关闭该Channel
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
2.1 引导客户端
package com.lincain.echo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class EchoClient {
private final int port;
private final String host;
public EchoClient(int port, String host) {
this.port = port;
this.host = host;
}
public void start() throws Exception {
// 创建EventLoopGroup的实现类NioEventLoopGroup对象
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 创建客户端引导Bootstrap的实例对象
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
// 指定所使用额额NIO传输Channel
bootstrap.channel(NioSocketChannel.class);
// 设置需要连接的服务器的IP和端口
bootstrap.remoteAddress(host,port);
// 通过ChannelInitializer添加一个EchoClientHandler到子Channel的ChannelPipeLine上
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoClientHandler());
}
});
// 连接到远程服务器,阻塞等待直到连接完成
ChannelFuture future = bootstrap.connect().sync();
// 获取Channel的CloseFuture,并阻塞当前线程直至它完成
future.channel().closeFuture().sync();
}finally {
// 关闭EventLoopGroup,并释放所有的资源
workerGroup.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception{
// 设置IP和端口号,并调用客户端的start()方法
new EchoClient(20000, "localhost").start();
}
}
2.2 客户端业务处理逻辑
package com.lincain.echo;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf>{
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg)
throws Exception {
// 将服务端应答的消息打印在控制台上
System.out.println("Client received:" + msg.toString(CharsetUtil.UTF_8));
}
@Override
public void channelActive(ChannelHandlerContext ctx)
throws Exception {
// 当被通知Channel是活跃的时候,发送一条消息
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!",
CharsetUtil.UTF_8));
}
// 打印异常栈跟踪,并关闭该Channel
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
3. 运行程序
首先运行EchoServer让服务端先启动,然后运行EchoClient让客户端启动。
服务端控制台的打印内容:
EchoServer started and listen on /0:0:0:0:0:0:0:0:20000
Server received:Netty rocks!
客户端控制台的打印内容:
Client received:Netty rocks!
通过上面代码,即实现了运用Netty完成Echo服务端和客户端的交互。Echo 客户端和服务器之间的交互是非常简单的;在客户端建立一个连接之后,它会向服务器发送一个或多个消息,反过来,服务器又会将每个消息回送给客户端。虽然它本身看起来好像用处不大,但它充分地体现了客户端/服务器系统中典型的请求-响应交互模式。
Goroutine是Golang中轻量级线程的实现,由Go Runtime管理。Golang在语言级别支持轻量级线程,叫做协程。Golang标准库提供的所有系统调用操作,都会出让CPU给其他Goroutine。这让事情变得非常简单,让轻量级线程的切换管理不依赖于系统的线程和进程,也不依赖于CPU的核心数量。 Goroutine非常亮眼,但是自从go1.4版本以后,Goroutin...
第三次在线作业单选题 (共40道题)收起1.(2.5分) --How about taking a break?--______. A、Thank you B、Good idea C、See you D、Good-bye我的答案:B此题得分:2.5分2.(2.5分) Seldom ____ any mistakes during my past few years of working here. A、would I mak...
描述Dr.Kong设计的机器人卡多掌握了加减法运算以后,最近又学会了一些简单的函数求值,比如,它知道函数min(20,23)的值是20 ,add(10,98) 的值是108等等。经过训练,Dr.Kong设计的机器人卡多甚至会计算一种嵌套的更复杂的表达式。假设表达式可以简单定义为:1. 一个正的十进制数 x 是一个表达式。2. 如果 x 和 y 是 表达式,则 函数min(x,y )也是表达式,其值...
@Documented,@Retention,@Target,@Inherited1. 编写自定义@Todo注解经常我们在写程序时,有时候有些功能在当前的版本中并不提供,或由于某些其它原因,有些方法没有完成,而留待以后完成,我们在javadoc中用@TODO来描述这一行为,下面用java注解来实现。public @interface Todo { } // Todo.java
12016年AlphaGo以总比分4:1轻松战胜围棋世界冠军李世石,这是AI第一次震惊世界。次年AlphaGo又以3:0的比分击败柯洁,面对强大的AI,柯洁遭遇职业生涯“...
(简单算例)基于Matlab的电力系统潮流编程计算基于Matlab的电力系统潮流编程计算口黄扬威吴喜春郭志峰张斯翔(三峡大学电气与新能源学院湖北·宜昌443002)摘要:通过介绍电力系统的实际运行情况,提出运用Matlab语言对电力系统潮流计算进行编程计算。在程序的编写过程中采用了数学模型建立,稀疏技术、节点编号顺序优化等方法。从潮流计算的基本方程出发。采用牛顿一拉夫逊法并通过建立矩阵的修正方程来...
如果您的计算机发生严重问题(例如无法进入操作系统),您可以使用恢复驱动器进入Windows恢复环境(WinRE),并协助您恢复Windows或是执行系统还原。若要使用恢复驱动器,首先您需要准备一个空的U盘(至少需要16GB的空间)来建立Windows恢复驱动器,且由于Windows会定期更新并改善安全性及计算机效能,建议您可以定期重新建立新的恢复驱动器。 ※ 注: 恢复驱动器不会备份个人档案以及计算机未随附的应用程序,了解更多如何通过文件历史记录备份文件。 建立恢复驱动器 1. 在Wi
1.c创建FIFO的IP核在IP catalog里面搜索FIFO并双击,保存为my_fifo然后一直点击next读和写的full和empty都要√上,不然后面定义要出错勾选inst文件2.对FIFO进行写入操作3.对FIFO进行读出操作4.顶层文件的编写及rtl结构图5.测试文件的编写6.仿真结果...
说明Gremlin是Apache TinkerPop的图形遍历语言。Gremlin是一个功能,数据流 语言,使用户能够简洁地表达复杂的遍历(或查询)的应用程序的性能曲线图。每个Gremlin遍历都由一系列(可能嵌套的)步骤组成。步骤对数据流执行原子操作。每一步都是map步骤(转换流中的对象),filter步骤(从流中移除对象)或sideEffect-step(计算有关流的统计信息)。Gremlin步骤库在这3个基本操作的基础上扩展,为用户提供了丰富的步骤集,用户可以编写这些步骤,以询问他们可能对Greml
github网址:一:https://github.com/nwojke/deep_sort #这个是论文代码deep sort 此论文的检测部分采取的是下面的这篇论文参数 ECCV2016 (https://blog.csdn.net/sunshinezhihuo/article/details/78885012)SORT:论文地址:http://arxiv.org/...
用PB这个开发工具已经有几年了,自从毕业到现在,也一直在用它做为主要吃饭家伙了.呵呵.包括现在,虽然公司的项目要往J2EE平台上发展,可历史遗留程序还是要维护的,而且目前还是公司业务的主要支撑平台.近来,对于客户提出的一些新的需求,我把以前的代码全部整理了一下,以前的代码好至少有三到五个人写过,风格不一,我把所有代码用OO的思想进行了重新整理的安排.这个过程的比较困难的,有时候很烦,不过终于
Cash MachineDescriptionA Bank plans to install a machine for cash withdrawal. The machine is able to deliver appropriate @ bills for a requested cash amount. The machine uses exactly N distinct b...