Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

如何把你的iPad变成一个桌面电脑?

$
0
0

20140505102903367

在使用iPad的时候,有时你会需要获得一份文件,或是访问一个程序;有时也会发现一些工作无法在iPad上面进行。以前,远程计算服务很少会应用在iPad上,但是随着人们工作范围越来越广,移动设备的远程连接逐渐变得越来越有用。

如果你的家中有一台个人桌面电脑,而且希望通过互联网将自己的iPad远程连接到PC上,不如来看看本文吧。

屏幕分享和远程桌面

苹果和微软在自己的移动设备里都提供了内置解决方案,允许用户远程连接到自己的桌面电脑上。苹果使用的技术大家已经比较熟悉了,该技术就是“虚拟网络计算(VNC)”,用户可以自行配置相关分享服务参数。微软使用的则是他们自己的“远程桌面协议(RDP)”,它可以在系统属性里面的“远程设置”里进行配置,用户可以右键点击“计算机”选择“属性”进入,或是在控制面板里搜索“远程配置”。

20140505100051651

一旦配置完成之后,你就可以在iPad上使用一些远程连接客户端了,比如 VNC Viewer(免费)和 微软远程桌面(免费)。这些应用可以把iPad远程连接到你的桌面电脑上,当然啦,前提是这些设备需要在相同的本地网络之内。

通过互联网实现远程连接

苹果为其iCloud用户提供跨互联网远程服务,该服务就是 Back to my Mac,它可以定位并连接到用户的Mac电脑。它的问题是,该服务只支持Mac电脑之间互联,而且经过这几年的使用,用户均反馈此服务速度很慢,而且稳定性不够可靠。另一方面,微软公司也提供了类似的远程探索服务,但是它要求用户安装配置一个 远程桌面网关(RDG)。

谷歌公司最近也推出了自己的远程连接服务解决方案,当然,它就是被人们熟知的 Chrome Remote Desktop,该解决方案可以通过互联网进行远程连接桌面系统。谷歌公司最近发布了这个Chrome应用的安卓版本,目前虽然尚未提供iOS版本,不过预计会在今年年末发布。

20140505100051354

远程设备连接的目标,其实还是要能通过互联网,快速便捷地定位、并连接到你的桌面电脑上面。但是对于绝大多数用户来说,需要手工配置的地方非常多,比如需要处理防火墙,端口转发,静态IP地址等等,这些都非常麻烦。下面我们要提到的一些应用程序都有一个主机应用,并且需要用户安装在自己的桌面电脑上。这些应用程序会自动通过互联网,在iPad和桌面电脑之间实现互联。在大多数情况下,你需要做的,就是去下载这些应用程序,然后安装,并登陆到远程服务上去。

支持远程连接的iPad App

1、 PocketCloud。 虽然它是一款含广告的App,但是如果想通过互联网访问桌面计算机的话,这款免费App仍然是最佳选择。它提供了一个触摸指针(Touch Pointer),感觉就像是在iPad上配了一个鼠标。用户可以在iPad屏幕上轻松移动虚拟鼠标,它还支持左键和右键点击,以及滚轮功能。

它提供了PC和Mac两个版本的App,该应用的免广告Pro版需要支付15美元,此外免费用户也可以升级成付费用户,年使用费为24美元,可以访问文件,流媒体视频,桌面搜索等功能。

2、 TeamViewer。这款远程桌面App是为了大型团队和企业级客户而设计的,当然它也支持个人用户使用。TeamViewer对个人用户是免费提供的,他们这么做主要是出于非商业目的。TeamViewer Version 9支持Mac,Windows,以及Linux系统。

3、 Desktop Connect。这款应用是市面上最早的远程桌面App之一,当然也是最好的App之一。如果你想连接多台电脑,或是希望有高速连接,那么Desktop Connect绝对是一款值得尝试的App,它售价为14.99美元。相对而言,PocketCloud 和TeamViewer的连接速度真的很慢,但是Desktop Connect的速度真的,真的非常快。其客户端支持PC,Mac,和Linux操作系统,用户还可以使用谷歌帐号直接登录。

4、 Parallels Access for iPad。 这款应用的开发商也开发了虚拟机软件,允许用户在自己的Mac电脑上运行Windows操作系统,Parallels Access应用是免费下载的,如果你认为这款应用是让你去访问虚拟机,那么就大错特错了。其客户端Parallels Access Agent可以在PC和Mac上安装,允许用户通过互联网访问任何桌面电脑,但是用户必须要每年支付49美元的订购费用。

5、 Splashtop 2 Remote Desktop。Splashtop 2的费用为19.99美元,它是一款非常稳定的App,其可调节触控板模式和屏幕滚动条两项功能也十分有用。这款应用的客户端Splashtop Streamer可以安装在Mac,PC,以及Linux操作系统上。用户如果需要使用还得支付每年16美元的订购费用,才能用自己的iPad连接到桌面电脑上。

20140505100050378

上述五款App都可以安装在用户桌面电脑上,在iPad上操作时,不仅控制便捷,而且,即便通过较慢的WiFi,LTE或是4G网络连接,他们的响应时间和连接时间也很快。当然最重要的是,这些App的价格也在用户的承受范围之内。

如何把你的iPad变成一个桌面电脑?,首发于 极客范 - GeekFan.net


[转]TCP协议疑难杂症全景解析

$
0
0

说明:

1) 本文以TCP的发展历程解析容易引起混淆,误会的方方面面
2) 本文不会贴大量的源码,大多数是以文字形式描述,我相信文字看起来是要比代码更轻松的
3) 针对对象:对TCP已经有了全面了解的人。因为本文不会解析TCP头里面的每一个字段或者3次握手的细节,也不会解释慢启动和快速重传的定义
4) 除了《TCP/IP详解》(卷一,卷二)以及《Unix网络编程》以及Linux源代码之外,学习网络更好的资源是RFC
5) 本文给出一个提纲,如果想了解细节,请直接查阅RFC
6) 翻来覆去,终于找到了这篇备忘,本文基于这篇备忘文档修改。

1.网络协议设计

ISO提出了OSI分层网络模型,这种分层模型是理论上的,TCP/IP最终实现了一个分层的协议模型,每一个层次对应一组网络协议完成一组特定的功能,该组网络协议被其下的层次复用和解复用。这就是分层模型的本质,最终所有的逻辑被编码到线缆或者电磁波。

分层模型是很好理解的,然而对于每一层的协议设计却不是那么容易。TCP/IP的漂亮之处在于:协议越往上层越复杂。我们把网络定义为互相连接在一起的设备,网络的本质作用还是“端到端”的通信,然而希望互相通信的设备并不一定要“直接”连接在一起,因此必然需要一些中间的设备负责转发数据,因此就把连接这些中间设备的线缆上跑的协议定义为链路层协议,实际上所谓链路其实就是始发与一个设备,通过一根线,终止于另一个设备。我们把一条链路称为“一跳”。因此一个端到端的网络包含了“很多跳”。

2.TCP和IP协议

终止于IP协议,我们已经可以完成一个端到端的通信,为何还需要TCP协议?这是一个问题,理解了这个问题,我们就能理解TCP协议为何成了现在这个样子,为何如此“复杂”,为何又如此简单。

正如其名字所展示的那样,TCP的作用是传输控制,也就是控制端到端的传输,那为何这种控制不在IP协议中实现的。答案很简单,那就是这会增加IP协议的复杂性,而IP协议需要的就是简单。这是什么原因造成的呢?

首先我们认识一下为何IP协议是沙漏的细腰部分。它的下层是繁多的链路层协议,这些链路提供了相互截然不同且相差很远的语义,为了互联这些异构的网络,我们需要一个网络层协议起码要提供一些适配的功能,另外它必然不能提供太多的“保证性服务”,因为上层的保证性依赖下层的约束性更强的保证性,你永远无法在一个100M吞吐量的链路之上实现的IP协议保证1000M的吞吐量…

IP协议设计为分组转发协议,每一跳都要经过一个中间节点 ,路由的设计是TCP/IP网络的另一大创举,这样,IP协议就无需方向性,路由信息和协议本身不再强关联,它们仅仅通过IP地址来关联,因此,IP协议更加简单。路由器作为中间节点也不能太复杂,这涉及到成本问题,因此路由器只负责选路以及转发数据包。

因此传输控制协议必然需要在端点实现。在我们详谈TCP协议之前,首先要看一下它不能做什么,由于IP协议不提供保证,TCP也不能提供依赖于IP下层链路的这种保证,比如带宽,比如时延,这些都是链路层决定的,既然IP协议无法修补,TCP也不能,然而它却能修正始于IP层的一些“不可保证性质”,这些性质包括IP层的不可靠,IP层的不按顺序,IP层的无方向/无连接。

将该小节总结一下,TCP/IP模型从下往上,功能增加,需要实现的设备减少,然而设备的复杂性却在增加,这样保证了成本的最小化,至于性能或者因素,靠软件来调节吧,TCP协议就是这样的软件,实际上最开始的时候,TCP并不考虑性能,效率,公平性,正是考虑了这些,TCP协议才复杂了起来。

3.TCP协议

这是一个纯软件协议,为何将其设计上两个端点,参见上一小节,本节详述TCP协议,中间也穿插一些简短的论述。

3.1.TCP协议

确切的说,TCP协议有两重身份,作为网络协议,它弥补了IP协议尽力而为服务的不足,实现了有连接,可靠传输,报文按序到达。作为一个主机软件,它和UDP以及左右的传输层协议隔离了主机服务和网络,它们可以被看做是一个多路复用/解复用器,将诸多的主机进程数据复用/解复用到IP层。可以看出,不管从哪个角度,TCP都作为一个接口存在,作为网络协议,它和对端的TCP接口,实现TCP的控制逻辑,作为多路复用/解复用器,它和下层IP协议接口,实现协议栈的功能,而这正是分层网络协议模型的基本定义(两类接口,一类和下层接口,另一类和对等层接口)。

我们习惯于将TCP作为协议栈的最顶端,而不把应用层协议当成协议栈的一部分,这部分是因为应用层被TCP/UDP解复用了之后,呈现出了一种太复杂的局面,应用层协议用一种不同截然不同的方式被解释,应用层协议习惯于用类似ASN.1标准来封装,这正体现了TCP协议作为多路复用/解复用器的重要性,由于直接和应用接口,它可以很容易直接被应用控制,实现不同的传输控制策略,这也是TCP被设计到离应用不太远的地方的原因之一。

总之, TCP要点有四,一曰有连接,二曰可靠传输,三曰数据按照到达,四曰端到端流量控制。注意,TCP被设计时只保证这四点,此时它虽然也有些问题,然而很简单,然而更大的问题很快呈现出来,使之不得不考虑和IP网络相关的东西,比如公平性,效率,因此增加了拥塞控制,这样TCP就成了现在这个样子。

3.2.有连接,可靠传输,数据按序到达的TCP

IP协议是没有方向的,数据报传输能到达对端全靠路由,因此它是一跳一跳地到达对端的,只要有一跳没有到达对端的路由,那么数据传输将失败,其实路由也是互联网的核心之一,实际上IP层提供的核心基本功能有两点,第一点是地址管理,第二点就是路由选路。TCP利用了IP路由这个简单的功能,因此TCP不必考虑选路,这又一个它被设计成端到端协议的原因。

既然IP已经能尽力让单独的数据报到达对端,那么TCP就可以在这种尽力而为的网络上实现其它的更加严格的控制功能。TCP给无连接的IP网络通信增加了连接性,确认了已经发送出去的数据的状态,并且保证了数据的顺序。

3.2.1.有连接

这是TCP的基本,因为后续的传输的可靠性以及数据顺序性都依赖于一条连接,这是最简单的实现方式,因此TCP被设计成一种基于流的协议,既然TCP需要事先建立连接,之后传输多少数据就无所谓了,只要是同一连接的数据能识别出来即可。

疑难杂症1:3次握手和4次挥手

TCP使用3次握手建立一条连接,该握手初始化了传输可靠性以及数据顺序性必要的信息,这些信息包括两个方向的初始序列号,确认号由初始序列号生成,使用3次握手是因为3次握手已经准备好了传输可靠性以及数据顺序性所必要的信息,该握手的第3次实际上并不是需要单独传输的,完全可以和数据一起传输。

TCP使用4次挥手拆除一条连接,为何需要4次呢?因为TCP是一个全双工协议,必须单独拆除每一条信道。注意,4次挥手和3次握手的意义是不同的,很多人都会问为何建立连接是3次握手,而拆除连接是4次挥手。 3次握手的目的很简单,就是分配资源,初始化序列号,这时还不涉及数据传输,3次就足够做到这个了,而4次挥手的目的是终止数据传输,并回收资源,此时两个端点两个方向的序列号已经没有了任何关系,必须等待两方向都没有数据传输时才能拆除虚链路,不像初始化时那么简单,发现SYN标志就初始化一个序列号并确认SYN的序列号。因此必须单独分别在一个方向上终止该方向的数据传输。

疑难杂症2:TIME_WAIT状态

为何要有这个状态, 原因很简单,那就是每次建立连接的时候序列号都是随机产生的,并且这个序列号是32位的,会回绕。现在我来解释这和TIME_WAIT有什么关系。

任何的TCP分段都要在尽力而为的IP网络上传输,中间的路由器可能会随意的缓存任何的IP数据报,它并不管这个IP数据报上被承载的是什么数据,然而根据经验和互联网的大小,一个IP数据报最多存活MSL(这是根据地球表面积,电磁波在各种介质中的传输速率以及IP协议的TTL等综合推算出来的,如果在火星上,这个MSL会大得多…)。

现在我们考虑终止连接时的被动方发送了一个FIN,然后主动方回复了一个ACK,然而这个ACK可能会丢失,这会造成被动方重发FIN,这个FIN可能会在互联网上存活MSL。

如果没有TIME_WAIT的话,假设连接1已经断开,然而其被动方最后重发的那个FIN(或者FIN之前发送的任何TCP分段)还在网络上,然而连接2重用了连接1的所有的5元素(源IP,目的IP,TCP,源端口,目的端口),刚刚将建立好连接,连接1迟到的FIN到达了,这个FIN将以比较低但是确实可能的概率终止掉连接2.

为何说是概率比较低呢?这涉及到一个匹配问题,迟到的FIN分段的序列号必须落在连接2的一方的期望序列号范围之内。虽然这种巧合很少发生,但确实会发生,毕竟初始序列号是随机产生了。因此终止连接的主动方必须在接受了被动方且回复了ACK之后等待2*MSL时间才能进入CLOSE状态,之所以乘以2是因为这是保守的算法,最坏情况下,针对被动方的ACK在以最长路线(经历一个MSL)经过互联网马上到达被动方时丢失。

为了应对这个问题,RFC793对初始序列号的生成有个建议,那就是设定一个基准,在这个基准之上搞随机,这个基准就是时间,我们知道时间是单调递增的。然而这仍然有问题,那就是回绕问题,如果发生回绕,那么新的序列号将会落到一个很低的值。 因此最好的办法就是避开“重叠”,其含义就是基准之上的随机要设定一个范围。

要知道,很多人很不喜欢看到服务器上出现大量的TIME_WAIT状态的连接,因此他们将TIME_WAIT的值设置的很低,这虽然在大多数情况下可行,然而确实也是一种冒险行为。最好的方式就是,不要重用一个连接。

疑难杂症3:重用一个连接和重用一个套接字

这是根本不同的,单独重用一个套接字一般不会有任何问题,因为TCP是基于连接的。比如在服务器端出现了一个TIME_WAIT连接,那么该连接标识了一个五元素,只要客户端不使用相同的源端口,连接服务器是没有问题的,因为迟到的FIN永远不会到达这个连接。记住,一个五元素标识了一个连接,而不是一个套接字(当然,对于BSD套接字而言,服务端的accept套接字确实标识了一个连接)。

3.2.2.传输可靠性

基本上传输可靠性是靠确认号实现的,也就是说,每发送一个分段,接下来接收端必然要发送一个确认,发送端收到确认后才可以发送下一个字节。这个原则最简单不过了,教科书上的“停止-等待”协议就是这个原则的字节版本,只是TCP使用了滑动窗口机制使得每次不一定发送一个字节,但是这是后话,本节仅仅谈一下确认的超时机制。

怎么知道数据到达对端呢?那就是对端发送一个确认,但是如果一直收不到对端的确认,发送端等多久呢?如果一直等下去,那么将无法发现数据的丢失,协议将不可用,如果等待时间过短,可能确认还在路上,因此等待时间是个问题,另外如何去管理这个超时时间也是一个问题。

疑难杂症4:超时时间的计算

绝对不能随意去揣测超时的时间,而应该给出一个精确的算法去计算。毫无疑问,一个TCP分段的回复到达的时间就是一个数据报往返的时间,因此标准定义了一个新的名词RTT,代表一个TCP分段的往返时间。然而我们知道,IP网络是尽力而为的,并且路由是动态的,且路由器会毫无先兆的缓存或者丢弃任何的数据报,因此这个RTT是需要动态测量的,也就是说起码每隔一段时间就要测量一次,如果每次都一样,万事大吉,然而世界并非如你所愿,因此我们需要找到的恰恰的一个“平均值”,而不是一个准确值。

这个平均值如果仅仅直接通过计算多次测量值取算术平均,那是不恰当的,因为对于数据传输延时,我们必须考虑的路径延迟的瞬间抖动,否则如果两次测量值分别为2和98,那么超时值将是50,这个值对于2而言,太大了,结果造成了数据的延迟过大(本该重传的等待了好久才重传),然而对于98而言,太小了,结果造成了过度重传(路途遥远,本该很慢,结果大量重传已经正确确认但是迟到的TCP分段)。

因此, 除了考虑每两次测量值的偏差之外,其变化率也应该考虑在内,如果变化率过大,则通过以变化率为自变量的函数为主计算RTT(如果陡然增大,则取值为比较大的正数,如果陡然减小,则取值为比较小的负数,然后和平均值加权求和),反之如果变化率很小,则取测量平均值。这是不言而喻的,这个算法至今仍然工作的很好。

疑难杂症5:超时计时器的管理-每连接单一计时器

很显然,对每一个TCP分段都生成一个计时器是最直接的方式,每个计时器在RTT时间后到期,如果没有收到确认,则重传。然而这只是理论上的合理,对于大多数操作系统而言,这将带来巨大的内存开销和调度开销,因此采取每一个TCP连接单一计时器的设计则成了一个默认的选择。可是单一的计时器怎么管理如此多的发出去的TCP分段呢?又该如何来设计单一的计时器呢。

设计单一计时器有两个原则:1.每一个报文在长期收不到确认都必须可以超时;2.这个长期收不到中长期不能和测量的RTT相隔太远。因此RFC2988定义一套很简单的原则:

a.发送TCP分段时,如果还没有重传定时器开启,那么开启它。
b.发送TCP分段时,如果已经有重传定时器开启,不再开启它。
c.收到一个非冗余ACK时,如果有数据在传输中,重新开启重传定时器。
d.收到一个非冗余ACK时,如果没有数据在传输中,则关闭重传定时器。

我们看看这4条规则是如何做到以上两点的,根据a和c(在c中,注意到ACK是非冗余的),任何TCP分段只要不被确认,超时定时器总会超时的。然而为何需要c呢?只有规则a存在的话,也可以做到原则1。实际上确实是这样的,但是为了不会出现过早重传,才添加了规则c,如果没有规则c,那么万一在重传定时器到期前,发送了一些数据,这样在定时器到期后,除了很早发送的数据能收到ACK外,其它稍晚些发送的数据的ACK都将不会到来,因此这些数据都将被重传。有了规则c之后,只要有分段ACK到来,则重置重传定时器,这很合理,因此大多数正常情况下,从数据的发出到ACK的到来这段时间以及计算得到的RTT以及重传定时器超时的时间这三者相差并不大,一个ACK到来后重置定时器可以保护后发的数据不被过早重传。

这里面还有一些细节需要说明。一个ACK到来了,说明后续的ACK很可能会依次到来,也就是说丢失的可能性并不大,另外,即使真的有后发的TCP分段丢失现象发生,也会在最多2倍定时器超时时间的范围内被重传(假设该报文是第一个报文发出启动定时器之后马上发出的,丢失了,第一个报文的ACK到来后又重启了定时器,又经过了一个超时时间才会被重传)。虽然这里还没有涉及拥塞控制,但是可见网络拥塞会引起丢包,丢包会引起重传,过度重传反过来加重网络拥塞,设置规则c的结果可以缓解过多的重传,毕竟将启动定时器之后发送的数据的重传超时时间拉长了最多一倍左右。最多一倍左右的超时偏差做到了原则2,即“这个长期收不到中长期不能和测量的RTT相隔太远”。

还有一点,如果是一个发送序列的最后一个分段丢失了,后面就不会收到冗余ACK,这样就只能等到超时了,并且超时时间几乎是肯定会比定时器超时时间更长。如果这个分段是在发送序列的靠后的时间发送的且和前面的发送时间相隔时间较远,则其超时时间不会很大,反之就会比较大。

疑难杂症6:何时测量RTT

目前很多TCP实现了时间戳,这样就方便多了,发送端再也不需要保存发送分段的时间了,只需要将其放入协议头的时间戳字段,然后接收端将其回显在ACK即可,然后发送端收到ACK后,取出时间戳,和当前时间做算术差,即可完成一次RTT的测量。

3.2.3.数据顺序性

基本上传输可靠性是靠序列号实现的。

疑难杂症7:确认号和超时重传

确认号是一个很诡异的东西,因为TCP的发送端对于发送出去的一个数据序列,它只要收到一个确认号就认为确认号前面的数据都被收到了,即使前面的某个确认号丢失了,也就是说,发送端只认最后一个确认号。这是合理的,因为确认号是接收端发出的,接收端只确认按序到达的最后一个TCP分段。

另外,发送端重发了一个TCP报文并且接收到该TCP分段的确认号,并不能说明这个重发的报文被接收了,也可能是数据早就被接收了,只是由于其ACK丢失或者其ACK延迟到达导致了超时。值得说明的是,接收端会丢弃任何重复的数据,即使丢弃了重复的数据,其ACK还是会照发不误的。

标准的早期TCP实现为,只要一个TCP分段丢失,即使后面的TCP分段都被完整收到,发送端还是会重传从丢失分段开始的所有报文,这就会导致一个问题,那就是重传风暴,一个分段丢失,引起大量的重传。这种风暴实则不必要的,因为大多数的TCP实现中,接收端已经缓存了乱序的分段,这些被重传的丢失分段之后的分段到达接收端之后,很大的可能性是被丢弃。关于这一点在拥塞控制被引入之后还会提及(问题先述为快:本来报文丢失导致超时就说明网络很可能已然拥塞,重传风暴只能加重其拥塞程度)。

疑难杂症8:乱序数据缓存以及选择确认

TCP是保证数据顺序的,但是并不意味着它总是会丢弃乱序的TCP分段,具体会不会丢弃是和具体实现相关的,RFC建议如果内存允许,还是要缓存这些乱序到来的分段,然后实现一种机制等到可以拼接成一个按序序列的时候将缓存的分段拼接,这就类似于IP协议中的分片一样,但是由于IP数据报是不确认的,因此IP协议的实现必须缓存收到的任何分片而不能将其丢弃,因为丢弃了一个IP分片,它就再也不会到来了。

现在,TCP实现了一种称为 选择确认的方式,接收端会显式告诉发送端需要重传哪些分段而不需要重传哪些分段。这无疑避免了重传风暴。

疑难杂症9:TCP序列号的回绕的问题

TCP的序列号回绕会引起很多的问题,比如序列号为s的分段发出之后,m秒后,序列号比s小的序列号为j的分段发出,只不过此时的j比上一个s多了一圈,这就是回绕问题,那么如果这后一个分段到达接收端,这就会引发彻底乱序-本来j该在s后面,结果反而到达前面了,这种乱序是TCP协议检查不出来的。我们仔细想一下,这种情况确实会发生,数据分段并不是一个字节一个字节发送出去的,如果存在一个速率为1Gbps的网络,TCP发送端1秒会发送125MB的数据,32位的序列号空间能传输2的32次方个字节,也就是说32秒左右就会发生回绕,我们知道这个值远小于MSL值,因此会发生的。

有个细节可能会引起误会,那就是TCP的窗口大小空间是序列号空间的一半,这样恰好在满载情况下,数据能填满发送窗口和接收窗口,序列号空间正好够用。然而事实上,TCP的初始序列号并不是从0开始的,而是随机产生的(当然要辅助一些更精妙的算法),因此如果初始序列号比较接近2的32次方,那么很快就会回绕。

当然,如今可以用时间戳选项来辅助作为序列号的一个识别的部分,接收端遇到回绕的情况,需要比较时间戳,我们知道,时间戳是单调递增的,虽然也会回绕,然而回绕时间却要长很多。这只是一种策略,在此不详谈。还有一个很现实的问题,理论上序列号会回绕,但是实际上,有多少TCP的端点主机直接架设在1G的网络线缆两端并且接收方和发送方的窗口还能恰好被同时填满。另外,就算发生了回绕,也不是一件特别的事情,回绕在计算机里面太常见了,只需要能识别出来即可解决,对于TCP的序列号而言,在高速网络(点对点网络或者以太网)的两端,数据发生乱序的可能性很小,因此当收到一个序列号突然变为0或者终止序列号小于起始序列号的情况后,很容易辨别出来,只需要和前一个确认的分段比较即可,如果在一个经过路由器的网络两端,会引发IP数据报的顺序重排,对于TCP而言,虽然还会发生回绕,也会慢得多,且考虑到拥塞窗口(目前还没有引入)一般不会太大,窗口也很难被填满到65536。

3.2.4.端到端的流量控制

端到端的流量控制使用滑动窗口来实现。滑动窗口的原理非常简单,基本就是一个生产者/消费者模型

疑难杂症10:流量控制的真实意义

很多人以为流量控制会很有效的协调两端的流量匹配,确实是这样,但是如果你考虑到网络的利用率问题,TCP的流量控制机制就不那么完美了,造成这种局面的原因在于,滑动窗口只是限制了最大发送的数据,却没有限制最小发送的数据,结果导致一些很小的数据被封装成TCP分段,报文协议头所占的比例过于大,造成网络利用率下降,这就引出了接下来的内容,那就是端到端意义的TCP协议效率。

~~~~~~~~~~~~~~~~~~~~
承上启下
终于到了阐述问题的时候了,以上的TCP协议实现的非常简单,这也是TCP的标准实现,然而很快我们就会发现各种各样的问题。这些问题导致了标准化协会对TCP协议进行了大量的修补,这些修补杂糅在一起让人们有些云里雾里,不知所措。本文档就旨在分离这些杂乱的情况,实际上,根据RFC,这些杂乱的情况都是可以找到其单独的发展轨迹的。
~~~~~~~~~~~~~~~~~~~~

4.端到端意义上的TCP协议效率

4.1.三个问题以及解决

问题1描述:接收端处理慢,导致接收窗口被填满

这明显是速率不匹配引发的问题,然而即使速率不匹配,只要滑动窗口能协调好它们的速率就好,要快都快,要慢都慢,事实上滑动窗口在这一点上做的很好。但是如果我们不得不从效率上来考虑问题的话,事实就不那么乐观了。考虑此时接收窗口已然被填满,慢速的应用程序慢腾腾的读取了一个字节,空出一个位置,然后通告给TCP的发送端,发送端得知空出一个位置,马上发出一个字节,又将接收端填满,然后接收应用程序又一次慢腾腾…这就是糊涂窗口综合症,一个大多数人都很熟悉的词。这个问题极大的浪费了网络带宽,降低了网络利用率。好比从大同拉100吨煤到北京需要一辆车,拉1Kg煤到北京也需要一辆车(超级夸张的一个例子,请不要相信),但是一辆车开到北京的开销是一定的…

问题1解决:窗口通告

对于问题1,很显然问题出在接收端,我们没有办法限制发送端不发送小分段,但是却可以限制接收端通告小窗口,这是合理的,这并不影响应用程序,此时经典的延迟/吞吐量反比律将不再适用,因为接收窗口是满的,其空出一半空间表示还有一半空间有数据没有被应用读取,和其空出一个字节的空间的效果是一样的,因此可以限制接收端当窗口为0时,直接通告给发送端以阻止其继续发送数据,只有当其接收窗口再次达到MSS的一半大小的时候才通告一个不为0的窗口,此前对于所有的发送端的窗口probe分段(用于探测接收端窗口大小的probe分段,由TCP标准规定),全部通告窗口为0,这样发送端在收到窗口不为0的通告,那么肯定是一个比较大的窗口,因此发送端可以一次性发出一个很大的TCP分段,包含大量数据,也即拉了好几十吨的煤到北京,而不是只拉了几公斤。即,限制窗口通告时机,解决糊涂窗口综合症

问题2描述:发送端持续发送小包,导致窗口闲置

这明显是发送端引起的问题,此时接收端的窗口开得很大,然而发送端却不积累数据,还是一味的发送小块数据分段。只要发送了任和的分段,接收端都要无条件接收并且确认,这完全符合TCP规范,因此必然要限制发送端不发送这样的小分段。

问题2解决:Nagle算法
Nagel算法很简单,标准的Nagle算法为:

IF 数据的大小和窗口的大小都超过了MSS
    Then 发送数据分段
ELSE
    IF 还有发出的TCP分段的确认没有到来
        Then 积累数据到发送队列的末尾的TCP分段
    ELSE
        发送数据分段
    EndIF
EndIF

可是后来,这个算法变了,变得更加灵活了,其中的:
IF 还有发出的TCP分段的确认没有到来
变成了
IF 还有发出的不足MSS大小的TCP分段的确认没有到来
这样如果发出了一个MSS大小的分段还没有被确认,后面也是可以随时发送一个小分段的,这个改进降低了算法对延迟时间的影响。这个算法体现了一种自适应的策略,越是确认的快,越是发送的快,虽然Nagle算法看起来在积累数据增加吞吐量的同时也加大的时延,可事实上,如果对于类似交互式的应用,时延并不会增加,因为这类应用回复数据也是很快的,比如Telnet之类的服务必然需要回显字符,因此能和对端进行自适应协调。
     注意,Nagle算法是默认开启的,但是却可以关闭。如果在开启的情况下,那么它就严格按照上述的算法来执行。
问题3.确认号(ACK)本身就是不含数据的分段,因此大量的确认号消耗了大量的带宽
这是TCP为了确保可靠性传输的规范,然而大多数情况下,ACK还是可以和数据一起捎带传输的。如果没有捎带传输,那么就只能单独回来一个ACK,如果这样的分段太多,网络的利用率就会下降。从大同用火车拉到北京100吨煤,为了确认煤已收到,北京需要派一辆同样的火车空载开到大同去复命,因为没有别的交通工具,只有火车。如果这位复命者刚开着一列火车走,又从大同来了一车煤,这拉煤的哥们儿又要开一列空车去复命了。
问题3的解决:
RFC建议了一种延迟的ACK,也就是说,ACK在收到数据后并不马上回复,而是延迟一段可以接受的时间,延迟一段时间的目的是看能不能和接收方要发给发送方的数据一起回去,因为TCP协议头中总是包含确认号的,如果能的话,就将ACK一起捎带回去,这样网络利用率就提高了。往大同复命的确认者不必开一辆空载火车回大同了,此时北京正好有一批货物要送往大同,这位复命者搭着这批货的火车返回大同。
     如果等了一段可以接受的时间,还是没有数据要发往发送端,此时就需要单独发送一个ACK了,然而即使如此,这个延迟的ACK虽然没有等到可以被捎带的数据分段,也可能等到了后续到来的TCP分段,这样它们就可以取最大者一起返回了,要知道,TCP的确认号是收到的按序报文的最后一个字节的后一个字节。最后,RFC建议,延迟的ACK最多等待两个分段的积累确认。

4.2.分析三个问题之间的关联

三个问题导致的结果是相同的,但是要知道它们的原因本质上是不同的,问题1几乎总是出现在接收端窗口满的情况下,而问题2几乎总是发生在窗口闲置的情况下,问题3看起来是最无聊的,然而由于TCP的要求,必须要有确认号,而且一个确认号就需要一个TCP分段,这个分段不含数据,无疑是很小的。

三个问题都导致了网络利用率的降低。虽然两个问题导致了同样的结果,但是必须认识到它们是不同的问题,很自然的将这些问题的解决方案汇总在一起,形成一个全局的解决方案,这就是如今的操作系统中的解决方案。

4.3.问题的杂糅情况

疑难杂症11:糊涂窗口解决方案和Nagle算法

糊涂窗口综合症患者希望发送端积累TCP分段,而Nagle算法确实保证了一定的TCP分段在发送端的积累,另外在延迟ACK的延迟的那一会时间,发送端会利用这段时间积累数据。然而这却是三个不同的问题。Nagle算法可以缓解糊涂窗口综合症,却不是治本的良药。

疑难杂症12:Nagle算法和延迟ACK

延迟ACK会延长ACK到达发送端的时间,由于标准Nagle算法只允许一个未被确认的TCP分段,那无疑在接收端,这个延迟的ACK是毫无希望等待后续数据到来最终进行积累确认的,如果没有数据可以捎带这个ACK,那么这个ACK只有在延迟确认定时器超时的时候才会发出,这样在等待这个ACK的过程中,发送端又积累了一些数据,因此延迟ACK实际上是在增加延迟的代价下加强了Nagle算法。在延迟ACK加Nagle算法的情况下,接收端只有不断有数据要发回,才能同时既保证了发送端的分段积累,又保证了延迟不增加,同时还没有或者很少有空载的ACK。

要知道,延迟ACK和Nagle是两个问题的解决方案。

疑难杂症13:到底何时可以发送数据

到底何时才能发送数据呢?如果单从Nagle算法上看,很简单,然而事实证明,情况还要更复杂些。如果发送端已经排列了3个TCP分段,分段1,分段2,分段3依次被排入,三个分段都是小分段(不符合Nagle算法中立即发送的标准),此时已经有一个分段被发出了,且其确认还没有到来,请问此时能发送分段1和2吗?如果按照Nagle算法,是不能发送的,但实际上它们是可以发送的,因为这两个分段已经没有任何机会再积累新的数据了,新的数据肯定都积累在分段3上了。问题在于,分段还没有积累到一定大小时,怎么还可以产生新的分段?这是可能的,但这是另一个问题,在此不谈。

Linux的TCP实现在这个问题上表现的更加灵活,它是这么判断能否发送的(在开启了Nagle的情况下):

IF (没有超过拥塞窗口大小的数据分段未确认 || 数据分段中包含FIN ) &&
    数据分段没有超越窗口边界
    Then
    IF 分段在中间(上述例子中的分段1和2) ||
           分段是紧急模式            ||
       通过上述的Nagle算法(改进后的Nagle算法)
        Then 发送分段
    EndIF
EndIF

曾经我也改过Nagle算法,确切的说不是修改Nagle算法,而是修改了“到底何时能发送数据”的策略,以往都是发送端判断能否发送数据的,可是如果此时有延迟ACK在等待被捎带,而待发送的数据又由于积累不够或者其它原因不能发送,因此两边都在等,这其实在某些情况下不是很好。我所做的改进中对待何时能发送数据又增加了一种情况,这就是“ACK拉”的情况,一旦有延迟ACK等待发送,判断一下有没有数据也在等待发送,如果有的话,看看数据是否大到了一定程度,在此,我选择的是MSS的一半:

IF (没有超过拥塞窗口大小的数据分段未确认 || 数据分段中包含FIN ) &&
     数据分段没有超越窗口边界                      
    Then
    IF 分段在中间(上述例子中的分段1和2) ||
           分段是紧急模式            ||
       通过上述的Nagle算法(改进后的Nagle算法)
        Then 发送分段
    EndIF
ELSE IF 有延迟ACK等待传输                &&
    发送队列中有待发送的TCP分段       &&
    发送队列的头分段大小大于MSS的一半
        Then 发送队列头分段且捎带延迟ACK
EndIF

另外,发送队列头分段的大小是可以在统计意义上动态计算的,也不一定非要是MSS大小的一半。我们发现,这种算法对于交互式网路应用是自适应的,你打字越快,特定时间内积累的分段就越长,对端回复的越快(可以捎带ACK),本端发送的也就越快(以Echo举例会更好理解)。 

疑难杂症14:《TCP/IP详解(卷一)》中Nagle算法的例子解读

这个问题在网上搜了很多的答案,有的说RFC的建议,有的说别的。可是实际上这就是一个典型的“竞态问题”:
首先服务器发了两个分段:
数据段12:ack 14
数据段13:ack 14,54:56
然后客户端发了两个分段:
数据段14:ack 54,14:17
数据段15:ack 56,17:18

可以看到数据段14本来应该确认56的,但是确认的却是54。也就是说,数据段已经移出队列将要发送但还未发送的时候,数据段13才到来,软中断处理程序抢占了数据段14的发送进程,要知道此时只是把数据段14移出了队列,还没有更新任何的状态信息,比如“发出但未被确认的分段数量”,此时软中断处理程序顺利接收了分段13,然后更新窗口信息,并且检查看有没有数据要发送,由于分段14已经移出队列,下一个接受发送检查的就是分段15了,由于状态信息还没有更新,因此分段15顺利通过发送检测,发送完成。

可以看Linux的源代码了解相关信息,tcp_write_xmit这个函数在两个地方会被调用,一个是TCP的发送进程中,另一个就是软中断的接收处理中,两者在调用中的竞态就会引起《详解》中的那种情况。注意,这种不加锁的发送方式是合理的,也是最高效的,因此TCP的处理语义会做出判断,丢弃一切不该接收或者重复接收的分段的

~~~~~~~~~~~~~~~~~
承上启下
又到了该承上启下,到此为止,我们叙述的TCP还都是简单的TCP,就算是简单的TCP,也存在上述的诸多问题,就更别提继续增加TCP的复杂性了。到此为止,我们的TCP都是端到端意义上的,然而实际上TCP要跑在IP网络之上的,而IP网络的问题是很多的,是一个很拥堵网络。不幸的是,TCP的有些关于确认和可靠性的机制还会加重IP网络的拥堵。
~~~~~~~~~~~~~~~~~~~~

5.IP网络之上的TCP

5.1.端到端的TCP协议和IP协议之间的矛盾

端到端的TCP只能看到两个节点,那就是自己和对方,它们是看不到任何中间的路径的。可是IP网络却是一跳一跳的,它们的矛盾之处在于TCP的端到端流量控制必然会导致网络拥堵。因为每条TCP连接的一端只知道它对端还有多少空间用于接收数据,它们并不管到达对端的路径上是否还有这么大的容量,事实上所有连接的这些空间加在一起将瞬间超过IP网络的容量,因此TCP也不可能按照滑动窗口流量控制机制很理想的运行。

势必需要一种拥塞控制机制,反应路径的拥塞情况。

疑难杂症15:拥塞控制的本质

由于TCP是端到端协议,因此两端之间的控制范畴属于流量控制,IP网络的拥塞会导致TCP分段的丢失,由于TCP看不到中间的路由器,因此这种丢失只会发生中间路由器,当然两个端点的网卡或者IP层丢掉数据分段也是TCP看不到的。因此拥塞控制必然作用于IP链路。事实上我们可以得知,只有在以下情况下拥塞控制才会起作用:

a.两个或两个以上的连接(其中一个一定要是TCP,另一个可以是任意连接)经过同一个路由器或者同一个链路时;
b.只有一个TCP连接,然而它经过了一个路由器时。

其它情况下是不会拥塞的。因为一个TCP总是希望独享整条网络通路,而这对于多个连接而言是不可能的,必须保证TCP的公平性,这样这种拥塞控制机制才合理。本质上,拥塞的原因就是大家都想独享全部带宽资源,结果导致拥塞,这也是合理的,毕竟TCP看不到网络的状态,同时这也决定了TCP的拥塞控制必须采用试探性的方式,最终到达一个足以引起其“反应”的“刺激点”。

拥塞控制需要完成以下两个任务:1.公平性;2.拥塞之后退出拥塞状态。

疑难杂症16:影响拥塞的因素

我们必须认识到拥塞控制是一个整体的机制,它不偏向于任何TCP连接,因此这个机制内在的就包含了公平性。那么影响拥塞的因素都有什么呢?具有讽刺意味的是,起初TCP并没有拥塞控制机制,正是TCP的超时重传风暴(一个分段丢失造成后续的已经发送的分段均被重传,而这些重传大多数是不必要的)加重了网络的拥塞。因此重传必然不能过频,必须把重传定时器的超时时间设置的稍微长一些,而这一点在单一重传定时器的设计中得到了加强。除此TCP自身的因素之外,其它所有的拥塞都可以靠拥塞控制机制来自动完成。

另外,不要把路由器想成一种线速转发设备,再好的路由器只要接入网络,总是会拉低网络的总带宽,因此即使只有一个TCP连接,由于TCP的发送方总是以发送链路的带宽发送分段,这些分段在经过路由器的时候排队和处理总是会有时延,因此最终肯定会丢包的。

最后,丢包的延后性也会加重拥塞。假设一个TCP连接经过了N个路由器,前N-1个路由器都能顺利转发TCP分段,但是最后一个路由器丢失了一个分段,这就导致了这些丢失的分段浪费了前面路由器的大量带宽。

5.2.拥塞控制的策略

在介绍拥塞控制之前,首先介绍一下拥塞窗口,它实际上表示的也是“可以发送多少数据”,然而这个和接收端通告的接收窗口意义是不一样的,后者是流量控制用的窗口,而前者是拥塞控制用的窗口,体现了网络拥塞程度。

拥塞控制整体上分为两类,一类是试探性的拥塞探测,另一类则是拥塞避免(注意,不是常规意义上的拥塞避免)。

5.2.1.试探性的拥塞探测分为两类,之一是慢启动,之二是拥塞窗口加性扩大(也就是熟知的拥塞避免,然而这种方式是避免不了拥塞的)。
5.2.2.拥塞避免方式拥塞控制旨在还没有发生拥塞的时候就先提醒发送端,网络拥塞了,这样发送端就要么可以进入快速重传/快速恢复或者显式的减小拥塞窗口,这样就避免网络拥塞的一沓糊涂之后出现超时,从而进入慢启动阶段。
5.2.3.快速重传和快速恢复。所谓快速重传/快速恢复是针对慢启动的,我们知道慢启动要从1个MSS开始增加拥塞窗口,而快速重传/快速恢复则是一旦收到3个冗余ACK,不必进入慢启动,而是将拥塞窗口缩小为当前阀值的一半加上3,然后如果继续收到冗余ACK,则将拥塞窗口加1个MSS,直到收到一个新的数据ACK,将窗口设置成正常的阀值,开始加性增加的阶段。
 

当进入快速重传时,为何要将拥塞窗口缩小为当前阀值的一半加上3呢?加上3是基于数据包守恒来说的,既然已经收到了3个冗余ACK,说明有三个数据分段已经到达了接收端,既然三个分段已经离开了网络,那么就是说可以在发送3个分段了,只要再收到一个冗余ACK,这也说明1个分段已经离开了网络,因此就将拥塞窗口加1个MSS。直到收到新的ACK,说明直到收到第三个冗余ACK时期发送的TCP分段都已经到达对端了,此时进入正常阶段开始加性增加拥塞窗口。

疑难杂症17:超时重传和收到3个冗余ACK后重传

这两种重传的意义是不同的,超时重传一般是因为网络出现了严重拥塞(没有一个分段到达,如果有的话,肯定会有ACK的,若是正常ACK,则重置重传定时器,若是冗余ACK,则可能是个别报文丢失或者被重排序,若连续3个冗余ACK,则很有可能是个别分段丢失),此时需要更加严厉的缩小拥塞窗口,因此此时进入慢启动阶段。而收到3个冗余ACK后说明确实有中间的分段丢失,然而后面的分段确实到达了接收端,这因为这样才会发送冗余ACK,这一般是路由器故障或者轻度拥塞或者其它不太严重的原因引起的,因此此时拥塞窗口缩小的幅度就不能太大,此时进入快速重传/快速恢复阶段。

疑难杂症18:为何收到3个冗余ACK后才重传

这是一种权衡的结构,收到两个或者一个冗余ACK也可以重传,但是这样的话可能或造成不必要的重传,因为两个数据分段发生乱序的可能性不大,超过三个分段发生乱序的可能性才大,换句话说,如果仅仅收到一个乱序的分段,那很可能被中间路由器重排了,那么另一个分段很可能马上就到,然而如果连续收到了3个分段都没能弥补那个缺漏,那很可能是它丢失了,需要重传。 因此3个冗余ACK是一种权衡,在减少不必要重传和确实能检测出单个分段丢失之间所作的权衡。

注意,冗余ACK是不能捎带的。

疑难杂症19:乘性减和加性增的深层含义

为什么是乘性减而加性增呢?拥塞窗口的增加受惠的只是自己,而拥塞窗口减少受益的大家,可是自己却受到了伤害。哪一点更重要呢?我们知道TCP的拥塞控制中内置了公平性,恰恰就是这种乘性减实现了公平性。拥塞窗口的1个MSS的改变影响一个TCP发送者,为了使得自己拥塞窗口的减少影响更多的TCP发送者-让更多的发送者受益,那么采取了乘性减的策略。

当然,BIC算法提高了加性增的效率,不再一个一个MSS的加,而是一次加比较多的MSS,采取二分查找的方式逐步找到不丢包的点,然后加性增。

疑难杂症20:TCP连接的传输稳定状态是什么

首先,先说一下发送端的发送窗口怎么确定,它取的是拥塞窗口和接收端通告窗口的最小值。然后,我们提出三种发送窗口的稳定状态:

a.IP互联网络上接收端拥有大窗口的经典锯齿状
b.IP互联网络上接收端拥有小窗口的直线状态
c.直连网络端点间的满载状态下的直线状态

其中a是大多数的状态,因为一般而言,TCP连接都是建立在互联网上的,而且是大量的,比如Web浏览,电子邮件,网络游戏,Ftp下载等等。TCP发送端用慢启动或者拥塞避免方式不断增加其拥塞窗口,直到丢包的发生,然后进入慢启动或者拥塞避免阶段(要看是由于超时丢包还是由于冗余ACK丢包),此时发送窗口将下降到1或者下降一半,这种情况下,一般接收端的接收窗口是比较大的,毕竟IP网络并不是什么很快速的网络,一般的机器处理速度都很快。

但是如果接收端特别破,处理速度很慢,就会导致其通告一个很小的窗口,这样的话,即使拥塞窗口再大,发送端也还是以通告的接收窗口为发送窗口,这样就不会发生拥塞。最后,如果唯一的TCP连接运行在一个直连的两台主机上,那么它将独享网络带宽,这样该TCP的数据流在最好的情况下将填满网络管道(我们把网络管道定义为带宽和延时的乘积),其实在这种情况下是不存在拥塞的,就像你一个人独自徘徊在飘雨黄昏的街头一样…

5.2.4.主动的拥塞避免

前面我们描述的拥塞控制方式都是试探性的检测,然后拥塞窗口被动的进行乘性减,这样在接收端窗口很大的情况下(一般都是这样,网络拥堵,分段就不会轻易到达接收端,导致接收端的窗口大量空置)就可能出现锯齿形状的“时间-窗口”图,类似在一个拥堵的北京X环上开车,发送机发动,车开动,停止,等待,发动机发动,车开动…听声音也能听出来。

虽然TCP看不到下面的IP网络,然而它还是可以通过检测RTT的变化以及拥塞窗口的变化推算出IP网络的拥堵情况的。就比方说北京东四环一家快递公司要持续送快递到西四环,当发件人发现货到时间越来越慢的时候,他会意识到“下班高峰期快到了”…

可以通过持续观测RTT的方式来主动调整拥塞窗口的大小而不是一味的加性增。然而还有更猛的算法,那就是计算两个差值的乘积:

(当前拥塞窗口-上一次拥塞窗口)x(当前的RTT-上一次的RTT)

如果结果是正数,则拥塞窗口减少1/8,若结果是负数或者0,则窗口增加一个MSS。注意,这回不再是乘性减了,可以看出,减的幅度比乘性减幅度小,这是因为这种拥塞控制是主动的,而不是之前的那种被动的试探方式。在试探方式中,乘性减以一种惩罚的方式实现了公平性,而在这里的主动方式中,当意识到要拥塞的时候,TCP发送者主动的减少了拥塞窗口,为了对这种自首行为进行鼓励,采用了小幅减少拥塞窗口的方式。需要注意的是,在拥塞窗口减小的过程中,乘积的前一个差值是负数,如果后一个差值也是负数,那么结果就是继续缩减窗口,直到拥塞缓解或者窗口减少到了一定程度,使得后一个差值成了正数或者0,这种情况下,其实后一个差值只能变为0。

疑难杂症21:路由器和TCP的互动

虽然有了5.2.4节介绍的主动的拥塞检测,那么路由器能不能做点什么帮助检测拥塞呢?这种对路由器的扩展是必要的,要知道,每天有无数的TCP要通过路由器,虽然路由器不管TCP协议的任何事(当然排除连接跟踪之类的,这里所说的是标准的IP路由器),但是它却能以一种很简单的方式告诉TCP的两端IP网络发生了拥堵, 这种方式就是当路由器检测到自己发生轻微拥堵的时候随机的丢包,随机丢包而不是连续丢包对于TCP而言是有重大意义的,随机丢包会使TCP发现丢弃了个别的分段而后续的分段仍然会到达接收端,这样TCP发送端就会接收到3个冗余ACK,然后进入快速重传/快速恢复而不是慢启动。 

这就是路由器能帮TCP做的事。

6.其它

疑难杂症22:如何学习TCP

很多人发帖问TCP相关的内容,接下来稀里哗啦的就是让看《TCP/IP详解》和《Unix网络编程》里面的特定章节,我觉得这种回答很不负责任。因为我并不认为这两本书有多大的帮助,写得确实很不错,然而可以看出Richard Stevens是一个实用主义者,他喜欢用实例来解释一切,《详解》通篇都是用tcpdump的输出来讲述的,这种方式只是适合于已经对TCP很理解的人,然而大多数的人是看不明白的。

如果想从设计的角度来说,这两本书都很烂。我觉得应该先看点入门的,比如Wiki之类的,然后看RFC文档,793,896,1122等),这样你就明白TCP为何这么设计了,而这些你永远都不能在Richard Stevens的书中得到。最后,如果你想,那么就看一点Richard Stevens的书,最重要的还是写点代码或者敲点命令,然后抓包自己去分析。

疑难杂症23:Linux,Windows和网络编程

我觉得在Linux上写点TCP的代码是很不错的,如果有BSD那就更好了。不推荐用Winsock学习TCP。虽然微软声称自己的API都是为了让事情更简单,但实际上事情却更复杂了,如果你用Winsock学习,你就要花大量的时候去掌握一些和网络编程无关但是windows平台上却少不了的东西

6.1.总结

TCP协议是一个端到端的协议,虽然话说它是一个带流量控制,拥塞控制的协议,然而正是因为这些所谓的控制才导致了TCP变得复杂。同时这些特性是互相杂糅的,流量控制带来了很多问题,解决这些问题的方案最终又带来了新的问题,这些问题在解决的时候都只考虑了端到端的意义,但实际上TCP需要尽力而为的IP提供的网络,因此拥塞成了最终的结症,拥塞控制算法的改进也成了一个单独的领域。

在学习TCP的过程中,切忌一锅粥一盘棋的方式,一定要分清楚每一个算法到底是解决什么问题的,每一个问题和其他问题到底有什么关联,这些问题的解决方案之间有什么关联,另外TCP的发展历史也最好了解一下,这些都搞明白了,TCP协议就彻底被你掌控了。接下来你就可以学习Socket API了,然后高效的TCP程序出自你手!

转载自: 笔不敌剑

阿里巴巴提交IPO招股书:核心数据全曝光(附:马云上市内部邮件)

$
0
0
阿里巴巴提交IPO招股书:核心数据全曝光(附:马云上市内部邮件)
2014-05-07 更多公司请戳>> 10%公司

文|综合华尔街日报、新浪财经等

当地时间5月6日,中国互联网巨头阿里巴巴集团盘后正式提交在美国进行首次公开募股(IPO)的计划,这可能是历史上最大规模的IPO之一。

阿里巴巴提交的IPO申请文件显示,该公司去年拥有2.31亿活跃买家。买家去年在阿里巴巴旗下三个购物网站共消费2480亿美元。

根据文件中披露的信息,4月份时阿里巴巴对自己的估值约为1090亿美元,分析师给出的估值预期在1360亿美元至2450亿美元不等。

阿里巴巴称,计划筹资10亿美元,不过外界普遍认为这只是一个暂定数字。熟悉阿里巴巴情况的人士曾表示,该公司可能在这宗交易中筹资逾200亿美元,而且预计该交易最早也要等到今夏较晚时候才会启动。

阿里巴巴最大业务是淘宝、天猫等三个网络交易市场,截至去年12月份的九个月中,这三个市场贡献了总计65亿美元的营收中的83%。

文件显示,阿里巴巴的移动支付交易量正在增长。该公司称,去年第四季度旗下中国在线购物网站的移动在线交易量占到了交易总量的20%左右,远高于上年同期的7.4%。去年12月,阿里巴巴购物网站的月度移动活跃用户数量达到1.36亿。

阿里巴巴没有说明计划在哪个交易所上市,但预计它将选择纽约证交所。

截至去年年底,这家中国公司持有79亿美元现金、现金等价物和短期投资,还有49亿美元的长期债务——这些是重要的衡量标准,因为阿里巴巴还在继续着收购狂欢。

文件称,阿里巴巴计划将筹资所得用于一般公司用途,这是在股票发行中用到的标准条款。

文件反映出阿里巴巴需要筹集更多资金的可能原因。阿里巴巴说它已经背负了高额债务,去年从一组大型银行获得的80亿美元信贷额度已经耗尽。

申请文件也揭示出阿里巴巴在公司架构上更多有争议的细节。马云是该公司的合伙人之一,而这些合伙人提名的董事占董事会人数的一半以上。这种合伙人制度是导致阿里巴巴与港交所上市谈判破裂的原因,阿里巴巴最初曾考虑在香港上市。

阿里巴巴介绍了公司的28位合伙人,其中22人为阿里巴巴集团的管理人士,另外6人为关联公司的经理。 

现有的内部人也有其他方法维持对公司的控制。软银是阿里巴巴最大的股东,IPO后,它仍将持有该公司超过30%的股权。

软银只要它持有阿里巴巴15%的股权,就有权推荐一名董事。申请文件称,雅虎和软银总共持有公开上市前阿里巴巴57%的股权,这两个股东已进一步同意投票支持合伙人提名。但雅虎的持股比例料将下降,因为按照先前的一份协议,雅虎将在IPO过程中出售手中约40%的持股。 

阿里巴巴目前的董事会成员包括马云、执行副主席蔡崇信、软银首席执行长孙正义以及雅虎首席开发长杰奎琳•雷瑟斯(Jacqueline Reses)。

招股书证实了目前为止承接阿里巴巴IPO工作的投行名单,它们是瑞士信贷集团、德意志银行、高盛集团、摩根大通和摩根士丹利。

据知情人士透露,阿里巴巴的IPO规模有望达到200亿美元,支付给投行的承销费可能达到4亿美元。

阿里巴巴IPO招股书数据要点:

2013年中国零售平台交易2460亿美元

招股书显示,阿里巴巴拥有全球最大的电子商务交易平台,涵盖零售与批发贸易两大领域。淘宝、天猫与聚划算,构成“中国零售平台”;阿里巴巴国际站和1688.com,分别是国际与国内批发贸易平台;速卖通是阿里旗下的国际零售平台。

截至2013年底,淘宝和天猫的活跃买家数超过2.31亿,活跃的卖家数大约为800万。

2013年,“中国零售平台”的交易总额(GMV)达到15420亿元,约合2480亿美元。这一规模,远超eBay和亚马逊,成为全球第一。

2013年,大约有113亿笔的交易在阿里巴巴旗下的“中国零售平台”达成,平均每个买家购买了49笔,每一笔的成交额约136元。

这一平台占据了中国网络零售的绝大部分市场份额。2013年,淘宝和天猫共产生了50亿个包裹,占中国当年包裹总量的54%。

营收保持50%-60%增长

阿里巴巴的平台生态模式,决定了其收入与盈利主要与平台交易额的增长密切相关。过去几年,阿里集团的收入和盈利与平台成交额(GMV)走出了几乎一致的增长曲线。

2012年,阿里的平台交易额和收入的增幅最高达80%。目前,增幅已依然在50%-60%的区间高速增长。

另外,季度之间的财务波动曲线也很明显。因为消费的季节因素显著。通常,第四季度为当年的最高峰。淘宝和天猫在第四季度举行大型促销活动,卖家也会把大量的营销预算安排在第四季度。从阿里集团的增长曲线看,上一年的峰值,大致就是下一年的常态。

未来几年,中国的在线零售市场仍有很大的增长空间。目前,在线零售占中国总零售市场的份额仅为7.9%。艾瑞咨询预计,到2016年,中国在线零售市场的普及率将达到11.5%,规模达3.79万亿元。这意味着从2013年至2016年,年复合增长率为27.2%。阿里巴巴旗下的淘宝和天猫平台,占据中国网购市场的绝对领先地位,预期增速将高于行业平均水平。

2013年移动交易额2320亿元

招股书显示,2013年12月,共有1.36亿的活跃用户通过移动端访问了淘宝、天猫和聚划算等平台。2013年全年,阿里的移动端商品交易额达到2320亿元,占平台总交易额的15%。

据艾瑞咨询的数据,2013年,阿里在移动成交额的交易额占中国移动端电子商务总交易额的76.2%。

手机淘宝是最大的移动电子商务平台。2014年2月份,中国市场月活跃用户数前五名的应用中,手机淘宝、支付宝钱包和UCWeb手机浏览器占据其三。阿里巴巴系UCWeb的控股公司66%的股份。

淘宝及天猫活跃卖家2.31亿

2013年,淘宝和天猫平台的活跃卖家数有2.31亿,平均每个活跃买家购买49单。2012年,活跃买家数量约1.60亿,平均购买为36单。2011年的平均购买为33单。

净利润率达43.8%

据招股书披露,2013年的2-4季度,阿里集团的收入为404.73亿元,运营利润达207.38亿元,运营利润率约51.2%;净利润177.42亿元,净利润率达43.8%。

阿里巴巴集团的收入主要来源于向卖家提供的互联网营销服务和从交易额中抽取的佣金。淘宝通过售卖展示广告、搜索关键词竞价等获得收入,天猫商城、聚划算和速卖通则从商家的成交额中抽取佣金和收取技术服务费,1688和阿里巴巴国际站通过收取会员费和提供互联网营销服务获得收入。

阿里巴巴本身并不直接售卖商品,也没有库存。纯粹的平台模式下,毛利率水平一直很高。2009年以来,阿里集团毛利率一直在70%上下浮动,最低66%,最高78%。

而且,随着平台交易额的放大,阿里巴巴的收入规模亦水涨船高,相应的成本和费用占比快速下降,运营利润率和净利润率则迅速攀升。从2010财年(2009年4月1日-2010年3月31日)至今,阿里集团的运营利润率从-13.1%上升到了51.2%,净利润率从-7.5%上升至43.8%。

注:2012年Q3,阿里巴巴集团一次性向雅虎集团支付“技术与知识产权授权协议”补偿款5.5亿美元,导致亏损。调整后的净利润率为25.8%。

2013年,阿里金融服务的贷款对象超过34.2万个。截至2013年底,阿里小贷的贷款余额为约126亿元。

截至2013年12月31日,阿里巴巴集团拥有的现金及短期投资约为78.76亿美元。

持股架构
日本软银集团持股797,742,980股,占比34.4%;
雅虎持股523,565,416股,占比22.6%;
阿里巴巴董事局主席马云持有206,100,673股,占比8.9%;
阿里巴巴联合创始人蔡崇信持有83,499,896股,占比3.6%。

附:马云上市内部邮件全文

新的机会 新的挑战 新的征程

  各位阿里人,
  几分钟后我们将会正式向美国证劵监管委员会首次递交上市注册登记书。这将意味着阿里巴巴即将进入新的挑战时代。
  15年前,阿里巴巴18个创始人立志创建一家中国人创立的属于全世界的互联网公司,希望能成为全球十大网络公司之一,成为一家能生存102年的企业。15年过去了,我们幸运的生存了下来。我们活的比我们当年想像的好得多。
  我们明白这不是因为我们多么的努力,或者是多么的聪明能干让我们走到了今天,而是因为我们幸运的生活在这个时代。感谢互联网,感谢生气勃勃的年轻人,感谢在一路相伴的创业者和追梦人,感谢这个国家改革和开放的进程。。。
  我们深知,我们生存下来不是因为战略多么的宏远,执行力多么的完美,而是我们十五年来坚持了“让天下没有难做的生意”这个使命,坚持了我们“客户第一”的价值观,坚持了相信未来,相信相信,坚持了平凡人一起做非凡事。
  相信公司的注册登记书(内含招股书)第一版公布之后,我们将会面临各类评论。15年来,在大量的支持,赞语和掌声背后,我们从来不缺指责,批评,漫骂和质疑。回应这一切最好的办法是用我们感恩和敬畏的心态,一如既往的追随“让天下没有难做的生意”的使命,专注工作,让小企业成功,让时间让结果去说明一切。
  上市从来就不是我们的目标,它是我们实现自己使命的一个重要策略和手段,是前行的加油站。但阿里人要清醒的认识到资本市场巨大利益诱惑背后有着无比巨大的无情和压力。只有很少数的杰出企业能够在资本市场持久驰骋。阿里巴巴这次在国际资本市场必将会因为规模,期待值,国界意识,文化冲突,区域政经。。。。遭遇空前绝后的挑战和压力。只有坚持我们的坚持,相信我们的相信,我们才有可能在压力和诱惑中度过未来艰辛的87年。能够面临这样全球性挑战的企业并不多,我们荣幸成为其中一个。
  上市后我们仍将坚持“客户第一,员工第二,股东第三”的原则。我们相信做任何艰难的决定,不管是在过去还是将来,坚持原则才是对各方利益最大的尊重和保护。上市某种意义上是让我们更有力量去帮助客户、支持员工、守护股东利益。
  最后,提醒大家注意严格遵守证券监管法规及集团的保密规定,不要对招股书做任何公开评论,所有采访须知会集团公关部,通过集团公关部进行协调。
  至于每个员工的股票事宜,集团HR将会发出处理方案。这是件令人高兴的事。我们也必须坚持“认真生活 快乐工作”的原则,请大家处理好自己的财富,在照顾好自己,家人的同时,力所能及的做些回报社会的工作和捐助。谢谢大家了。
  “兴于诗 立于礼 成于乐”,阿里人,过去15年我们过的很艰难,但很精彩。未来的每一天注定不会平凡,不会简单。今天不努力,我们可能看不到后天的太阳。没有一家企业会持久顺利,我们在坚持我们坚持的同时,必须为客户而变,为世界而变,为未来而变。


  马云
  阿里巴巴集团董事局主席
  2014.5.7

  青春就应该这样绽放   游戏测试:三国时期谁是你最好的兄弟!!   你不得不信的星座秘密

5种实用App导航菜单设计方案

$
0
0

手机分辨率比桌面平台小很多,所以设计手机网站或是移动应用的时候,导航菜单都需要考虑周全,尽量保持简约和易用性高,这里我们整理了5种实用的移动手机App导航菜单设计方案,你可以尝试这些菜单设计模式用在你的新设计项目上,好用而且有新鲜感。

这5种App菜单设计方案也许有很多设计师已经在使用,但不能否认它是目前实用的,而且能提高用户体验的菜单设计方案。下面摘选移动手机UI设计美观、时尚,希望你看了后会有灵感收获,能把你的菜单设计得更棒,好好学习吧。

APP导航设计类型:

  1. 列表式菜单
  2. 矩阵、网格式菜单
  3. 底部菜单
  4. 顶部菜单
  5. 扩展菜单

一、列表式菜单

列表式菜单设计这个从网站到手机APP上都很常用的,遵循由上至下的阅读习惯方式,所以使用起来用户不会觉得困难。另外我们可以通过漂亮的配色、图标组合来设计,使得菜单更多加美观。

GIF Aimation

Elevatr

Elevatr by Fueled 导航菜单设计

HabitClock App

HabitClock App by Kutan URAL 菜单设计 APP设计

Instagrab for iOS

Instagrab for iOS by Davis Yeung 导航设计 界面设计

二、矩阵、网格式菜单设计

网格式菜单就类似于metro UI的堆砌色块,优点简约而不简陋,导航清晰、明显,并能提高效率。但设计时切记不分青红皂白的去使用色彩哦,这可能会让用户不知所措和产生疲倦感。

Vectra

vectra - branding by Michal Galubinski and thoke design 导航设计 界面设计

Arrivo Mobile App

Arrivo Mobile App 导航设计 界面设计

Abracadabra App

TRAVERSE

T R A V E R S E by  Willis 菜单设计 APP设计

三、底部菜单

底部菜单主要是列出应用程序重要的功能。

Badoo concept

Badoo concept by Jakub Antalik 菜单设计 APP设计

Animated sliding tab bar

四、顶部菜单

顶部菜单和底部意义差不多,把菜单放在顶部,可以遵循上至下的阅读习惯,不过我认为有个缺点就是不能单手操作。

Horner

Horner by Cuneyt SEN UI界面设计

Discovery Channel

Discovery Channel UI界面设计

Air flow calculation app

UI设计

 

Shario App

Shario App by MING Labs&Pierrick Calvez 航行设计 UI设计

五、扩展菜单

扩展式菜单设计现在连网站也很常用,当我们觉得菜单比较点用位置的时候,可以尝试用这种方式来隐藏菜单,需要注意的是设计展开菜单按钮大部设计在左或右上角这些显示的位置。

MuSeek

MuSeek by Al Power 菜单设计 APP设计

Univit UI

Univit UI by Mohammed Alyousfi & Alex Casabo 菜单设计 APP设计

SVOY app design

SVOY app design by Alexandre Efimov 菜单设计 APP设计

Időkép

Idokep by Attila Szabo 菜单设计 APP设计

总结

从上面5个菜单设计方案中可以看出都有自己的优缺点,所以我们应该选择对你项目最为有效的方案,并能提高用户体验。

原文地址:shejidaren

您可能也喜欢:

手机界面常用导航设计分析

APP 创业 煎熬生存的持久战

Web导航设计二三事

在屏幕上画个C—基于人机工程学的安卓导航碎片化解决方案

如何输出有效的设计方案
无觅


(关注更多人人都是产品经理观点,参与微信互动(微信搜索“人人都是产品经理”或“woshipm”)

hashCode()方法的性能优化

$
0
0

原文链接译文链接,原文作者: Robert Nystrom,译者:有孚

本文主要讨论下不同的hashCode()的实现对应用程序的性能影响。

hashCode()方法的主要目的就是使得一个对象能够成为hashMap的key或者存储到hashset中。这种情况下对象还得实现equals(Object)方法,它的实现和hashCode()必须是一致的:

  • 如果a.equals(b)那么a.hashCode == b.hashCode()
  • 如果hashCode()在同一个对象上被调用两次,它应该返回的是同一个值,这表明这个对象没有被修改过。

hashCode的性能

从性能的角度来看的话,hashCode()方法的最主要目标就是尽量使得不同的对象拥有不同的哈希值。JDK中所有基于哈希的集合都是存储在数组中的。哈希值会用来计算数组中的初始的查找位置。然后调用equals方法将给定的值和数组中存储对象的值进行比较。如果所有的值的哈希值都不一样,这会减少hash的碰撞概率。换句话说,如果所有的值都共享一个哈希值的话,hashmap(或者hashset)会蜕化成一个列表,操作的时间复杂度会变成O(n2)。

更多细节可以看下 hash map碰撞的解决方案。JDK用了一个叫开放寻址的方法,不过还有一种方法叫拉链法。所有hashcode一样的值存储在一个链表里(说反了吧)。

我们来看下不同质量的哈希值的区别是什么。我们拿一个正常的String和它的包装类进行比较,包装类重写了hashCode方法,所有的对象返回的是同一个哈希值。

private static class SlowString
{
    public final String m_str;

    public SlowString( final String str ) {
        this.m_str = str;
    }

    @Override
    public int hash Code() {
        return 37;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        final SlowString that = ( SlowString ) o;
        return !(m_str != null ? !m_str.equals(that.m_str) : that.m_str != null);
    }
}

下面是一个测试方法。后面我们还会再用到它,所以这里还是简单介绍一下 。它接收一个对象列表,然后对列表中的每个元素依次调用Map.put(), Map.containsKey()方法。

private static void testMapSpeed( final List lst, final String name )
{
    final Map<Object, Object> map = new HashMap<Object, Object>( lst.size() );
    int cnt = 0;
    final long start = System.currentTimeMillis();
    for ( final Object obj : lst )
    {
        map.put( obj, obj );
        if ( map.containsKey( obj ) )
            ++cnt;
    }
    final long time = System.currentTimeMillis() - start;
    System.out.println( "Time for "  + name + " is " + time / 1000.0 + " sec, cnt = " + cnt );
}

String和SlowString对象都是通过一个”ABCD”+i的格式生成的。处理100000个String对象的话,需要的时间是0.041秒。而处理SlowString对象的话,则需要82.5秒。

结果表明,String类的hashCode()方法的质量明显胜出。我们再做另外一个测试。我们创建一个字符串列表,前半部分的格式是”ABCdef*&”+i,后半部分的是”ABCdef*&”+i+”ghi”(确保字符串的中间部分变化而结尾不变,不会影响hashCode的质量)。我们会创建1M,5M,10M,20M个字符串,来看下有多少个字符串是共享哈希值的,同一个哈希值又被多少个字符串共享。下面是测试的结果:

Number of duplicate hash Codes for 1000000 strings = 0

Number of duplicate hash Codes for 5000000 strings = 196
Number of hash Code duplicates = 2 count = 196

Number of duplicate hash Codes for 10000000 strings = 1914
Number of hash Code duplicates = 2 count = 1914

Number of duplicate hash Codes for 20000000 strings = 17103
Number of hash Code duplicates = 2 count = 17103

可以看到,很少有字符串是共享同一个哈希值的,而一个哈希值被两个以上的字符串共享的概率则非常之小。当然了,你的测试数据可能不太一样——如果用这个测试程序测试你自己特有的一些key的话。

自动生成long字段的hashCode()方法

值得一提的是,大多数IDE是如何自动生成long类型的hashcode方法的。下面是一个生成的hashCode方法,这个类有两个long字段。

Number of duplicate hash Codes for 1000000 strings = 0

Number of duplicate hash Codes for 5000000 strings = 196
Number of hash Code duplicates = 2 count = 196

Number of duplicate hash Codes for 10000000 strings = 1914
Number of hash Code duplicates = 2 count = 1914

Number of duplicate hash Codes for 20000000 strings = 17103
Number of hash Code duplicates = 2 count = 17103

下面给只有两个int类型的类生成的方法:

public int hash Code() {
int result = val1;
result = 31 * result + val2;
return result;
}

可以看到,long类型的处理是不一样的。java.util.Arrays.hashCode(long a[])用的也是同样的方法。事实上,如果你将long类型的高32位和低32位拆开当成int处理的话,生成的hashCode的分布会好很多。下面是两个long字段的类的改进后的hasCode方法(注意,这个方法运行起来比原来的方法要慢,不过新的hashCode的质量会高很多,这样的话hash集合的执行效率会得到提高,虽然hashCode本身变慢了)。

public int hash Code() {
    int result = (int) val1;
    result = 31 * result + (int) (val1 >>> 32);
    result = 31 * result + (int) val2;
    return 31 * result + (int) (val2 >>> 32);
}

下面是testMapSpeed 方法分别测试10M个这三种对象的结果。它们都是用同样的值进行初始化的。

Two longs with original hashCodeTwo longs with modified hashCodeTwo ints
2.596 sec1.435 sec0.737 sec

可以看到,更新后的hashCode方法的效果是不太一样的。虽然不是很明显,但是对性能要求很高的地方可以考虑一下它。

高质量的String.hashCode()能做些什么

假设我们有一个map,它是由String标识符指向某个特定的值。map的key(String标识符)只会存储在这个map中(某一时间可能有一小部分是存储在别的地方)。假设我们已经收集了map的所有entry,比如说在某个两阶段算法中的第一个阶段。第二个阶段我们需要通过key来查找map。我们只会用已经map里存在的key进行查找。

我们如何能提升map的性能?前面你已经看到了,String.hashCode()返回的几乎都是不同的值,我们可以扫描所有的key,计算出它们的哈希值,找出那些不唯一的哈希值:

Map<Integer, Integer> cnt = new HashMap<Integer, Integer>( max );
for ( final String s : dict.keySet() )
{
    final int hash = s.hash Code();
    final Integer count = cnt.get( hash );
    if ( count != null )
        cnt.put( hash, count + 1 );
    else
        cnt.put( hash, 1 );
}

//keep only not unique hash codes
final Map<Integer, Integer> mult = new HashMap<Integer, Integer>( 100 );
for ( final Map.Entry<Integer, Integer> entry : cnt.entrySet() )
{
    if ( entry.getValue() > 1 )
        mult.put( entry.getKey(), entry.getValue() );
}

现在我们可以创建两个新的map。为了简单点,假设map里存的值就是Object。在这里,我们创建了Map<Integer, Object> 和Map<String, Object>(生产环境推荐使用TIntObjectHashMap)两个map。第一个map存的是那些唯一的hashcode以及对应的值,而第二个,存的是那些hashCode不唯一的字符串以及它们相应的值。

final Map<Integer, Object> unique = new HashMap<Integer, Object>( 1000 );
final Map<String, Object> not_unique = new HashMap<String, Object>( 1000 );

//dict - original map
for ( final Map.Entry<String, Object> entry : dict.entrySet() )
{
    final int hash Code = entry.getKey().hash Code();
    if ( mult.containsKey( hash Code ) )
        not_unique.put( entry.getKey(), entry.getValue() );
    else
        unique.put( hash Code, entry.getValue() );
}

//keep only not unique hash codes
final Map<Integer, Integer> mult = new HashMap<Integer, Integer>( 100 );
for ( final Map.Entry<Integer, Integer> entry : cnt.entrySet() )
{
    if ( entry.getValue() > 1 )
        mult.put( entry.getKey(), entry.getValue() );
}

现在,为了查找某个值,我们得先查找第一个hashcode唯一的map,如果没找到,再查找第二个不唯一的map:

public Object get( final String key )
{
final int hash Code = key.hash Code();
Object value = m_unique.get( hash Code );
if ( value == null )
value = m_not_unique.get( key );
return value;
}

在一些不太常见的情况下,不唯一的map里的对象可能会很多。遇见这种情况的话,先尝试用java.util.zip.CRC32或者是java.util.zip.Adler32来替换掉hashCode的实现方法(Adler32比CRC32要快,不过它的分布较差些)。如果实在不行,尝试用两个不同的函数来计算hashcode:低32位和高32位分别用不同的函数生成。hash函数就用Object.hashCode, java.util.zip.CRC32或者java.util.zip.Adler32。

(译注:这么做的好处就是压缩了map的存储空间,比如你有一个map,它的KEY存100万个字符串的话,压缩了之后就只剩下long类型以及很少的字符串了)

set的压缩效果更明显

前面那个例子中,我们讨论了如何去除map中的key值。事实上,优化set的话效果会更加明显。set大概会有这么两个使用场景:一个是将原始的set拆分成多个子set,然后依次查询标识符是否属于某个子set;还有就是是作为一个拼写检查器(spellchecker )——有些要查询的值是预想不到的值(比如拼写错误了),而就算出了些错误的话影响也不是很大(如果碰巧另一个单词也有同样的hashCode,你会认为这个单词是拼写正确的)。这两种场景set都非常适用。

如果我们延用前面的方法的话,我们会得到一个唯一的hashcode组成的Set,以及不唯一的hashCode组成的一个Set。这里至少能优化掉不少字符串存储的空间。

如果我们可以把hashCode的取值限制在一定的区间内(比如说2^20),那么我们可以用一个BitSet来代替Set,这个在 BitSet一文中已经提到了。一般来说如果我们提前知道原始set的大小的话,hashcode的范围是有足够的优化空间的。

下一步就是确定有多少个标识符是共享相同的hashcode的。如果碰撞的hashcode比较多的话,改进你的hashCode方法,或者增加hashcode的取值范围。最完美的情况就是你的标记符全都有唯一的hashcode( 这其实不难实现)。优化完的好处就是,你只需要一个BitSet就够了,而不是存储一个大的字符串集合。

总结

改进你的hashCode算法的分布。优化它比优化这个方法的执行速度要重要多了。千万不要写一个返回常量的hashCode方法。

String.hashCode的实现已经相当完美了,因此很多时候你可以用String的hashCode来代替字符串本身了。如果你使用的是字符串的set,试着把它优化成BitSet。这将大大提升你程序的性能。

本文最早发表于 Java译站

(全文完)如果您喜欢此文请点赞,分享,评论。





您可能感兴趣的文章

转 redis vs memcached

$
0
0

传统MySQL+ Memcached架构遇到的问题

  实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都曾经使用过这样的架构,但随着业务数据量的不断增加,和访问量的持续增长,我们遇到了很多问题:

  1.MySQL需要不断进行拆库拆表,Memcached也需不断跟着扩容,扩容和维护工作占据大量开发时间。

  2.Memcached与MySQL数据库数据一致性问题。

  3.Memcached数据命中率低或down机,大量访问直接穿透到DB,MySQL无法支撑。

  4.跨机房cache同步问题。

  众多NoSQL百花齐放,如何选择

  最近几年,业界不断涌现出很多各种各样的NoSQL产品,那么如何才能正确地使用好这些产品,最大化地发挥其长处,是我们需要深入研究和思考的问题,实际归根结底最重要的是了解这些产品的定位,并且了解到每款产品的tradeoffs,在实际应用中做到扬长避短,总体上这些NoSQL主要用于解决以下几种问题

  1.少量数据存储,高速读写访问。此类产品通过数据全部in-momery 的方式来保证高速访问,同时提供数据落地的功能,实际这正是Redis最主要的适用场景。

  2.海量数据存储,分布式系统支持,数据一致性保证,方便的集群节点添加/删除。

  3.这方面最具代表性的是dynamo和bigtable 2篇论文所阐述的思路。前者是一个完全无中心的设计,节点之间通过gossip方式传递集群信息,数据保证最终一致性,后者是一个中心化的方案设计,通过类似一个分布式锁服务来保证强一致性,数据写入先写 内存和redo log,然后定期compat归并到磁盘上,将随机写优化为顺序写,提高写入性能。

  4.Schema free,auto-sharding等。比如目前常见的一些文档数据库都是支持schema-free的,直接存储json格式数据,并且支持auto-sharding等功能,比如mongodb。

  面对这些不同类型的NoSQL产品,我们需要根据我们的业务场景选择最合适的产品。

  Redis适用场景,如何正确的使用

  前面已经分析过,Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢?

 

如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:

1  Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

2  Redis支持数据的备份,即master-slave模式的数据备份。

3  Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

抛开这些,可以深入到Redis内部构造去观察更加本质的区别,理解Redis的设计。

在Redis中,并不是所有的数据都一直存储在内存中的。这是和Memcached相比一个最大的区别。Redis只会缓存所有的 key的信息,如果Redis发现内存的使用量超过了某一个阀值,将触发swap的操作,Redis根据“swappability = age*log(size_in_memory)”计 算出哪些key对应的value需要swap到磁盘。然后再将这些key对应的value持久化到磁盘中,同时在内存中清除。这种特性使得Redis可以 保持超过其机器本身内存大小的数据。当然,机器本身的内存必须要能够保持所有的key,毕竟这些数据是不会进行swap操作的。同时由于Redis将内存 中的数据swap到磁盘中的时候,提供服务的主线程和进行swap操作的子线程会共享这部分内存,所以如果更新需要swap的数据,Redis将阻塞这个 操作,直到子线程完成swap操作后才可以进行修改。

使用Redis特有内存模型前后的情况对比:
VM off: 300k keys, 4096 bytes values: 1.3G used
VM on:  300k keys, 4096 bytes values: 73M used
VM off: 1 million keys, 256 bytes values: 430.12M used
VM on:  1 million keys, 256 bytes values: 160.09M used
VM on:  1 million keys, values as large as you want, still: 160.09M used

当 从Redis中读取数据的时候,如果读取的key对应的value不在内存中,那么Redis就需要从swap文件中加载相应数据,然后再返回给请求方。 这里就存在一个I/O线程池的问题。在默认的情况下,Redis会出现阻塞,即完成所有的swap文件加载后才会相应。这种策略在客户端的数量较小,进行 批量操作的时候比较合适。但是如果将Redis应用在一个大型的网站应用程序中,这显然是无法满足大并发的情况的。所以Redis运行我们设置I/O线程 池的大小,对需要从swap文件中加载相应数据的读取请求进行并发操作,减少阻塞的时间。

如果希望在海量数据的环境中使用好Redis,我相信理解Redis的内存设计和阻塞的情况是不可缺少的。

 

补充的知识点:

memcached和redis的比较

1 网络IO模型

  Memcached是多线程,非阻塞IO复用的网络模型,分为监听主线程和worker子线程,监听线程监听网络连接,接受请求后,将连接描述字pipe 传递给worker线程,进行读写IO, 网络层使用libevent封装的事件库,多线程模型可以发挥多核作用,但是引入了cache coherency和锁的问题,比如,Memcached最常用的stats 命令,实际Memcached所有操作都要对这个全局变量加锁,进行计数等工作,带来了性能损耗。

(Memcached网络IO模型)

  Redis使用单线程的IO复用模型,自己封装了一个简单的AeEvent事件处理框架,主要实现了epoll、kqueue和select,对于单纯只有IO操作来说,单线程可以将速度优势发挥到最大,但是Redis也提供了一些简单的计算功能,比如排序、聚合等,对于这些操作,单线程模型实际会严重影响整体吞吐量, CPU计算过程中,整个IO调度都是被阻塞住的。

  2. 内存管理方面

  Memcached使用预分配的内存池的方式,使用slab和大小不同的chunk来管理内存,Item根据大小选择合适的chunk存储,内存池的方式可以省去申请/释放内存的开销,并且能减小内存碎片产生,但这种方式也会带来一定程度上的空间浪费,并且在内存仍然有很大空间时,新的数据也可能会被剔除,原因可以参考Timyang的文章:http://timyang.net/data/Memcached-lru-evictions/

  Redis使用现场申请内存的方式来存储数据,并且很少使用free-list等方式来优化内存分配,会在一定程度上存在内存碎片,Redis跟据存储命令参数,会把带过期时间的数据单独存放在一起,并把它们称为临时数据,非临时数据是永远不会被剔除的,即便物理内存不够,导致swap也不会剔除任何非临时数据(但会尝试剔除部分临时数据),这点上Redis更适合作为存储而不是cache。

  3.数据一致性问题

  Memcached提供了cas命令,可以保证多个并发访问操作同一份数据的一致性问题。 Redis没有提供cas 命令,并不能保证这点,不过Redis提供了事务的功能,可以保证一串 命令的原子性,中间不会被任何操作打断。

  4.存储方式及其它方面

  Memcached基本只支持简单的key-value存储,不支持枚举,不支持持久化和复制等功能

  Redis除key/value之外,还支持list,set,sorted set,hash等众多数据结构,提供了KEYS

  进行枚举操作,但不能在线上使用,如果需要枚举线上数据,Redis提供了工具可以直接扫描其dump文件,枚举出所有数据,Redis还同时提供了持久化和复制等功能。

  5.关于不同语言的客户端支持

  在不同语言的客户端方面,Memcached和Redis都有丰富的第三方客户端可供选择,不过因为Memcached发展的时间更久一些,目前看在客户端支持方面,Memcached的很多客户端更加成熟稳定,而Redis由于其协议本身就比Memcached复杂,加上作者不断增加新的功能等,对应第三方客户端跟进速度可能会赶不上,有时可能需要自己在第三方客户端基础上做些修改才能更好的使用。

  根据以上比较不难看出,当我们不希望数据被踢出,或者需要除key/value之外的更多数据类型时,或者需要落地功能时,使用Redis比使用Memcached更合适。

  关于Redis的一些周边功能

  Redis除了作为存储之外还提供了一些其它方面的功能,比如聚合计算、pubsub、scripting等,对于此类功能需要了解其实现原理,清楚地了解到它的局限性后,才能正确的使用,比如pubsub功能,这个实际是没有任何持久化支持的,消费方连接闪断或重连之间过来的消息是会全部丢失的,又比如聚合计算和scripting等功能受Redis单线程模型所限,是不可能达到很高的吞吐量的,需要谨慎使用。

  总的来说Redis作者是一位非常勤奋的开发者,可以经常看到作者在尝试着各种不同的新鲜想法和思路,针对这些方面的功能就要求我们需要深入了解后再使用。

  总结:

  1.Redis使用最佳方式是全部数据in-memory。

  2.Redis更多场景是作为Memcached的替代者来使用。

  3.当需要除key/value之外的更多数据类型支持时,使用Redis更合适。

  4.当存储的数据不能被剔除时,使用Redis更合适。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



hbase0.96数据导入以及Kettle操作hbase问题

$
0
0

版本:

cdh5.0.0+hadoop2.3.0+hbase0.96.1.1+Spoon5.0.1

一、HBase数据导入

HBase数据导入使用org.apache.hadoop.hbase.mapreduce.ImportTsv 的两种方式,一种是直接导入,一种是转换为HFile,然后再次导入。

1. HDFS数据为(部分):

[root@node33 data]# hadoop fs -ls /input
Found 1 items
-rwxrwxrwx   1 hdfs supergroup   13245467 2014-05-01 17:09 /input/hbase-data.csv
[root@node33 data]# hadoop fs -cat /input/* | head -n 3
1,1.52101,13.64,4.49,1.1,71.78,0.06,8.75,0,0,1
2,1.51761,13.89,3.6,1.36,72.73,0.48,7.83,0,0,1
3,1.51618,13.53,3.55,1.54,72.99,0.39,7.78,0,0,1

2. 使用直接导入的方式

a. 建立hbase-employees-1表,使用hbase shell,进入shell模式,使用命令:create 'hbase-employees-1','col' ,建立表;

b. 进入hbase安装目录,如果使用cdh默认安装,一般在目录/usr/lib/hbase/bin中,运行:

./hbase org.apache.hadoop.hbase.mapreduce.ImportTsv -Dimporttsv.separator="," -Dimporttsv.columns=HBASE_ROW_KEY,col:x1,col:x2,col:x3,col:x4,col:x5,col:x6,col:x7,col:x8,col:x9,col:y hbase-employees-1 hdfs://node33:8020/input/hbase-data.csv

日志如下:

2014-05-02 13:15:07,716 INFO  [main] mapreduce.JobSubmitter: Submitting tokens for job: job_1398958404577_0018
2014-05-02 13:15:08,674 INFO  [main] impl.YarnClientImpl: Submitted application application_1398958404577_0018
2014-05-02 13:15:09,101 INFO  [main] mapreduce.Job: The url to track the job: http://node33:8088/proxy/application_1398958404577_0018/
2014-05-02 13:15:09,103 INFO  [main] mapreduce.Job: Running job: job_1398958404577_0018
2014-05-02 13:15:34,169 INFO  [main] mapreduce.Job: Job job_1398958404577_0018 running in uber mode : false
2014-05-02 13:15:34,207 INFO  [main] mapreduce.Job:  map 0% reduce 0%
2014-05-02 13:16:32,789 INFO  [main] mapreduce.Job:  map 1% reduce 0%
2014-05-02 13:16:53,477 INFO  [main] mapreduce.Job:  map 5% reduce 0%
2014-05-02 13:16:56,701 INFO  [main] mapreduce.Job:  map 9% reduce 0%
2014-05-02 13:16:59,928 INFO  [main] mapreduce.Job:  map 13% reduce 0%
2014-05-02 13:17:02,970 INFO  [main] mapreduce.Job:  map 16% reduce 0%
2014-05-02 13:17:07,260 INFO  [main] mapreduce.Job:  map 22% reduce 0%
2014-05-02 13:17:10,472 INFO  [main] mapreduce.Job:  map 29% reduce 0%
2014-05-02 13:17:12,879 INFO  [main] mapreduce.Job:  map 36% reduce 0%
2014-05-02 13:17:16,555 INFO  [main] mapreduce.Job:  map 45% reduce 0%
2014-05-02 13:17:43,452 INFO  [main] mapreduce.Job:  map 48% reduce 0%
2014-05-02 13:17:45,629 INFO  [main] mapreduce.Job:  map 63% reduce 0%
2014-05-02 13:17:52,845 INFO  [main] mapreduce.Job:  map 79% reduce 0%
2014-05-02 13:17:55,862 INFO  [main] mapreduce.Job:  map 91% reduce 0%
2014-05-02 13:18:01,000 INFO  [main] mapreduce.Job:  map 100% reduce 0%
2014-05-02 13:18:14,644 INFO  [main] mapreduce.Job: Job job_1398958404577_0018 completed successfully
2014-05-02 13:18:17,123 INFO  [main] mapreduce.Job: Counters: 31
	File System Counters
		FILE: Number of bytes read=0
		FILE: Number of bytes written=115531
		FILE: Number of read operations=0
		FILE: Number of large read operations=0
		FILE: Number of write operations=0
		HDFS: Number of bytes read=13245571
		HDFS: Number of bytes written=0
		HDFS: Number of read operations=2
		HDFS: Number of large read operations=0
		HDFS: Number of write operations=0
	Job Counters 
		Launched map tasks=1
		Data-local map tasks=1
		Total time spent by all maps in occupied slots (ms)=146367
		Total time spent by all reduces in occupied slots (ms)=0
		Total time spent by all map tasks (ms)=146367
		Total vcore-seconds taken by all map tasks=146367
		Total megabyte-seconds taken by all map tasks=149879808
	Map-Reduce Framework
		Map input records=203500
		Map output records=203500
		Input split bytes=104
		Spilled Records=0
		Failed Shuffles=0
		Merged Map outputs=0
		GC time elapsed (ms)=8595
		CPU time spent (ms)=11290
		Physical memory (bytes) snapshot=99639296
		Virtual memory (bytes) snapshot=647782400
		Total committed heap usage (bytes)=26517504
	ImportTsv
		Bad Lines=0
	File Input Format Counters 
		Bytes Read=13245467
	File Output Format Counters 
		Bytes Written=0


查看hbase表数据大小:

bash-4.1$ hadoop fs -count /hbase/data/default/hbase-employees-1
           6            4           60100796 /hbase/data/default/hbase-employees-1


大约57.3M数据,原始数据是12.3M的。耗时方面:从任务提交到任务结束耗时3分6秒。

3. 使用HFile方式:

a. 转换为HFile并新建hbase-employee表:

./hbase org.apache.hadoop.hbase.mapreduce.ImportTsv -Dimporttsv.separator="," -Dimporttsv.bulk.output=hdfs://node33:8020/output/hbase_bulk -Dimporttsv.columns=HBASE_ROW_KEY,col:x1,col:x2,col:x3,col:x4,col:x5,col:x6,col:x7,col:x8,col:x9,col:y hbase-employees hdfs://node33:8020/input/hbase-data.csv

这样运行后,可以查看HFile的大小:

hadoop fs -count /output/hbase_bulk

同时数据库中已经新建了hbase-employees表,但是表中没有数据。

b. 插入数据到表中

这里也有两种方式,但是这两种方式操作前都需要把相应目录的权限放开。简单来说可以这样:

bash-4.1$ hadoop fs -chmod -R 777 /output
bash-4.1$ hadoop fs -chmod -R 777 /hbase

(1)使用hbase的命令:

/hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles hdfs://node33:8020/output/hbase_bulk hbase-employees

ps:这个我没有试,官网说可以这样。
(2)使用hadoop jar方式:

hadoop jar /usr/lib/hbase/lib/hbase-server-0.96.1.1-cdh5.0.0.jar completebulkload hdfs://node33:8020/output/hbase_bulk hbase-employees


不过做这个前需要把hbase的相关包拷贝到hadoop的lib目录下,可以拷贝到/usr/lib/hadoop-mapreduce目录下,jar包如下:

-rw-r--r--. 1 root root  818519 May  2 10:35 hbase-client.jar
-rw-r--r--. 1 root root  360422 May  2 10:36 hbase-common.jar
-rw-r--r--. 1 root root   66898 May  2 10:36 hbase-hadoop2-compat.jar
-rw-r--r--. 1 root root    4071 May  2 10:36 hbase-it.jar
-rw-r--r--. 1 root root   87533 May  2 10:36 hbase-prefix-tree.jar
-rw-r--r--. 1 root root 3142247 May  2 10:36 hbase-protocol.jar
-rw-r--r--. 1 root root 3110607 May  2 10:36 hbase-server.jar
-rw-r--r--. 1 root root    4499 May  2 10:37 hbase-shell-0.96.1.1-cdh5.0.0.jar
-rw-r--r--. 1 root root 2270053 May  2 10:37 hbase-thrift-0.96.1.1-cdh5.0.0.jar


这种方式的log,如下:

2014-05-02 13:39:27,916 INFO  [main] mapreduce.JobSubmitter: Submitting tokens for job: job_1398958404577_0020
2014-05-02 13:39:28,408 INFO  [main] impl.YarnClientImpl: Submitted application application_1398958404577_0020
2014-05-02 13:39:28,552 INFO  [main] mapreduce.Job: The url to track the job: http://node33:8088/proxy/application_1398958404577_0020/
2014-05-02 13:39:28,553 INFO  [main] mapreduce.Job: Running job: job_1398958404577_0020
2014-05-02 13:39:44,376 INFO  [main] mapreduce.Job: Job job_1398958404577_0020 running in uber mode : false
2014-05-02 13:39:44,402 INFO  [main] mapreduce.Job:  map 0% reduce 0%
2014-05-02 13:40:24,580 INFO  [main] mapreduce.Job:  map 67% reduce 0%
2014-05-02 13:40:38,125 INFO  [main] mapreduce.Job:  map 100% reduce 0%
2014-05-02 13:41:09,295 INFO  [main] mapreduce.Job:  map 100% reduce 67%
2014-05-02 13:41:17,905 INFO  [main] mapreduce.Job:  map 100% reduce 77%
2014-05-02 13:41:20,928 INFO  [main] mapreduce.Job:  map 100% reduce 90%
2014-05-02 13:41:23,948 INFO  [main] mapreduce.Job:  map 100% reduce 100%
2014-05-02 13:41:29,514 INFO  [main] mapreduce.Job: Job job_1398958404577_0020 completed successfully
2014-05-02 13:41:31,530 INFO  [main] mapreduce.Job: Counters: 50
	File System Counters
		FILE: Number of bytes read=48950370
		FILE: Number of bytes written=98519119
		FILE: Number of read operations=0
		FILE: Number of large read operations=0
		FILE: Number of write operations=0
		HDFS: Number of bytes read=13245571
		HDFS: Number of bytes written=73861410
		HDFS: Number of read operations=8
		HDFS: Number of large read operations=0
		HDFS: Number of write operations=3
	Job Counters 
		Launched map tasks=1
		Launched reduce tasks=1
		Data-local map tasks=1
		Total time spent by all maps in occupied slots (ms)=51857
		Total time spent by all reduces in occupied slots (ms)=42035
		Total time spent by all map tasks (ms)=51857
		Total time spent by all reduce tasks (ms)=42035
		Total vcore-seconds taken by all map tasks=51857
		Total vcore-seconds taken by all reduce tasks=42035
		Total megabyte-seconds taken by all map tasks=53101568
		Total megabyte-seconds taken by all reduce tasks=43043840
	Map-Reduce Framework
		Map input records=203500
		Map output records=203500
		Map output bytes=48339864
		Map output materialized bytes=48950370
		Input split bytes=104
		Combine input records=203500
		Combine output records=203500
		Reduce input groups=203500
		Reduce shuffle bytes=48950370
		Reduce input records=203500
		Reduce output records=2035000
		Spilled Records=407000
		Shuffled Maps =1
		Failed Shuffles=0
		Merged Map outputs=1
		GC time elapsed (ms)=1573
		CPU time spent (ms)=26250
		Physical memory (bytes) snapshot=341180416
		Virtual memory (bytes) snapshot=1296404480
		Total committed heap usage (bytes)=152965120
	ImportTsv
		Bad Lines=0
	Shuffle Errors
		BAD_ID=0
		CONNECTION=0
		IO_ERROR=0
		WRONG_LENGTH=0
		WRONG_MAP=0
		WRONG_REDUCE=0
	File Input Format Counters 
		Bytes Read=13245467
	File Output Format Counters 
		Bytes Written=73861410


这个是转为HFile的job,插入数据到HBase表中2秒不到就完成了。

查看HBase中数据大小:

bash-4.1$ hadoop fs -count /hbase/data/default/hbase-employees
           5            3           73861752 /hbase/data/default/hbase-employees


一共大概70.4M数据,原始数据是12.3M,耗时为:2分2秒。

总结:HBase的数据导入都会把数据扩大,一般大概为5倍左右,同时使用HFile的方式,在时间上会有优势,但是数据会比较大。ps:这个仅仅是一次测试总结而已,实际情况可能不同。(为什么HBase的数据被扩大了?参考前篇,Hive的数据如果是直接导入,一般是不会扩大数据占用空间的,而且使用orc方式的时候还可以达到很好的压缩比,但是HBase却是扩大的,这是为什么?)

二、Kettle操作Hbase0.96

1. HBase Output组件,可以参考 http://wiki.pentaho.com/display/BAD/Loading+Data+into+HBase 。

2. HBase Input组件,可以参考 http://wiki.pentaho.com/display/BAD/Extracting+Data+from+HBase+to+Load+an+RDBMS

参考这两个操作步骤,都是可以得到正确结果的。这里只是说说遇到的问题。

(1)zookeeper和hbase-site.xml文件两者选择一个配置即可,配置两个可能有问题。

(2)mapping配置的是HBase表中Family的名称。

(3)如果遇到下面的问题:

java.lang.IllegalArgumentException: offset (0) + length (8) exceed the capacity of the array: 1
	at org.apache.hadoop.hbase.util.Bytes.explainWrongLengthOrOffset(Bytes.java:602)
	at org.apache.hadoop.hbase.util.Bytes.toLong(Bytes.java:580)
	at org.apache.hadoop.hbase.util.Bytes.toLong(Bytes.java:553)
	at org.pentaho.hbase.shim.common.CommonHBaseBytesUtil.toLong(CommonHBaseBytesUtil.java:87)
	at org.pentaho.hbase.shim.api.HBaseValueMeta.decodeKeyValue(HBaseValueMeta.java:567)
	at org.pentaho.di.trans.steps.hbaseinput.HBaseInputData.getOutputRow(HBaseInputData.java:464)
	at org.pentaho.di.trans.steps.hbaseinput.HBaseInput.processRow(HBaseInput.java:281)
	at org.pentaho.di.trans.step.RunThread.run(RunThread.java:60)
	at java.lang.Thread.run(Thread.java:724)


或者

Unknown type for column

	at org.pentaho.hbase.shim.api.HBaseValueMeta.decodeColumnValue(HBaseValueMeta.java:769)
	at org.pentaho.di.trans.steps.hbaseinput.HBaseInputData.getOutputRow(HBaseInputData.java:495)
	at org.pentaho.di.trans.steps.hbaseinput.HBaseInput.processRow(HBaseInput.java:281)
	at org.pentaho.di.trans.step.RunThread.run(RunThread.java:60)
	at java.lang.Thread.run(Thread.java:724)

或者

org.pentaho.di.core.exception.KettleException: 
Length of integer column value is not equal to the defined length of a short, int or long

	at org.pentaho.hbase.shim.api.HBaseValueMeta.decodeColumnValue(HBaseValueMeta.java:711)
	at org.pentaho.di.trans.steps.hbaseinput.HBaseInputData.getOutputRow(HBaseInputData.java:495)
	at org.pentaho.di.trans.steps.hbaseinput.HBaseInput.processRow(HBaseInput.java:281)
	at org.pentaho.di.trans.step.RunThread.run(RunThread.java:60)
	at java.lang.Thread.run(Thread.java:724)


则把Mapping的时候的数据格式全部设置为String就可以了,这个是因为,如果HBase表中的数据是以其他方式导入的话,那么其存储是以String格式存储的(但是如果使用kettle的HBase output组件导入数据,那么数据格式可以调整),所以使用其他的数据格式就会报上面的错误。

 

分享,成长,快乐

转载请注明blog地址: http://blog.csdn.net/fansy1990


 

作者:fansy1990 发表于2014-5-8 17:04:12 原文链接
阅读:67 评论:0 查看评论

Hive优化

$
0
0
     使用Hive有一段时间了,目前发现需要进行优化的较多出现在出现join、distinct的情况下,而且一般都是reduce过程较慢。
     Reduce过程比较慢的现象又可以分为两类:
情形一:map已经达到100%,而reduce阶段一直是99%,属于数据倾斜
情形二:使用了count(distinct)或者group by的操作,现象是reduce有进度但是进度缓慢,31%-32%-34%...一个附带的提示是使用reduce个数很可能是1
下面,笔者将以亲身经历的两次优化过程为例,给大家说明下笔者优化的思路及过程:
情形二(distinct):
示例sql:
select count(distinct A),count(B),count(distinct C) from T;

T的数据量峰值时候大概在2亿左右,文件大小约10G+,优化的思路:
1. 问题产生的RC是需要在大数据集下做多个字段的distinct,而且还是多个字段,所以最先需要设置set hive.groupby.skewindata=true
打散去重字段,但是目前hive不支持在一个sql中去重几个不同字段需要拆分后join;
2. 因为T是外部装载进入hive的,文件比较散乱,考虑先使用合并小文件,确保Map过程中不会因为资源不足而持续append状态;
3. 适当减少每个reduce处理的数据量,默认1G
综上,调整的参数如下:
set mapreduce.job.queuename=root.mapreduce.hourlyetl;
set mapred.min.split.size=128000000;
set mapred.min.split.size.per.node=128000000;
set mapred.min.split.size.per.rack=128000000;
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
set hive.exec.reducers.bytes.per.reducer=100000000;
set hive.groupby.skewindata=true;
代码调整为
select t1.a,t2.c
from
(
select count(distinct A) as a from T
)t1 join
(
select count(distinct C) as c from T
)t2
;

Job执行时间从13min缩减到5min(同一数据量下的T)


参考文章:
1.浅析hadoop中的数据倾斜:http://my.oschina.net/leejun2005/blog/100922
2.数据倾斜总结:http://www.alidata.org/archives/2109


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




php初学者常见问题

$
0
0

最令PHP初学者头痛的十四个问题

管理提醒: 本帖被 haowubai 执行置顶操作(2009-04-16) 【1】面之间无法传递变量 get,post,session在最新的php 版本中自动全局变量是关闭的,所以要从上一面取得提交过来得变量要使用$_GET[’foo’],$_POST[’foo’],$_SESSION[’foo’]来得到。当然也可以修改自动全局变量为开(php .ini改为register_globals = On);考虑到兼容性,还是强迫自己熟悉新的写法比较好。 

【2】Win下apache 用get方法传递中文参数会出错: 

  test.php ?a=你好&b=你也好 

  传递参数是会导致一个内部错误  
   
  解决办法:"test.php ?a=".urlencode(你好)."&b=".urlencode(你也好) 

【3】win下的session不能正常工作 

  php .ini默认的session.save_path = /tmp 

  这显然是linux下的配置,win下php 无法读写session文件导致session无法使用,把它改成一个绝对路径就可以了,例如session.save_path = c:windows emp 

【4】显示错误信息 

  当php .ini的display_errors = On并且error_reporting = E_ALL时,将显示所有的错误和提示,调试的时候最好打开以便纠错,如果你用以前php 写法错误信息多半是关于未定义变量的。变量在赋值以前调用会有提示,解决办法是探测或者屏蔽。 

  例如显示$foo,可以if(isset($foo)) echo $foo 或者echo @$foo 

【5】Win下mail()不能发送电子邮件 

  在linux下配置好的sendmail可以发送,在win下需要调用smtp服务器 来发送电子邮件,修改php .ini的SMTP = ip //ip是不带验证功能的smtp服务器 (网上很难找到) 

  php 发送邮件的最好解决方法是用socket直接发送到对方email服务器 而不用转发服务器 。 

【6】初装的mysql如果没有设置密码,应该使用update mysql.user set password="yourpassword" where user="root" 修改密码 

【7】header already sent 

  这个错误通常会在你使用HEADER的时候出现,他可能是几种原因:,你在使用HEADER前PRING或者ECHO了.你当前文件前面有空行.你可能INCLUDE了一个文件,该文件尾部有空行或者输出也会出现这种错误。! 

【8】更改php .ini后没有变化 

  重新启动web server,比如IIS,Apache等等,然后才会应用最新的设置。 

【9】php 在00上面安装(ISAPI的安装方法恳请高手指教) 

  PHP的php isapi.dll好像和00有些冲突,只能用CGI 模式安装 

  步骤一,先www.php .net 下在一个安装程序,我是装的是:php -..-installer.exe,你也可以去找最新的版本,在安装php -..-installer.exe之前保证你的IIS6.0启动了,并能够访问。安装好以后,在默认网站-->应用程序配置。 

  步骤二:点击 web服务扩展 -->新建web服务扩展。 

  步骤三: 扩展名-->php ,然后添加 

  步骤四:找到php .exe的路径添加上去。 

  步骤五: 确定就可以了!  
   
  步骤六: 选择php 的服务扩展,然后点击允许。 

【10】有时候sql语句不起作用,对数据库操作失败,最简便的调试方法,echo那句sql,看看变量的值能得到不。 

【11】include和require的区别 

  两者没有太大的区别,如果要包含的文件不存在,include提示notice,然后继续执行下面的语句,require提示致命错误并且退出。 

  据我测试,win平台下它们都是先包含后执行,所以被包含文件里最好不要再有include或require语句,这样会造成目录混乱。或许*nux下情况不同,暂时还没测试。 

  如果一个文件不想被包含多次可以使用include_once或require_once## 读取,写入文档数据。 

function r($file_name) {  
 $filenum=@fopen($file_name,"r");  
 @flock($filenum,LOCK_SH);  
 $file_data=@fread($filenum,filesize($file_name));  
 @fclose($filenum);  
 return $file_data;  
}  
function w($file_name,$data,$method="w"){  
 $filenum=@fopen($file_name,$method);  
 flock($filenum,LOCK_EX);  
 $file_data=fwrite($filenum,$data);  
 fclose($filenum);  
 return $file_data;  
}  
【12】isset()和empty()的区别 

  两者都是测试变量用的,但是isset()是测试变量是否被赋值,而empty()是测试一个已经被赋值的变量是否为空。 

  如果一个变量没被赋值就引用在php 里是被允许的,但会有notice提示,如果一个变量被赋空值,$foo=""或者$foo=0或者 $foo=false,那么empty($foo)返回真,isset($foo)也返回真,就是说赋空值不会注销一个变量。  
   
  要注销一个变量,可以用 unset($foo)或者$foo=NULL 

【13】mysql查询语句包含有关键字 

  php 查询mysql的时候,有时候mysql表名或者列名会有关键字,这时候查询会有错误。例如表名是order,查询时候会出错,简单的办法是sql语句里表名或者列名加上`[tab键上面]来加以区别,例如select * from `order` 

【14】通过HTTP协议一次上传多个文件的方法 

  有两个思路,是同一个方法的两种实现。具体程序还需自己去设计。 

  、在form中设置多个文件输入框,用数组命名他们的名字,如下: 

<form action="" method=post>  
<input type=file name=usefile[]>  
<input type=file name=usefile[]>  
<input type=file name=usefile[]>  
</form>  
  这样,在服务器 端做以下测试: 

echo "<pre>";  
print_r($_FILES);  
echo "</pre>";  
  、在form中设置多个文件输入框,但名字不同,如下: 

<form action="" method=post>  
<input type=file name=usefile_a>  
<input type=file name=usefile_b>  
<input type=file name=usefile_c>  
</form>  
  在服务器 端做同样测试: 

echo "<pre>";  
print_r($_FILES);  
echo "</pre>";  



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



为什么App抛弃旧设备

$
0
0

几个月之前,ZM同学向我投诉了iPad的恶行:为什么新的App都不再支持我的iPad1呢?

这个问题有个背景知识:iPad 1无法安装iOS 6,而现在,支持iOS 5的App基本上已经绝迹(实际上支持iOS 6的App也日渐稀少)。
当时我回答不出这个问题。
夏教授调笑说:只怪iPad的质量太好,如果是别的产品用了两年肯定早就坏了,问题也就不存在了。
这个问题一直在我脑中思考。
随着我对iOS平台开发经验的积累,我对这个问题的理解也越来越深。
第一篇文章:《 The Fallacy Of Android-First》,EMU,一个社交App,因为安卓平台首发而名噪一时。但最近他们决定把安卓App下线雪藏起来。他们的CEO写了一篇文章解释为什么他们要这么做。文章讲了很多细节问题,但归根结底就是一个原因:维护安卓App的代价太大,每天都要付出大量的时间精力来修复各种Bug,外加客服电话给用户打爆。而iOS App则稳定得多。
第二篇文章《 Why Android First Is a Myth》,讲了为什么大多数真刀真枪的开发者的App都会选择在iOS上平台上首发。
提到这两个文章,似乎有点跑题。问题是为什么iOS App不再支持旧设备。而前面两个文章是论述为啥开发者喜欢iOS平台而不是安卓平台。
其实这两个问题是有联系的,非常大的联系。
安卓为什么会出现这么大的开发难度,正是因为它试图兼容旧设备!这个旧设备:是指手机内部的操作系统使用的是旧版本。
为什么安卓必须支持旧设备,上次回国的时候,我把高中同学的手机统计了一下,一半人用iPhone,一半人用安卓。而在安卓用户中,2.2版本占了一半以上。2.2版本发布于2010年5月,也就是4年前。
基于这个现状,开发者必须支持旧设备,否则等于自己舍弃掉一半以上的潜在客户。而这些旧设备五花八门,搞得开发者不堪其扰。
那么iOS那么不需要支持旧设备了吗?我在做iOS开发的时候,看了一眼iOS设备的版本占有率图表。
2014年4月6日的图表如下:
ios_share
一开始我准备兼容到iOS5(这样等于支持了iPad 1),看完这个图,我就改主意了。
做事都有个投入产出比。如果你花了大力气去做一件事情,最好能够赚得金满筐银满筐。
我算了一下,如果支持iOS5,我不能用自动布局这个特性:这意味着每个界面我都写代码让它手动适配3.5寸屏幕和4寸屏幕。
这个任务也不算难但也有好几个小时的工作量;作为回报,我换来了额外的2%的用户。
换句话讲,如果我不支持iOS5,也就是说,每50个人里面,可能有一个人用不了我写的App(其中包括ZM同学)。我一琢磨,觉得连iOS6也不升级的用户,有非常大的可能他从来都不会去App Store购买App。换句话讲,这部分用户的含金量比较低。
大部分开发都会选择不支持iOS 5,我也一样。
苹果并没有阻止开发者去支持iOS 5,开发者自己做出选择决定不支持旧设备。原因是两个:
  1. 支持旧版本性价比太低
  2. 使用新版本可以使用新特性
所以对于开头这个问题,很好解答了:“因为顾客太少,所以没人卖”。App开发者并不是势利眼,他们不过是在遵循市场规律而已。
后继:我在开发过程中,又遇到了网络部分的API的处理,iOS7提供了新的API来简化代码优化性能。我犹豫了一下,还是决定不使用iOS7的新API,毕竟11%的潜在用户在使用iOS 6。等到iOS 6的市场占有率继续萎缩,那么去掉对iOS 6的支持是早晚的事情。

科学家仍然在广泛使用 Fortran 语言

$
0
0

有机会参观美国大学物理系的研究实验室或国家实验室,你会看到被称之为前沿和研究的东西,它们大多数都是你从未见过的,其中你可能会看到研究人员在超级计 算机终端上进行的前沿模拟。然而在几乎所有的美国大学,这些计算机模拟程序使用的都是1950年代的语言Fortran。

美国国家大气研究中心的大气模型和气候预测程序,洛斯阿拉莫斯国家实验室和劳伦斯利弗莫尔国家实验室的机密核武器和激光核聚变程序,NASA的全球气候变化模型,量子色动力学研究人员计算夸克行为、质子和中子成分的代码,等等,它们都是使用Fortran写就的。

http://static.cnbetacdn.com/newsimg/2014/0508/25_1jqMZ4bns.jpg

图灵奖得主Tony Hoare曾在1982年说过,他不知道2000年的语言是什么样子,但他知道它一定叫Fortran。为什么从事尖端科研的研究人员 仍然在使用计算机时代早期发明的语言?即使新兴的语言Haskell、Clojure和Julia提供了类似Fortran的特性和抽象概念?Ars的一篇文章分析了科学家仍然广泛使用Fortran的原因。Linuxlinks的一篇文章介绍了一些优秀的 免费Fortran图书,供感兴趣的人参考和学习。

使用 Capistrano 部署 Discourse

$
0
0

Discourse是目前 Opensource 界相當受歡迎的一套論壇軟體。通常用來架設技術社群專屬討論區。

Discourse 官方目前的推薦部署模式目前是採 Docker,直接在 Digital Ocean 部署。這個方案可以少去很多不熟 Rails 的人安裝上的求助需求。

但也大大限制了社群未來想 patch 功能的自由性。(社群貢獻代碼和功能的方式,通常是透過 Github Fork 原始碼,拉 pull request 回去,而用 Docker deploy 的架構,很難讓想幫忙的人直接插手)。

而 Discourse 的設計與架構,也與傳統在設計 Rails Application 的做法大大不同(其 Anti-Pattern 真是多到令人髮指)。一般的 Rails Application 都是會設計成使用 capistrano deploy,然後 config 拆開另外放。這樣的好處是 source code 可公開,可讓人自由 fork,config 的敏感資訊又不會被泄露。

這篇文章就是一個引子,讓有心想使用 Capistrano 架設 Discourse 的人可以找到線索架設。

安裝

我的機器是再 Linode 租的。跑 Ubuntu 13.10

  1. 按照 https://github.com/rocodev/guides/wiki/setup-production-development安裝正常的 Rails Production 環境
  2. 幾個記得要安裝的套件
    • PostgreSQL database ( database backend )
    • Redis Server ( 要給 sidekiq 跑 queue 用)

PG SQL 注意事項

我們用來部署的 agent 跑在 apps 上,但 pgsql 會先吃 apps 這個賬號。所以要記得開 apps 再 pgsql 上的帳號,然後再開 discourse這個 db,把權限 assign 上去。

另外 discourse 有用了一些 extension,跑 migration 時會跳警告,按照警告的內容處理就可以了...

Capistrano / Unicorn 檔

值得注意的是

discourse 的 production config 並非跑在 config/enviorments/production.rb,而是 https://github.com/xdite/discourse/blob/master/config/discourse_defaults.conf

所以我在 production branch 上 remove & ignore 這個檔,改用 deploy symlink 的方式。

(避免上面的 database / smtp password 外洩)

Nginx Config

discourse 完全不建議跑在 passenger 上,因為 discourse 是採用 long polling 的方式作 Ajax,所以架在 passenger 上會奇慢無比。通常會跑在 thin 或 unicorn 上,我是放在 unicorn。

這是與 unicorn 搭配的 nginx config。

(記得要先建這個資料夾 ../discourse/shared/sockets)

  upstream g0v_unicorn_socket {
    server unix:/home/apps/discourse/shared/sockets/unicorn.sock fail_timeout=0;
  }

  server {
      listen 80;
      server_name community.g0v.tw;
      root /home/apps/discourse/current/public;

      client_max_body_size 5M;

      location ~* ^/assets/ {
        expires 1y;
        add_header Cache-Control public;
        if ($request_filename ~* ^.*?\.(eot)|(ttf)|(woff)|(svg)|(otf)$){
           add_header Access-Control-Allow-Origin *;
        }
        break;
      }
     try_files $uri/index.html $uri.html $uri @app;
     location @app {
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header Host $http_host;
       proxy_redirect   off;
       proxy_pass       http://g0v_unicorn_socket;
       proxy_connect_timeout 600;
       proxy_send_timeout    600;
       proxy_read_timeout    600;
       send_timeout          600;
     }
  }   

SMTP

SMTP 我是直接用 Mailgun。設一個賬號密碼就完成了。

其他注意事項

..discourse/shared/uploads 這個資料夾也要記得開。否則圖片會上傳不上去。

Contribute on Github

慣例是 master追原始 Discourse branch,production 上跑自己的客制版本。

如果要追官方更新

  • 加上 upstream git remote add upstream https://github.com/discourse/discourse.git
  • git fetch upstream
  • git checkout master; git merge upstream/master

“破网”利器一箩筐——移动互联网分析工具推荐

$
0
0

随着移动互联网的快速发展,基于手持设备(尤其是智能手机)的各类应用急剧增多,对于这些移动应用的数据收集、研究和分析工作就变得尤为重要。

下面我们就为大家推荐几款市面上好用(且免费)的“破网”利器。

一、友盟    http://www.umeng.com/ 

友盟于2010年4月在北京创建,以移动应用统计分析为产品起点,发展成为综合性的移动开发者服务平台,现拥有超过百人的专业团队。2013年4月,阿里巴巴日正式签约收购友盟,交易价格约为8000万美元。

友盟提供的数据包括三类:数据报告、指数和友盟观点。

pw_1

图片来源:http://www.umeng.com/umengdata_reports

1.数据报告( http://www.umeng.com/umengdata_reports

友盟会在每个季度初(到2013年改为每半年)发布上季度的移动互联网行业数据报告,报告中会分平台(Android和iOS)发布用户数、系统版本份额、智能设备数、各品牌设备市场份额、APP分类别使用情况以及开发者情况等基础数据,大家可以随时免费下载查阅。

最新一期的报告是2013年9月出版的《友盟2013年中国移动互联网上半年报告》,其报告主要结论截图如下:

pw_2

图片来源:http://www.umeng.com/umengdata_reports

从2010年第四季度起,友盟共发布了17份报告,通过历次报告的数据对比,大家可以很清楚的看到移动互联网的发展趋势。

2. 指数( http://www.umindex.com/

友盟对移动互联网关键数据进行深入挖掘,形成了一套指数系统,以帮助大家把握移动互联网发展趋势。指数平台每月会更新上个月(整月)的移动互联网关键数据,现在能够随时查询到的分平台指标包括:

•        设备

•        操作系统

•        分辨率

•        联网方式

•        地域

•        应用排名

•        应用类别分布

这些指标从2012年6月开始每个月更新,并保留历史数据,方便进行月份/年份的深入趋势研究和分析。以下是部分指标的截图:

Android设备品牌及型号占有率排名:

pw_3

图片来源:http://umindex.com/#android_device

iOS操作系统各版本占有率排名

pw_4

图片来源:http://umindex.com/#ios_system

 Android操作系统分辨率占有率排名

pw_5

图片来源:http://umindex.com/#android_resolution

友盟在近期新增2个指标:应用排名和应用类别分布。这两个指标仅是依据主流应用市场的公开数据计算得出,不涉及友盟应用数据。

移动应用分为应用(非游戏类应用)和游戏2类,在2类下有细分分类,如图所示:

pw_6-1

图片来源:http://umindex.com/#android_app

应用排名及应用类别分布情况统计图表如下:

Android应用排名统计图表截图

pw_7

图片来源:http://umindex.com/#android_app

 

Android应用类别分布情况统计图表截图

pw_8

图片来源:http://umindex.com/#android_industry

 

3. 友盟观点( http://blog.umeng.com/

友盟观点就是友盟的官方Blog,上面会不定期更新一些关于产品、数据方面的信息,偶尔关注即可。

 

二、百度开放云  http://developer.baidu.com/report

百度开放云为大家提供了4类数据报告:

pw_9

图片来源:http://developer.baidu.com/report

 

1.    百度无线数据报告

数据报告根据百度移动搜索、百度手机助手、百度移动统计及相关数据挖掘及线上线下用户调研获取信息,尽可能公正、客观的反映国内移动互联网发展趋势,但并不保证报告所述信息的准确性和完整性。

该报告由百度移动下属的云事业部与百度商业分析部联合撰写,从2010年第四季度以来,每个季度发布一个季度报告,至今已经发布了12份报告,相比友盟而言,百度无线数据报告的发布更规律,用户属性及使用行为数据更深入,而且增加了移动搜索这一垂直领域的数据。

下面是2013年最新一期的百度移动互联网发展趋势报告(2013年Q3)的主要结论截图如下:

pw_10pw_11pw_12

图片来源:http://developer.baidu.com/static/assets/reportpdf/百度移动互联网发展趋势报告2013Q3.pdf

2.     移动开发者解决方案

该报告由DCCI联合百度共同发布,主要针对移动互联网创业者,从2012年7月以来共发布3期的报告:

•        2013年9月发布“应用分析方法论”——《移动应用分析白皮书V1.0》,由百度移动统计团队历时8个月,在5000余个样本应用中分析提炼得出,通过通俗易懂的语言向开发者讲述如何科学有效地对应用进行统计分析;

•        2012年11月发布第二期报告:《成功应用案例剖析与趋势解读》,调研剖析国内成功应用案例,并对国内主流开放平台进行对比,以此为开发者提供参考和指引;

•        2012年7月发布第一期报告:《中国开发者生存状况调查》,呈现中国移动开发者人群的整体生存状态。

我们认为,这些信息对于产品、市场及用户研究人员的参考意义不大,当然,如果你即将创业或是想了解一下开发者的生存现状,这是一个很好的参考资料。

3.     移动应用统计报告  http://mtj.baidu.com/web/welcome/industry

该统计报告的数据来源于百度移动统计覆盖的终端中在指定时段启动过至少一次应用的活跃设备,透过此报告,大家可以把握移动移动应用的发展趋势,用户对移动应用(类别)的偏好及在APP上的使用行为。

该报告从2012年12月发布第一期报告以来,一共发布了3期报告:2012年Q3、Q4和13年的Q1。最新一期就是13年Q1的数据了,更新频率比较低。

最新一期的报告主要结论截图如下所示:

pw_13pw_14pw_15pw_16

 

图片来源:http://developer.baidu.com/static/assets/reportpdf/2013年第一季度移动应用统计报告.pdf

4.     移动应用搜索行业报告

目前应用搜索行业的资源储备已经成熟,移动应用的数量已经接近于2004年网站的数量。限于技术门槛较高,目前应用搜索的核心是功能搜索技术,即根据用户主动搜索需求推荐最合适的移动应用。未来应用内搜索技术可针对主动搜索需求直接推荐应用内内容,大幅降低用户使用门槛;基于自然语言的搜索技术更能够让应用搜索真正成为用户的贴身助手。

因此百度推出了“移动应用搜索行业报告”,其目的就是为自己的移动应用分发做广告,吸引中小应用开发者进驻合作。至今百度仅在2013年3月的时候做了一期的广告:《移动应用搜索行业报告_2013年第一期》,后续不要有什么期待了。

当然,如果你想在百度上做移动应用分发,这个可以作为一个参考,以便了解百度应用分发的渠道、机制和优劣势。其报告主要内容截图如下:

pw_17pw_18pw_19

图片来源:http://developer.baidu.com/static/assets/reportpdf/移动应用搜索行业报告_2013年第一期.pdf

 

 

 

三、豌豆荚  http://www.wandoujia.com/apps

豌豆荚作为一款优秀的Android平台手机助手工具,其在移动应用分发市场牢牢占据12.4%的市场份额,处于第三的位置。

笔记认为,由于豌豆荚现在仍在独立运营,暂时不会受制于国内巨头,其统计数据相对中立客观,尤其是一些核心应用的下载趋势及排名上,具有很大的参考价值。

pw_20

2013年Q3国内Android平台移动应用(APP)分发量市场份额

数据来源:易观国际( http://roll.sohu.com/20131108/n389774200.shtml

豌豆荚提供两方面的数据资源:下载排行总榜和应用分类排行榜。

1.    应用下载排行榜( http://www.wandoujia.com/top

该栏目下,主要提供三个分类榜单:

•        安卓游戏周下载排行

•        安卓软件周下载排行

•        安卓应用下载总榜(游戏+软件)

其排行版截图如下:

pw_21

图片来源:http://www.wandoujia.com/top/app

 

在排行中,包含主流Top应用的周下载量和总下载量,如果长期监控,大家可以拿到应用下载量的周变化趋势,进而方便追踪竞品。

2.    应用分类排行( http://www.wandoujia.com/tag/app

除了总榜,豌豆荚还提供了软件及游戏的分类榜单,这点和友盟指数的“应用排行榜”比较类似,不过在具体分类上有些差异。

豌豆荚的游戏和软件分类如下:

pw_22

图片来源:http://www.wandoujia.com/tag/app

http://www.wandoujia.com/tag/game

 

四、360手机助手  http://zhushou.360.cn/list/index/

360凭借其安全卫士及手机助手的庞大用户量,在应用分发市场牢牢占据24.8%的市场份额,处于绝对第二的位置,仅次于百度系。

如果从单一分发渠道而言,360的手机助手可以牢牢占据第一的位置,份额高达23.2%,超过了百度旗下的安卓市场16.3%。

pw_23

2013年Q3国内Android平台移动应用(APP)分发量市场份额

数据来源:易观国际( http://roll.sohu.com/20131108/n389774200.shtml

与豌豆荚类似,360手机助手也提供应用下载数据及排行榜。

1.    软件分类排行榜( http://zhushou.360.cn/list/index/cid/1

pw_24

图片来源:http://zhushou.360.cn/list/index/cid/1

 

2.    游戏分类排行榜( http://zhushou.360.cn/list/index/cid/2

pw_25

图片来源:http://zhushou.360.cn/list/index/cid/2

 

 

 

五、腾讯移动分析  http://mta.qq.com/mta/

腾讯在移动应用分发领域,主要有2个渠道:应用宝和QQ浏览器。而在移动应用分析领域,腾讯有2款专门的分析工具:海纳应用搜索和腾讯移动分析(MTA)。

pw_26

2013年Q3国内Android平台移动应用(APP)分发量市场份额

数据来源:易观国际( http://roll.sohu.com/20131108/n389774200.shtml

1.    海纳应用搜索( http://haina.myapp.com/

腾讯于2012年6月推出海纳应用搜索服务,海纳是基于应用功能搜索的跨平台应用搜索引擎,专注于APP搜索以及根据搜索行为的应用推荐。海纳应用搜索整合日均使用人次达十亿级的手机腾讯网、QQ浏览器等产品,通过独特的算法,为手机用户提供多维度的精准的应用搜索体验,为合作伙伴带去下载流量。

海纳应用提供的数据包含以下3类:

•        搜索风云榜( http://haina.myapp.com/android/appranking.jsp

pw_27

图片来源:http://haina.myapp.com/android/appranking.jsp

 

•        分类榜单( http://haina.myapp.com/android/appcategory.jsp

pw_28

图片来源:http://haina.myapp.com/android/appcategory.jsp

 

•        行业报告( http://haina.myapp.com/android/report.jsp

海纳应用搜索每半年会发布一个应用搜索行业报告,至今发布了2期:


image037

图片来源:http://haina.myapp.com/android/report.jsp

 

最新一期(2013年上半年)的报告主要结论截图如下:

pw_29

图片来源:http://3gimg.qq.com/web/haina/android/report/海纳安卓应用搜索行业报告-2013上半年.pdf

 

2.    腾讯移动分析( http://mta.qq.com/

腾讯移动分析(MTA),隶属于腾讯分析(TA),作为移动应用领域的专业分析工具,提供以下2类数据:

•        情报中心( http://mta.qq.com/mta/trend/ctr_top

pw_31

图片来源:http://mta.qq.com/mta/trend/ctr_top

 

这个排名数据来源于海纳应用搜索,但多提供了应用市场的筛选功能,经过对比,2个平台的排名数据完全一致。

•        移动用户终端环境报告( http://mta.qq.com/mta/operation/?cat=2&s=

pw_32

图片来源:http://mta.qq.com/mta/operation/?cat=2&s=

 

腾讯移动分析从2013年2月开始,不定期发布移动用户终端环境分析的报告,针对操作系统版本、网络运营商、屏幕分辨率、品牌和机型进行了统计分析,方便大家获取腾讯用户的终端环境情况。

最新一期(2013年9月)的报告主要结论截图如下:

pw_33

图片来源:http://mta.qq.com/mta/operation/?p=106

总结

以上就是5款免费的移动互联网分析工具及其使用方法的介绍,大家在实际使用过程中可以灵活运用,取长补短,进而与传统的问卷调研相结合,输出更有价值的分析报告,为您的产品提供更好的方向指引。

这里,再总结一下通过这5款工具可以获取到的移动互联网的重要基础数据:

•        设备激活量及品牌型号分布情况

•        设备分辨率及联网方式

•        操作系统及版本分布情况

•        无线互联网用户群体属性及地域分布

•        应用下载排行总榜及分类榜单

•        应用下载量统计及监测(历史下载量统计、日/月下载量监测)

•        应用使用情况大盘(日/月活跃用户数、日/月启动次数、日/月使用时长等)

后续我们将会对这5款分析工具的数据来源、收集渠道及统计方法进行更深入的研究,以便大家可以更好了解每款工具的内部统计模式,从而能够更客观的使用这些数据,如果对这个课题感兴趣者,欢迎与我们联系。

 

 

(本文出自腾讯CDC博客: http://cdc.tencent.com/?p=8046)

Spark的硬件配置

$
0
0

从MapReduce的兴起,就带来一种思路,就是希望通过大量廉价的机器来处理以前需要耗费昂贵资源的海量数据。这种方式事实上是一种架构的水平伸缩模式——真正的以量取胜。毕竟,以现在的硬件发展来看,CPU的核数、内存的容量以及海量存储硬盘,都慢慢变得低廉而高效。然而,对于商业应用的海量数据挖掘或分析来看,硬件成本依旧是开发商非常关注的。当然最好的结果是:既要马儿跑得快,还要马儿少吃草。

Spark相对于Hadoop的MapReduce而言,确乎要跑得迅捷许多。然而,Spark这种In-Memory的计算模式,是否在硬件资源尤其是内存资源的消耗上,要求更高呢?我既找不到这么多机器,也无法租用多台虚拟instance,再没法测评的情况下,只要寻求Spark的官方网站,又或者通过Google搜索。从Spark官方网站,Databricks公司Patrick Wendell的演讲以及Matei Zaharia的Spark论文,找到了一些关于Spark硬件配置的支撑数据。

Spark与存储系统

如果Spark使用HDFS作为存储系统,则可以有效地运用Spark的standalone mode cluster,让Spark与HDFS部署在同一台机器上。这种模式的部署非常简单,且读取文件的性能更高。当然,Spark对内存的使用是有要求的,需要合理分配它与HDFS的资源。因此,需要配置Spark和HDFS的环境变量,为各自的任务分配内存和CPU资源,避免相互之间的资源争用。

若HDFS的机器足够好,这种部署可以优先考虑。若数据处理的执行效率要求非常高,那么还是需要采用分离的部署模式,例如部署在Hadoop YARN集群上。

Spark对磁盘的要求

Spark是in memory的迭代式运算平台,因此它对磁盘的要求不高。Spark官方推荐为每个节点配置4-8块磁盘,且并不需要配置为RAID(即将磁盘作为单独的mount point)。然后,通过配置spark.local.dir来指定磁盘列表。

Spark对内存的要求

Spark虽然是in memory的运算平台,但从官方资料看,似乎本身对内存的要求并不是特别苛刻。官方网站只是要求内存在8GB之上即可(Impala要求机器配置在128GB)。当然,真正要高效处理,仍然是内存越大越好。若内存超过200GB,则需要当心,因为JVM对超过200GB的内存管理存在问题,需要特别的配置。

内存容量足够大,还得真正分给了Spark才行。Spark建议需要提供至少75%的内存空间分配给Spark,至于其余的内存空间,则分配给操作系统与buffer cache。这就需要部署Spark的机器足够干净。

考虑内存消耗问题,倘若我们要处理的数据仅仅是进行一次处理,用完即丢弃,就应该避免使用cache或persist,从而降低对内存的损耗。若确实需要将数据加载到内存中,而内存又不足以加载,则可以设置Storage Level。Spark提供了三种Storage Level:MEMORY_ONLY(这是默认值),MEMORY_AND_DISK,以及DISK_ONLY。

关于数据的持久化,Spark默认是持久化到内存中。但它也提供了三种持久化RDD的存储方式:

  • in-memory storage as deserialized Java objects

  • in-memory storage as serialised data

  • on-disk storage

第一种存储方式性能最优,第二种方式则对RDD的展现方式(Representing)提供了扩展,第三种方式则用于内存不足时。

注意,RDDs是Lazy的,在执行Transformation操作如map、filter时,并不会提交Job,只有在执行Action操作如count、first时,才会执行Job,此时才会进行数据的加载。要计算Spark加载的Dataset大小,可以通过Spark提供的Web UI Monitoring工具来帮助分析与判断。

Spark的RDD是具有分区(partition)的,Spark并非是将整个RDD一次性加载到内存中。Spark针对partition提供了eviction policy,这一Policy采用了LRU(Least Recently Used)机制。当一个新的RDD分区需要计算时,如果没有合适的空间存储,就会根据LRU策略,将最少访问的RDD分区弹出,除非这个新分区与最少访问的分区属于同一个RDD。这也在一定程度上缓和了对内存的消耗。

Spark对内存的消耗主要分为三部分:1. 数据集中对象的大小;2. 访问这些对象的内存消耗;3. 垃圾回收GC的消耗。一个通常的内存消耗计算方法是:内存消耗大小= 对象字段中原生数据 * (2~5)。 这是因为Spark运行在JVM之上,操作的Java对象都有定义的“object header”,而数据结构(如Map,LinkedList)对象自身也需要占用内存空间。此外,对于存储在数据结构中的基本类型,还需要装箱(Boxing)。Spark也提供了一些内存调优机制,例如执行对象的序列化,可以释放一部分内存空间。还可以通过为JVM设置flag来标记存放的字节数(选择4个字节而非8个字节)。在JDK 7下,还可以做更多优化,例如对字符编码的设置。这些配置都可以在spark-env.sh中设置。

Spark对网络的要求

Spark属于网络绑定型系统,因而建议使用10G及以上的网络带宽。

Spark对CPU的要求

Spark可以支持一台机器扩展至数十个CPU core,它实现的是线程之间最小共享。若内存足够大,则制约运算性能的就是网络带宽与CPU数。

Spark官方利用Amazon EC2的环境对Spark进行了基准测评。例如,在交互方式下进行数据挖掘(Interative Data Mining),租用Amazon EC2的100个实例,配置为8核、68GB的内存。对1TB的维基百科页面查阅日志(维基百科两年的数据)进行数据挖掘。在查询时,针对整个输入数据进行全扫描,只需要耗费5-7秒的时间。如下图所示:

在Matei Zaharia的Spark论文中还给出了一些使用Spark的真实案例。视频处理公司Conviva,使用Spark将数据子集加载到RDD中。报道说明,对于200GB压缩过的数据进行查询和聚合操作,并运行在两台Spark机器上,占用内存为96GB,执行完全部操作需要耗费30分钟左右的时间。同比情况下,Hadoop需要耗费20小时。注意:之所以200GB的压缩数据只占用96GB内存,是因为RDD的处理方式,使得我们可以只加载匹配客户过滤的行和列,而非所有压缩数据。

前端开发工程师:不是IE的IE11

$
0
0

IE11浏览器自去年六月份发布以来虽然据说也取得了不错的成绩,但是根据百度流量研究院的浏览器数据显示,国内好像还看不到其占有率,但全球范围内 IE11的占有率已经超过IE10和IE9的总和。获取IE11可以从两种方式:安装 Win8.1 里面内置IE11;从Win 7的 IE9/10 升级,但XP在中国仍然有超过 60%的占有率,而XP最高也只能升级到IE8。

前端测试仍在徘徊在IE6 ~ IE10之间,但在开发过程中偶然间发现了IE11与众IE不同之处。

前端开发一直将浏览器分为 W3C 和 IE 两种,特别是将 IE 众版本当做异类对待。微软这次决定洗心革面,将 IE11 打造成一款不是“IE”的浏览器,拥有与其它“W3C”浏览器一样的特性。

重要变动

IE11支持了完整的 flex 能力(参见 利用flexbox构建可伸缩布局),于 Firefox 之后第二个拥有无前缀 CSS 属性名。

支持了 SPDY协议。

ActiveXObject 对象不再存在了,这使得许多依靠检测该对象来嗅探浏览器类型的代码失效。

同样,document.all 的布尔值也会返回 false 。许多浏览器拥有该对象但是在转成布尔值时故意返回 false 用以兼容之前无数使用该对象判断浏览器类型的代码。现在,只有 IE6~IE9 会返回 true。

UA中一直存在的 MSIE 被删除,依赖于此获取浏览器类型和版本号的代码将失效。要判断其类型和版本,必须MSIE 和 Trident 共用了。

溢出了 attachEvent ,以 addEventListener 取代之,这也会破坏使用该方法判断浏览器类型的代码。

IE8引入的 XDomainRequest被删除,取而代之的是支持跨域资源共享( CORS)的 XMLHttpRequest。

总结

显然 IE11 的改进破坏了几乎所有用于区分 W3C 和 IE 浏览器的方法,使得无数现存代码无法取得其是 IE 浏览器的事实。或许这就是微软的目的:IE11 不再是“IE”了,你不需要在判断出来它了。

下面给出使用 UA 获取 IE 版本的代码:

/**
 * Get version if it's a microsoft internet explorer.
 *
 * @return version code,or else null if it's not a IE.
 */
function getIEVersion(){
    var ua = navigator.userAgent,matches,tridentMap={'4':8,'5':9,'6':10,'7':11};
 
    matches = ua.match(/MSIE (\d+)/i);
 
    if(matches&&matches[1])
    {   
        //find by msie
        return +matches[1];
    }
 
    matches = ua.match(/Trident\/(\d+)/i);
    if(matches&&matches[1])
    {   
        //find by trident
        return tridentMap[matches[1]]||null;
    }
 
    //we did what we could
    return null;
}

由于 UA 可以随意伪造,所以并没有合适的方法能够保证检测出真正的浏览器环境,因此代码都不应依赖于具体的环境和版本。

This article addresses: http://www.iefans.net/qianduan-kaifa-bushi-ie11/

By the time your rss reader get this post here is 1 comments ,Welcome you come to leave your opinion !

1.1 ireport之 ireport特性

$
0
0

先介绍一下ireport5和4.7的一些新功能,具体如下:

ireport 5的一些最重要的新功能:

  • 支持时区。
  • HTML5图表。
  • XML/A支持的MSAS。
  • 嵌套表。

4.7版增加了以下功能:

  • 100%支持JasperReports XML标签。
  • 具有创建报表的WYSIWYG编辑器。它有完整的工具可绘制矩形,直线,椭圆,文本框,标签,图表,子报表等等。
  • 语法高亮
  • 支持Unicode和非拉丁语言(俄罗斯,中国,日本,韩国,等)。
  • 浏览器的文档结构。
  • 集成报告编译,填充,和导出。
  • 支持所有的JDBC访问数据库。
  • 支持所有类型的虚拟数据源。向导自动创建报表和子报表。
  • 支持文档模板。
  • 支持TrueType字体。
  • 支持本地化。
  • 支持通过插件扩展。
  • 支持图表。
  • 支持标准的对象库(例如,页码)。
  • 拖放功能。
  • 无限的撤销/重做。
  • 向导创建交叉表。
  • 样式库。
  • 完整预览。
  • 错误管理。
  • jasperserver库浏览器。
  • 集成SQL和MDX查询器。


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



tomcat 高并发优化

$
0
0

 

<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"
maxThreads="15000"   minSpareThreads ="50"  acceptCount = "5000"
/>
maxThreads:tomcat起动的最大线程数,即同时处理的任务个数,默认值为200
minSpareThreads 表示空闲的线程数,据我的理解,类似于连接池

acceptCount:当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100


   另外,有时候,还需要调整jvm的启动参数。这里就不讲了。再者有时候系统默认的一些值不符合我们高并发的要求,我们也需要做下调整修改。。这样并发数才能上去。。

  如修改 ulimit -n 65535 

  -n size:设置 内核可以同时打开的 文件描述符的最大值.单位:n

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



网络互联设备

$
0
0

      互联硬件分为硬件的设备和用来传输数据的介质,而传输的设备又由于其所在的协议层不同而不同.分为物理层、数据链路层、网络层的设备。

 

物理层

中继器


目的:扩展网络

特点:成本低,但不能保证网络之间的安全

 

中继器


1.首先要保证每一个分支中的数据包和逻辑链路协议是相同的。例如,在802.3以太局域网和802.5之间,中继器是无法使它们通信的。

2.中继器可以用来连接不同的物理介质,并在各种物理介质中传输数据包。它可以连接不同类型的介质。

3.采用中继器连接网络分支的数目要受具体的网络体系结构限制。

4.中继器没有隔离和过滤功能,它不能阻挡含有异常的数据包从一个分支传到另一个分支。这意味着,一个分支出现故障可能影响到其它的每一个网络分支。

相当于:多端口的中继器。试想,如果每个设备只有一个对外接口,那么意味着只能建立一对点好点的通信。为了能够让通信“一对多”,需要将信号复制广播,于是,产生了集线器:把一个端口的信息重复广播到其它7个端口上(假设是8口HUB)。所以HUB也可以叫做multiportrepeater。广播会产生冲突,HUB都有碰撞检测功能,有碰撞基本上就是避让,一个人说完了,另一个人再说,所以效率低。

 

集线器


目的:数字信号放大和中转的作用

分类:无源集线器、有源集线器、智能集线器

 

集线器的主要功能是对接收到的信号进行再生整形放大,以扩大网络的传输距离,同时把所有节点集中在以它为中心的节点上。它工作于OSI(开放系统互联参考模型)参考模型第一层,即“物理层”。集线器与网卡、网线等传输介质一样,属于局域网中的基础设备,采用CSMA/CD(一种检测协议)介质访问控制机制.

 

集线器又分为这三类。


无源集线器:负责多段介质连接,不处理

有源集线器:相对无源集线器增加再生和放大

智能集线器:相对有源集线器增加如网络管理,选择网络传输线路。

 

总的来说,集线器就像是一个更加智能和端口更加丰富的中继器。针对的协议更多,这些一些特点就使得集线器相对中继器更多的功能。比如:集线器可以连接不同协议的网络和网段,而中继器是不可以的,只能针对相同的协议。


链路层

 

链路层的主要设备就是网桥

 

特点:

数据链路层上实现局域网互连;

互连的网络在数据链路层以上采用相同的协议

可靠性、安全性

 

网桥:有中继器的功能,但是更加智能。

原理:网络1 和网络2 通过网桥连接后,网桥接收网络1发送的数据包,检查数据包中的地址,如果地址属于网络1 ,它就将其放弃,相反,如果是网络2的地址,它就继续发送给网络2.这样可利用网桥隔离信息,将同一个网络号划分成多个网段(属于同一个网络号),隔离出安全网段,防止其他网段内的用户非法访问。由于网络的分段,各网段相对独立(属于同一个网络号),一个网段的故障不会影响到另一个网段的运行。

 

网络层

 

网络层的主要设备就是交换机和路由器。

一种用于电信号转发的网络设备。它可以为接入交换机的任意两个网络节点提供独享的电信号通路。机房一般的就是以太网的交换机。更多的是转发数据包的作用,而相对路由器呢就是一个更加智能的中继器可以用来更加更好的选择一个用于传输数据的网络最佳路径。


原理和过程


    


路由器


作用:连接逻辑上分开的网络。

种类:单协议路由,多协议路由

功能:选择路径、加密、数据压缩


网关


作用:用于应用层的互连设备,更为复杂,可以连接广域网和局域网。相当于咱们平时家庭中的门,

从一个房间走到另一个房间,必然要经过一扇门。同样,从一个网络向另一个网络发送信网关

息,也必须经过一道“关口”,这道关口就是网关。通常一个计算机会有一个默认网关,必须是和自己计算机内同网段内的。


常见互连设备


计算机生成了可选文字:常见设各在05一搜型中所处层次川搜收佑污进行丙生和攻遇只自到细口帕拍巨离用对高层侧旧是盈明的但住用个口自限(以太月是。+}报.翻的口她皿行网络间拍息翻敌可粗归月招通信.忙度祀离致*只臼够进目韶网M八C层的一络通过口阅地城进行门络甸伯息转趾司完成异沟门结之阅的两笼互泪只.峪口搜用翻冈月目层协议解子闷且泣爪的一用互峰设备用于垃皿一峨层上抽」坏颐肋砚的子门(侧勿卜切司与.卜八)多月口中口.多门口门桥,路由功能的层交峨机带协议阅协的交肠机-心‘川en羞墨篡噩署!一l}一薰

介质

传输介质就是用来传输数据的媒体。根据有无线路分为有线介质和无线介质。


有线介质

1.双绞线

 

由两條相互绝缘的导线互相缠绕(一般以顺时针缠绕)在一起而制成的一种通用配线,属于信息通信网络传输介质。

 

按屏蔽层分类

 

屏蔽双绞线

非屏蔽双绞线




如图为常用的屏蔽双绞线和非屏蔽双绞线,屏蔽双绞线就是右边这位。相对更加可靠和传输效率,与之对应而来就是成本高;

按粗细又分为这样一些类。


按线径粗细分类


1)一类线:最高频率带宽是750kHZ

2)二类线:最高频率带宽是1MHZ

3)三类线:该电缆的传输频率16MHz,最高传输速率为10Mbps(10Mbit/s

4)四类线:该类电缆的传输频率为20MHz,最高传输速率16Mbps

5)五类线:最高频率带宽为100MHz,最高传输率为100Mbps

6)六类线:传输频率为1MHz~250MHz,传输性能远远高于超五类标准,适用于传输速率高于1Gbps。

7)七类线:传输频率为600MHz,传输速度为10Gbps

这些类也就是随着需要的不同而进行一代代进行更迭的,不一定说最后一种最好。在适合的地方使用合适的工具才是最重要的。


2.同轴电缆


同轴电缆是什么的,具体什么结构见下图既得。


计算机生成了可选文字:单股或多股纹合,.1口口口口口口口口来乙烯绝缘介质

 

3.光纤

光纤相对之上几类更加速度快,更加可靠,更轻。就像刚说的,随之而来的就是成本的加大。

有线介质的比较


计算机生成了可选文字:

无线介质

1.微波


微波就是一种利用无线电波进行传输的一种传输方式。频率范围:2-40GHZ。一般通信距离:30-50km.必要时需要借助中继站来进行接力。

特点

传输质量稳定,易受天气和地理因素影响

2.红外线和激光

3.卫星通信

在无线传输过程中,更加容易受到天气和气候的影响,但是相对来说更加便捷和适应未来的需要。

 

综上,网络设备大体根据不同的协议层分为不同的设备,物理层,链路层,网络层以及更高的层次。也是随着需求的不同进行不同的改变的,不一定在某个时刻使用最好的但最好是更加合适的。就像上次五一听课时,老师说不一定某个开发方式好或者不好。某个时刻对应更加合适的开发和团队,网络这里也是一样,没有最好,只要更合适。合适就从传输效率,成本等因素来决定。

作者:chenfanglincfl 发表于2014-5-8 21:31:53 原文链接
阅读:84 评论:0 查看评论

PostgreSQL系统配置优化

$
0
0

原文: http://blog.csdn.net/xto/article/details/5403782

PG的配置文件是数据库目录下的postgresql.conf文件,8.0以后的版本可支持K,M,G这样的参数,只要修改相应参数后重新启动PG服务就OK了。

shared_buffers:这是最重要的参数,postgresql通过shared_buffers和内核和磁盘打交道,因此应该尽量大,让更多的数据缓存在shared_buffers中。通常设置为实际RAM的10%是合理的,比如50000(400M)。将所有的内存都给shared_buffers 将导致没有内存来运行程序。 unix 管理内存使用 swap, 当没有内存可用的时候,内核会将长时间不用的内存挪到 swap 去,这个叫:swap pageout,当你再次使用它的时候内核又会将其挪回来,这个叫:swap pagein,这个操作有很大的不好,因为它会导致你的程序挂起直到操作完成。

正确的 Shared Buffer Cache 大小:

(1) 它应该足够大来应付通常的表访问操作。

(2) 它应该足够小来避免 swap pagein 的发生。

work_mem: 在pgsql 8.0之前叫做sort_mem。postgresql在执行排序操作时,会根据work_mem的大小决定是否将一个大的结果集拆分为几个小的和 work_mem查不多大小的临时文件。显然拆分的结果是降低了排序的速度。因此增加work_mem有助于提高排序的速度。通常设置为实际RAM的2% -4%,根据需要排序结果集的大小而定,比如81920(80M)。

cache size 和 sort size 都影响内存的使用。记住, cache size 是在postmaster 启动的时候就申请好的,sort size 的改变是依赖于执行多少个排序。通常,cache size 比 sort size 更有效果。 一开始调整, 如果你只有一些大的session而有更多的小session: 10% of RAM for cache size 2-4% of RAM for sort size 还有一个很有价值的参数: effective_cache_size。 优化器用这个参数来预估内核的硬盘 buffer cache. 当内核有 unified buffer cache (统一缓冲), 这个值将是没被使用的内存的平均值,因为这种内核会使用所有没有使用的内存来作最近硬盘访问的缓存的;当内核用 fixed-sized disk buffer cache 时,这个参数应该设成一样的,通常是内存的 10%.

     effective_cache_size:是postgresql能够使用的最大缓存,这个数字对于独立的pgsql服务器而言应该足够大,比如4G的内存,可以设置为3.5G(437500)

     maintence_work_mem:这里定义的内存只是在CREATE INDEX, VACUUM等时用到,因此用到的频率不高,但是往往这些指令消耗比较多的资源,因此应该尽快让这些指令快速执行完毕:给maintence_work_mem大的内存,比如512M(524288)

max_connections: 通常,max_connections的目的是防止max_connections * work_mem超出了实际内存大小。比如,如果将work_mem设置为实际内存的2%大小,则在极端情况下,如果有50个查询都有排序要求,而且都使用2%的内存,则会导致swap的产生,系统性能就会大大降低。当然,如果有4G的内存,同时出现50个如此大的查询的几率应该是很小的。不过,要清楚 max_connections和work_mem的关系。

maintenance_work_mem (integer)声明在维护性操作中使用的最大的内存数,比如 VACUUM, CREATE INDEX,和 ALTER TABLE ADD FOREIGN KEY 等。 数值是用千字节计的,缺省是 16384 千字节(16 MB)。因为在一个数据库会话里,任意时刻只有一个这样的操作可以执行,并且一个数据库安装通常不会有太多这样的工作并发执行,把这个数值设置得比 work_mem 更大是安全的。 更大的设置可以改进清理和恢复数据库转储的速度。

max_stack_depth (integer):声明服务器的执行堆栈的最大安全深度。为此设置一个参数的原因是内核强制的实际堆栈尺寸(就是 ulimit -s 或者局部等效物的设置),小于一个安全的一兆字节左右的范围。 需要这么一个安全的界限是因为在服务器里,并非所有过程都检查了堆栈深度,儿只是在可能递规的过程,比如表达式计算这样的过程里面进行检查。 把这个参数设置得大于实际的内核限制讲意味着一个正在跑的递归函数可能会导致一个独立服务器进程的崩溃。缺省设置是 2048 KB (两兆),这个值相对比较小,不容易导致崩溃。 但是,这个值可能太小了,以至于无法执行复杂的函数。

自由空间映射 max_fsm_pages (integer)设置在共享的自由空间映射表里自由空间会跟踪的最大数目的磁盘页面数。 每个页面槽位需要消耗六个字节的共享内存。这个设置必须大于 16 * max_fsm_relations。 缺省是 20000。这个选项只能在服务器启动的时候设置。

max_fsm_relations (integer)设置自由空间将在共享地自由空间映射里跟踪的最大数目的关系(表和索引)。每个槽位大概要使用五十字节左右。缺省是 1000。这个选项只能在服务器启动的时候设置。

内核资源使用 max_files_per_process (integer):设置每个服务器进程允许同时打开的最大的文件数目。缺省是 1000。 如果内核强制一个合理的每进程限制, 那么你不用操心这个设置。但是在一些平台上(特别指出的是,大多数BSD 系统), sysconf 返回一个系统真正可以支持的数目大的多的数值。如果你发现有 "Too many open files" 这样的失败现象,那么就尝试缩小这个设置。这个选项只能在服务器启动时设置。

preload_libraries (string):这个变量声明一个或者多个在服务器启动的时候预先装载的共享库。可以选择在装载每个库的时候调用一个无参数的初始化函数。 要声明这个函数,可以在库名字后面加一个冒号,然后增加一个初始化函数名字。 比如 '$libdir/mylib:mylib_init' 会预先装载 mylib 并且执行 mylib_init。 如果装载了多过一个库,用逗号分隔它们。如果没有找到声明的库或者没有找到初始化函数,那么服务器将启动失败。可以用这个方法预先装载PostgreSQL 的过程语言库, 通常是使用 '$libdir/plXXX:plXXX_init' 语法,这里的 XXX 是 pgsql,perl,tcl,或者 python。

通过预先装载一个共享库(以及在需要的时候初始化它), 我们就可以避免第一次使用这个库的那些启动时间。不过,启动每个服务器进程的时间可能会增加,即使进程从来没有使用过这些库也这样。因此我们只是建议对那些将被大多数会话使用的库才使用这个选项。

基于开销的清理延迟:在 VACUUM 和 ANALYZE 命令执行过程中, 系统维护一个内部的指针,这个指针跟踪所执行的各种 I/O 操作的近似开销。 如果积累的开销达到了一个限制(通过 vacuum_cost_limit 声明),那么执行这个操作的进程将睡眠一会儿(用 vacuum_cost_delay 声明)。 然后它会重置指针然后继续执行。

这个特性的目的时允许管理员减少这些命令在并发活动的数据库上的 I/O 影响。 有些情况下,像 VACUUM 和ANALYZE 这样的维护命令并不需要迅速完成;但是,通常都不希望这些命令会严重干扰系统执行其它数据库操作的响应能力。 基于开销的清理延迟为管理员提供了一个实现这个目的的手段。

缺省的时候,这个特性是关闭的。要想打开它,把 vacuum_cost_delay 变量设置为一个非零值。

vacuum_cost_delay (integer):以毫秒计的时间长度,如果超过了开销限制,那么进程将睡眠一会儿。缺省值是 0,它关闭基于开销的清理延迟特性。正数值打开基于开销的清理。 不过,要注意在许多系统上,sleep 延迟的有效分辨率是 10 毫秒; 把 vacuum_cost_delay 设置为一个不是 10 的整数倍的数值与将它设置为下一个 10 的整数倍作用相同。

vacuum_cost_page_hit (integer):清理一个在共享缓存里找到的缓冲区的开销。它代表锁住缓冲池,查找共享的散列表以及扫描页面的内容的开销。缺省值是 1。

vacuum_cost_page_miss (integer):清理一个要从磁盘上读取的缓冲区的估计开销。这个行为代表锁住缓冲池,查找共享散列表,从磁盘读取需要的数据块以及扫描它的内容的开销。 缺省值是 10。

     vacuum_cost_page_dirty (integer):如果清理修改一个原先是干净的块的预计开销。它需要一个把脏的磁盘块再次冲刷到磁盘上的额外开销。缺省值是 20。

vacuum_cost_limit (integer):导致清理进程休眠的积累开销。缺省是 200。

注意: 有些操作会持有关键的锁,并且应该尽快结束。 在这样的操作过程中,基于开销的清理延迟不会发生作用。为了避免在这种情况下的长延时,实际的延迟是这样计算的: vacuum_cost_delay * accumulated_balance / vacuum_cost_limit 与 vacuum_cost_delay * 4 之间的最大值。

后端写进程

从 PostgreSQL 8.0 开始,就有一个独立的服务器进程,叫做后端写进程, 它唯一的功能就是发出写"脏"共享缓冲区的命令。 这么做的目的是让持有用户查询的服务器进程应该很少或者几乎不等待写动作的发生,因为后端写进程会做这件事情。这样的安排同样也减少了检查点造成的性能下降。 后端写进程将持续的把脏页面刷新到磁盘上,所以再检查点到来的时候,只有几个页面需要刷新到磁盘上。但是这样还是增加了 I/O 的总净负荷,因为以前的检查点间隔里,一个重复弄脏的页面可能只会冲刷一次, 而同一个间隔里,后端写进程可能会写好几次。在大多数情况下,连续的低负荷要比周期性的尖峰负荷好,但是在本节讨论的参数可以用于为本地需要调节其行为。

bgwriter_delay (integer):声明后端写进程活跃回合之间的延迟。在每个回合里,写进程都会为一些脏的缓冲区发出写操作(可以用下面的参数控制)。选取的缓冲区总是那些在当前的脏缓冲区里当前最少使用的。 然后它就休眠bgwriter_delay 毫秒,然后重复动作。缺省值是 200。 请注意在许多系统上,休眠延时的有效分辨率是 10 毫秒;因此,设置 bgwriter_delay 为一个不是 10 的倍数的数值与把它设置为下一个 10 的倍数是一样的效果。 这个选项只能在服务器启动的时候或者 postgresql.conf 文件里设置。

bgwriter_percent (integer)在每个回合里,当前的脏缓冲区中不超过这个百分比的量将被写到磁盘上 (把小数圆整为下一个整数缓冲区的数值)。 这个选项只能在服务器启动的时候或者 postgresql.conf 文件里设置。

bgwriter_maxpages (integer)在每个回合里,不超过这个数值的脏缓冲区写入。缺省值是 100。 这个选项只能在服务器启动的时候或者 postgresql.conf 文件里设置。

小的 bgwriter_percent 和 bgwriter_maxpages 减少后端写进程导致的额外 I/O 负荷, 但是会导致在检查点的时候的更多工作。要降低检查点时的峰值负荷,增加这些值。要想完全关闭后台写进程,可以把 bgwriter_percent 和/或bgwriter_maxpages 设置为零。 

有三个主要方面可以提升PostgreSQL的潜能。

查询方式的变化 
这主要涉及修改查询方式以获取更好的性能: 
创建索引,包括表达式和部分索引; 
使用COPY语句代替多个Insert语句; 
将多个SQL语句组成一个事务以减少提交事务的开销; 
从一个索引中提取多条记录时使用CLUSTER; 
从一个查询结果中取出部分记录时使用LIMIT; 
使用预编译式查询(Prepared Query); 
使用ANALYZE以保持精确的优化统计; 
定期使用 VACUUM 或 pg_autovacuum 
进行大量数据更改时先删除索引(然后重建索引)



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



[原]Pgpool使用心得体会

$
0
0

pgpool作为postgresql的集群应用兼有代理功能,实在是强大,在无数次的实验失败之后,总结一下我的使用心得。

一、下载安装

首先提供下载页: 点击这里。我下载的是installer-pg93-3.3.3.tar.gz,解压之后安装postgresql92-libs-9.2.4-1PGDG.didt.ep.x86_64.rpm和pgpool-II-pg93-3.3.3-1.pgdg.x86_64.rpm,这时pgpool的安装已经结束。

二、模式

pgpool给用户提供了三种模式供用户选择,分别是复制模式,主备模式和流复制模式。
pgpool模式
模式示例文件
复制模式pgpool.conf.sample-replication
主/备模式(Slony-I)pgpool.conf.sample-master-slave
主/备模式(流复制)pgpool.conf.sample-stream

1、复制模式

对于模式的配置,这里不做多讲,我使用的是复制模式,简单配置如下:(数据库节点为192.168.8.142,192.168.8.143;pgpool节点为192.168.8.35)
详细配置内容参见pgpool使用手册: 点击这里
首先修改两个节点的/etc/pgpool-II/pcp.conf
这个文件的作用是pgpool连接postgresql数据库的用户名密码认证。我们数据库的用户名密码为postgres:postgres。
[root@ucloud35 ~]# pg_md5 postgres
e8a48653851e28c69d0506508fb27fc5
然后在pcp.conf追加如下:
[root@ucloud35 ~]# echo "postgres:e8a48653851e28c69d0506508fb27fc5" >> /etc/pgpool-II/pcp.conf 
进入pgpool配置目录,讲现有模版备份,拷贝复制模版为现有模版
[root@ucloud35 ~]# cd /etc/pgpool-II/
[root@ucloud35 pgpool-II]# mv pgpool.conf pgpool.conf.bak
[root@ucloud35 pgpool-II]# cp pgpool.conf.sample-replication pgpool.conf
[root@ucloud35 pgpool-II]# chmod 764 pgpool.conf
进入配置文件,修改以下内容(内容旁边都有注释)
[root@ucloud35 pgpool-II]# vim pgpool.conf
listen_addresses = '*'   监听全网段
port = 9999    开放端口9999
backend_hostname0 = '192.168.8.142'    节点1
backend_port0 = 5432    节点1接入的端口
backend_weight0 = 1    节点1的权重
backend_hostname1 = '192.168.8.143'    节点2
backend_port0 = 5432    节点1接入的端口
backend_weight1 = 1    节点2的权重
enable_pool_hba = on    接入代理的客户端认证
基本配置是这些
将142和143节点的数据库开启:
/opt/PostgreSQL/9.3/bin/pg_ctl -D /opt/PostgreSQL/9.3/data/ start
(关闭命令:/opt/PostgreSQL/9.3/bin/pg_ctl -D /opt/PostgreSQL/9.3/data/ -m fast stop)
然后开启pgpool,
[root@office-cmdb01 ~]# pgpool
第一次开启会提示pid文件不存在,需要手工创建,若父目录不存在手动添加。
再次开启pgpool就可以了,第一次开启出现的提示可以忽略。

三、注意事项

出于安全考虑,需要对数据库进行安全认证,数据库安全认证模式为md5,pgpool也是同样的。对于节点的数据库安全认证文件,我的配置如下:
[root@office-cmdb01 data]# tail pg_hba.conf -n 15


# "local" is for Unix domain socket connections only
local   all             all                                     md5
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
host    all             all             192.168.8.142/32            trust
host    all             all             192.168.8.143/32            trust
host    all             all             0.0.0.0/0            md5
# IPv6 local connections:
host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
#local   replication     postgres                                md5
#host    replication     postgres        127.0.0.1/32            md5
#host    replication     postgres        ::1/128                 md5
此处语法很简单,只需要制定好相应IP或者IP段,写好认证方式为trust或者md5即可。
为了实现其他用户接入数据库密码认证,这里对节点数据库采用trust,对其他IP采用md5认证,优先级为从上往下。
下面是对pgpool认证文件的配置:
[root@ucloud35 pgpool-II]# tail pool_hba.conf 
# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
# "local" is for Unix domain socket connections only
local   all         all                               trust
# IPv4 local connections:
host    all         all         127.0.0.1/32          trust
host    all         all         0.0.0.0/0          md5
对全网段采用md5认证。
下面生成密码文件:
[root@ucloud35 pgpool-II]#pg_md5 --md5auth -u postgres -p
输入和数据库一样的密码(postgres)。
[root@ucloud35 pgpool-II]# tail pool_passwd 
postgres:md574d9e50349a805a667ad9e325fb02ebd
此时认证已经做好,用数据库客户端连接35的9999端口即可访问。
这里需要补充一下的是,数据库节点和代理节点必须分开,因为他们都是做md5认证,但是代理程序的认证是基于数据库的,由于数据库之间需要做trust认证,如果把代理节点和其中一个数据库节点放在一起,代理节点的认证则会不起作用,所以必须要和数据库节点分开使用。
同时为了达到稳定和灾备,可以搭建两个pgpool代理用keepalive实现动态迁移,在访问的时候需要访问VIP的9999端口才能实现复制模式或者其他模式。

作者:u013526896 发表于2014-4-25 18:27:07 原文链接
阅读:23 评论:0 查看评论
Viewing all 15843 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>