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. ES6 promise用法总结

    一   什么时候promise? promise是异步编程的一个解决方案,是一个构造函数,身上带着all,resolve,reject,原型上有cath,then等方法 promise有两个特点: 1 ...

  2. Package Control:There are no packages available for installation

    百度推荐的sublime3,里面好多全家桶,注意安装. 我的问题报错是:Package Control:There are no packages available for installation ...

  3. .Net Core 集成ExceptionLess分布式日志框架之本地化部署

    前言 公司目前使用的项目中关于日志记录这块,之前一直都是使用的Log4net 存放于后台文件中的,对于异常错误啊,或者需要查看一些详情错误的时候感觉很不方便,要到服务器上去打开日志文件检索错误,降低了 ...

  4. python读取文件路径

    不同系统对文件路径的分割符不同: 在Windows系统下的分隔符是:\ (反斜杠). 在Linux系统下的分隔符是:/(斜杠). 绝对路径和相对路径 绝对路径就是文件的真正存在的路径,是指从硬盘的根目 ...

  5. 【科普】Scrum——从橄榄球争球到敏捷开发

    对敏捷开发Scrum稍有了解的都知道Scrum来源于橄榄球,但你知道为何要以这项球类运动的术语来命名这个敏捷开发方法论吗? Scrum与橄榄球对应关系 Scrum 一词源于英式橄榄球运动,是指双方球员 ...

  6. Spring Security(三) —— 核心配置解读

    摘要: 原创出处 https://www.cnkirito.moe/spring-security-3/ 「老徐」欢迎转载,保留摘要,谢谢! 3 核心配置解读 上一篇文章<Spring Secu ...

  7. docker 容器中 apt-get install 软件时,提示无法定位软件包

    [解决] 执行 apt-get update 然后再进行安装,即可. (完)

  8. css制作简单loading动画效果【css3 loading加载动画】

    曾经以为,loading的制作需要一些比较高深的web动画技术,后来发现大多数loading都可以用“障眼法”做出来.比如一个旋转的圆圈,并不都是将gif图放进去,有些就是画个静止图像,然后让它旋转就 ...

  9. Python——查看目录下所有的目录和文件

    写程序我们经常会遇到需要遍历某一个目录下的所有文件这个操作,然而python有现成的库,只需要2个循环就可以搞定. import os def all_path(dirname): result = ...

  10. 我终于弄懂了Python的装饰器(四)

    此系列文档: 1. 我终于弄懂了Python的装饰器(一) 2. 我终于弄懂了Python的装饰器(二) 3. 我终于弄懂了Python的装饰器(三) 4. 我终于弄懂了Python的装饰器(四) 四 ...