1 问题

  术语:压缩率,compression ratio,压缩后的大小/压缩前的大小,越小说明压缩效果越好。

  在使用netty的JdkZlibEncoder进行压缩时,发现了一个问题:它对于短文本(小于2K)的压缩效果很差,压缩率在80%-120%,文本越短,压缩效果越差,甚至可能比没压缩前更大。

  通过研究发现,使用字典可以改进压缩效果。以下详细介绍如何做。

2 提取字典

  我们要传输的文本类似于:

 <?xml version="1.0" encoding="utf-8" ?>
<Event attribute="TRANSIENT">
<outer id="" from="" to="" trunk="" callid=""/>
<ext id=""/>
</Event>

  提取字典的原则:将重复出现的字符串加入到字典。

  可以提取以下字典:

 String[] dictionary = {
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
"Event", "TRANSIENT", "attribute", "outer", "from", "trunk",
"callid", "id", "to", "ext"
};
 

3 测试用例

  使用EmbeddedChannel API来构建测试用例。EmbeddedChannel能够模拟入站和出站的数据流,对于测试ChannelHandler非常有用。

  JdkZlibEncoder的构造函数可以接受一个字典参数:

  下面是测试代码:

 public class GzipTest {

     private String xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<Event attribute=\"TRANSIENT\">" +
"<outer id=\"11\" from=\"1005\" to=\"915880056212\" trunk=\"83057387\" callid=\"24587\" />" +
"<ext id=\"1005\" />" +
"</Event>"; private String[] dictionary = {
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
"Event", "TRANSIENT", "attribute", "outer", "from", "trunk",
"callid", "id", "to", "ext"
}; /**
* 不使用字典压缩
*/
@Test
public void test1() {
EmbeddedChannel embeddedChannel = new EmbeddedChannel();
ChannelPipeline pipeline = embeddedChannel.pipeline();
//
pipeline.addLast("gzipDecoder", new JdkZlibDecoder());
pipeline.addLast("gzipEncoder", new JdkZlibEncoder(9));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
//
System.out.println("*******不使用字典压缩*******");
int compressBefore = xml.getBytes(StandardCharsets.UTF_8).length;
System.out.printf("压缩前大小:%d \n", compressBefore);
// 模拟输出
embeddedChannel.writeOutbound(xml);
ByteBuf outboundBuf = embeddedChannel.readOutbound();
int compressAfter = outboundBuf.readableBytes();
System.out.printf("压缩后大小:%d, 压缩率:%d%% \n", compressAfter,
compressAfter * 100 / compressBefore); } /**
* 使用字典压缩
*/
@Test
public void test2() {
EmbeddedChannel embeddedChannel = new EmbeddedChannel();
ChannelPipeline pipeline = embeddedChannel.pipeline();
// 字典
byte[] dictionaryBytes = String.join("", dictionary)
.getBytes(StandardCharsets.UTF_8);
//
pipeline.addLast("gzipDecoder", new JdkZlibDecoder(dictionaryBytes));
pipeline.addLast("gzipEncoder", new JdkZlibEncoder(9, dictionaryBytes));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
//
System.out.println("*******使用字典压缩*******");
int compressBefore = xml.getBytes(StandardCharsets.UTF_8).length;
System.out.printf("压缩前大小:%d \n", compressBefore);
// 模拟输出
embeddedChannel.writeOutbound(xml);
ByteBuf outboundBuf = embeddedChannel.readOutbound();
int compressAfter = outboundBuf.readableBytes();
System.out.printf("压缩后大小:%d, 压缩率:%d%% \n", compressAfter,
compressAfter * 100 / compressBefore);
} }

输出:

*******不使用字典压缩*******

压缩前大小:173

压缩后大小:150, 压缩率:86%

*******使用字典压缩*******

压缩前大小:173

压缩后大小:95, 压缩率:54%

  从输出可以看到,压缩率由86%提升至了54%。

4 进一步

  如果觉得手工提取字典效率太低,还可以试一下zstd。zstd是由facebook提供的一个压缩库,它提供了自动提取字典的工具。命令如下:

 zstd --train ./dictionary/* -o ./dict.bin

5 参考资料

zstd github

文本压缩算法的对比和选择

netty 使用字典提升短文本的压缩效果的更多相关文章

  1. redis底层数据结构--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表

    1.动态字符串 redis中使用c语言的字符床存储字面量,默认字符串存储采用自己构建的简单动态字符串SDS(symple dynamic string) redis包含字符串的键值对都是用SDS实现的 ...

  2. 【redis】redis底层数据结构原理--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表等

    redis有五种数据类型string.list.hash.set.zset(字符串.哈希.列表.集合.有序集合)并且自实现了简单动态字符串.双端链表.字典.压缩列表.整数集合.跳跃表等数据结构.red ...

  3. APK瘦身记,如何实现高达53%的压缩效果

    作者:非戈@阿里移动安全 1.我是怎么思考这件事情的 APK是Android系统安装包的文件格式,关于这个话题其实是一个老生常谈的题目,不论是公司内部,还是外部网络,前人前辈已经总结出很多方法和规律. ...

  4. APK瘦身记,怎样实现高达53%的压缩效果

    作者:非戈@阿里移动安全,很多其它技术干货.请訪问阿里聚安全博客 1.我是怎么思考这件事情的 APK是Android系统安装包的文件格式.关于这个话题事实上是一个老生常谈的题目.不论是公司内部.还是外 ...

  5. nginx_gzip压缩提升网站的传输速度

    gzip on; gzip_min_length 1k; gzip_buffers 16k; #gzip_http_version 1.0; gzip_comp_level ; gzip_types ...

  6. Netty 系列之 Netty 高性能之道

    1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用 Netty4 + Thrift 压缩二进制编解码技术,他们实现了 10 W TPS(1 K 的复杂 POJO 对象)的跨 ...

  7. Netty系列之Netty高性能之道

    转载自http://www.infoq.com/cn/articles/netty-high-performance 1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Ne ...

  8. Netty高性能之道

    1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用.相比于 ...

  9. 转:Netty系列之Netty高性能之道

    1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用 ...

随机推荐

  1. 浅谈dfs

    搜索(dfs) 搜索分为bfs与dfs 他们的算法思路都是相同的--穷举 可以说,搜索是万能的,但是复杂度往往是指数级的,往往是穷途末路才用的最后方案 dfs dfs核心操作:回溯+前进 想想你第一次 ...

  2. 新建maven项目总是需要重新选择maven的配置文件

    解决办法: other settings->settings for new projects... 找到maven设置自己的目录和配置

  3. Python3笔记004 - 2.1 python的语法特点

    第2章 python语言基础 python语法特点 保留字与标识符 变量 数据类型 运算符 输入和输出 2.1 python的语法特点 2.1.1 注释 注释的内容将被python解释器忽略,并不会在 ...

  4. 特殊方格棋盘【状压DP】

    特殊方格棋盘[状压DP] 讲真状压DP这个东西只不过是有那么亿丢丢考验心态罢了(确信) 先从板子题说起,另加一些基础知识 题目描述 在的方格棋盘上放置n 个车,某些格子不能放,求使它们不能互相攻击的方 ...

  5. 让 JavaScript 与 CSS 和 Sass 对话

    JavaScript 和 CSS 已经并存超过了 20 年.但是在它们之间共享数据非常困难.当然也有大量的尝试.但是我所想到的是一些简单而直观的内容——不涉及结构更改,而是使用 CSS 自定义属性甚至 ...

  6. 通过注入DLL后使用热补丁钩取API

    通过注入DLL后使用热补丁钩取API 0x00 对比修改API的前五个字节钩取API 对前一种方法钩取API的流程梳理如下: 注入相应的DLL 修改原始AI的函数的前五个字节跳往新函数(钩取API) ...

  7. SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(五): 数据表设计、使用 jwt、redis、sms 工具类完善注册登录逻辑

    (1) 相关博文地址: SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(一):搭建基本环境:https://www.cnblogs.com/l-y-h/p ...

  8. matlab 打包exe

    mcc -m gui_abc.m https://blog.csdn.net/hujiameihuxu/article/details/53525373 deploytool app compiler

  9. 数据可视化实例(九): 边缘箱形图(matplotlib,pandas)

    https://datawhalechina.github.io/pms50/#/chapter7/chapter7 边缘箱形图 (Marginal Boxplot) 边缘箱图与边缘直方图具有相似的用 ...

  10. HangFire多集群切换及DashBoard登录验证

    项目中是有多个集群的,现在存在一个是:在切换web集群时,如何切换HangFire的周期性任务. 先采取的解决办法是: 每个集群分一个队列,在周期性任务入队时分配当前web集群的集群id单做队列名称. ...