zoukankan      html  css  js  c++  java
  • 高性能NIO框架Netty入门篇

    http://cxytiandi.com/blog/detail/17345

    Netty介绍

    Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

    也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。

    官网地址:http://netty.io/

    使用场景

    Netty之所以能成为主流的NIO框架,是因为它有下面的优点:

    • NIO的类库和API使用难度较高,Netty进行了封装,容易上手
    • 高性能,功能强大,支持多种编解码功能,支持多种主流协议
    • 成熟,稳定,已经在多个大型框架中使用(dubbo,RocketMQ,Hadoop,mycat,Spring5)
    • …..

    在几年之前我上家公司用的是Mina来开发一个IM的系统,Mina也是一个很好的框架(http://mina.apache.org/)。
    如今很多的框架都改成用Netty来做底层通讯了,我司现在还有一个代理框架用Mina写的,等把Netty玩遛了可以重构了。

    不知道大家看完了上面的介绍是不是已经知道Netty能用在什么场景了,下面我结合一个我之前做过的事情来进行详细的说明,当然只是使用场景的一方面而已。

    之前做抓取的时候,有一些小型的网站,页面结构比较复杂,还需要登录等操作,这种就不能用统一的抓取系统去抓取,只能通过写脚本的方式针对具体的网站做抓取,抓取必备的一个条件就是代理IP,为了方便抓取,特意封装了一个抓取的SDK,提供了抓取的方法,内置了切换代理。

    我们有一个代理池服务,通过一个网址去获取能使用的代理IP,在刚开始用的Http请求去获取代理IP,由于抓取量比较大,通过Http请求去获取代理IP效率不行,后面用Netty改造了获取IP这部分,通过Netty来获取数据,解决了实时获取的性能问题。

    通过长连接的方式,避免了Http请求每次都要建立连接带来的性能消耗问题,通过二进制的数据传输减少网络开销,性能更高。

    简单入门

    我们编写一个服务端和客户端,客户端往服务端发送一条消息,消息传输先用字符串进行传递,服务端收到客户端发送的消息,然后回复一条消息。

    首先编写服务端代码:

    1. public class ImServer {
    2. public void run(int port) {
    3. EventLoopGroup bossGroup = new NioEventLoopGroup();
    4. EventLoopGroup workerGroup = new NioEventLoopGroup();
    5. ServerBootstrap bootstrap = new ServerBootstrap();
    6. bootstrap.group(bossGroup, workerGroup)
    7. .channel(NioServerSocketChannel.class)
    8. .childHandler(new ChannelInitializer<SocketChannel>() {
    9. @Override
    10. public void initChannel(SocketChannel ch) throws Exception {
    11. ch.pipeline().addLast("decoder", new StringDecoder());
    12. ch.pipeline().addLast("encoder", new StringEncoder());
    13. ch.pipeline().addLast(new ServerStringHandler());
    14. }
    15. })
    16. .option(ChannelOption.SO_BACKLOG, 128)
    17. .childOption(ChannelOption.SO_KEEPALIVE, true);
    18. try {
    19. ChannelFuture f = bootstrap.bind(port).sync();
    20. f.channel().closeFuture().sync();
    21. } catch (InterruptedException e) {
    22. e.printStackTrace();
    23. } finally {
    24. workerGroup.shutdownGracefully();
    25. bossGroup.shutdownGracefully();
    26. }
    27. }
    28. }
    • 通过ServerBootstrap 进行服务的配置,和socket的参数可以通过ServerBootstrap进行设置。
    • 通过group方法关联了两个线程组,NioEventLoopGroup是用来处理I/O操作的线程池,第一个称为“boss”,用来accept客户端连接,第二个称为“worker”,处理客户端数据的读写操作。当然你也可以只用一个NioEventLoopGroup同时来处理连接和读写,bootstrap.group()方法支持一个参数。
    • channel指定NIO方式
    • childHandler用来配置具体的数据处理方式 ,可以指定编解码器,处理数据的Handler
    • 绑定端口启动服务

    消息处理:

    1. /**
    2. * 消息处理
    3. * @author yinjihuan
    4. *
    5. */
    6. public class ServerStringHandler extends ChannelInboundHandlerAdapter {
    7. @Override
    8. public void channelRead(ChannelHandlerContext ctx, Object msg) {
    9. System.err.println("server:" + msg.toString());
    10. ctx.writeAndFlush(msg.toString() + "你好");
    11. }
    12. @Override
    13. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    14. cause.printStackTrace();
    15. ctx.close();
    16. }
    17. }

    启动服务,指定端口为2222:

    1. public static void main(String[] args) {
    2. int port = 2222;
    3. new Thread(() -> {
    4. new ImServer().run(port);
    5. }).start();
    6. }

    编写客户端连接逻辑:

    1. public class ImConnection {
    2. private Channel channel;
    3. public Channel connect(String host, int port) {
    4. doConnect(host, port);
    5. return this.channel;
    6. }
    7. private void doConnect(String host, int port) {
    8. EventLoopGroup workerGroup = new NioEventLoopGroup();
    9. try {
    10. Bootstrap b = new Bootstrap();
    11. b.group(workerGroup);
    12. b.channel(NioSocketChannel.class);
    13. b.option(ChannelOption.SO_KEEPALIVE, true);
    14. b.handler(new ChannelInitializer<SocketChannel>() {
    15. @Override
    16. public void initChannel(SocketChannel ch) throws Exception {
    17. ch.pipeline().addLast("decoder", new StringDecoder());
    18. ch.pipeline().addLast("encoder", new StringEncoder());
    19. ch.pipeline().addLast(new ClientStringHandler());
    20. }
    21. });
    22. ChannelFuture f = b.connect(host, port).sync();
    23. channel = f.channel();
    24. } catch(Exception e) {
    25. e.printStackTrace();
    26. }
    27. }
    28. }

    客户端消息处理:

    1. /**
    2. * 当编解码器为字符串时用来接收数据
    3. * @author yinjihuan
    4. *
    5. */
    6. public class ClientStringHandler extends ChannelInboundHandlerAdapter {
    7. @Override
    8. public void channelRead(ChannelHandlerContext ctx, Object msg) {
    9. System.out.println("client:" + msg.toString());
    10. }
    11. @Override
    12. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    13. cause.printStackTrace();
    14. ctx.close();
    15. }
    16. }

    客户端启动入口,然后发送消息给服务端:

    1. public static void main(String[] args) {
    2. String host = "127.0.0.1";
    3. int port = 2222;
    4. Channel channel = new ImConnection().connect(host, port);
    5. channel.writeAndFlush("yinjihuan");
    6. }

    测试步骤如下:

    1. 首先启动服务端
    2. 启动客户端,发送消息
    3. 服务端收到消息,控制台有输出server:yinjihuan
    4. 客户端收到服务端回复的消息,控制台有输出client:yinjihuan你好

    源码参考:https://github.com/yinjihuan/netty-im

    更多技术分享请关注微信公众号:猿天地

  • 相关阅读:
    [ APIO 2015 ] 雅加达的摩天楼
    「POI2011 R1」Conspiracy
    「COCI2016/2017 Contest #2」Bruza
    「THUWC 2017」随机二分图
    「HAOI2015」按位或
    Topcoder Srm 726 Div1 Hard
    「LOJ6482」LJJ爱数数
    「2017 山东一轮集训 Day4」基因
    「Codechef April Lunchtime 2015」Palindromeness
    「UOJ207」共价大爷游长沙
  • 原文地址:https://www.cnblogs.com/yaowen/p/9562793.html
Copyright © 2011-2022 走看看