Java性能优化中如何进行压缩,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
在微服务调用,如果需要传入的内容过长,压缩是个不错的办法,能提高传输的速度。压缩有很多方法,一种方法是在传输对象的属性名字上做调整,尽量减少传输报文大小,这比较适合传输的是JSON或者XML,比如
public class OrderRequest{ private String orderId; private String userId; }
如果使用JSON传输,内容是
{"orderId":xxx,"userId":yyyyy}
可以调整为
public class OrderRequest{ private String oid; private String uid; }
使用JSON传输 {"oid":xxx,"uid":yyyyy},这种调整,显然传输报文大小体积小一点。也可以对传输对象的一些字段做合并,比如“订单状态“,"用户状态","测试订单"合并成一个int类型,通过“位“来区分状态
public class OrderRequest{ //用户状态 private int userStatus; //订单状态 private int orderStatus; //测试订单 private int testFlag }
改成如下,需要通过位运算获取订单的各个状态
public class OrderRequest { /** * 0位表示是否测试订单,1-4位节表示用户状态,5-8位表示订单状态 */ int s; public boolean isTest(){ //取出第1位的值 return (status&0b1)==1; } public int getUserStatus(){ // 右移1,取出1-4位的值 return (status>>1&0b1111); } public int getOrderStatus(){ //右移5,取出5-8位的值 return (status>>5&0b1111); } }
这样,OrderRequest本来需要3个int类型,总计12个字节来保存的订单状态,现在只需要4个字节保存即可。如果有更多的状态,也可以用s值的剩下的位来表示。比如,订单新增一个状态表示是否是包含大件,可以用第9位表示
public boolean isLargeProduct(){ return (status>>9&0b1)==1; }
如果s值是0b1_0100_0110_1(对应的10进制653),那么isTest返回true,getUerStatus返回6,getOrderStatus返回4,isLargeProduct返回true
还有一种压缩方法是在传输协议上进行压缩,比如JSON就比XML更加节省,使用MessagePack又比JSON更加节省空间,关于MessagePack用法,会在第5章用一节详细介绍。
在传输内容过多的时候,可以考虑对内容进行压缩,对内容进行压缩再传送有如下好处
压缩后减少网络传送的字节,节约了带宽,网络可以同时传送的内容更多了
相比于压缩耗时,网络传送更加耗时。尤其是现在的分布式系统,服务器非常便宜,可以无限扩展,从数十台服务器到数万台服务都可以,然而带宽有限且价格不菲,有些企业专网带宽只有1M,非常小。
压缩有各种算法,会输出不同的压缩比的内容,以及压缩耗时也不一样,本节选取zip,针对5K,20K,100k的做一个性能测试。一般来说,压缩比越大,越耗时。在实际分布式系统调用,需要根据业务需求,确定采用什么样的压缩算法。
压缩会使用JDK自带的zip包中的Deflater类进行压缩,提供了最快压缩BEST_SPEED(值是1),最大压缩比BEST_COMPRESSION(值是9),还有默认压缩DEFAULT_COMPRESSION(值是-1)
//ZipUtil.java public static byte[] zip(byte[] bs) throws IOException { return compress(bs,DEFAULT_COMPRESSION); } public static byte[] compress(byte[] input, int compressionLevel ) throws IOException { //zip压缩 Deflater compressor = new Deflater(compressionLevel, false); //压缩内容 compressor.setInput(input //压缩结束 compressor.finish(); //获取压缩内容 ByteArrayOutputStream bao = new ByteArrayOutputStream(); //一个缓冲 byte[] readBuffer = new byte[1024]; int readCount = 0; //如果压缩内容,则循环 while (!compressor.finished()) { readCount = compressor.deflate(readBuffer); if (readCount > 0) { bao.write(readBuffer, 0, readCount); } } compressor.end(); return bao.toByteArray(); }
可以测试一个100k报文,对于三种压缩比,有如下数据,说明ZIP的默认压缩级别已经足够好 DEFAULT_COMPRESSION 压缩后是34.8K BEST_SPEED 压缩是39K BEST_COMPRESSION 压缩后是34.7K
为了测试压缩性能,通过Content工具类分别生成5K,20K,100K报文供测试,并且分别测试默认压缩,最快压缩和最好压缩。
// ZipTest.java byte[] k5 = null; byte[] k20 = null; byte[] k100 = null; //默认,最快,最好压缩 @Param({"-1", "1", "9"}) int level; @Setup public void init() { Content content = new Content(); this.k5 = content.genContentBySize(1000 * 5); this.k20 = content.genContentBySize(1000 * 20); this.k100 = content.genContentBySize(1000 * 100); } @Benchmark public byte[] k5() throws IOException { return ZipUtil.compress(k5, level); } @Benchmark public byte[] k20() throws IOException { return ZipUtil.compress(k20, level); } @Benchmark public byte[] k100() throws IOException { return ZipUtil.compress(k100, level); }
JMH测试结果如下。可以看到即使20K报文内容,压缩的速度还是非常快的。在不到1毫秒。100K报文,则需要较长时间,默认压缩(-1)需要4毫秒左右。
Benchmark (level) Score Score error Units c.i.c.c.ZipTest.k100 -1 4.467 0.233 ms/op c.i.c.c.ZipTest.k100 1 1.773 0.097 ms/op c.i.c.c.ZipTest.k100 9 5.028 0.152 ms/op c.i.c.c.ZipTest.k20 -1 0.571 0.016 ms/op c.i.c.c.ZipTest.k20 1 0.310 0.010 ms/op c.i.c.c.ZipTest.k20 9 0.592 0.023 ms/op c.i.c.c.ZipTest.k5 -1 0.157 0.008 ms/op c.i.c.c.ZipTest.k5 1 0.112 0.005 ms/op c.i.c.c.ZipTest.k5 9 0.151 0.003 ms/op
这个测试选用的是一篇文章作为压缩内容,你需要根据你的业务情况,用真实的报文来做压缩测试,事实上,如果是XML或者JSON报文,有着非常大的压缩比。
本节选择了zip压缩方式,还有其他可选方式,比如gzip,bzip2,7z等等,可以使用开源库Apache Commons Compress进行压缩,在笔者测试后,发现zip还是一种压缩比和性能都比较好的方式。7z具有最大的压缩比,但压缩时长超过百毫秒,在实时的业务系统中是不可接受的。
对于解压来说,无论采用何种压缩方式,何种压缩级别,解压需要的时间都是非常少的。
看完上述内容,你们掌握Java性能优化中如何进行压缩的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。