先了解java的网络编程

Netty为何支持高并发

netty是基于java的nio非阻塞通信,而原始的阻塞通信无法满足高并发。下面我们通过两幅图来简要说明

BIO:

这种模式下一个线程处理一个连接,当某个连接阻塞处于等待状态时该线程也处于阻塞等待状态,而我们的机器一共支持的线程数是有限的,如:你的机器只支持30000个线程,那么它最大的并发量也就只有30000。

NIO:

在NIO模式下,我们看到一个线程并不是单一的去处理某一个连接。事实是这样的:先去建立连接,不管建立多少个,建立好之后都交给一个叫selector的处理器,这个处理器的作用就是循环遍历你建立的所有的连接,当遍历到某个连接时,它就判断这个连接是否处于阻塞等待状态,如果处于阻塞等待状态则什么都不做让它继续去等待,继续判断下一个;如果判断到某个连接已经建立通信,则将该连接交给thread去执行通信,这样我们的一个thread就不用专门去为一个连接服务,当某个连接阻塞时我们的线程仍然可以处理其他的连接;即,此时我们的一个线程可以处理多个连接,这就是为什么Netty支持高并发的原因

netty为何通讯快

netty传输速度快的主要原因是NIO的另一个特点:零拷贝。什么是0拷贝呢?我们先说说JAVA的内存机制,JAVA的内存分为5大块:栈、堆、方法区、本地方法区和寄存器。而这中间最重要的一块就是堆,也就是java存放对象的地方。当我们进行通信的时候,数据从客户端传输过来本来是先要将数据拷贝到Socket的缓冲区,再将这些数据拷贝到堆内存,再供程序去使用,当数据量较大的时候平凡的大量的数据拷贝就会占用大量的资源。而NIO的零拷贝是在java的内存里面又分配出一块新的内存专门用来存储客户端传过来的数据,也就是数据不经过socket的缓冲区和堆内存,直接到新开辟的这块内存供程序使用,使用的时候Netty为其提供了专门的Api:ByteBuf。由于数据直接从客户端到ByteBuf,应用程序直接从ByteBuf那里取数据,中间做到了零拷贝,所以netty的传输速度比较快。

netty为何封装好

阻塞I/O

public class BioServer {

    public void serve(int port) throws IOException {
ServerSocket socket = new ServerSocket(8088); //1创建服务端
try {
for (;;) {//死循环用来不断监听客户端
Socket clientSocket = socket.accept(); //2监听客户端,如果客户端未建立则程序阻塞挂起,处于等待状态
System.out.println("Accepted connection from " + clientSocket); new Thread(new Runnable() { //3创建线程处理已经建立的线程
@Override
public void run() {
OutputStream out;
try {
out = clientSocket.getOutputStream();//从客户端通道中获取输出流对象
out.write("Hi!\r\n".getBytes(Charset.forName("UTF-8"))); //4写数据到客户端
out.flush();
clientSocket.close(); // } catch (IOException e) {
e.printStackTrace();
try {
clientSocket.close();
} catch (IOException ex) {
// ignore on close
}
}
}
}).start(); //
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

NIO

public class NioServer {
public void serve(int port) throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);//默认就是非阻塞
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ss.bind(address); //1
Selector selector = Selector.open(); //2
serverChannel.register(selector, SelectionKey.OP_ACCEPT); //3注册Selector监听客户端连接 还有OP_CONNECT、OP_READ、OP_WRITE等事件
final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());//将数据格式化成ByteBuf格式
for (;;) {
try {
selector.select(); //4
} catch (IOException ex) {
ex.printStackTrace();
// handle exception
break;
}
Set<SelectionKey> readyKeys = selector.selectedKeys(); //5获取要监听的事件
Iterator<SelectionKey> iterator = readyKeys.iterator(); //创建迭代器
while (iterator.hasNext()) {
SelectionKey key = iterator.next();//遍历到某个事件
iterator.remove();
try {
if (key.isAcceptable()) { //6是否是客户端访问事件
ServerSocketChannel server =
(ServerSocketChannel)key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_WRITE |
SelectionKey.OP_READ, msg.duplicate()); //7
System.out.println(
"Accepted connection from " + client);
}
if (key.isWritable()) { //8 是否是写事件
SocketChannel client =
(SocketChannel)key.channel();
ByteBuffer buffer =
(ByteBuffer)key.attachment();
while (buffer.hasRemaining()) {
if (client.write(buffer) == 0) { //9
break;
}
}
client.close(); //10
}
} catch (IOException ex) {
key.cancel();
try {
key.channel().close();
} catch (IOException cex) {
// 在关闭时忽略
}
}
}
}
}
}

Netty

public class NettyServer {

    public void server(int port) throws Exception {
final ByteBuf buf = Unpooled.unreleasableBuffer(
Unpooled.copiedBuffer("Hi!\r\n", Charset.forName("UTF-8")));
EventLoopGroup group = new OioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // b.group(group) //
.channel(OioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {//
@Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { //
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);//
}
});
}
});
ChannelFuture f = b.bind().sync(); //
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync(); //
}
}
}

从代码量上来看,Netty就已经秒杀传统Socket编程了,但是有些概念可能不是很明确,在这里给大家介绍一下Netty的一些重要概念,让大家更理解Netty。

  • Channel,表示客户端和服务端建立的一次通信连接
  • ChannelHandler,处理业务逻辑的处理器。
  • ChannelHandlerContext,传输处理过程中的业务数据。
  • ChannelPipeline,用于保存处理过程需要用到的ChannelHandler和ChannelHandlerContext。

ByteBuf:

ByteBuf是一个存储字节的容器,最大特点就是使用方便,它有自己的读索引和写索引,方便你对整段字节缓存进行读写,也支持get/set,方便你对其中每一个字节进行读写,他的数据结构如下图所示:

 
 

Netty入门一:何为Netty的更多相关文章

  1. Netty入门教程——认识Netty

    什么是Netty? Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架. Netty 是一个广泛使用的 Java 网络编程框架(N ...

  2. Netty入门教程:Netty拆包粘包技术讲解

    Netty编解码技术是什么意思呢?所谓的编解码技术,说白了就是java序列化技术.序列化有两个目的: 1.进行网络传输2.对象持久化 虽然我们可以使用java进行序列化,Netty去传输.但是java ...

  3. 深入了解Netty【六】Netty工作原理

    引言 前面学习了NIO与零拷贝.IO多路复用模型.Reactor主从模型. 服务器基于IO模型管理连接,获取输入数据,又基于线程模型,处理请求. 下面来学习Netty的具体应用. 1.Netty线程模 ...

  4. (入门篇 NettyNIO开发指南)第三章-Netty入门应用

    作为Netty的第一个应用程序,我们依然以第2章的时间服务器为例进行开发,通过Netty版本的时间服务报的开发,让初学者尽快学到如何搭建Netty开发环境和!运行Netty应用程序. 如果你已经熟悉N ...

  5. netty 入门(一)

    netty Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更确切的讲是一个组件,没有那么复杂. 例子 一  Discard服务器端 我们 ...

  6. Netty入门之客户端与服务端通信(二)

    Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码 ...

  7. Netty入门之HelloWorld

    Netty系列入门之HelloWorld(一) 一. 简介 Netty is a NIO client server framework which enables quick and easy de ...

  8. Netty入门

    一.NIO Netty框架底层是对NIO的高度封装,所以想要更好的学习Netty之前,应先了解下什么是NIO - NIO是non-blocking的简称,在jdk1.4 里提供的新api,他的他的特性 ...

  9. netty入门(一)

    1. netty入门(一) 1.1. 传统socket编程 在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,这可能算是一种资源浪费. 需要为每个线程的调用栈都分配内存,其默认值 ...

随机推荐

  1. asp.net core webapi Session 跨域

    在ajax 请求是也要加相应的东西 $.ajax({ url:url, //加上这句话 xhrFields: { withCredentials: true } success:function(re ...

  2. 深度剖析前端JavaScript中的原型(JS的对象原型)

          这张图片有点劝退了,哈哈哈~    通过原型机制,JavaScript 中的对象从其他对象继承功能特性:这种继承机制与经典的面向对象编程语言的继承机制不同.本文将探讨这些差别,解释原型链如 ...

  3. 关于json转义中文

    服务器传递或者程序传递中,不识别读取到的JSON数据中 \u开头的数据. PHP 生成JSON的时候,必须将汉字不转义为 \u开头的UNICODE数据. 网上很多,但是其实都是错误的,正确的方法是在j ...

  4. 2019-2020-1 20199325《Linux内核原理与分析》第一周作业

    1.显示一句话welcome !/bin/bash script4-1.sht var1="welcome to use Shell script" echo $var1 pwd ...

  5. libeay32.dll 1.0.2j crash

    https://github.com/BOINC/boinc/issues/2470 他们认为是CPU不同造成的 另外一个可能的原因 Changes between 1.0.2j and 1.0.2k ...

  6. QString 转换成 wchar 的一个小陷阱

    QString::toWCharArray(wchar_t * array) 其中 wchar_t * array 除了要分配内存之外,必须用 wmemset 初始化. 环境是 Visual Stud ...

  7. Solidity的Bytecode和Opcode简介

    Solidity的Bytecode和Opcode简介 随着我们更深入地编写智能合约,我们将遇到诸如" PUSH1"," SSTORE"," CALLV ...

  8. HDU 5725 Game

    1. 笔记 题意是求距离的期望(距离仍指连接两点且有效的路径长度的最小值).直观想象可以发现,该距离与曼哈顿距离相比最多多2(可以构造这样的路径). 答案=(任意两点曼哈顿距离的总和 - 至少有一点是 ...

  9. Linux运维基础阶段部分复习概要

    [jj@oldboy ~]$ hostnamectl set-hostname zj 主机名只有root用户才有权限修改,普通用户想要修改要知道root密码,sudo提权,重启虚拟机或者打开新的窗口新 ...

  10. 【Linux常见命令】date命令

    Linux date命令:可以用来显示或设定系统的日期与时间. 在显示方面,使用者可以设定欲显示的格式,格式设定为一个加号后接数个标记,其中可用的标记列表如下: 时间方面: %H : 小时(00..2 ...