转自:https://www.jdon.com/concurrent/netty.html

  Netty是一个高性能 事件驱动的异步的非堵塞的IO(NIO)框架,用于建立TCP等底层的连接,基于Netty可以建立高性能的Http服务器。支持HTTP、 WebSocket 、Protobuf、 Binary TCP |和UDP,Netty已经被很多高性能项目作为其Socket底层基础,如HornetQ Infinispan Vert.x

Play Framework Finangle和 Cassandra。其竞争对手是:Apache MINA和 Grizzly。

 传统堵塞的IO读取如下:

InputStream is = new FileInputStream("input.bin");
int byte = is.read(); // 当前线程等待结果到达直至错误

   而使用NIO如下:

while (true) {
 selector.select(); // 从多个通道请求事件
 Iterator it = selector.selectedKeys().iterator();
 while (it.hasNext()) {
  SelectorKey key = (SelectionKey) it.next();
  handleKey(key);
  it.remove();
 }

堵塞与非堵塞原理

  传统硬件的堵塞如下,从内存中读取数据,然后写到磁盘,而CPU一直等到磁盘写完成,磁盘的写操作是慢的,这段时间CPU被堵塞不能发挥效率。

  使用非堵塞的DMA如下图:CPU只是发出写操作这样的指令,做一些初始化工作,DMA具体执行,从内存中读取数据,然后写到磁盘,当完成写后发出一个中断事件给CPU。这段时间CPU是空闲的,可以做别的事情。这个原理称为Zero.copy零拷贝。

  Netty底层基于上述Java NIO的零拷贝原理实现:

比较

  • Tomcat是一个Web服务器,它是采取一个请求一个线程,当有1000客户端时,会耗费很多内存。通常一个线程将花费 256kb到1mb的stack空间。
  • Node.js是一个线程服务于所有请求,在错误处理上有限制
  • Netty是一个线程服务于很多请求,如下图,当从Java NIO获得一个Selector事件,将激活通道Channel。

演示

Netty的使用代码如下:

Channel channel = ...
ChannelFuture cf = channel.write(data);
cf.addListener(
  new ChannelFutureListener() {
   @Override
   public void operationComplete(ChannelFuture future) throws Exception {
     if(!future.isSuccess() {
        future.cause().printStacktrace();
        ...
     }
     ...
   }
});
...
cf.sync();

通过引入观察者监听,当有数据时,将自动激活监听者中的代码运行。

我们使用Netty建立一个服务器代码:

public class EchoServer {

private final int port;

public EchoServer(int port) { 
        this.port = port; 
    }

public void run() throws Exception { 
        // Configure the server. 
        EventLoopGroup bossGroup = new NioEventLoopGroup(); 
        EventLoopGroup workerGroup = new NioEventLoopGroup(); 
        try { 
            ServerBootstrap b = new ServerBootstrap(); 
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100) 
                   .handler(new LoggingHandler(LogLevel.INFO)).childHandler(newChannelInitializer<SocketChannel>() { 
                       @Override 
                       public void initChannel(SocketChannel ch) throws Exception { 
                           ch.pipeline().addLast( 
                           // new LoggingHandler(LogLevel.INFO), 
                                   new EchoServerHandler()); 
                       } 
                   });

// Start the server. 
            ChannelFuture f = b.bind(port).sync();

// Wait until the server socket is closed. 
            f.channel().closeFuture().sync(); 
        } finally { 
            // Shut down all event loops to terminate all threads. 
            bossGroup.shutdownGracefully(); 
            workerGroup.shutdownGracefully(); 
        } 
    }

}

这段代码调用:在9999端口启动

new EchoServer(9999).run();

我们需要完成的代码是EchoServerHandler:

public class EchoServerHandler extends ChannelInboundHandlerAdapter {

private static final Logger logger = Logger.getLogger(EchoServerHandler.class.getName());

@Override 
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
        ctx.write(msg); 
    }

@Override 
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 
        ctx.flush(); 
    }

@Override 
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 
        // Close the connection when an exception is raised. 
        logger.log(Level.WARNING, "Unexpected exception from downstream.", cause); 
        ctx.close(); 
    } 
}

原理

   一个Netty服务器的原理如下:

  图中每次请求的读取是通过UpStream来实现,然后激活我们的服务逻辑如EchoServerHandler,而服务器向外写数据,也就是响应是通过DownStream实现的。每个通道Channel包含一对UpStream和DownStream,以及我们的handlers(EchoServerHandler),如下图,这些都是通过channel pipeline封装起来的,数据流在管道里流动,每个Socket对应一个ChannelPipeline。

   CHANNELPIPELINE是关键,它类似Unix的管道,有以下作用:

  • 为每个Channel 保留 ChannelHandlers ,如EchoServerHandler
  • 所有的事件都要通过它
  • 不断地修改:类似unix的SH管道: echo "Netty is shit...." | sed -e 's/is /is the /'
  • 一个Channel对应一个 ChannelPipeline
  • 包含协议编码解码 安全验证SSL/TLS和应用逻辑

客户端代码

  前面我们演示了服务器端代码,下面是客户端代码:

public class EchoClient { 
    private final String host; 
    private final int port; 
    private final int firstMessageSize;

public EchoClient(String host, int port, int firstMessageSize) { 
        this.host = host; 
        this.port = port; 
        this.firstMessageSize = firstMessageSize; 
    }

public void run() throws Exception { 
        // Configure the client. 
        EventLoopGroup group = new NioEventLoopGroup(); 
        try { 
            Bootstrap b = new Bootstrap(); 
           b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() { 
                @Override 
                public void initChannel(SocketChannel ch) throws Exception { 
                   ch.pipeline().addLast( 
                   // new LoggingHandler(LogLevel.INFO), 
                           new EchoClientHandler(firstMessageSize)); 
                } 
            });

// Start the client. 
            ChannelFuture f = b.connect(host, port).sync();

// Wait until the connection is closed. 
            f.channel().closeFuture().sync(); 
        } finally { 
            // Shut down the event loop to terminate all threads. 
            group.shutdownGracefully(); 
        } 
    } 
}

客户端的应用逻辑EchoClientHandler:

public class EchoClientHandler extends ChannelInboundHandlerAdapter {

private static final Logger logger = Logger.getLogger(EchoClientHandler.class.getName());

private final ByteBuf firstMessage;

/** 
     * Creates a client-side handler. 
     */ 
    public EchoClientHandler(int firstMessageSize) { 
        if (firstMessageSize <= 0) { 
            throw new IllegalArgumentException("firstMessageSize: " + firstMessageSize); 
        } 
        firstMessage = Unpooled.buffer(firstMessageSize); 
        for (int i = 0; i < firstMessage.capacity(); i++) { 
            firstMessage.writeByte((byte) i); 
        } 
    }

@Override 
    public void channelActive(ChannelHandlerContext ctx) { 
        ctx.writeAndFlush(firstMessage); 
        System.out.print("active"); 
    }

@Override 
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
        ctx.write(msg); 
        System.out.print("read"); 
    }

@Override 
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 
        ctx.flush(); 
        System.out.print("readok"); 
    }

@Override 
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 
        // Close the connection when an exception is raised. 
        logger.log(Level.WARNING, "Unexpected exception from downstream.", cause); 
        ctx.close(); 
    }

}

每日扫盲(四):java之Netty原理和使用的更多相关文章

  1. Java实习生常规技术面试题每日十题Java基础(四)

    目录 1.String 和StringBuffer的区别. 2.数组有没有length()这个方法? String有没有length()这个方法? 3.final, finally, finalize ...

  2. 10分钟看懂, Java NIO 底层原理

    目录 写在前面 1.1. Java IO读写原理 1.1.1. 内核缓冲与进程缓冲区 1.1.2. java IO读写的底层流程 1.2. 四种主要的IO模型 1.3. 同步阻塞IO(Blocking ...

  3. Java实习生常规技术面试题每日十题Java基础(三)

    目录 1.是否可以从一个static方法内部发出对非static方法的调用? 2.Integer与int的区别? 3.Overload和Override的区别.参数列表相同,返回值不同的方法,是否是重 ...

  4. Java实习生常规技术面试题每日十题Java基础(二)

    目录 1. JAVA 的反射机制的原理. 2.静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同? 3.如何将String类型转化成Number类型. 4.什 ...

  5. Java实习生常规技术面试题每日十题Java基础(一)

    目录 1.Java 的 "一次编写,处处运行"如何实现? 2.描述JVM运行原理. 3.为什么Java没有全局变量? 4.说明一下public static void main(S ...

  6. java复习要点(一)------- java语言的特点、java的工作原理、配置环境变量、java命令的使用

    一.java语言的特点: (1)简单并面向对象 (2)鲁棒并安全: java语言在编译及运行程序时,都要进行严格的检查,防止不匹配问题的发生.如果引用一个非法类型,或执行一个非法类型操作,java减肥 ...

  7. 全面解读Java NIO工作原理(2)

    全面解读Java NIO工作原理(2) 2011-12-14 10:31 Rollen Holt Rollen Holt的博客 我要评论(0) 字号:T | T JDK 1.4 中引入的新输入输出 ( ...

  8. [零] Java 语言运行原理 JVM原理浅析 入门了解简介 Java语言组成部分 javap命令使用

    Java Virtual Machine  官方介绍 Java虚拟机规范官方文档 https://docs.oracle.com/javase/specs/index.html 其中以java8的为 ...

  9. Java并发编程原理与实战五:创建线程的多种方式

    一.继承Thread类 public class Demo1 extends Thread { public Demo1(String name) { super(name); } @Override ...

随机推荐

  1. ES6标准入门(第三版).pdf----推荐指数⭐⭐⭐⭐⭐

    链接: https://pan.baidu.com/s/13RHsyTMNx7s1oMqQeYCm3Q 提取码: ikg3 -------------------------------------- ...

  2. CSS基础之浮动属性float图文详解

      宏观地讲,我们的web页面的制作,是个“流”,必须从上而下,像“织毛衣”.   标准流里面的限制非常多,导致很多页面效果无法实现.如果我们现在就要并排.并且就要设置宽高,那该怎么办呢?办法是:超脱 ...

  3. JS高级---案例:验证用户输入的是不是邮箱

    案例:验证用户输入的是不是邮箱 <!DOCTYPE html> <html lang="en"> <head> <meta charset ...

  4. vue组件中的data为什么是函数?

    一.vue组件中的data为什么是函数 为了保证组件的独立性 和 可 复用性,data 是一个函数,组件实例化的时候这个函数将会被调用,返回一个对象,计算机会给这个对象分配一个内存地址,你实例化几次, ...

  5. Base64编码和其在图片的传输的应用

    Base64 [原文链接] 目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一.做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后再进行签名或加密,之后再进 ...

  6. spring 切点表达式

    spring切点表达式: 1.*通配符:该通配符主要用于匹配单个单词. 例如:execution(* com.bonnie.Controller.TestController.*()) 上述表达式表示 ...

  7. ASP.NET Razor 语法

    主要的 Razor C# 语法规则 Razor 代码块包含在 @{ ... } 中 内联表达式(变量和函数)以 @ 开头 代码语句用分号结束 变量使用 var 关键字声明 字符串用引号括起来 C# 代 ...

  8. [HEOI2015] 小Z的房间 - 矩阵树定理

    #include <bits/stdc++.h> using namespace std; #define int long long const int N = 105; const i ...

  9. js清空子节点

    删除全部子节点 function removeAllChild(){ var div = document.getElementById("div1"); while(div.ha ...

  10. make && make install

    ./configure  --prefix= /usr/local/python3.6.6 make &&  make  install  prefix=/usr/local/pyth ...