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

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

  • 相关阅读:
    使用 asp.net mvc和 jQuery UI 控件包
    ServiceStack.Redis 使用教程
    HTC T8878刷机手册
    Entity Framework CodeFirst 文章汇集
    2011年Mono发展历程
    日志管理实用程序LogExpert
    使用 NuGet 管理项目库
    WCF 4.0路由服务Routing Service
    精进不休 .NET 4.0 (1) asp.net 4.0 新特性之web.config的改进, ViewStateMode, ClientIDMode, EnablePersistedSelection, 控件的其它一些改进
    精进不休 .NET 4.0 (7) ADO.NET Entity Framework 4.0 新特性
  • 原文地址:https://www.cnblogs.com/yaowen/p/9562793.html
Copyright © 2011-2022 走看看