在本章节,咱们将从编码的概念开始,谈谈常见的一些编码类型,再接着谈谈信息论中两个最重要的编码过程-压缩和加密,分析它们在网站中的应用,最后深刻理解在分布式系统中的编码过程-序列化。html
对信息论有足够理解的朋友能够快速略过前4节,阅读后面的序列化及思考部分。前端
让咱们回到数百年前的古代,在那个时候可没有电话,若是有人想给远方的朋友传个信,那就只能修书一封,飞鸽传书了。web
咱们分析下这个过程当中,某人将想说的话(语言),写到(存储到)纸上(文字),而后飞鸽传书(传输)。古代识字率比较低,万一朋友不识字,还得请隔壁的教书先生念信,将文字表述为语言。算法
在这个场景中,咱们看到了一句话经历了语言到文字,而后文字再到语言的转化,这个过程在信息论中被称为编码。所以,广义上的编码是信息在不一样形式下的表示和转换的过程。数据库
编码有两个重要的组成部分,一是多种表现形式,好比语言和文字,二是不一样编码形式间的对应关系,好比“编码”读做“[biān mǎ]”,写做“编码”。而编码的主要应用场景是传输和存储。特别的,逆向的编码过程咱们称为解码。json
固然,编码自己是一个大主题,咱们今天谈到的编码,将是狭义上的编码,指计算机中,特别是软件开发中,不一样数据格式的表示和转化过程。后端
现代计算机一般是基于二进制的,用0和1表状态,二进制属于进位计数制一种,另外一种十六进制也在计算机中常用,而生活中经常使用的是十进制。浏览器
之因此使用二进制,是由于电子计算机利用的是通电、断电(或高电平、低电平)两种状态。这两种状态能够表示为1/0,正好与二进制相对应,一个状态表示一个二进制位,单位称为bit,而后即可以编码表示任一数字,好比7=111。安全
再进一步,若是让第一位表示符号,即正负,那么负数就能够编码了;若是约定前n位表整数位,后m位表小数位,这就是浮点数了。固然在实际中,负数咱们是经过补码的方式表示,浮点也有单精度和双精度之分,本文不作进一步阐述。服务器
另外一方面,0、1也与布尔代数理论中的两种逻辑值“True”、“False”对应,再结合逻辑电路,这就为计算机提供了逻辑基础。
Niklaus Wirth写过一本书《算法 + 数据结构 = 程序》,那么在这里,逻辑运算+数值编码=现代计算机
光有数字的编码是不够的,在过去的几千年,人类留下来的历史的信息大可能是靠文字记录,数字从0到9不过10个,而汉字则以十万计,还不包括一些繁体和变体,其余语言文字就更别说了。所以,在计算机中,咱们也须要一种特定的编码规则来表示文字。
ASCII码是英语字符与二进制位的对应的编码机制,采用8个二进制位来表示一个字符(8bit),8bit能够表示从0000000到11111111的数字,共256位。而英文大小写共52个,再加上数字字符,空格,换行的特殊符号,8bit彻底足够了。而实际上ASCII码只规定了128个字符的编码,只用了8bit的后7位,第一位统一为0。这就有了另外一个单位字节(byte),即1byte占8bit。
随着计算机的推广,其余国家有本身的语言和文字,它们也须要在计算机中表示,ASCII只用了后7位,因此最开始一些欧洲的语言好比法语,经过扩展ASCII码到8位来表示对应语言的字符,这个字符集即是ISO-8859-1。
ISO-8859-1容量仍是256位,其余语言的文字,好比数以万计的汉字就没法加入了,所以1981年中国推出了汉字字符编码GB2312,为了兼容ASCII,英文字母仍是用1byte表示,汉字则用两个字节表示。
既然汉字有对应编码了,还有日,韩等诸多文字也得编码。再者,若是咱们用汉字写邮件发往英文地区,也会有编码不一样没法识别的问题,这些就须要一个统一的解决方案了。
不一样国家使用不一样编码天然就会致使信息沟通的困难,咱们天然就想到了须要一个统一的标准来编码这个世界上全部的字符,因而Unicode就诞生了。
Unicode之所被称为字符集,由于它并非一种编码规则,仅仅只规定了字符与数值的对应关系-码点(Code Point),但没有规定具体实现,好比用几个字节存储。
常见的实现有以下几种:
通常来讲,国外的网站广泛用ISO-8859-1,国内一般使用GBK,面向国际的网站一般基于UTF-8。
编码的兼容性
本小节咱们谈到了多种字符编码,不少编码都只适用于特定语言,最后UNICODE的出现才统一编码标准,这里就体现了编码须要考虑的第一个方面-兼容性,在制定编码时须要评估跨语言,跨系统的场景。
在计算机中广泛数据的存储方式是经过文件存放的,因此,各类格式的文件自己就是一种编码方式,通常来讲咱们能够经过后缀名来区分,好比.txt,.doc,*.gif等等,固然后缀是能够被篡改,严格一点能够根据文件头信息来分别。
常见的文件类型好比:
文本文件(*.txt)
文本文件都是以2.2节谈到字符编码方式存储的,在Windows下,用记事本打开txt文件,点击文件->另存为,能够看到可选的编码方式,默认是ANSI(这是一种Windows用来兼容不一样语言的编码方式),也能够改成Unicode等等。
音频文件(*.mp3)
在生活中,咱们一般用MP3来指代音频文件,实际上MP3只是音频文件的一种编码方式,常见的音频编码类型有MP三、WMA、OGG…,它们在音质,存储,兼容性等方面各有所长。
其余还有视频文件,压缩文件等等,在计算机中,每一种文件的格式都是一种编码格式。
考虑到这么一段大写的数据AAABBB,长度为6。
若是咱们定义一种编码机制:
AAA=A3 BBB=B3
那么上面的数据能够表示为A3B3,长度仅为4。一样的编码,再给这一段信息D3C3,你也能轻松转义成DDDCCC。咱们能够看到后一种表示方式的长度更小。这种编码方式叫游程编码。
将数据经过特定的编码方式减小数据包大小的机制咱们称之为压缩,编码机制即指压缩算法,而上面的AAA=A3这一对应关系,称之为字典,它的解码过程天然是解压了。
在信息论中,压缩被称为信源编码,这是一种对输入信息进行编码,优化信息和压缩信息的编码方式。
咱们对比下编码与压缩:
咱们再定义一个字典,用01来编码字符串。
AA=0 BB=1 CC=01
那么就有以下编码:
AACCCC=00101
若是咱们换一种编码方式:
AA=0 BB=01 CC=1
那么就有
AACCCC=011
由于CC出现了两次,若是咱们用较短的编码来表示出现频率高的字符,就能够获得更好的压缩比(压缩先后数据大小的比例)。
那么,如何计算出这样一个字典?这就有了很是经典的压缩算法-霍夫曼编码。
Huffman算法是一种依据字符出现频率来计算最优压缩字典的算法。
算法摘要以下
好比如下频率
编码可表示为
其余压缩算法还有LZ77,以及基于相似原理的一些变种算法,本文再也不阐述。
在基于这些压缩算法的常见程序有gzip,winrar,7z等等,咱们将在3.3节介绍压缩的应用场景。
以下是两张图片,都是经过一张图处理得来的,它们的色彩,清晰度可有差别?
肉眼是很难看出,实际上右图的像素比左图少5%,并经过放大到同等大小的。
上一节谈到的Huffman算法是一种无损压缩算法,也就是说,压缩后的数据能够经过逆向过程,解压还原出原数据的。
而像图片这种容许一部分数据丢失但不影响效果的场景,咱们就能够用有损压缩的算法下降文件大小。
有损压缩是利用了人类视觉,听觉等方面不敏感的特性,容许压缩过程当中损失必定的信息;虽然不能彻底恢复原数据,但能够得到更大的压缩比。所以,常见的有损压缩每每应用在图片,音频,视频等文件编码上。
最后,咱们谈谈压缩的一些应用场景。
谈编码的概念时,介绍了编码是用在传输和存储中的,咱们就从这两个角度谈谈压缩的应用。
在前端部分,诸如Nginx,Tomcat等Web服务器均可以配置gzip压缩,浏览器在发送请求时经过添加Accept-Encoding: gzip;
请求头,从而实现各种资源的压缩传输。
文件压缩也是经常使用场景,咱们曾遇到过因为图片没有压缩,致使一个网页须要传输上百M数据的问题,严重影响用户体验。
在后端的各种分布式系统中,一般会根据特定的数据格式自定义压缩规则,咱们会在第5节谈谈序列化的例子。
在数据库和日志系统中都提供了压缩功能,好比MySQL就提供了压缩表选项,能够在建表时进行配置。腾讯的TMySQL和阿里的AliSQL等还提供针对列压缩的特性,为blob/text等大字段进行灵活的压缩配置。
在分布式文件系统中,文件的压缩应用就普遍了,像用户上传的图片,不少网站都会进行统一的格式转换,针对不一样的场景,压缩到不一样的分辨率,好比缩略图就是经过高压缩比的有损压缩而来。特别的,HDFS(Hadoop Distributed File System)还提供了合并压缩存储的方案,咱们会在本书第二部分,分布式文件系统一文中谈谈这个话题。
编码的效率
咱们知道,衡量算法复杂度分为时间复杂度和空间复杂度,其实说的是算法的效率从空间和时间两个方面来衡量。而压缩正是提升了编码的空间效率,所以,效率是编码须要考虑的第二个方面。
回到本文开头谈到了寄信场景,假设咱们寄的不是家书,而是一名将军要下达给部队的命令函,那这封信就不能白纸黑字的写清楚了,万一信使被抓就泄密了,咱们须要作一些加密的手段。
密码本是最古老的保密方式之一,在北宋的军事著做《武经总要》记载着一种名为“字验”的加密手段。
他们收集了军队中经常使用的40种战斗状况,如请弓、请粮料、都将病等等,而后战前约定一首五言律诗编码,五言律诗共8行,每行5字,正好40字,每一个字对应一种战斗状况。这样传令时只需传一个字就能够完成通信了。
如王维的使至塞上:
单车欲问边,属国过居延。 征蓬出汉塞,归雁入胡天。 大漠孤烟直,长河落日圆。 萧关逢候骑,都护在燕然。
回到计算机中,咱们为文本消息定义一种编码,称为字符x+5,每一个字符用它后面的第5个字符代替,如:
A->F B->G
经过这种方式,只要x+5这个规则不被破解,就起到了加密做用,而接收者只须要知道x+5的值,就能够解码出x的值。因为加密和解密都用到了同一个密钥,因此这个方式咱们称为对称加密。
固然,实际应用中的加密算法比这个复杂的多,一般密钥长度在几十到数百位不等。常见的对称加密算法有DES、IDEA、RC等等。
咱们对比下编码与加密:
因为对称加密只有一个密钥,一旦公开全部人均可以解密信息,在多方传输中存在安全风险。所以也就有了非对称加密,这种方式将加密密钥(公钥)和解密密钥(私钥)分开,任何发信人均可以用公钥发送加密信息,但只有收信人有私钥解密。
网上有这么一段形象的解释:
非对称加密算法就是别人想要发信息给你。你先造一个保险箱。保险箱关上是不用钥匙的。把这个保险箱开着递给别人。别人发信息就把信息放进去而后“砰”一下关上还给你。只有你有钥匙信息能够查看信息,除非别人有其它方式破解。
咱们来简单描述下非对称加密算法RSA的编解码过程。
RSA基于这样一个事实,将两个大素数相乘是很容易,但从结果中分解出它们则很是耗时。选取大素数可长达数千位,固然这样的素数选取出来也不容易,一般是用Miller Rabin等素数测试算法来检测素数,会存在必定的误判。
令
N=A×B T=(A-1)×(B-1)
则
N=37×23=851 T=(37-1)×(23-1)=36×22=792
2.选取第三个随机数(E)做为公钥,它必须与T互质,则私钥(D)知足
D×E mod T = 1
如选E(公钥)=5
则
D×5 mod 792=1 D(私钥)=317
3.把明文(M)加密为密文(C):
C=M^E mod N
4.解密为
M=C^D mod N
如 M(明文)=7
C(密文)=7^5 mod 851 = 638 M(明文)=638^317 mod 851 =7
Hash函数是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数,能够充当原数据的映射,用于快速查找和校验。
通常状况下因为输出Hash值的空间一般远小于输入的空间,不一样的输入可能会计算出相同的Hash值,因此没法从Hash来惟一的肯定输入值,会出现值冲突,难以求逆。可是,经过精心设计的Hash算法,如MD5,SHA,其值域极大,在现有计算机条件实现了无冲突,同时求逆也更加不可能。所以,对于一些无需解密的数据,这些安全Hash算法就能用来充当加密工具。
虽然Hash自己不可逆,但并不是不可破解,如字符串123456,咱们用MD5算法计算出它的Hash值为7e8feb2276322ecddd4423b649dfd4d9,这样,只要看到是这段hash值的地方,咱们就能够认为这段数据是123456。这就有了一种暴力破解方式-彩虹表。
彩虹表是预先将数以百亿级的各类字符串,特别是有规律的字符串的Hash计算出来,整个表的大小一般在数百G以上,而后经过碰撞的方式破解Hash值。
既然通常字符串的Hash表是能够被计算的,咱们能够经过往输入项里面加一段信息,咱们称之为盐(salt),好比123456改成salt123456,而后再计算Hash值。以后每次查询的时候,都在输入项前面加上salt,而后再查询,只要不被人知道盐值,用普通的彩虹表是没法破解信息的。
前面谈到各类加密方式并不是没法破解,只是以现有计算机的运算能可能须要十几甚至上百年的时间。这就有了密码学领域的另外一个通识,就是加密方式并不是要作到绝对没法破解,不少信息是有时效性的,只须要在必定年限内没法破解,过时以后的价值就不大了,这也是不少国家机密会设置保密期的缘由。
所以,就有了另外一种对抗破解的方式-慢Hash,慢哈希是指执行这个哈希函数很是慢,这样暴力破解须要枚举遍历全部可能结果时,就须要花上很是很是长的时间。因为慢Hash耗计算量,咱们每每会把这些计算量转嫁到客户端,让客户端计算完Hash传给服务端校验。
咱们也从传输和存储方面谈谈加密的应用。
常见的加密传输有HTTPS,SSH等协议,它们每每是非对称加密与对称加密算法相结合制定的,咱们会在第二章通讯协议中进一步谈谈HTTPS的内容。
存储方面最重要是密码存储了,一般基于安全Hash,附加加盐、慢hash等手段进行复杂的加密。
编码的安全性
在这一节,咱们谈到了多种加密方式,这些方式都是为了保证数据在存储和传输过程当中的安全,那么加密天然就是体现了编码的安全性。
在如今的分布式系统中,特别是以微服务为表明的分布式服务技术,序列化是其中的关键技术点之一。
在深刻理解序列化以前,咱们先来准确描述下它的概念。
序列化是将数据结构或对象转换为可存储(如在文件或内存缓冲区)或传输(如经过网络)的格式并稍后重建(可能在不一样的计算机中)的过程。
数据结构和对象这个不用解释,但可存储或传输的格式最多见的天然是字节了,除此以外还有基于字符串的JSON。
好比,有这么一个对象
class Person { String name="Jim"; }
能够用JSON表示为{ name:"Jim"}
。所以,一些JSON框架,好比Java中的FastJson,也能够归类到序列化框架之中。至于重建的过程即是反序列化了。
咱们对比下编码的概念:编码是信息在不一样形式下的表示和转换的过程。在这里,数据结构、对象、字节、json是否是信息的表示方式?反序列化是否是对应解码?序列化的过程是否是一个编码过程?
理解这一点,咱们就能够回答出,序列化的本质是编码。
既然这样,咱们就能够从编码的角度来分析序列化了。前面的文章,咱们从字符集中谈到编码的兼容性,从压缩中谈到编码的效率,从加密中谈到编码的安全性,那边这里咱们就从这三个方面谈谈序列化。
序列化的兼容性有这么三个方面。
开发语言有JAVA,C++等等不少种,若是涉及到多语言应用之间的交互,那么序列化框架必须有多种语言的实现版本。固然,若是一个公司的内部应用全是用同一语言开发的,天然就无需引入这种兼容性。
平台方面,好比浏览器和服务器这两个平台,常见JSON格式就能很好的兼容两者,因此咱们每每提供基于JSON的API。
以JAVA为例,JAVA的复杂对象场景包括继承、组合、泛型、循环依赖等等。
以循环依赖为例,循环依赖是指两个以上对象相互引用,如
class A { B b; } class B { A a; }
一旦它们相互引用,一些序列化框架在序列化时,序列化A发现里面包含B,而后又序列化B,发现B又包含A,又序列化A…,这样就出现死循环了。
一般在一些JSON序列化框架中会遇到这种问题,JSON格式自己也难以体现循环依赖关系。
现实场景每每更复杂,咱们就遇到过一个接口包含数百个对象,上千字段,各类继承、泛型,在测试时,四五种序列化框架,仅一种能应对这种复杂场景。
着重提一句,兼容性是一个序列化框架最重要的指标,没有之一,必要的时候咱们能够牺牲效率,但序列化自己不能出错,特别是核心流程上,由于序列化一旦出错,整个业务逻辑就直接崩溃了。
序列化的场景一般是用于不一样服务器间的数据传输,在JAVA中,咱们会经过jar的方式,让client和server引入同一个类。
若是有一天由于业务须要,要在某个类中增减字段,但咱们没法作到client和server同时升级,这样,client和server的序列化结构就不一致了,一些依赖字段顺序的序列化框架这时就每每会出现问题,因此,可以兼容这种不一致场景也是评估一个序列化框架
的指标之一。
咱们谈效率无非就是时间和空间。
序列化的时间效率天然就是序列化与反序列化的耗时,在不考虑硬件的场景下,一般取决于语言效率,数据量等等。好比在JAVA,基于反射的方法调用和普通调用是有性能差别的,而序列化每每基于反射操做,所以对这一块进行优化就能提高必定的效率。
空间效率一般就是压缩了,好比一个boolean值True,将它序列化成True
,和序列化成T
,二者之间长度是不同的,不少序列化框架就是经过这种自定义的压缩字典来处理数据。
序列化一般是用于传输场景,传输的安全性在内网下基本是无需考虑的,对于提供出去的外网服务,好比与客户端APP的通讯,其安全性每每是基于传输协议自己来实现的,好比基于HTTPS来传输。
另外一个安全风险来自于注入攻击,注入攻击每每是由于本应用来查询或保存的字符串被当成命令代码执行了。特别是基于JSON的序列化框架,因为JSON自己是基于字符串的,若是里面的字符串被看成代码执行了,就会产生比较严重的后果。
咱们从了解编码是信息的转换过程开始,从兼容性、效率及安全性三个方面分析了编码的原理。而后,谈到了分布式系统中的序列化,而序列化的本质是编码,所以,也从一样的三个方面分析了序列化。
那么,咱们能够进一步:
再进一步:
还再进一步:
如:
回答完这些问题,感受像是造了一把锤子,而后全部的东西当钉子,但这并非个人初衷,回到本书的序中写到的,这本书真正要写的是我对第一性原理,结构化思惟,系统思惟这些的思惟方式的应用。
既然讲了底层的逻辑思惟了,其实本文最底层的逻辑是用到了逻辑思惟的两种方式-概括和演绎。咱们从字符,压缩,加密的编码中概括出了一套分析编码的结构,而后演绎出序列化的第一性原理是编码,用编码的结构去分析了序列化,框架,甚至现实生活中的产品,最后完成了这一篇系统化的文章。
附本文的导图。
以上就是编码的相关内容,咱们会在后面的协议,服务化框架等文章里面将本章内容关联起来,进一步完善整个编码的知识体系。
编码:隐匿在计算机软硬件背后的语言 [美] Charles Petzold
信息论、编码与密码学 [印]博斯
Serialization and Unserialization:https://web.archive.org/web/20150405013606/http://isocpp.org/wiki/faq/serialization
Hessian协议规范:http://hessian.caucho.com/doc/hessian-serialization.html
做者:初开
发表于:博客园
本文基于 知识共享-署名-非商业性使用-禁止演绎 4.0 国际许可协议发布,转载必须保留署名及连接。