Netty框架介绍及实战

Netty框架模型
NIO 的类库和API繁杂,使用麻烦:需要熟练掌握Selector、ServerSocket、ChannelSocketChannel、 ByteBuffer等。开发工作量和难度都非常大: 例如客户端面临断连重连、 网络闪断、心跳处理、半包读写、 网络拥塞和异常流的处理等等。
Netty 对 JDK 自带的 NIO 的 API 进行了良好的封装,解决了上述问题。且Netty拥有高性能、 吞吐量更高,延迟更低,减少资源消耗,最小化不必要的内存复制等优点。
Netty 现在都在用的是4.x,5.x版本已经废弃,Netty 4.x 需要JDK 6以上版本支持。
Netty的使用场景:
1)互联网行业:在分布式系统中,各个节点之间需要远程服务调用,高性能的 RPC 框架必不可少,Netty 作为异步高性能的通信框架,往往作为基础通信组件被这些 RPC 框架使用。典型的应用有:阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信,Dubbo 协议默认使用Netty 作为基础通信组件,用于实现。各进程节点之间的内部通信。Rocketmq底层也是用的Netty作为基础通信组件。
2)游戏行业:无论是手游服务端还是大型的网络游戏,Java 语言得到了越来越广泛的应用。Netty作为高性能的基础通信组件,它本身提供了 TCP/UDP 和 HTTP 协议栈。
3)大数据领域:经典的 Hadoop 的高性能通信和序列化组件 Avro 的 RPC 框架,默认采用 Netty进行跨界点通信,它的 Netty Service 基于 Netty 框架二次封装实现。

Netty线程模型

Netty线程模型

 

模型解释

Netty抽象2个线程池,Boss Group和Work Group。Boss Group用于处理Accept事件,Work Group 用于处理 Read 和Write事件。
Boss Group和Work Group都属于EventLoopGroup。
NioEventLoopGroup 相当于一个事件循环线程组, 这个组中含有多个事件循环线程 , 每一个事件循环线程是NioEventLoop 。
每一个NioEventLoop都会有一个Selector用于监听绑定的Chanel
Boss NioEventLoop的处理流程如下:
a. 进行accpet操作,返回SocketChanel
b.将该Socketchanel注册到某个work NioEventLoop的seletor上
c.处理任务队列的任务 , 即runAllTasks
Work NioEventLoop的处理流程如下:
a. 轮询所有注册到该Loop Seletor上的Socketchanel的Read/Write事件
b.处理Read/Write事件
c.runAllTasks处理任务队列TaskQueue的任务 ,一些耗时的业务处理一般可以放入
Netty 实战
写一个聊天室程序,服务端具备上线检测,群发聊天内容等。Netty极大的简化了NIO编程,开发者更多的是编写ChannelHandle逻辑做业务处理
服务端代码 Server

 package com.qinghaihu.chat;

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;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
* @ClassName ChatServer
* @Description TODO
* @Author:Zhang Lianzhong
* @Date 2020/11/22 10:13 下午
* @Version 1.0
**/
public class ChatServer {

   public void openServer(int port){
       ServerBootstrap bootstrap = new ServerBootstrap();
       EventLoopGroup boss = new NioEventLoopGroup(1);  //create boss group, threadpool size is 1
       EventLoopGroup work = new NioEventLoopGroup(5); //create work group, threadpool size is 5
       bootstrap.group(boss,work);   //组合netty组件
       //配置handle组件
       bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

           @Override
           protected void initChannel(SocketChannel ch) throws Exception {
               ch.pipeline().addLast("encoder",new StringEncoder());
               ch.pipeline().addLast("decoder",new StringDecoder());
               ch.pipeline().addLast(new ServerChanelHandle());
           }
       });
       bootstrap.channel(NioServerSocketChannel.class);

       try{
           ChannelFuture channel = bootstrap.bind(port).sync();
           System.out.println(("服务端已启动,绑定端口:" + port));
           channel.channel().closeFuture().sync();

       } catch (InterruptedException e) {
           e.printStackTrace();
       }finally {
           boss.shutdownGracefully();
           work.shutdownGracefully();
       }


   }

   public static void main(String[] args){
      ChatServer server = new ChatServer();
      server.openServer(8090);
   }

}


服务端ChannelHandle

package com.qinghaihu.chat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * @ClassName ServerChanelHandle
 * @Description TODO
 * @Author:Zhang Lianzhong
 * @Date 2020/11/22 10:21 下午
 * @Version 1.0
 **/
public class ServerChanelHandle extends SimpleChannelInboundHandler {

    //必须定义为类成员变量。每个客户端连接时,都会new ChatServerHandler。static保证数据共享
    public static ChannelGroup cg = new  DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel ch = ctx.channel();
        for(Channel chanel: cg){
            chanel.writeAndFlush(ch.remoteAddress()+"进来啦!");
        }
        cg.add(ch);
    }

    /**
     * 上线处理
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        for(Channel ch: cg){
          ch.writeAndFlush(channel.remoteAddress()+"上线啦");
        }
    }


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        Channel channel = ctx.channel();
        for(Channel ch: cg){
            if(channel ==ch){
                ch.writeAndFlush("我说"+msg);
            }else {
                ch.writeAndFlush(channel.remoteAddress()+"说:"+msg);
            }
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        cg.remove(channel);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        String customerAddress = channel.remoteAddress().toString();
        for(Channel ch:cg){
            ch.writeAndFlush("客户端" + customerAddress + "下线了!");
        }
    }

}


客户端Client

package com.qinghaihu.chat;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
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;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.Scanner;

/**
 * @ClassName ChatClient
 * @Description TODO
 * @Author:Zhang Lianzhong
 * @Date 2020/11/22 10:54 下午
 * @Version 1.0
 **/
public class ChatClient implements Runnable{


    private String serverIP;
    private int port;
    public ChatClient(String serverIp,int port ){
        this.serverIP = serverIp;
        this.port = port;
    }

    @Override
    public void run() {

        Bootstrap bootstrap = new Bootstrap();
        EventLoopGroup work = new NioEventLoopGroup(1);
        bootstrap.group(work);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {

            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
               ch.pipeline().addLast("encode",new StringEncoder());
               ch.pipeline().addLast("decode",new StringDecoder());
               ch.pipeline().addLast(new ClientChanelHandle());
            }
        });
        bootstrap.channel(NioSocketChannel.class);

        ChannelFuture channelFuture = bootstrap.connect(serverIP,port);
        Channel channel = channelFuture.channel();

        Scanner scanner = new Scanner(System.in);
        while(scanner.hasNext()){
            String sendMsg = scanner.nextLine();
            channel.writeAndFlush(sendMsg);
        }
        work.shutdownGracefully();
    }

    public static void main(String[] args){
       new Thread(new ChatClient("127.0.0.1",8090)).start();
    }
}


客户端ChannelHandle

package com.qinghaihu.chat;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * @ClassName ClientChanelHandle
 * @Description TODO
 * @Author:Zhang Lianzhong
 * @Date 2020/11/22 11:08 下午
 * @Version 1.0
 **/
public class ClientChanelHandle extends SimpleChannelInboundHandler {


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(msg);
    }
}


运行效果

服务端运行情况

 ClientA

 

————————————————
版权声明:本文为CSDN博主「lianzhongzhang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lianzhongzhang/article/details/109961148

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值