`

Netty websocket 推送数据压缩以 js解压

阅读更多


       在项目开发的时候利用基于Netty 的websocket项目,有时会发现在推送过程中经常不推送了。经过研究调查发现服务器在高并发的情况下,推送的数据流量接近带宽流量峰值,会导致带宽不足无法继续推送新的数据。

      为了解决这个问题:方法1:加大带宽。(花费多点钱买带宽流量)

                                     方法2:压缩数据。(减少网络传输带宽流量)

方法1没什么好说的,给钱就可以了。

主要讲讲方法2:压缩传输数据。(我网上我搜了好久没有比较完整的处理方法和代码,特记录一下处理过程给有需要的人参考)

首先:在websocket 服务端推送数据的时候,对要传输的数据进行压缩,这里我用GZIP进行压缩并用BASE64进行编码。代码如下:

 /**
     * 发送消息
     * @param set 频道通道集合
     * @param msg 发送消息内容
     */
    public static void sendBinaryMsg(CopyOnWriteArraySet<Channel> set,String msg,String channelName){
     
     if(set==null)return;
  try {
   if(set.size()==0)return;
   String frameType = "";
   msg = ZipUtil.gzip(msg);//压缩数据
   AttributeKey<String> key = AttributeKey.valueOf(channelName+"_binary");
   Iterator<Channel> it=set.iterator();
   while(it.hasNext()){
    Channel channel=it.next();
    if(channel.isActive()){
      if(channel.attr(key)!=null){
        frameType = channel.attr(key).get();
      }
     if(frameType != null && frameType.equals("false")){
      channel.writeAndFlush(new TextWebSocketFrame(msg));
     }else{
      channel.writeAndFlush(new BinaryWebSocketFrame(Unpooled.wrappedBuffer(msg.getBytes())));
     }
    }else{
     set.remove(channel);
    }
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
    }

 

此处用到 ZipUtil.java的gzip压缩:

 

 package com.world.common;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * 解压缩字符串工具类
 * @author zhanglinbo 20160827
 *
 */
public class ZipUtil {

 /**
  *
  * 功能:使用gzip进行压缩,然后再用Base64进行编码
  * @param 待压缩字符串
  * @return 返回压缩后字符串
  * @author zhanglinbo  20160827
  */
 @SuppressWarnings("restriction")
 public static String gzip(String primStr) {
  if (primStr == null || primStr.length() == 0) {
   return primStr;
  }

  ByteArrayOutputStream out = new ByteArrayOutputStream();
  
  GZIPOutputStream gzip = null;
  try {
   gzip = new GZIPOutputStream(out);
   gzip.write(primStr.getBytes());
   
  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   if (gzip != null) {
    try {
     gzip.close();
     
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }

  return  new sun.misc.BASE64Encoder().encode(out.toByteArray());
 }

 /**
  *
  * <p>
  * Description:使用gzip进行解压缩
  * 先对压缩数据进行BASE64解码。再进行Gzip解压
  * </p>
  *
  * @param compressedStr 压缩字符串
  * @return 返回解压字符串
  */
 @SuppressWarnings("restriction")
 public static String gunzip(String compressedStr) {
  if (compressedStr == null) {
   return null;
  }

  ByteArrayOutputStream out = new ByteArrayOutputStream();
  ByteArrayInputStream in = null;
  GZIPInputStream ginzip = null;
  byte[] compressed = null;
  String decompressed = null;
  try {
   compressed = new sun.misc.BASE64Decoder().decodeBuffer(compressedStr);
   in = new ByteArrayInputStream(compressed);
   ginzip = new GZIPInputStream(in);

   byte[] buffer = new byte[1024];
   int offset = -1;
   while ((offset = ginzip.read(buffer)) != -1) {
    out.write(buffer, 0, offset);
   }
   decompressed = out.toString();
  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   if (ginzip != null) {
    try {
     ginzip.close();
    } catch (IOException e) {
    }
   }
   if (in != null) {
    try {
     in.close();
    } catch (IOException e) {
    }
   }
   if (out != null) {
    try {
     out.close();
    } catch (IOException e) {
    }
   }
  }

  return decompressed;
 }

 /**
  * 使用zip进行压缩
  *
  * @param str
  *            压缩前的文本
  * @return 返回压缩后的文本
  */
 @SuppressWarnings("restriction")
 public static final String zip(String str) {
  if (str == null)
   return null;
  byte[] compressed;
  ByteArrayOutputStream out = null;
  ZipOutputStream zout = null;
  String compressedStr = null;
  try {
   out = new ByteArrayOutputStream();
   zout = new ZipOutputStream(out);
   zout.putNextEntry(new ZipEntry("0"));
   zout.write(str.getBytes());
   zout.closeEntry();
   compressed = out.toByteArray();
   compressedStr = new sun.misc.BASE64Encoder().encodeBuffer(compressed);
  } catch (IOException e) {
   compressed = null;
  } finally {
   if (zout != null) {
    try {
     zout.close();
    } catch (IOException e) {
    }
   }
   if (out != null) {
    try {
     out.close();
    } catch (IOException e) {
    }
   }
  }
  return compressedStr;
 }

 /**
  * 使用zip进行解压缩
  *
  * @param compressed
  *            压缩后的文本
  * @return 解压后的字符串
  */
 @SuppressWarnings("restriction")
 public static final String unzip(String compressedStr) {
  if (compressedStr == null) {
   return null;
  }

  ByteArrayOutputStream out = null;
  ByteArrayInputStream in = null;
  ZipInputStream zin = null;
  String decompressed = null;
  try {
   byte[] compressed = new sun.misc.BASE64Decoder().decodeBuffer(compressedStr);
   out = new ByteArrayOutputStream();
   in = new ByteArrayInputStream(compressed);
   zin = new ZipInputStream(in);
   zin.getNextEntry();
   byte[] buffer = new byte[1024];
   int offset = -1;
   while ((offset = zin.read(buffer)) != -1) {
    out.write(buffer, 0, offset);
   }
   decompressed = out.toString();
  } catch (IOException e) {
   decompressed = null;
  } finally {
   if (zin != null) {
    try {
     zin.close();
    } catch (IOException e) {
    }
   }
   if (in != null) {
    try {
     in.close();
    } catch (IOException e) {
    }
   }
   if (out != null) {
    try {
     out.close();
    } catch (IOException e) {
    }
   }
  }
  return decompressed;
 }
 
}

 

--------------------------以上是netty websocket 服务端发送数据前的数据压缩----------------------------------

在浏览器端,利用js对压缩的数据进行解压缩。这里用到了pako项目的高效js解压缩组件。项目地址为:https://github.com/nodeca/pako 实测解压速度是毫秒级。

以下是测试例子,对压缩编码过的数据进行解压,打印日志,看是否正常

<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/pako/1.0.3/pako.min.js"></script>
<script>
'use strict';

var pako = window.pako;

// In browser

// Get some base64 encoded binary data from the server. Imagine we got this:
var b64Data     = 'H4sIAAAAAAAAAJWSwW6DMAyGX4WbpQpFcRJM4Lqde+l6mCoOjOZQbcAE6QFVffcFWBq6VkjL4ZftfPplO7nA0fQQ5RFAFEdw6nfnakxtdzbRWDmWtpyAC+x3ry/bd8hhs4EYqraxXVnZfXOyvjbCkB8OqFKUkgRxd2LkDPGJCsaLeGKVJtKrLGdcBDhVHn4QzgKX4noDjl+4plL9g87+Dkd3YcIE6QBL4WF3J5hIlqFUzln9wgnyAHN8ovK2NiJUtMoue9YktVrQkvul+ViwLKNAZ7P3fDdrdhPF0Bu76dTccnbPTILuYZlM9ALWD753ynSAJapVOOHj8qgoYqjL7tPYbVkb/x3rtjHD2/A9FqpmcJV+qD/aL5caW8H1+gMYWAQw/gIAAA==';

// Decode base64 (convert ascii to binary)
var strData     = atob(b64Data);

// Convert binary string to character-number array
var charData    = strData.split('').map(function(x){return x.charCodeAt(0);});

// Turn number array into byte-array
var binData     = new Uint8Array(charData);

// Pako magic
var data        = pako.inflate(binData);

// Convert gunzipped byteArray back to ascii string:
var strData     = String.fromCharCode.apply(null, new Uint16Array(data));

// Output to console
console.log(strData);

</script>
</html>
<body>
Sending objects to server, run server code to see result.
</body>


 在项目中实际应用:

在监听websocket 消息方法中进行对数据解压:此处还判断了服务端返回的是二进制数据还是文本数据。

通过  ungzip 方法进行解压,并在回调函数中进行业务逻辑的后续处理。之所以要用回调函数,因为在等解压完成后,才能进行后续的操作,否则返回的数据可能是空的。

 //websocket 接收消息处理

    $this.socket.onmessage = function(result) {

    var json = null;

    if (result.data instanceof Blob) {    

        var blob = result.data;            

        //js中的blob没有没有直接读出其数据的方法,通过FileReader来读取相关数据      

        var reader = new FileReader();            

        reader.readAsText(blob);          

        //  当读取操作成功完成时调用.        

        reader.onload = function(evt){       

        if(evt.target.readyState == FileReader.DONE){ 

        var beford = lenght = evt.target.result.length;

        ungzip(evt.target.result,function(result){

                                                console.log("解压前:"+beford+" 解压后:"+result.length);

             

                                                if(result.indexOf("(")!=0){

          json = eval("("+result+")");

          }else{

          json = eval(result);

          }

         

          //处理业务逻辑

          $this.dealMessage(json);

        });

        

        }

        }

  }else{

   ungzip(result.data,function(result){

//console.log("解压后:"+result);

      if(result.indexOf("(")!=0){

      json = eval("("+result+")");

      }else{

      json = eval(result);

      }

     //处理业务逻辑

     $this.dealMessage(json);

  });

 

      }

        };

 

 js 解压方法: 这里使用了seajs 的模块化处理引入pako.如不用seajs,直接在页面代码引入pako.min.js即可。

 

 /**

 * 解压缩字符串

 * @param zipData 经过 gzip压缩和base64编码的字符串

 * @param callback 回调函数 用解压缩后的数据进行处理后续操作

 * @author zhanglinbo 20160827

 */

function ungzip(zipData,callback){

var unZipData = "";//解压缩后数据

//引入pako模块进行数据的解压缩

seajs.use(["module_pako"],function(pako){

try{

// Decode base64 (convert ascii to binary)

var strData     = atob(zipData);

// Convert binary string to character-number array

var charData    = strData.split('').map(function(x){return x.charCodeAt(0);});

// Turn number array into byte-array

var binData     = new Uint8Array(charData);

// Pako magic

var data        = pako.inflate(binData);

// Convert gunzipped byteArray back to ascii string:

unZipData     = String.fromCharCode.apply(null, new Uint16Array(data));

}catch(e){

unZipData = zipData;//解压出现异常,说明数据未压缩。用原有数据进行操作

}

//利用回调进行处理后续操作

if($.isFunction(callback)){

callback(unZipData);

}

});

}

 

通过对数据的压缩传输和页面的解压,可以极大的剩下带宽。实测在数据长度大于2000以上的情况下压缩比例可以达到60-70%。对小数据字符串压缩比例不明显,有时反而比未压缩前大一点点。。不过影响不大。

 

 

  • 大小: 8.3 KB
分享到:
评论

相关推荐

    netty websocket通讯接收数据不完整问题

    这是一个java web项目集成了netty websocket的完整代码。java web项目作为服务器端和客户端进行数据通信。但是常常存在提示Max frame length of 65536 has been exceeded问题。初始化握手对象时指定了...

    Spring Boot 整合 Netty + WebSocket 实时消息推送

    综上所述,Spring Boot与Netty的结合使用,能够构建出强大的实时消息推送系统,满足现代Web应用中对于数据实时同步的需求。通过理解并熟练掌握上述知识点,开发者可以创建出高效、可靠的通信平台。

    spring boot + netty websocket

    这使得实时推送、游戏、协作编辑等应用成为可能。Spring Boot提供了一种方便的方式来集成WebSocket,但其底层实现可能不够高效,这时Netty就派上用场了。 Netty是一个高性能、异步事件驱动的网络应用框架,适用于...

    Spring+Netty+WebSocket实例

    3. **Netty服务器**:创建一个Netty服务器,配置WebSocket服务器通道处理器,处理WebSocket的连接请求和数据帧。 4. **Spring与Netty的集成**:通过Spring的`WebSocketMessageBrokerConfigurer`接口,将Spring的...

    netty+websocket实现心跳和断线重连

    在本文中,我们将深入探讨如何利用 Netty 和 WebSocket 实现心跳检测和断线重连机制。 首先,我们需要理解 WebSocket 协议。WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它为客户端和服务器提供了低...

    springboot+netty+websocket+redis

    WebSocket是一种在单个TCP连接上进行全双工通信的协议,允许服务器主动推送数据给客户端,无需频繁的轮询。在Spring Boot中,我们可以利用WebSocket实现即时通讯,用户之间的消息可以实时传递,提高用户体验。Stomp...

    基于netty搭建websocket实现消息主动推送【Springbooot项目,可直接使用】

    总的来说,这个项目结合了WebSocket、Netty和SpringBoot的优势,提供了一个高效、便捷的消息推送解决方案。开发者可以在此基础上根据实际需求进行定制和扩展,例如增加身份验证、权限控制、消息加密等功能,以满足...

    Netty WebSocket服务端

    WebSocket是一种在客户端和服务器之间建立长连接的协议,它提供了双向通信的能力,使得服务器可以主动向客户端推送数据。Netty是Java领域一个高性能、异步事件驱动的网络应用框架,常用于开发高并发、低延迟的网络...

    基于netty的websocket

    3. **Netty中的WebSocket**: Netty提供了WebSocketServer和WebSocketClient两个处理器,它们实现了WebSocket协议的握手和数据帧处理。开发者可以通过继承这两个处理器,自定义自己的业务逻辑。 4. **WebSocket握手*...

    netty-websocket-example 基于netty的websocket实现示例

    通过 JMX,我们可以对 Netty 服务器的性能进行监控,例如并发连接数、处理速率等,以确保在高并发情况下系统仍能稳定运行。 5. **配置与启动**:Netty 服务器和客户端通常需要配置线程池、缓冲区大小、心跳机制等。...

    使用Netty搭建WebSocket服务器,可修改单包大小限制

    WebSocket是一种在客户端和服务器之间建立持久连接的协议,它提供了双向通信能力,使得服务器和客户端可以实时、高效地交换数据。在开发WebSocket服务时,我们可能会遇到数据包大小的限制问题,这会影响到大文件或者...

    基于Netty的Java WebSocket集群框架。.zip

    WebSocket是一种在客户端和服务器之间建立长连接的协议,它允许双向通信,即服务器可以主动向客户端推送数据。在Web应用中,WebSocket极大地提高了实时性,广泛应用于在线聊天、股票更新、游戏、远程控制等场景。而...

    netty-websocket.zip

    而WebSocket是一种在单个TCP连接上进行全双工通信的协议,常用于实现服务器主动向客户端推送数据的需求。 描述中提到了几个关键点: 1. **服务端主动推送消息**:在传统的HTTP协议中,服务器通常只能响应客户端的...

    netty+websocket聊天室例子(可私聊)

    在聊天室应用中,WebSocket尤其适合,因为它能实现消息的即时推送,无需频繁的轮询请求。 **Netty与WebSocket结合** 在Netty中,WebSocket的处理可以通过`WebSocketServerProtocolHandler`来实现,这个处理器负责...

    基于Netty5.0的高级案例NettyWebsocket

    "基于Netty5.0的高级案例NettyWebsocket" 这个标题揭示了我们将会探讨一个使用Netty 5.0框架实现的WebSocket服务器的高级应用场景。Netty是一个高性能、异步事件驱动的网络应用程序框架,主要用于快速开发可维护的高...

    netty websocket源码

    WebSocket是Web交互的一种高效协议,它允许服务器与客户端进行全双工通信,即双方可以同时发送数据,极大地提升了实时性。在Java世界中,Netty框架因其高效的性能和易用性,成为了实现WebSocket服务器的首选工具。本...

    netty之websocket协议开发

    支持WebSocket的浏览器通过webSocket协议发送请求给我们编写的webSocket服务器,服务器对请求消息进行判断,如果是合法的webSocket请求,则获取请求消息体,并在后面追加字符串:“欢迎使用Netty WebSocket 服务,...

    简易版netty websocket通讯demo 聊天

    在本示例中,"简易版netty websocket通讯demo 聊天" 提供了一个基础的 WebSocket 协议通信的实现,用于构建聊天应用。WebSocket 是一种在客户端和服务器之间建立持久连接的协议,它允许双方进行全双工通信,即数据...

    netty实现websocket例子

    WebSocket 协议则是一种在单个TCP连接上进行全双工通信的协议,它允许服务器和客户端实时交换数据,常用于实现聊天、实时推送通知等场景。 在"Netty实现WebSocket例子"中,我们将探讨如何使用Netty来搭建WebSocket...

    WebSocket利用netty连接入门项目

    WebSocket是一种在客户端和服务器之间建立持久连接的协议,它允许双方进行全双工通信,即数据可以在任意方向流动,而无需反复发起请求。在Web开发中,WebSocket为实时交互提供了强大的支持,比如在线聊天、股票实时...

Global site tag (gtag.js) - Google Analytics