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. mybatis缓存之一级缓存(一)

    对于mybatis框架.仿佛工作中一直是在copy着使用.对于mybatis缓存.并没有一个准确的认知.趁着假期.学习下mybatis的缓存.这篇主要学习mybatis的一级缓存. 为什么使用缓存 其 ...

  2. coderfoces#414 div.2

    第一次打cf 感觉很奇妙 开始看到题目感觉极其怪异 然后忽然发现第一题一堆数中的因数出现最多的不是2么 然后过了5分钟就被一个专门攻击的人hack掉了 不得不说题并不难甚至很水(都是几行的入门题) 但 ...

  3. LQR要点

    新的“A”变成着了这样:Ac = A - KB 基于对象:状态空间形式的系统 能量函数J:也称之为目标函数 Q:半正定矩阵,对角阵(允许对角元素出现0) R:正定矩阵,QR其实就是权重 下面这段话可能 ...

  4. [javascript]js原型链以及原型链继承

    基础的三个要素: 函数 ,函数实例,实例原型. 实例原型相当于 父类, 函数相当于构造函数 举例: class Fn extends Fn.prototype{ } 实例: let f = new F ...

  5. SerializableClob转String

    ORACLE数据库读取CLOB字段,JAVA中获取到类型为SerializableClob,如何转换成String类型那? 以下是代码示例:         SerializableClob sc = ...

  6. Linux下nginx反向代理服务器安装与配置实操

    1.我们只要实现访问nginx服务器能跳转到不同的服务器即可,我本地测试是这样的, 在nginx服务器里面搭建了2个tomcat,2个tomcat端口分别是8080和8081,当我输入我nginx服务 ...

  7. mybatis源码配置文件解析之五:解析mappers标签流程图

    前面几篇博客分析了mybatis解析mappers标签的过程,主要分为解析package和mapper子标签.补充一张解析的总体过程流程图,画的不好,多多谅解,感谢.

  8. (八十九)c#Winform自定义控件-自定义滚动条(treeview、panel、datagridview、listbox、listview、textbox)

    官网 http://www.hzhcontrols.com/ 前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kw ...

  9. 解决wpf项目中无法添加OpenFileDialog 实例的问题

    直接添加引用:using Microsoft.Win32; 或者放置鼠标于OpenFileDialog OpenFileDialog ofd = new OpenFileDialog(); 操作点击

  10. Volatile关键字的解读

    原子性 定义: 在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行. Java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围 ...