Java性能优化-压缩

本博客来自我的新书Java性能优化(暂定名),第5章的Java代码优化技巧节选20,也欢迎阅读我的新书 《Spring Boot 2 精髓 》

4.20 压缩

在微服务调用,如果需要传入的内容过长,压缩是个不错的办法,能提高传输的速度。压缩有很多方法,一种方法是在传输对象的属性名字上做调整,尽量减少传输报文大小,这比较适合传输的是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具有最大的压缩比,但压缩时长超过百毫秒,在实时的业务系统中是不可接受的。

对于解压来说,无论采用何种压缩方式,何种压缩级别,解压需要的时间都是非常少的。限于篇幅就不再说明,读者有兴趣可以使用本书附带例子ZipUtil.decompress 测试对比一下。

转载于:https://my.oschina.net/xiandafu/blog/3071993

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一,JAVA性能优化之设计优化 设计优化处于性能优化手段的上层。它往往须要在软件开发之前进行。在软件开发之前,系统架构师应该就评估系统可能存在的各种潜在问题和技术难点,并给出合理的设计方案,因为软件设计和系统架构对软件总体设计质量有决定性的影响。所以,设计调优对系统的性能影响也是最大的,假设说,代码优化。JVM优化都是对系统微观层次的“量”的优化,那设计优化就是对系统”质”的优化. 设计优化的一大显著特征是:它能够规避某一个组件的性能问题,而是改良组件的实现;比方:组件A通过循环监控不断的检測时间E是否发生,其检測行为必定会占用部分系统资源,因此,开发者必须检測频率和资源消耗上取得平衡,假设检測频率太低,尽管降低了资源消耗,可是系统实时反应性就会降低,假设进行代码层的调优,就须要优化检測方法的实现及要求得一个最为恰当的检測频率.对于这个问题我们就能够用设计模式中的观察者模式 ,当事件E发生的时刻,由事件E通知组件A,从而触发组件A的行为.这样的设计从根本上攻克了存在性能隐患的循环监控,从根本上攻克了这一问题. 进行设计优化时,设计人员和必须熟悉经常使用的设计方法,设计模式,以及主要的性能组件和经常使用的优化思想,并将其有机地集成在软件系统中. 注意:一个良好的系统设计能够规避非常多潜在在的性能问题.因此,尽可能多花些时间在系统设计上,是创建高性能程序的关键
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值