Netty搭建简易聊天室

NIO框架Netty搭建简易聊天室

Netty Reactor模型

单线程模型
用户发起 IO 请求到 Reactor 线程
Ractor 线程将用户的 IO 请求放入到通道,然后再进行后续处理
处理完成后,Reactor 线程重新获得控制权,继续其他客户端的处理
单线程模型图
缺点:
这种模型一个时间点只有一个任务在执行,这个任务执行完了,再去执行下一个任务。

  1. 但单线程的 Reactor 模型每一个用户事件都在一个线程中执行:
  2. 性能有极限,不能处理成百上千的事件
  3. 当负荷达到一定程度时,性能将会下降
  4. 某一个事件处理器发生故障,不能继续处理其他事件

多线程模型
Reactor 多线程模型是由一组 NIO 线程来处理 IO 操作(之前是单个线程),所以在请求处理上会比上一中模型效率更高,可以处理更多的客户端请求。
这种模式使用多个线程执行多个任务,任务可以同时执行
多线程模型图
缺点:
但是如果并发仍然很大,Reactor 仍然无法处理大量的客户端请求

主从多线程模型
这种线程模型是 Netty 推荐使用的线程模型
这种模型适用于高并发场景,一组线程池接收请求,一组线程池处理 IO主从多线程模型

Netty聊天室案例搭建

后端部分

1.创建Maven项目引入netty依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.paic</groupId>
    <artifactId>netty_chat</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.25.Final</version>
        </dependency>
    </dependencies>

</project>

2.创建netty服务器

package com.paic.netty_chat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @ProjectName: netty_chat
 * @Package: com.paic.netty_chat
 * @ClassName: WebsocketNettyServer
 * @Author: Administrator
 * @Description: ${description}
 * @Date: 2019/5/7 21:59
 * @Version: 1.0
 */
public class WebsocketNettyServer {
    public static void main(String[] args) {
        //创建两个线程池
        NioEventLoopGroup mainGrp = new NioEventLoopGroup();//主线程池
        NioEventLoopGroup subGrp = new NioEventLoopGroup();//从线程池

        try {
            //创建Netty服务器启动对象
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            //初始化服务器启动对象
            serverBootstrap
                    //指定使用上面创建的两个线程池
                    .group(mainGrp,subGrp)
                    //指定Netty通道类型
                    .channel(NioServerSocketChannel.class)
                    //指定通道初始化器用来加载当Channel收到消息事件后,如何进行业务处理
                    .childHandler(new WebsocketChannelInitializer());
            //绑定服务器端口,启动服务器
            ChannelFuture future = serverBootstrap.bind(9090).sync();
            //等待服务器关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //关闭服务器
            mainGrp.shutdownGracefully();
            subGrp.shutdownGracefully();
        }
    }
}

3.创建通道初始化器

package com.paic.netty_chat;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * @ProjectName: netty_chat
 * @Package: com.paic.netty_chat
 * @ClassName: WebsocketChannelInitializer
 * @Author: Administrator
 * @Description: ${description}
 * @Date: 2019/5/7 22:07
 * @Version: 1.0
 */

/**
 * 通道初始化器
 * 用来加载通道处理器
 */
public class WebsocketChannelInitializer extends ChannelInitializer<SocketChannel> {

    protected void initChannel(SocketChannel ch) throws Exception {
       //获取管道,将一个个的ChannelHandler添加到管道中
        ChannelPipeline pipeline = ch.pipeline();
        //添加一个http的编解码器
        pipeline.addLast(new HttpServerCodec());
        //添加一个用于支持大数据流的支持
        pipeline.addLast(new ChunkedWriteHandler());
        //添加一个聚合器,这个聚合器主要讲HttpMessage聚合成FullHttpRequest/Response
        pipeline.addLast(new HttpObjectAggregator(1024*64));

        //需要指定接收请求的路由
        // 必须使用以ws后缀结尾的url才能访问
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));

        //添加自定义的Handler
        pipeline.addLast(new ChatHandler());
    }
}

4.实现自定义Handler

package com.paic.netty_chat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
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.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @ProjectName: netty_chat
* @Package: com.paic.netty_chat
* @ClassName: ChatHandler
* @Author: Administrator
* @Description: ${description}
* @Date: 2019/5/7 22:26
* @Version: 1.0
*/
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

   //用来保存所有客户端连接
   private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
   private SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd hh:MM");
   //当channel中有新的消息会自动调用
   protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
       //当接收到数据后会自动调用

       //获取客户端发送过来的文本消息
       String text = msg.text();
       System.out.println("接收到消息数据为:"+text);

       for(Channel client:clients){
           client.writeAndFlush(new TextWebSocketFrame(sdf.format(new Date())+":"+text));
       }
   }

   //当有新的客户端连接服务器之后,会自动调用这个方法
   @Override
   public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
       clients.add(ctx.channel());
   }
}
前端部分
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>在线聊天室</title>
</head>
<body>
   <input type="text" id ="message">
   <input type="button" value ="发送消息" onclick="sendMsg()">
   接收到的消息
   <p id="server_message" style="background-color: #AAAAAA"></p>
   <script>
       var websocket =null;
       //判断当前浏览器是否支持websocket
       if(window.WebSocket){
           websocket = new WebSocket("ws://127.0.0.1:9090/ws");
           websocket.onopen = function () {
               console.log("建立连接");
           }
           websocket.onclose = function () {
               console.log("断开连接");
           }
           websocket.onmessage = function (e) {
               console.log("接收到服务器消息:"+e.data);
               var server_message = document.getElementById("server_message");
               server_message.innerHTML +=e.data+"<br/>";
           }
       } else {
           alert("当前浏览器不支持webSocket");
       }
       function sendMsg(){
           var message = document.getElementById("message");
           websocket.send(message.value);
       }
   </script>
</body>
</html>
项目目录

项目目录

运行效果

图1
图二
图三

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值