1.å¦ä½è§£å³httpå°å
ä¸gzipç¼ç çhtml
2.HTTP协议之Chunked解析
3.Feign踩åè®°å½ï¼JSON parse error
4.[译] 理解 zip 和 gzip 压缩格式背后的解码解码压缩算法
5.Java字符串的压缩与解压缩的两种方法
å¦ä½è§£å³httpå°å ä¸gzipç¼ç çhtml
ããå¦ä½è§£å³,还有就是源码大部分打开GZip压缩的php论坛。
根据本人的工具理解,使用Chunked编码的解码解码主要好处就在于一些程序的运算出过程中,可以动态的源码输出内容。
例如,工具听书源码修复要在后台处理一个小时的解码解码运算,但又不希望用户等一个小时才能看到结果。源码这时就可采用Chunked编码将内容分块输出,工具用户随时都可以接收到最新的解码解码处理结果。
asp关闭了缓存的源码输出模式,就是工具Chunked编码的。(Response.Buffer = false)
而每一次的解码解码Response.Write,都是源码一个Chunked,所以不要使用的工具太频繁哦,否则Chunk数量太多,额外的数据太浪费空间了。
若想了解Chunked的具体编码结构,用ASP关闭缓存调试蛮方便的。)
我们先来看看RFC中对Chunked的定义:
Chunked-Body = *chunk
last-chunk
trailer
CRLF
chunk = chunk-size [ chunk-extension ] CRLF
chunk-data CRLF
chunk-size = 1*HEX
last-chunk = 1*("0") [ chunk-extension ] CRLF
chunk-extension= *( "" chunk-ext-name [ "=" chunk-ext-val ] )
chunk-ext-name = token
chunk-ext-val = token | quoted-string
chunk-data = chunk-size(OCTET)
trailer = *(entity-header CRLF)
我们来模拟一下数据结构:
[Chunk大小][回车][Chunk数据体][回车][Chunk大小][回车][Chunk数据体][回车][0][回车]
注意chunk-size是以十六进制的ASCII码表示的,比如AE(实际的十六进制应该是:),计算成长度应该是:,表示从回车之后有连续的字节的数据。
跟踪了www.yahoo.com的返回数据,发现在chunk-size中,还会多一些空格。可能是时间显示源码大全固定长度为7个字节,不满7个字节的,就以空格补足,空格的ASCII码是0x。
以下是解码过程的伪代码:
length := 0//用来记录解码后的数据体长度
read chunk-size, chunk-extension (if any) and CRLF//第一次读取块大小
while (chunk-size > 0) { //一直循环,直到读取的块大小为0
read chunk-data and CRLF//读取块数据体,以回车结束
append chunk-data to entity-body//添加块数据体到解码后实体数据
length := length + chunk-size//更新解码后的实体长度
read chunk-size and CRLF//读取新的块大小
}
read entity-header//以下代码读取全部的头标记
while (entity-header not empty) {
append entity-header to existing header fields
read entity-header
}
Content-Length := length//头标记中添加内容长度
Remove "chunked" from Transfer-Encoding//头标记中移除Transfer-Encoding
有空再研究一下GZip+Chunked是如何编码的,估计是每个Chunk块进行一次GZip独立压缩。
使用了Chunked,自然会在性能上稍微打点折扣,因为比正常的数据体多出了一些额外的消耗。
但是有一些情况下,必需要使用分块输出,这也是不得已而为之.
Feign踩åè®°å½ï¼JSON parse error
1.è·è¸ªæåºå¼å¸¸çå æ ï¼åç°å¨å¯¹è¿åç»æçjson解æä¸æåºå¼å¸¸2.为ä»ä¹ä¼è§£æjson失败å¢ï¼æ们åç¬è°ç¨feign对åºçæ¥å£æ¯æ£å¸¸çï¼jsonä¹æ¯æ£å¸¸å¯ä»¥è§£æç
3.é¾éfeignçå¤çè¿è¿åçå 容ï¼åå»è·äºä¸feginå¤çè¿ç¨åç°ä»responseè·åå°æµå¹¶æ²¡æä»»ä½å¼å¸¸ï¼é¾éæ¯åºå¨äºæºå¤´ï¼ä½æ¯æºå¤´å没æä»»ä½å¼å¸¸ï¼æ¤æ¶æ绪已ç»æ··ä¹±ï¼è¯çå¨googleä¸æ¥æ¾æ没æç¸å ³çé®é¢ï¼æ²¡æ³å°å¨feignçgithubä¸æ¾å°ç±»ä¼¼é®é¢ /OpenFeign/feign/issues/
4.é®é¢å·²ç¶åç°ï¼å°±æ¯ååºçå 容ç»è¿gzipç¼ç ï¼feigné»è®¤çClientä¸æ¯ægzip解ç ãé£ä¹å¨æ¤è·è¸ªä¸ä¸feignçæºç æ¥çå¤çè¿ç¨ï¼ä»å ¥å£ SynchronousMethodHandler å¼å§ï¼å¨è¡å¼å§è·åååºå 容
æç»å¨ Logger çè¡æ¾å°ååºæµç读åï¼è¯»åçæµç¨å¦ä¸ï¼
5.æç»é®é¢åºå¨feign使ç¨é»è®¤çHttpURLConnectionï¼å¹¶æ²¡æç»è¿ä»»ä½å¤çï¼å¯¼è´è¯»åçæ¯gzipå缩åçå 容ãæ¤æ¶æ们å¯ä»¥å°å ¶ç½®æ¢ä¸ºHttpclientï¼å ¶å é¨ ResponseContentEncoding ç process æ¹æ³ï¼ååºäºContent-Encoding并å¤æä¸ä¸ºç©ºï¼ç¶åè·å对åºçå¤çæ¹å¼ã
ä¸é¢æ说feigné»è®¤çClientä¸æ¯ægzip解ç å¯è½å®¹æå¼èµ·æ§ä¹ï¼åºè¯¥æ¯feginé»è®¤çClient对ååºæµä¸æ¯æ对gzipåçåèæµè¿è¡è§£æï¼æ以å¨åºååæ对象æ¶ä¼åå¨è§£æé®é¢ãå¦æä¸å®è¦æ¥æ¶å¯ä»¥ä½¿ç¨ ResponseEntity<byte[]> æ¥æ¥æ¶,è¿æ ·feignå°±ä¸ä¼å¯¹å ¶ååºååäºãè³äº feign.compression.request.enabled=true , feign.compression.response.enabled=true é ç½®çå å®¹å¨ FeignAcceptGzipEncodingInterceptor , FeignContentGzipEncodingInterceptor ï¼å¤§è´å¯ä»¥çåºåªæ¯å¨è¯·æ±å¤´æ·»å äºHeaderèå·²
/3/
spring已添å æ¯æï¼SpringCloudçå级å°Hoxtonå³å¯
/spring-cloud/spring-cloud-openfeign/pull/
//
对äºä»ç¶åå¨é®é¢çä¼ä¼´ï¼å¯ä»¥ç´æ¥ä½¿ç¨OkHttp设置为feignç客æ·ç«¯ï¼å 为okhttpæ¯é»è®¤æ¯ægzipå缩ï¼ï¼ä¸éè¦å ³æ³¨spring cloudçæ¬ï¼æç®åçæ¹æ¡ï¼ä¹æ¯ææ¨èçæ¹æ¡ã
[译] 理解 zip 和 gzip 压缩格式背后的压缩算法
众所周知,在网络传输数据时,每个字节都需要占用流量,这意味着需要支付费用。尽管存在多种压缩算法,但zip算法可能是最流行的。gzip算法虽然与zip同名,但却是另一种不同的算法。gzip算法在HTTP协议中也得到了广泛应用。尽管各种压缩算法适用于不同场景,但它们的底层都是基于DEFLATE算法。DEFLATE是一种结合了LZ算法和哈夫曼编码的无损数据压缩算法。
LZ算法是一种用于文本压缩的无损压缩技术。该算法通过使用编码器或解码器中已经出现过的源码分享歌曲推荐匹配数据信息来替换当前数据,从而实现压缩功能。
LZ算法并非在整个文本中同时查找重复的字母,而是首先设置一个固定大小的搜索缓冲区,例如(在实际场景中,这个缓冲区的大小通常是几十KB)。在逐一对文本中的字母进行编码时,首先会判断当前字母是否出现在前面缓冲区的个字母中。如果能找到匹配的字母,就记录下当前字母与找到的字母的偏移量d,这样就完成了一个字母编码的第一阶段。接下来,用当前在编码字母邻近的下一个字母与缓冲区中匹配上字母邻近的下一字母进行匹配,如果匹配上就继续进行下一个字母的匹配,如此循环往复,直到缓冲区个字母匹配完或者邻近的字母未匹配上,就结束匹配过程。完成上述过程后,将当前位置匹配上的连续字母替换成与缓冲区字母的偏移量以及这段连续字母的个数l。这样,字母编码的第二阶段就完成了。
让我们用一个例子来看看它是如何工作的:
首先,最简单的方法就是直接替换第二次出现的O为指向第一次出现的O的一个标记,或者替换第二次出现的RTO为指向第一次出现的RTO。
下面更具体地描述一下这个过程。假定我们设置的缓冲区大小是4,把这4个长度的源码下载歌曲伤感缓冲区看成是一个滑动窗口沿着正文文本向右滑动:
滑动窗口随着编码的迭代一步步向右移动,前面4步中滑动窗口内的字母都没有发现重复。到了第5步,滑动窗口内字母O已经出现重复了,然后查看字母O右侧的R,发现滑动窗口中匹配字母O右侧相邻的字母并非R,便不再继续向右进行匹配,将第2个O替换成(1, 1)(表示:滑动窗口中匹配的字母离当前字母偏移距离为1,匹配上的连续字母长度为1)。在第6步中,滑动窗口中字母R与其左边第4个字母匹配上了,继续检查下一个字母T的匹配情况,然后发现滑动窗口中RT也匹配上了。然后继续下一个字母O,在滑动窗口中匹配RTO也匹配上了,并且到此为止,因为下一个字母匹配上了。滑动窗口中匹配上的字母与当前字母的偏移距离为4,同时有连续3个字母匹配上了,所以这里将匹配上的3个字母替换成(4, 3)。接着在第7步中,字母R与偏移距离3出的字母匹配上,但是接下来的RR并未匹配上,在第8步中发现最近的匹配上的R的偏移距离为1。最终整段文本经过编码的结果如下:
压缩过的文本其实是由一系列的这种(d, 1)标记对和字母组成,标记对无法直接找到相匹配的字母。在解压过程中,蓝码指标源码字母保持不变,这种标记对转换为其指向位置的字母。下面看一个解压的例子:
字母abc保持不变,标记对(3, 2)表示从当前位置向左移动3个单位,然后取出2个字母,因此其转换为ab。现在原始文本变成了这样abcab(1, 1),最后一个标记对表示从当前位置向左移动1个单位,然后取出1个字母,因此转换为b。最终解压完成的文本为abcabb。
在用LZ消除文本中重复的字母后,再使用哈夫曼编码进行第二次压缩。这种方法用较短的编码代替较常用的字母,用较长的编码代替较少用的字母,从而减少了文本的总长度。
让我们用一个简单的示例文本来看看它是如何工作的。
这个例子中,我们希望能无损地压缩这段文本。通常一个字母占用8字节,所以这段文本总长度有字节。在这段文本中,我们发现其中字母F只出现了1次,而字母E出现了7次。哈夫曼编码正是利用了这一特性,通过减少出现频率高的字母本身的字节长度,来减少整个文本所占的总长度。
要采用哈夫曼编码压缩文章,首先需要统计各个文本中各个字母的出现频率。上述例子中的字母频率如下:
我们需要使用文本中的字母作为叶子节点来构建一颗二叉树,通过这棵二叉树来编码文本中的每一个字母。从出现频率最小的字母:P和F开始,让其作为底层的叶子节点,将其频率相加的值作为父节点,这样便得到了如下的二叉树:
重复上面的步骤,依次使用频率最小的字母:U和O以及R和T,最后剩下频率最高的字母E先单独放着。
接下来使用上面得到的4个二叉树作为子节点来创建一颗更大的二叉树,将上面的二叉树的根节点的频率值递增排序,优先使用根节点频率值小的二叉树作为新的二叉树子节点。这里使用U和O、R和T这组二叉树组成了如下的一颗二叉树:
这时候还有3颗二叉树,根节点分别为:9、9、7(第一个9是上一步创建的二叉树),同样的,将根节点频率值最小的两个作为子节点创建新的二叉树如下:
现在剩下一颗将根节点值为的大二叉树和根节点值为9叶子节点为R、T的二叉树,将其作为子节点创建一颗新的二叉树如下:
现在我们要做的就是根据这棵二叉树来对文本进行编码。依次从跟节点访问各个字母,遇到左分支当成0,遇到右分支当成1,按照字母沿着二叉树访问路径的顺序所将这些0、1连接起来。比如,从根节点到字母E先后需要经过1次左分支和1次右分支,所以字母E的编码为。字母U需要经过4次左分支,其编码为;F需要经过2次左分支和2次右分支,其编码为。可以发现,在这里例子中出现频率非常高的字母E编码后位数比出现频率较少的字母F编码后位数要少。经过这样的编码处理,最终压缩过的文本如下:
这段压缩后的文本长度只有位,远比原始的位长度小。
假如收到这样一段压缩过的文本,我们希望能够解压它让其变得可以理解。我们都知道一段未压缩过的文本中的一个字符占用8位,上面说过经过哈夫曼编码压缩后一个字符的位数并不是固定8位的,所以并不清楚一段数据(比如:)是表示1个字符、2个字符或者3个字符,因此这段压缩过的文本将如何解压呢?
这一步不存在任何奇迹,要准确解压还需要上面编码中构建的二叉树。得到这个用于编码的二叉树有两种方案,第一种是其和压缩后的文本放一起作为原始文本的压缩结果,这可能会导致压缩后的文本比原始文本还要大;第二种方案是使用预先定义好的二叉树。我们知道各个字母在英语中的使用频率,完全可以根据这个频率来构建上述的二叉树。使用这种预先定义的公共字母频率二叉树压缩部分文本的结果可能比根据文本内容字母频率二叉树压缩的效果差一些,但是这样不再需要将字母频率二叉树保存到压缩后的文件中。总而言之,这两种方案各有优缺点。
虽然本文没有深入的分析各种压缩算法原理的细节和对应的实现,但是经过上述讲解你应该已经对文本如何被压缩成zip和gzip等格式有了大概的认识。希望本文能满足你对压缩算法神秘面纱的好奇心:)
*从技术上来说,zip压缩格式是支持使用其他的压缩算法的,但是DEFLATE是其中最常用的一种。
如果发现译文存在错误或其他需要改进的地方,欢迎到掘金翻译计划对译文进行修改并PR,也可获得相应奖励积分。文章开头的本文永久链接即为本文在GitHub上的MarkDown链接。
掘金翻译计划是一个翻译优质互联网技术文章的社区,文章来源为掘金上的英文分享文章。内容覆盖Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注掘金翻译计划、官方微博、知乎专栏。
Java字符串的压缩与解压缩的两种方法
在处理字符串时,尤其是当字符串长度过长时,我们需要考虑压缩与解压缩的方法以优化数据存储和传输。文章将介绍两种实现字符串压缩与解压的方法,适用于Java环境。
首先,可以使用Java8中的GZIP进行字符串的压缩与解压。这种方法基于Zlib压缩算法,适用于大多数情况。使用GZIP时,先将字符串转换为字节数组,然后使用GZIPOutputStream进行压缩,最后将压缩后的字节数组存储或传输。在解压时,使用GZIPInputStream进行解压缩,还原为原始字符串。
另一种方法是利用Apache Commons Codec库中的Base编码。这种方法首先将字符串通过Base编码转换为二进制格式,然后可以进行安全的存储和传输。在接收端,使用BaseDecoder进行解码,最后解码后的结果通过BaseDecoder的nextBytes方法转换回原始字符串。Base编码的额外一步可以提高数据传输的安全性和稳定性。
在应用这些方法时,需要特别注意一些细节。例如,在Web项目中,服务器与前端通过HTTP进行数据交换时,可能会遇到Base加密字符串在传输过程中被修改的问题。为了解决这个问题,可以将压缩后的字符串通过Base加密,然后再发送给前端。前端在接收到加密字符串后,使用Base解密,解密后的字符串进行GZIP解压,从而确保了数据的完整性和安全性。
综上所述,通过使用GZIP压缩和Base编码,可以在确保数据完整性的同时,有效地处理过长的字符串,节省存储空间和优化数据传输效率。在实施这些方法时,务必遵循相应的编码与解码步骤,以避免数据丢失或解压缩错误。