zoukankan      html  css  js  c++  java
  • 基于Netty实现高性能弹幕系统

    一、弹幕系统概要设计

    什么是弹幕系统?

    img

    弹幕系统特点:

    1. 实时性高:你发我收, 毫秒之差

    2. 并发量大:一人吐槽,万人观看

    弹幕系统架构设计:

    业务架构:

    img

    实现方案一:

    img

    实现方案二:

    img

    二、NettyHttp协议解析实现

    在上述方案中 浏览器不能直接能和Netty 建立连接 其必须借助http 请求 进行协议升级才能实现服务端与客户端基于Web Socket通信,其过程如下图:

    img

    也就是说如果我们想实现弹幕就必须先实现Http服务,那么Netty如何实现Http服务呢?

    Http协议交互过程

    协议交互本质是指协议两端(客户端、服务端)如何传输数据?如何交换数据?

    传输数据一般基于TCP/IP 实现,体现到开发语言上就是我们所熟悉的Socket 编程。

    交换数据本质是指,两端(客户端、服务端)能各自识别对方所发送的数据。那么这就需要制定一套报文编码格式,双方以该格式编码数据发送给对方。Http 对应的Request 与Response报文格式如下图:

    request 报文:

    img

    response 报文:

    img

    1 http报文解析方案:

    1:请求行的边界是CRLF(回车),如果读取到CRLF(回车),则意味着请求行的信息已经读取完成。

    2:Header的边界是CRLF,如果连续读取两个CRLF,则意味着header的信息读取完成。

    3:body的长度是有Content-Length 来进行确定。

    2 netty关于http 的解决方案:

    // 解析请求

    很多http server的实现都是基于servlet标准,但是netty对http实现并没有基于servlet。所以在使用上比Servlet复杂很多。比如在servlet 中直接可以通过 HttpServletRequest 获取 请求方法、请求头、请求参数。而netty 确需要通过如下对象自行解析获取。

    HttpMethod:主要是对method的封装,包含method序列化的操作

    HttpVersion: 对version的封装,netty包含1.0和1.1的版本

    QueryStringDecoder: 主要是对urI进行解析,解析path和url上面的参数。

    HttpPostRequestDecoder:对post 中body 内容进行解析获取 form 参数。

    HttpHeaders:包含对header的内容进行封装及操作

    HttpContent:是对body进行封装,本质上就是一个ByteBuf。如果ByteBuf的长度是固定的,则请求的body过大,可能包含多个HttpContent,其中最后一个为LastHttpContent(空的HttpContent),用来说明body的结束。

    HttpRequest:主要包含对Request Line和Header的组合

    FullHttpRequest: 主要包含对HttpRequest和httpContent的组合

    img

    3 请求头与请求体

    3.1 HttpRequest:主要包含对Request Line和Header的组合

    浏览器中输入http://127.0.0.1:8080/ 进行访问
    调试handler程序 一次请求分两次发送分别为请求头 和请求体

    • 第一次:请求头
      在这里插入图片描述
    • 第二次 :请求体
      在这里插入图片描述
      post请求携带参数的请求体
      在这里插入图片描述
          @Override
            protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
    			
                List<InterfaceHttpData> list = null;
                if (msg instanceof HttpContent) {
                	//注意: HttpPostRequestDecoder((HttpRequest) requestHead) 参数中msg需要传递第一次的请求头才可以 所以两次请求 第一次保持请求头(小心风险) 第二次解析请求体
                    HttpPostRequestDecoder decoder = new 
                    HttpPostRequestDecoder((Httequest) requestHead);
                    decoder.offer((HttpContent) msg);
                    list = decoder.getBodyHttpDatas();
                }
    

    3.2. FullHttpRequest: 主要包含对HttpRequest和httpContent的组合

    • 使用方法:
      代码片段
    //ChannelInitializer<NioSocketChannel>(){}中
      //添加对HttpRequest和httpContent的组合处理器
      ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
    
    • 这样一次就发一个包过来了
      在这里插入图片描述

    3.3. 如果传输包过大处理方法

    传输包过大不应该使用FullHttpRequest 应该采用HttpRequest进行接收

    • handler 代码片段
       protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
            DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
            //只有是最后一个包才会写回数据关闭流
            if(msg instanceof LastHttpContent){
                //写入请求头
                response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html ; charset=UTF-8");//写入请求体
                response.content().writeBytes("luban is a good man".getBytes());
                ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); 
            }
        }
    

    3.4 Netty Http的请求处理流程:

    img

    从图中可以看出做为服务端的Netty 就是在做 编码和解码操作。其分别通过以下两个ChannelHandler对象实现:

    HttpRequestDecoder :用于从byteBuf 获取数据并解析封装成HttpRequest 对象

    HttpResponseEncoder:用于将业务返回数据编码成 Response报文并发送到ByteBuf。

    将以上两个对象添加进 Netty 的 pipeline 即可实现最简单的http 服务。

    通过一个示例演示Http实现:

    编写serverBootstrap

    初始pipeline

    编写ServletHandler

    Http完整流程介绍:

    Decoder 流程:

    最少分为两次两次请求
    第一次: 请求头
    第二次:请求体
    注意:如果请求体过大可能被拆分成多个包 LastHttpContent是请求体最后一个包
    在这里插入图片描述

    encode 流程:

    img

    三、协议

    Http协议:半双工

    半双工:浏览器可以主动向服务器发送数据 但是服务器不可以主动向浏览器发送数据
    服务器也监听不到浏览器的状态(断开关闭等)
    文本协议:

    WS协议:全双工

    全双工 :webSocket 全连接 浏览器和服务器连接状态一直保持,可以使用心跳机制 浏览器或服务器的状态 在双方都可以进行判断监听
    二进制协议 :

    WebScoket协议实现

    webSocket 协议简介:

    webSocket 是html5 开始提供的一种浏览器与服务器间进行全双工二进制通信协议,其基于TCP双向全双工作进行消息传递,同一时刻即可以发又可以接收消息,相比Http的半双工协议性能有很大的提升,

    webSocket特点如下:

    1. 单一TCP长连接,采用全双工通信模式
    2. 对代理、防火墙透明

    和http共用端口号 ,不需要在防火墙中开放端口号 比如Tomcat Redis 需要在防火墙中开放端口(8080或6379)

    • 比如:
      RMI远程调用
      注册端口: 由我们程序指定8080等 ,需要去防火墙中开放 --> 防火墙透明
      通信商品:由程序随机生成如4251…等等 因为他是随机生成的我们不可能去防火墙中都打开 -->不透明的 无法部署到公网
    1. 无头部信息、消息更精简
    2. 通过ping/pong 来保活
    3. 服务器可以主动推送消息给客户端,不在需要客户轮询

    WebSocket 协议报文格式:

    我们知道,任何应用协议都有其特有的报文格式,比如Http协议通过 空格 换行组成其报文。如http 协议不同在于WebSocket属于二进制协议,通过规范进二进位(不是字节)来组成其报文。具体组成如下图:

    img

    Http协议报文:

    报文说明:

    FIN

    标识是否为此消息的最后一个数据包,占 1 bit

    RSV1, RSV2, RSV3: 用于扩展协议,一般为0,各占1bit

    Opcode

    数据包类型(frame type),占4bits

    0x0:标识一个中间数据包

    0x1:标识一个text类型数据包

    0x2:标识一个binary类型数据包

    0x3-7:保留

    0x8:标识一个断开连接类型数据包

    0x9:标识一个ping类型数据包

    0xA:表示一个pong类型数据包

    0xB-F:保留

    MASK:占1bits

    用于标识PayloadData是否经过掩码处理。如果是1,Masking-key域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。

    Payload length

    Payload data的长度,占7bits,7+16bits,7+64bits:

    如果其值在0-125,则是payload的真实长度。

    如果值是126,则后面2个字节形成的16bits无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。

    如果值是127,则后面8个字节形成的64bits无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。

    Payload data

    应用层数据

    WebSocket 在浏览当中的使用

    Http 连接与webSocket 连接建立示意图:

    img

    通过javaScript 中的API可以直接操作WebSocket 对象,其示例如下:

    var ws = new WebSocket(“ws://localhost:8080);
    ws.onopen = function()// 建立成功之后触发的事件 {
    console.log(“打开连接”); ws.send("ddd"); // 发送消息
    };
    
    ws.onmessage = function(evt) { // 接收服务器消息
    console.log(evt.data);
    };
    
    ws.onclose = function(evt) {
    console.log(“WebSocketClosed!); // 关闭连接
    };
    
    ws.onerror = function(evt) {
    console.log(“WebSocketError!); // 连接异常
    };
    

    弹幕系统实现讲解:

    Http 协议后台实现:

    webSocket 协议后台实现

    弹幕系统前台实现

    弹幕系统实时演示:

    #启动服务

    #查看当前端口连接数

    netstat -nat|grep -i “8880”|wc -l

    #查看指定进程线程数

    pstree -p 3000 | wc -l

  • 相关阅读:
    mac 通过SSH连接服务器aws和github
    Android开发 View与Activity的生命周期[转载]
    Android开发 APP闪退Fragment重叠泄露问题
    3月3日 一堆一堆事
    杭州.net俱乐部 新开qq群
    招聘 .net 开发工程师
    852009
    872009
    01背包和完全背包
    8142009
  • 原文地址:https://www.cnblogs.com/idcode/p/14551391.html
Copyright © 2011-2022 走看看