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

运营商资费下降背后:混改、5G暗战和中国移动的野心

$
0
0

导语:惠民的同时,运营商也希望借此寻求竞争中的制高点,在5G临近之下,这一愿望显得更为迫切。

运营商.jpg

从2015年起,提速降费便成为中国三大电信运营商每年的工作重点之一。惠民的同时,运营商也希望借此寻求竞争中的制高点,在5G临近之下,这一愿望显得更为迫切。

昨日,工业和信息化部召开新闻发布会,在谈到提速降费的进展时,工信部信息通信发展司司长闻库表示,今年,专项行动的实施意见即将发布,通过17项具体措施提速降费。

据了解,该意见包括加大电信基础设施投入、深挖宽带网络降费的潜力、鼓励宽带应用融合创新和优化提速降费政策环境四个方面。

按照工信部的规划,预计到今年年底,全国固定宽带家庭普及率达到63%,移动宽带用户普及率达到75%,超过85%的固定宽带用户将使用20Mbps以上的宽带接入服务,超过50%的用户将使用50Mbps以上的宽带接入服务。

专项行动意见的提出,意味着国家对提速降费的力度要求会越来越大。但由于三家运营商体量不同,所产生的效应反应在竞争层面上耐人寻味。

中国移动的另一个“野心”

今年3月6日,国务院新闻办公室举行国务院政策例行吹风会。工信部副部长陈肇雄表示,今年工信部将深入推进网络提速降费相关工作。这也拉开了2017新一轮的提速降费。

运营商老大中国移动响应迅速,昨日,最先通报了提速降费的最新措施。包含七大方面,国际漫游费、专线接入资费、个人数据流量资费成为重点。会上,中国移动集团公司市场部副总经理包建益表,2014年-2016年间手机上网平均单价累计降幅63.5%;各项降费举措2015年、2016年分别惠及客户7.4亿次,12.6亿次。

对于三大运营商而言,降费一定程度上带来营收和利润压力。但对于体量较大的中国移动而言,业界普遍认为影响相对较小,这从其2016年的财报便可窥见一斑。

据中国移动2016年财报显示,中国移动营运收入为人民币7084亿元,增长6.0%。其中,通信服务收入6234亿元,增幅为6.7%,增幅达到近五年新高,位居行业首位。而在通行服务中,无线上网收入较去年上升43.5%,占通信服务收入比达到46.2%,年度首次超过语音和短彩信收入之和,成为公司第一大收入来源。

不难看出,中国移动流量经营的战略转型已初见成效,而降费也还在持续中。

若按照当前业务结构进行静态测算,中国移动预计三项“降费”举措(国内长途和漫游费、国际漫游、专线接入)对其2017年收入和营运利润的影响为:取消手机国内长途和漫游费约为一个季度40亿元,降低中小企业专线接入和国际长途资费合计约30亿元。

手握中国超过六成的4G用户(5.58亿户),薄利多销或许是中国移动当下竞争的最好策略,甚至从某种程度上而言,降费还帮助中国移动巩固了4G用户。最新一季度用户数据显示,中国移动首季度4G用户净增3304万户,继续保持着高速增长,手机上网流量比上年同期增长101%,手机上网客户DOU突破1000MB。

增长之外让中国移动真正顾忌的问题是:随着流量单价不断降低,流量价格战会导致运营商的纯管道流量收入增长趋缓,传统的流量经营模式也将碰到“天花板”,所以商业模式的创新迫在眉睫。

从目前市场的表现来看,后向流量要比前向流量成熟更快,也易被市场所接受。如此次中国移动推出的9元和24元两档视频流量优惠包,客户可分别获得 3GB和30GB视频流量。还有中国 联通(微博)、中国电信(微博)和腾讯王者荣耀游戏合作的定制流量包。

分析人士指出,运营商在流量优化配置方面尚有较大的提升空间,而随着5G到来,流量经营的好与坏将决定运营商未来的话语权,中国移动手中的王牌便是庞大用户所产生的流量。

在此情形下,中国移动希望在2018年能够推动5G的规模实验和试商用,除了看重高速率网络带来的流量经济外,中国移动更看重5G下的物联网市场。

中国移动总裁 李跃曾表示,4G应用的场景主要是手机,5G将以用于各行各业。4G是为上网设计的,5G是为万物互联设计的。5G的发展会给实现物联网的普及打下基础,同时物联网的发展也会给通讯运营商带来更大的发展机遇。

中国移动预计,2020年全球市场连接规模将达到500亿,中国市场的连接数有望突破100亿;但在同时,网络连接的价值只占到物联网产业的15%,运营商必须向产业的上下游进行拓展。

联通、电信的抱团会掀起多大浪?

对于体量较小的中国电信、中国联通而言,降费所带来的经营压力远比中国移动大,相比之下,提速就显得至关重要了。

谈及5G问题,工信部的最新披露是,下一步要推动开展第二阶段的技术研发试验,重点开展面向移动互联网、低时延高可靠和低功耗大连接这三大5G典型场景的无线空口和网络技术方案的研发与试验,并将引入国内外芯片和仪表厂商,共同推动5G产业链成熟,预计到2017年底二阶段试验完成。

由于4G战略滞后导致的落后局面,5G对于中国联通的意义不言而喻。用董事长王晓初的话来讲,5G时代的到来是中国联通掰回一局的关键机会,中国联通绝不会再犯4G时代的错误。

所以在布局上,中国联通的动作明显快了很多。据了解,广东联通近日携手爱立信在广州珠江新城开通国内首个5G商用网站点,同时也是全球首个基于双频段的5G点系统站点。此次部署的5G商用网站点,灵活支持5G、4G和3G多制式、多频段技术,能够帮助中国联通高效实现2G、3G、4G的频谱资源整合并简化网络架构,使中国联通网络平滑演进到5G。

对于5G,中国联通曾表示已连续两年投资累计超过2000亿元,今年将放缓投资。“一是为了提高现在的资产有效利用率,使得公司的盈利状况改善,更主要的也是为5G发展准备一部分资金。”王晓初说。

提速对中国电信同样至关重要,早在今年的2月18日,中国电信在南京已成功建成4个5G基站,总投资3亿多元,而未来这个区域的5G基站数目将达到600多个。

对于中国电信而言,与体量相差无几的中国联通“联手”发展或许是一个好办法。事实上,这样的“联手”也让双方尝到了甜头,全网通终端的推广就是最好的例证。上月,两家联合发布了有关六模全网通终端的新举措。

所谓六模全网通手机,可实现一部手机、两个卡槽、三网通用、4G随意切换,用户可以不再受手机制式的束缚而自主选择运营商。这对用户规模落后的中国联通、中国电信而言,很大程度上分流了中国移动的用户。

中国移动对于两家的“联手”效应自然不会置之不理。据了解,从去年开始,中国移动强力推进了定制手机(也就是所谓阉割手机)的策略,而且在2017年还将继续强化推进定制手机的策略,从而应对中国联通、中国电信提出的抢占第二卡槽策略的竞争。

但全网通已是大势所趋。据了解,全网通终端发展迅猛,市场销售份额由2015年底不到20%,已快速增长至目前的75%,六模全网通已成为市场主流。同时,消费者手机消费支出也随之降低,2016年消费者购买4G手机支出减少约180亿元。此外,六模全网通已被国际标准组织GCF采纳,未来有望成为全球标准。

在今日举行的2017中国联通终端产业链峰会,主题也是全网通,两家的“联手”持续升级。一个有趣的画面是,虽然是中国联通的活动,但却得到一些中国电信管理层微信朋友圈的转发和点赞,这在以往是很难见到的。

据悉,本次会上中国联通还将成立全网通联盟,一方面实现消费者利益最大化,另一目的是不会受制于一家运营商。

从行业来看,5G的到来,是中国联通、中国电信“翻身”的一个机会,很有可能打破中国移动一家垄断的局面。这不仅仅体现在基础网络、业务上的追赶,更重要的是有机会实现商业模式的变革,用“混改”改变格局。

目前,中国联通的“混改”进入关键时期,持续停盘等待最后的批复。


日处理 20 亿数据,实时用户行为服务系统架构实践

$
0
0

作者丨陈清渠

责编丨钱曙光


携程实时用户行为服务作为基础服务,目前普遍应用在多个场景中,比如猜你喜欢(携程的推荐系统)、动态广告、用户画像、浏览历史等等。


以猜你喜欢为例,猜你喜欢为应用内用户提供潜在选项,提高成交效率。旅行是一项综合性的需求,用户往往需要不止一个产品。作为一站式的旅游服务平台,跨业务线的推荐,特别是实时推荐,能实际满足用户的需求,因此在上游提供打通各业务线之间的用户行为数据有很大的必要性。


携程原有的实时用户行为系统存在一些问题,包括:1)数据覆盖不全;2)数据输出没有统一格式,对众多使用方提高了接入成本;3)日志处理模块是Web Service,比较难支持多种数据处理策略和实现方便扩容应对流量洪峰的需求等。


而近几年旅游市场高速增长,数据量越来越大,并且会持续快速增长。有越来越多的使用需求,对系统的实时性,稳定性也提出了更高的要求。总的来说,当前需求对系统的实时性/可用性/性能/扩展性方面都有很高的要求。


一、架构


这样的背景下,我们按照如下结构重新设计了系统:



图1 实时用户行为系统逻辑视图


新的架构下,数据有两种流向,分别是处理流和输出流。


在处理流,行为日志会从客户端(App/Online/H5)上传到服务端的Collector Service。Collector Service将消息发送到分布式队列。数据处理模块由流计算框架完成,从分布式队列读出数据,处理之后把数据写入数据层,由分布式缓存和数据库集群组成。


输出流相对简单,Web Service的后台会从数据层拉取数据,并输出给调用方,有的是内部服务调用,比如推荐系统,也有的是输出到前台,比如浏览历史。系统实现采用的是Java+Kafka+Storm+Redis+MySQL+Tomcat+Spring的技术栈。


  • Java:目前公司内部Java化的氛围比较浓厚,并且Java有比较成熟的大数据组件

  • Kafka/Storm:Kafka作为分布式消息队列已经在公司有比较成熟的应用,流计算框架Storm也已经落地,并且有比较好的运维支持环境。

  • Redis: Redis的HA,SortedSet和过期等特性比较好地满足了系统的需求。

  • MySQL: 作为基础系统,稳定性和性能也是系统的两大指标,对比NoSQL的主要选项,比如HBase和ElasticSearch,十亿数据级别上MySQL在这两方面有更好的表现,并且经过设计能够有不错的水平扩展能力。

目前系统每天处理20亿左右的数据量,数据从上线到可用的时间在300毫秒左右。查询服务每天服务8000万左右的请求,平均延迟在6毫秒左右。下面从实时性/可用性/性能/部署几个维度来说明系统的设计。


二、实时性


作为一个实时系统,实时性是首要指标。线上系统面对着各种异常情况。例如如下几种情况:


  1. 突发流量洪峰,怎么应对;

  2. 出现失败数据或故障模块,如何保证失败数据重试并同时保证新数据的处理;

  3. 环境问题或bug导致数据积压,如何快速消解;

  4. 程序bug,旧数据需要重新处理,如何快速处理同时保证新数据。

系统从设计之初就考虑了上述情况。


首先是用Storm解决了突发流量洪峰的问题。Storm具有如下特性:



图2 Storm特性


作为一个流计算框架,和早期大数据处理的批处理框架有明显区别。批处理框架是执行完一次任务就结束运行,而流处理框架则持续运行,理论上永不停止,并且处理粒度是消息级别,因此只要系统的计算能力足够,就能保证每条消息都能第一时间被发现并处理。


对当前系统来说,通过Storm处理框架,消息能在进入Kafka之后毫秒级别被处理。此外,Storm具有强大的scale out能力。只要通过后台修改Worker数量参数,并重启Topology(Storm的任务名称),可以马上扩展计算能力,方便应对突发的流量洪峰。


对消息的处理Storm支持多种数据保证策略,at least once,at most once,exactly once。对实时用户行为来说,首先是保证数据尽可能少丢失,另外要支持包括重试和降级的多种数据处理策略,并不能发挥exactly once的优势,反而会因为事务支持降低性能,所以实时用户行为系统采用的at least once的策略。这种策略下消息可能会重发,所以程序处理实现了幂等支持。


Storm的发布比较简单,上传更新程序jar包并重启任务即可完成一次发布,遗憾的是没有多版本灰度发布的支持。



图3 Storm架构


在部分情况下数据处理需要重试,比如数据库连接超时,或者无法连接。连接超时可能马上重试就能恢复,但是无法连接一般需要更长时间等待网络或数据库的恢复,这种情况下处理程序不能一直等待,否则会造成数据延迟。实时用户行为系统采用了双队列的设计来解决这个问题。



图4 双队列设计


生产者将行为纪录写入Queue1(主要保持数据新鲜),Worker从Queue1消费新鲜数据。如果发生上述异常数据,则Worker将异常数据写入Queue2(主要保持异常数据)。


这样Worker对Queue1的消费进度不会被异常数据影响,可以保持消费新鲜数据。RetryWorker会监听Queue2,消费异常数据,如果处理还没有成功,则按照一定的策略(如下图)等待或者重新将异常数据写入Queue2。



图5 补偿重试策略


另外,数据发生积压的情况下,可以调整Worker的消费游标,从最新的数据重新开始消费,保证最新数据得到处理。中间未经处理的一段数据则启动backupWorker,指定起止游标,在消费完指定区间的数据之后,backupWorker会自动停止(如下图)。



图6 积压数据消解


三、可用性


作为基础服务,对可用性的要求比一般的服务要高得多,因为下游依赖的服务多,一旦出现故障,有可能会引起级联反应影响大量业务。项目从设计上对以下问题做了处理,保障系统的可用性:


  1. 系统是否有单点?

  2. DB扩容/维护/故障怎么办?

  3. Redis维护/升级补丁怎么办?

  4. 服务万一挂了如何快速恢复?如何尽量不影响下游应用?

首先是系统层面上做了全栈集群化。Kafka和Storm本身比较成熟地支持集群化运维;Web服务支持了无状态处理并且通过负载均衡实现集群化;Redis和DB方面携程已经支持主备部署,使用过程中如果主机发生故障,备机会自动接管服务;通过全栈集群化保障系统没有单点。


另外系统在部分模块不可用时通过降级处理保障整个系统的可用性。先看看正常数据处理流程(如下图):



图7 正常数据流程


在系统正常状态下,Storm会从Kafka中读取数据,分别写入到Redis和MySQL中。服务从Redis拉取(取不到时从DB补偿),输出给客户端。DB降级的情况下,数据流程也随之改变(如下图)。



图8 系统降级-DB


当MySQL不可用时,通过打开DB降级开关,Storm会正常写入Redis,但不再往MySQL写入数据。数据进入Reids就可以被查询服务使用,提供给客户端。另外Storm会把数据写入一份到Kafka的Retry队列,在MySQL正常服务之后,通过关闭DB降级开关,Storm会消费Retry队列中的数据,从而把数据写入到MySQL中。Redis和MySQL的数据在降级期间会有不一致,但系统恢复正常之后会通过Retry保证数据最终的一致性。Redis的降级处理也类似(如下图):



图9 系统降级-Redis


唯一有点不同的是Redis的服务能力要远超过MySQL。所以在Redis降级时系统的吞吐能力是下降的。这时我们会监控db压力,如果发现MySQL压力较大,会暂时停止数据的写入,降低MySQL的压力,从而保证查询服务的稳定。


为了降低故障情况下对下游的影响,查询服务通过Netflix的Hystrix组件支持了熔断模式(如下图)。



图10 Circuit Breaker Pattern


在该模式下,一旦服务失败请求在给定时间内超过一个阈值,就会打开熔断开关。在开关开启情况下,服务对后续请求直接返回失败响应,不会再让请求经过业务模块处理,从而避免服务器进一步增加压力引起雪崩,也不会因为响应时间延长拖累调用方。


开关打开之后会开始计时,timeout后会进入Half Open的状态,在该状态下会允许一个请求通过,进入业务处理模块,如果能正常返回则关闭开关,否则继续保持开关打开直到下次timeout。这样业务恢复之后就能正常服务请求。


另外,为了防止单个调用方的非法调用对服务的影响,服务也支持了多个维度限流,包括调用方AppId/ip限流和服务限流,接口限流等。


四、性能&扩展


由于在线旅游行业近几年的高速增长,携程作为行业领头羊也蓬勃发展,因此访问量和数据量也大幅提升。公司对业务的要求是可以支撑10倍容量扩展,扩展最难的部分在数据层,因为涉及到存量数据的迁移。


实时用户行为系统的数据层包括Redis和MySQL,Redis因为实现了一致性哈希,扩容时只要加机器,并对分配到新分区的数据作读补偿就可以。


MySQL方面,我们也做了水平切分作为扩展的准备,分片数量的选择考虑为2的n次方,这样做在扩容时有明显的好处。因为携程的MySQL数据库现在普遍采用的是一主一备的方式,在扩容时可以直接把备机拉平成第二台(组)主机。假设原来分了2个库,d0和d1,都放在服务器s0上,s0同时有备机s1。扩容只需要如下几步:


  1. 确保s0 -> s1同步顺利,没有明显延迟

  2. s0暂时关闭读写权限

  3. 确认s1已经完全同步s0更新

  4. s1开放读写权限

  5. d1的dns由s0切换到s1

  6. s0开放读写权限

迁移过程利用MySQL的复制分发特性,避免了繁琐易错的人工同步过程,大大降低了迁移成本和时间。整个操作过程可以在几分钟完成,结合DB降级的功能,只有在DNS切换的几秒钟时间会产生异常。


整个过程比较简单方便,降低了运维负担,一定程度也能降低过多操作造成类似GitLab式悲剧的可能性。


五、部署


前文提到Storm部署是比较方便的,只要上传重启就可以完成部署。部署之后由于程序重新启动上下文丢失,可以通过Kafka记录的游标找到之前处理位置,恢复处理。 


另外有部分情况下程序可能需要多版本运行,比如行为纪录暂时有多个版本,这种情况下我们会新增一个backupJob,在backupJob中运行历史版本。

利用一点机器学习来加速你的网站

$
0
0

大数据

在生活中,我有 73% 的时间在考虑 web 性能-在低配手机上达到 60 FPS、 有序加载资源、离线缓存任何能缓存的资源。还有一些其他的优化。

最近,我发现自己对 web 性能的定义可能太狭隘了,从用户的角度上来说,这些只是 web 性能中的一些小插曲。

所以我打开了我经常去的网站,尝试了所有的用户可能的操作,并记录操作所花费的时间。(我们需要一些用户操作时光轴工具)

之后,我发现了一个可行的提升性能的方案。

下面的文章内容聚焦在某个具体网站的具体操作步骤。但是我觉得这个解决方案(嗯,没错!就是机器学习)可以应用到很多其他类型的网站上去。

问题,如何才能节约时间

这个网站,用于卖家出售没用的东西,买家通过购买这些东西来淘一些有价值的东西。

当卖家要在网站上出售东西时候,要先选择分类, 再选择对应的模版,然后填写细节信息,预览,最后发布。

然而第一步  —  选择分类  —  就把我带进了一条弯路

首先,一共有674个类别,我根本不知道我你破旧的皮划艇属于哪个类别( Steve Krug 说的好,不要让用户去思考)

第二步,即使我知道商品所属的类别  —  子类别  —  子子类别,我也要至少花费12秒的时间。

如果我跟你说,我能把你的页面的加载时间减少12秒,你一定觉得我疯了。那么为什么不在一些别的地方来节约这12秒呢。

正如凯撒大帝所说,时间很宝贵的呢。

我一直认为用户无知是福。我如果把商品的标题、描述、价格放到机器学习的模型里面,系统应该能自动计算出商品所属的分类。

这样子用户选类别的时间就能省下来了。他们就可以开心的把这些时间拿来去 reddit 找 DIY 的双层床了。

机器学习-你不该逃避它,你要去拥抱它

一开始的时候,我对机器学习一点概念都没有。我是在游戏 AI ,以及 Alpha 狗战胜人类顶级围棋棋手之后才有所了解的。

因此我打算开始去了解它,下面的几步一个小时都不需要。

  1. Google 搜索 ‘machine learning’
  2. 查看大量的关于机器学习的文章
  3. 发现了亚马逊发布的机器学习相关的服务
  4. 我开始意识到我不需要知道太多的关于机器学习的东西
  5. 嗯。好开心

一个简单的实现流程

亚马逊发布了他的机器学习文档。如果你不是对这个文档很感兴趣,打算花5个小时去阅读,那么就来看下我写的一些总结吧。

整理如下:

  • 获取一些 CSV 数据文件,每行都是一个商品项(^_^我的皮划艇),列名是标题、描述、价格、所属分类。
  • 把数据传送到亚马逊的 AWS S3 bucket 里面
  • 用数据去训练机器。这样子,这个小小云机器人就能通过商品的标题,描述和价格去预测他的分类了。
  • 在前端页面上,写一些代码,获取用户输入的 标题/描述/价格,发给这个云机器人,经过计算,就能向预测这个商品所属的分类了。

实战模拟

下面是我写的一个表单,模拟了卖家发布信息的几个关键流程。

下面的结果一定会让你对机器学习保持兴趣。你只要相信我,建议类别是由深度学习模拟预测出来的。

让我们去卖一个冰箱

大数据

再来试一下卖个水族馆:

大数据

这个云机器人居然能识别出水族馆!

当我看到这个结果的时候,手舞足蹈,是不是棒棒哒?

(我偷偷的告诉你我是怎么实现的:React, Redux, JQuery, Mox, RxJs, BlueBird, Bootstrap, Sass, Compass, NodeJs, Express, Loadsh。项目是使用 webpack 打包。最后生成的文件在1M左右)

嗯。不 BB 了。开始讲正经事。

一开始为了拿到机器学习用的数据。我也是想破了头。我大概需要10K条数据。后来是在一个当地的交易网站上面发现有这些数据。看了一下 URL 和 DOM 结构之后,我用 Google Scraper 插件提取了一些数据。导出成 CSV 文件。在这些数据上我大概花费了四个小时。将近整个项目时间的一半了。

数据整理好之后,上传到了 Amazon S3 上,配置了一下机器学习的参数,设置了数据模型。整个学习的 CPU 耗时才3分钟。

界面上还有一个实时预测功能,所以我打算用一些参数测试一下。

大数据

 

嗯。还挺好用的。

为了不在浏览器里面暴露出我的 Amazon API ,所以我把 API 放到了 Node 服务器上。

后台代码(Node)

使用方式很简单。接口参数为 modelId, 服务器返回一个 prediction :

constAWS =require('aws-sdk');constmachineLearning =newAWS.MachineLearning();constparams = {
  MLModelId:'some-model-id',
  PredictEndpoint:'some-endpoint',
  Record: {},
};

machineLearning.predict(params, (err, prediction) => {// we have a prediction!});

这里参数用大写字母开头,本来打算改掉的。后来想想还是算了。

Record, 是一个JSON对象。属性值是(title, description, price)

我不想只提供一些代码片段。为了帮助大家更好的理解。我把所有的服务端代码都贴上来了。

server.js:

constexpress =require('express');constbodyParser =require('body-parser');constAWS =require('aws-sdk');constapp = express();
app.use(express.static('public'));
app.use(bodyParser.json());

AWS.config.loadFromPath('./private/aws-credentials.json');constmachineLearning =newAWS.MachineLearning();

app.post('/predict', (req, res) => {constparams = {
    MLModelId:'my-model-id',
    PredictEndpoint:'https://realtime.machinelearning.us-east-1.amazonaws.com',
    Record: req.body,
  };

  machineLearning.predict(params, (err, data) => {if(err) {console.log(err);
    }else{
      res.json({ category: data.Prediction.predictedLabel });
    }
  });
});

app.listen(8080);

aws-credentials.json:

{"accessKeyId":"my-access-key-id","secretAccessKey":"shhh-secret-squirrel","region":"us-east-1"}

(在.gitignore 中忽略 /private 文件夹)

上面就是所有的后台代码。

前端代码

表单里面的代码功能比较简单。

  • 监听几个输入框的 blur 事件
  • 读取表单里面的字段值
  • POST 给 API 端
  • 把 API 端返回的 prediction 显示在页面上
(function(){consttitleEl =document.getElementById('title-input');constdescriptionEl =document.getElementById('desc-input');constpriceEl =document.getElementById('price-input');constcatSuggestionsEl =document.getElementById('cat-suggestions');constcatSuggestionEl =document.getElementById('suggested-category');functionpredictCategory(){constfetchOptions = {
      method:'POST',
      headers: {'Content-Type':'application/json',
      },
      body:JSON.stringify({
        title: titleEl.value,
        description: descriptionEl.value,
        price: priceEl.value,
      })
    };

    fetch('/predict', fetchOptions)
      .then(response => response.json())
      .then(prediction => {
        catSuggestionEl.textContent = prediction.category;
        catSuggestionsEl.style.display ='block';
      });
  }document.querySelectorAll('.user-input').forEach(el => {
    el.addEventListener('blur', predictCategory);
  });
})();

上面就是全部的前端代码了。

啊啊啊……云服务还要收费呢

别忙着收起你的帽子,魔术表演怎么可能是免费呢。

我上面用到的 model 数据(10K行/4列)有6.3MB. 云端在等待接受请求的时候,消耗了6.3MB的内存。这些资源的开销是每小时0.0001刀。或者每年8刀。 我在手套上面花的钱都比它多。

每次进行 prediction 的时候,也要0.0001刀。所有就不要随随便便就调用这个 API 了。

虽然目前不仅仅是 Amazon 提供了这个服务,但是我还是没有找到另外两个大厂家的价目表。

Google 有 TensorFlow, 但是我看了一下入门教程就跑了。

Microsoft 有 Machine Learning offering, 但是IE6还是让我有点耿耿于怀 (可能不久后,Amazon 和 Microsoft 之间会有一场大战吧)。

一些总结

或许只是我感到有些许惊讶(我还记得当我意识到‘news’是‘new’的复数的时候),我认为这些都十分让人惊讶。它允许像你我这样的普通人(对发展影响的程度较小的人)在机器学习中进行挖掘,可能会促成那些用户很大的改进。

下一步在哪?

上面的例子显然是进行过设计的,并且,我承认,我省略了一些话题。

如果我可以的话,我应该列出所有问题,但要是你自己去做你自己发现问题那也是很有趣的。

因此,去做吧,如果你取得了一些成功,我将乐于在评论中看到它们。

英文原文: Speed up your site with a little machine learning

End.

转载请注明来自36大数据(36dsj.com): 36大数据» 利用一点机器学习来加速你的网站

为什么你总是这么被动?

$
0
0

VCG41AB28206_副本.jpg

文:达芙妮 
责任编辑:萤火虫 我去姐姐
题图来源:视觉中国

01

你是被动的人吗?

生活中我们会经常遇见一种人,从不主动,特别被动,比如在男女感情里,有些从不主动追求女孩,有些人必须要等别人主动太能谈恋爱,俩被动的人明明彼此有意,但因为俩性格都被动,结果谁都无法先跨出那一步,最后杀进了个主动的人,恋情就此告吹。

还有的人在工作和生活中从来不能主动,在大会上从来不发言,在群体聚会中沉默寡言,几乎从来不表达意见,点菜时要不是有人使劲邀请他,绝不会主动点。被动最让人印象深刻的大概是好像特别没主见,自己没主意,怎么都行,永远处于配合外界的状态。

对于一个支配欲望强的人来说,有个被动的搭档真是一拍即可,但对于一般人来说,一个人太被动,会有点让对方觉得无趣和心累,比如恋爱中,一个人总是被动,估计恋爱也谈不下去,毕竟恋爱也是一件你来我往的事。

02

是追求成功还是避免失败?

这决定了你是被动还是主动

同样都是人,为什么人的性格有的主动,有的被动?那种过于被动的人又经历了什么导致今天这样?

很早的时候,心理学界有研究动机的心理学家就提出了人类的个人动机有两种: 一种是追求成功,一种是避免失败。根据动机的不同,人类因此被分为两种:追求成功的人和避免失败的人。

①追求成功型

两类人在行为模式上具有截然不同的反映。对于追求成功类型的人来说,行动的关注点在于如何取到我要的成功,我要实现的目标,为了达到这个目标,他们会全力以赴,竭尽自己的潜能,即便失败也在所不辞。

在这种人的价值体系里,成功和追求带来的满足感要超过失败带来的恐惧感,所以他们往往做事比较主动。

②避免失败型

而对于避免失败类型的人来说,他们行动的关注点在于如何避免失败,在他们的价值体系里,失败是件不能承受的事,做事的唯一动机就是尽可能避免失败,让自己处在最大的安全区域内,所以他们的表现相对来说就更容易被动。

是的,被动者就是那种具有这种价值体系的人,在被动者的心理,失败、出丑是难以忍受的,人生最该避免的就是失败和出丑,他们把避免失败等同于自己的成功,把避免出丑等同了成功。

所以他们不敢主动,每次主动都是降低他们成功概率的风险点,在严重被动的人的心里,这种恐惧会让他们生不如死,陷入失控的境地。

那么这种避免失败和出丑的价值评价体系又是怎么来的呢?为什么同样是人,会有两种截然不同的动机体系?

03

“我没有资格拥有这些。”

被动型人格的一个根源

在成年人的世界里,一个人跟外界的关系模式基本都是他幼年跟父母关系模式的翻版和重现。

避免失败和出丑的心理追求来源于对一个标准的忠诚,这个标准就是完美主义,正是因为之前在很小的时候经历过苛刻的评判和不接纳,让一个人把自己曾经表现出的缺点和不足当成了不可接受的东西。

幼年的这种评判和不接纳相当根深蒂固,它会跟羞耻和恐惧结合在一起,深埋在当事人的潜意识深处。

让他们在遇到类似的情境或事件时,容易触发这种不良体验,为了避免出现这种体验,他们只能控制不再让自己陷入早年的那种生活情境里,即尽量不自发做出行为,以避免达不到标准,引来不好的评价,让自己更加不堪。

毫无疑问,在被动者的内心里,他们的自我评价是极低的,这种低的自我评价正是他们容易没有主见,不敢冒险,走向被动的根源,而这种自我评价不过是他们在幼年时父母对他们评价的内化。

也就是说,被动者的父母在被动者年幼时,有意无意的表达了太多的不满意,指出了太多的缺点,他们潜意识了表达了世界应该有个标准,人们都应该向那个标准看齐,因为他们过多关注自己孩子的不足,希望他能改进,走向完美。

甚至有一些父母,不允许孩子出现错误的行为,一旦孩子犯了错误,父母就大吼大叫,对孩子严厉指责,甚至表现出一旦有错误,天就塌了的感觉。

幼小的孩子如果被长期灌输这种意识,就会逐渐丧失掉探索环境的天性和勇气,而变得被恐惧包围,从而感觉自己十分的不好,类似于一个不合格的儿童,他们对自我充满怀疑,长大后,对做其他的事情也无法拥有勇气,而是满怀怀疑。

其实深层次的核心概念是:

“我到底是不是个合格的人?我有资格拥有这些吗?”

“我可能没有资格拥有这些,因为我如此不好,有这么多缺点。”

一个塑造被动者孩子的家庭,父母与孩子的关系模式一定是以父母为中心的,而不是以孩子为中心。

父母从不鼓励孩子的探索行为,他们只注重孩子是否犯错,注重孩子是否满足他们的标准,这样的父母,往往自认为自己有一套教育孩子的完美标准或理想标准,只要把孩子按照这套标准教育,孩子就是OK的。

塑造被动者孩子的家庭,孩子的需求是被无视的,久而久之,孩子的主动发出自己需求的能力就渐渐丧失,转而聚焦到如何配合和满足家长的标准和要求,而当这些要求达不到时,父母又启用了苛刻的评价模式,这让他们进一步扔掉了自己可以提需求,要求别人的能力,而把全部的关注点都放在了满足外界,及不让外界标准失望的关注上。

也就是说,害怕失败的恐惧统治了他们的内心,追求成功,自我探索的那一部分自我功能完全萎缩掉。因为他们从来没有体验过自我探索,追求成功冒险带来的成就感。

04

“我没有能力为自己负责。”

被动型人格的第二个根源

还有一种被动人格的形成是源于父母的溺爱,但其实溺爱也是对孩子不放心低评价的一种,正是因为害怕孩子犯错吃亏,所以才有溺爱的模式,溺爱就如同给孩子套上一层人为的保护壳,本质上也会让孩子天性的探索本能得到遏制和压抑。

因为在孩子的世界里,事情是没有好坏之分的,孩子也没有多少危险的意识,危险正是过度溺爱孩子的家长传达和灌输给孩子的。

如果孩子从小就被整天灌输这种外界风险很大,我很弱小的信息,他就无法认为自己有能力去控制外界,只会养成依赖性人格。

而依赖性人格的本质还是对自己评价过低,认为自己没有能力应对外界,自然也就不可能发出主动地行为,因为他们认为自己没有能力为自己负责,而只能让别人为自己负责。

还有的人是在别的领域不那么被动,但只在某一个领域被动,比如一些程序员在业务上可能非常活跃,而在感情就非常被动,那是因为在这些人的心里,实在没有多少感情成功的经验。

他们的父母在他们小时候也没有开发他们讨人喜欢的技能和潜力,长大后的他们也没有自己挖掘,导致他们认为自己是缺乏魅力的自我认知。一旦有这种自我认知,就很难主动了。

05

“一直在等待一双伸过来的手”

被动型人格的现状

一个人如果在生活中总是很被动,会压抑自己的生命能量,会人为的自我设限,完全不能实现自己的潜能,甚至严重被动者能量被压抑后,会出现被动攻击型行为。

也就说如果一个人被动久了,他也会有很强的攻击欲,这种攻击欲没有学会正面表达,他会用被动攻击的方式表现出来,以发泄内心的不满。

被动的人就像缩在一个有着坚硬外壳的保护罩里,在这个罩子里,一切都是可控的,但是范围很小,他们在里面按部就班的行动,不能越雷池一步。

如果有的人足够幸运,可能会遇到从罩子外伸出的手,并且这手足够坚定有力,引导他们走出罩子,并带领他们起舞。

而更多的人不过是遇到过一双或几双手,但终因为自己恐惧的力量太大,超过了那双手可以拉出罩子的力气,而最终又缩回了罩子。

还有的人是终生在等待,希望有人能认出被动的他们,把他们拯救出去,但是终究到现在,他们还没有发现一双伸过来的手。

06

如何改变自己的被动型人格?

①告诉自己,这些都是不真实的

改变被动的方式首先是要改变自己对自己的认知,你的那些对自己过低的评价都不是客观真实的,而是来源于幼时不当的父母评价模式,所以当内心再涌出自己对自己不好的评价时,要及时的跳出来觉察这种评价的来源,并及时的纠正它。

告诉自己,提醒自己,这是不对的。

每个生命都是独一无二的,因此你值得拥有好的评价,值得要像这个世界索取你要的东西,值得让世界爱你,你也要自己爱自己。

②关注自己的需求,不要凡事配合别人

其次要多关注自己的需求,顺着自己的需求来,尝试一点点的改变,被动者往往泯灭掉自己的需求,而过度关注别人的需求,形成了凡事配合别人需求的模式。

可是凭什么呢?你也有自己的需求啊,怎么能处处以别人为中心,配合别人的目标,成就别人的主角。当你旁观这样的自己的时候,如果觉得委屈不值得。那就要去尝试改变。

这个改变就是一点一点的感受你自己的需求,不要泯灭它,勇敢的把它提出来,你应该为自己的需求而活。

最后,其实想说的是,生命这么短暂,一个人怎么能忍心让自己如此蜷缩的活着?那也太对不起自己了,怎么样也应该豁出去活一场啊。不然呢,畏畏缩缩,你究竟在害怕什么呢?


族群歧视与用户画像

$
0
0

  • 族群标签

题图是这两天的新闻人物美籍越南人 Dr. Dao。美国朋友觉得奇怪,为什么要说他是越南人?我们只认得他是 Asian。另一位 Asian,估计是位澳大利亚籍香港人,发了一条推特说——Dr. Dao 当时反抗的暴力其实是合法的强制执法。第三位 Asian,相信是位中国籍大陆知友,读了这条推特很愤慨,挥键写就高赞爆款推送《比打人更可怕的是国人的落井下石》。

每当读到这些族群标签信息,我们的大脑就会搞一串可能正确更可能错误的神经网络模式匹配;我们的电脑读到这些标签,也会搞一串可能错误更可能正确的统计回归预测。这两种运算的结果就是 歧视(=Discrimination),中英文语义都是贬义。如果不说语义只看字面,中、英文字面都很理中客。比如「犹太人心智超群」这样的反向歧视,一样符合 Discrimination/歧视=「区别对待」的字面意思。

  • 用户画像

让部分国人感到反转的是 Dr. Dao 律师 Demetrio 的声明:不相信这件事的背后有族群歧视动机。如果人脑根据族群标签比如Asian,对客户作出区别对待,这就叫族群歧视动机。如果人脑根据其它公开信息比如头像、姓氏,得到族群标签预测——比如「国人」,然后再作出区别对待,这也叫族群歧视动机。Demetrio 的意思是:不相信美联航经手这件事的职员作了基于族群标签自变量或中介变量的这两种区别对待。

麻烦的是,人工智能不经过人脑,仍然可能作出类似的区别对待。如果美联航的系统用了「用户画像 (Personas)」,就可以对不含族群标签的一摞变量应用机器学习,识别用户的类型加以区别对待。这种歧视甚至可以说是族群歧视的升级版——如果你虽然是 Asian 但不象 Asian 那样,它并不把你与其他 Asian 归成一类;如果你不是 Asian 但象多数 Asian 那样,它仍会把你与多数 Asian 归成一类;如果有两类 Asian 彼此非常不同,它还会小心地分开成两个归类;最妙的是,它并不把归出的类型打上任何族群标签。

与参数节俭的统计回归预测不同,机器学习的区别对待不是在算法层面写入歧视的族群标签,机器学习的区别对待是从数据自身面貌错落识别出歧视。反讽的是,人脑不经深思熟虑的直觉更象机器学习、更不象统计学习——人脑歧视更多时候也不是被教唆的算法,人脑歧视更多时候只是给数据面貌标了政治不正确的族群标签。现在好了,人工智能不仅可以帮人脑背锅完成分类区别对待,甚至可以比人脑更富于理性,甚之又甚者可以比人脑更富于(免于政治不正确的)德性。

  • 心理剖面

用户画像技术近期最重大的进展,是对心理剖面 (Psychological Profiling) 的整合。英国脱欧与川普胜选背后隐藏着同一支代表了先进生产力的团队—— Cambridge Analytica。在这支团队的工作之前,业界的用户画像通常只用到人口学变量、用户行为变量、设备变量。例如 Bilibili 的用户画像,主要通过年龄段、性别、客户端、关注与上传的视频类别,将用户划分为不同的世代。Cambridge Analytica 革命性地引入测评技术已经很成熟的大五模型(Five-Factor Model),具体而言,是五个缩写为 OCEAN 的人格变量——

此前的用户画像只强调大数据,心理剖面反其道而行。从用户行为的一摞自变量到 OCEAN 得分的预测,这一步用的是监督学习范式。建立这个预测模型的样本量虽然不太小(五位数),每个个案的施测成本可也不少。这就不再是大数据而是深数据,得到的预测模型最后再整合到大数据(八位数以上)的多种应用场景。于是,共和党的助选团队从人口学的「区别对待」升级为心理学的「操控干预」,最终在关键选区获取关键优势,打破绝大多数主流调查机构预期,全球政局为之扭转。

如果熟悉同卵孪生相关系数的研究,就比较容易理解心理剖面怎样在技术上革命性地升级了用户画像。基因变量数量极其庞大,如果直接用来预测个体的干预操控效果,只适用大数据机器学习范式。目前基因测序行业还停留在这个层面,为客户作出的各种预测相当不靠谱,典型的预测比如「你得青光眼的概率比普通人高五倍」。但如果研究者掌握了其中一份表现型(同卵孪生兄妹)的关键变量,再从这组关键变量去预测客户的情况,预测的准确程度将大大超出公众媒体的认知。心理剖面先用几万被试烧进好多钱,得到行为变量→关键变量的映射,其精确程度接近于偷看了你克隆兄妹的OCEAN得分。

  • 行为科学的政治正确

用户画像区别对待,怎么听都觉得政治不太正确。心理剖面操控干预更等而下之,可谓看人下菜、对症下迷药。芝加哥机场安保如果用心理剖面,可以对 OCEAN 的 N 高分乘客准备电棍以避免流血——N 高分更倾向抵抗执法暴力;美联航如果采用心理剖面,可以精准筛选 OCEAN 的 A 高分乘客请君出瓮——A 高分更容易配合、更倾向事后不起诉。

用户体验研究下的整个行为设计领域,更是在明火执仗地这么干。行为科学从根子上就是个非常政治不正确的学科。行为科学史许多研究者与极权政体有特殊关系。巴甫洛夫虽然是沙俄旧政权的知识分子,晚年却在苏联获得无上尊崇;民国最有影响力的行为主义学者、复旦心理系之父郭任远,直接参与了黄金十年南京政府的法西斯意识形态工作;斯金纳在美国正相反,被广泛视作政治极其不正确的异类。从行为设计产业回顾,可以发现行为主义学者的政治不正确正是对移动互联世代同一命题的预见前瞻。川普助选团队运用心理剖面破坏民主制度,其背景同样可以在行为科学史往回追溯。

斯金纳有句赤裸裸的名言:「人类的真问题不是如何 破操控获得自由,而是如何改良升级所 的操控」。改良升级操控也许不是人类的真问题,但显然是行为设计的真问题。吊诡的是,行为设计特别强调用户体验的「操控感」,交互界面力求流畅,触控反馈绝少迟滞。那么,行为设计带给用户是操控还是受操控?卡尼曼的峰终律(Peak-End Rule)研究给出一个意外的深刻回答——

用经济学的术语,被试个体在每个时点当下的效用函数与长时段之后的效用函数完全不同,二者仅有近 0.5 的正相关。这个相关系数有多低,可以对比一下由不同家庭收养的同卵双胞胎,他们成年后的心理变量往往有超过 0.7 的正相关。把经济学黑话翻译成心理学黑话——短时记忆的你与长时记忆的你是两个不同的人格,差别要大过不同后天成长环境的遗传克隆同胞。行为设计帮助短时记忆直觉行为的你最大程度地增强操控,让长时记忆日常语言的您更「好」地受操控。

小结一下:用户的行为类型族群分野被无人干预的用户画像更好地识别,人工智能「升级改良」了族群歧视;用户的短时当下人格被无人干预的心理剖面更好地预测,行为设计升级改良了当下人格的操控感体验、同时「升级改良」了长时人格受操控的程度。

相关 Live 广告

  • Live 《习得自助》后半场与文本相关,详见《习得自助》Live 后记
  • 即将开讲的 Live《遗传进化》前半场内容是同卵孪生相关系数研究,为先天基因与后天家庭一解纠结
  • 最早的 Live 《幸福三味》想讲的东西太多,Peak-End Rule 相关内容其实基本没讲,与文本相关内容只讲到「体验内容派生意识自我」
  • 文中还提及 《哔哩哔哩用户画像分析》Live,免费软广没收 B站的钱

基于rsync的文件增量同步方案

$
0
0

背景

犀牛云盘是美团点评内部一个基于美团云的文件协作平台,核心是文件的结构化云存储以及上传和下载的体验优化。文件同步是云盘功能的重要部分(包括文件内容的同步和文件增删的同步,应该有上传、下载、创建、删除等动作,但在本文的叙述中,主要关注文件内容的传输,即上传、下载),如何快速高效地进行文件同步,就成了云盘亟需解决的技术难题。本文阐述的方案就是在这种场景下提出来的,我们希望通过rsync增量传输算法,来提高文件同步速度。但原始rsync算法在高并发的服务上会存在性能问题,所以本方案也借鉴zsync的思路,做了优化。

rsync增量传输算法

rsync增量传输算法首度发表于1996年6月19日,原始作者为Andrew Tridgell与Paul Mackerras [1]。实现增量传输的主要过程,就是差异检测和差异数据组织及传输,前者是rsync增量传输算法的核心。

rsync增量传输算法是一种滑动块差异检测算法。以检测文件A和B的差异为例,首先对A按固定长度L划分为若干块,并对每一块生成弱摘要(Adler-32:速度快)和强摘要(MD5:鉴别度高),然后对B从第一个字节开始,以长度为L的滑动窗口,遍历整个文件,计算每个窗口块的弱、强摘要,并与A中的摘要值进行比较,弱、强摘要都相同者,即视为相同数据块,否即为差异块。如下图所示:

rsync差异检测示意图

rsync增量传输算法主要有两个特点:

  • 固定块摘要和滑动块检测的结合,提高命中率;
  • 弱摘要和强摘要的结合,加快比对速度。

酷壳网有篇文章对rsync增量传输算法有比较详细的介绍[2]。

rsync性能优秀且简单容易理解,作者用了两年研究出来,而使用者只用两小时就可以理解了。

rsync工具的工作机制

rsync增量传输算法使用最多的场景就是类UNIX系统上的rsync同步工具。该工具非常流行,被应用于大量的文件传输场景。比如现在美团点评发布系统就用rsync同步发布机器上编译后文件到生产机器上的。

rsync工具的工作机制,如下阐述。

目标:主机A、B,A要同步文件F-new给B,B上已有文件F-old,跟F-new相似度高。

步骤:

  • B对文件F-old分块计算强弱摘要,链接起来生成sign文件,此过程简称sign,把sign文件发送给A;
  • A根据sign文件和本地文件F-new比较,滑动块进行差异检测,把相同块的序号和不同块的内容拼装为delta文件,此过程也简称delta,发送给B;
  • B拿到delta文件,并与本地源文件F-old结合,生成F-new文件,此过程简称patch。

如果目标是B要同步文件给A,那就是步骤中把A、B换一下位置。

小结:同步的双方A、B基本是对等的,一方计算sign和合并文件,一方计算delta。 双方都有较大计算量,这在一个服务器多客户端场景下,服务端压力会过大。

zsync工具的工作机制

zsync是Ubuntu上使用比较多的工具,主要用于分发Ubuntu的安装镜像ISO文件。zsync是rsync的一种变体,对rsync增量传输算法有所改造,并且基于HTTP协议,适合广域网应用[3]。

zsync适用的场景是:大文件、变动少、一个分发点(服务端)、大量下载(客户端)。

使用步骤为:

  • 发布方制作好新版系统安装ISO镜像(大文件),同时生成对应的sign文件,两者都提供HTTP下载地址;
  • 客户端如果没有旧版本镜像,那么全量下载ISO文件;
  • 客户端如果有旧版本镜像,那么下载sign文件,自己计算delta文件,同时自己合并出新版本镜像。合并过程是,发现相同块就从本地旧版本读取,不同的块,则使用HTTP Range的方式从服务端下载。

zsync算法,使发布方(服务端)只要一次签名文件的计算即可支撑大量客户端增量下载,缓解服务端压力。需要增加的签名文件存储空间,也是成本很低的。

云盘的文件增量同步方案

基于上面介绍的rsync工具的传输步骤,并借鉴zsync增量下载的思路,制定云盘文件增量同步方案,如下图所示:

增量同步-pc客户端

增量同步-web浏览器
主要的方案设计要点是:

  • 计算sign和计算delta都在PC客户端进行,服务器端只做必不可少的合并处理,同时客户端根据结算结果,如果发现命中率低,也可以选择全量传输;
  • 增量下载时,借鉴zsync,也把计算量放在PC客户端进行,这个实现也需要参考zsync对rsync原算法进行一定改造;
  • 浏览器处理能力有限,无法实现增量同步;
  • 服务端需要存(一定量的)sign文件、delta文件;
  • 服务端还要合并出新文件并存储,主要是基于这些考虑:
    ① 防止delta管理的复杂;
    ② 有完整文件,下载简单,浏览器下载可以直接通过mss(美团云对象存储服务,犀牛云盘的文件数据的存储工具) tempurl下载;
    ③ 增量同步出问题还可以降级服务,保证基本功能正常。

方案还存在的问题

  1. 碎片块,这是rsync增量传输算法特点造成的,由于是滑动窗口检测,在两个相同块之间,有可能存在一个长度不定的差异块。如果相同块不连续,就会形成一系列碎片块。减少滑动块长度(也即是sign计算的固定块长度),可以提高命中率、减少碎片块,但计算量也随着加大,sign文件也变大,可能得不偿失。所以只能根据试验情况,取一个折衷的块长度。目前我们采取默认2KB的块长度,对每块生成4B弱摘要,16B强摘要,那么sign文件与源文件的长度比=20/2K=1/100(这里忽略sign文件头固定8B的小量影响)。

  2. 对JPEG、视频等类型的文件,局部改变可能性小,且文件一般比较大,差异检测计算量大但命中率低,不进行增量同步尝试。

  3. 基于以上设计方案,服务器端要做合并patch操作,但合并操作的时间和资源消耗还是挺大的,需要做:

  • 接收并缓存delta文件;
  • 从底层存储(mss)下载旧文件;
  • 合并文件;
  • 向底层存储上传新文件。

该问题可从以下两方面做优化尝试:

  • 改进点1:合并文件流式处理,但网络的流对流处理容易不稳定。而旧文件还得全部下载,因为有随机读;
  • 改进点2:把合并过程作为异步处理,接收delta文件后,就返回给客户端“成功”,服务端慢慢合并,但如果失败了,很难有手段再重新从客户端取到正确文件,需要借助消息推送辅助。

算法的后续优化项

第一,rsync工具及类库中为了做到极致的最小传输量,sign文件头没有保存源文件长度,delta文件块长度用不同数量的Byte来表示。建议修改。前者保存文件长度,方便做类似zsync的改造(zsync算法起作用需要整个文件长度)和下载时的长度校验;后者用固定数量Byte表示长度(每个长度值都要使用多个Byte),虽然多消耗一些传输量,但编码简单,处理效率高。

第二,对某些文件格式已知的文件,可以根据格式特点,做变长分块。比如MS Office的Open XML格式,其实是Zip组织方式,可以按Zip协议的分界标识来分块,提高命中率,但这需要对rsync增量传输算法进行修改。

第三,结合CDC(content-defined chunking)做变长分块检测,这方面属于研究的方向,但目前还没有比较通用可靠的解决方案。下面根据找到的资料做一下描述:

CDC算法是一种变长分块算法,它应用数据指纹(如Rabin指纹[5])将文件分割成长度大小不等的分块策略。与定长分块算法不同,它是基于文件内容进行数据块切分的,因此数据块大小是可变化的。算法执行过程中,CDC使用一个固定大小(如48字节)的滑动窗口对文件数据计算数据指纹。如果指纹满足某个条件,如当它的值模特定的整数等于预先设定的数时,则把窗口位置作为块的边界。

CDC算法可能会出现病态现象,即指纹条件不能满足、块边界不能确定,导致数据块过大。实现中可以对数据块的大小进行限定,通过设定上下限来解决这种问题。CDC算法对文件内容变化不敏感,插入或删除数据只会影响到较少的数据块,其余数据块不受影响。CDC算法也有缺陷,数据块大小的确定比较困难,粒度太细则开销太大,粒度过粗则检测效果不佳。如何两者之间权衡折衷,这是一个难点。

相比CDC,rsync是滑动块算法。滑动块算法对插入和删除问题处理非常高效,并且能够检测到比CDC更多的冗余数据,它的不足是容易产生数据碎片。如果能结合两者的优点,那就能更高效做差异检测,比如上面提到的Open XML格式按Zip标记分界的思路,其实是CDC思路的一个特例。

参考文档

  1. Tridgell A, Mackerras, P. The rsync algorithm. The Australian National University, 1996.
  2. rsync核心算法.
  3. zsync — Optimised rsync over HTTP.
  4. Rabin指纹.
  5. 刘爱贵. 数据同步算法研究. CSDN博客.

合并两条有序链表

$
0
0

有序链表的合并是面试的时候常考的一道链表算法题:
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则(即升序)。
对于这道题目有两种解法:一是非递归形式,二是递归形式。
1.非递归算法,我们解题思路如下:
(1)先考虑这几种特殊情况,
如果两条链表相等,我们将返回任意一条即可;
如果一条链表为空,另一条不为空,那么我们返回不为空的那条链表即可;
(2)这些情况处理完之后,我们再来进行下一步骤,
先确定两条不为空的链表排序后的头部,也就是比较第一个节点值的大小;
排序新链表头部确定好后,我们再来比较后面的值进行连接,这里维护一个尾指针比较好操作;
最后这个问题容易被忽略,比如说,链表1为 1->3->5;链表2为 2->4->6
在前面的步骤中已经将链表1处理完,但链表2里面还有6未处理,所以对于这种情况,我们就应该检测,如果一条链表已经为空(即处理完),另一条链表不为空时,我们就直接将不为空的链表链在排序新链表的尾部。
代码如下:

struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};
 ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1==pHead2)
            return pHead1;
        if(pHead1==NULL)
            return pHead2;
        if(pHead2==NULL)
            return pHead1;

        ListNode* NewHead=NULL;
        ListNode* tail=NULL;
        ListNode* cur1=pHead1;
        ListNode* cur2=pHead2;
        if(cur1->val < cur2->val)
            {
            NewHead=cur1;
            tail=cur1;
            cur1=cur1->next;
        }
        else
        {
            NewHead=cur2;
            tail=cur2;
            cur2=cur2->next;
        }
        while(cur1 && cur2)
            {
            if(cur1->val < cur2->val)
                {
                tail->next=cur1;
                tail=tail->next;
                cur1=cur1->next;
            }
            else
                {
                tail->next=cur2;
                tail=tail->next;
                cur2=cur2->next;
            }
        }

        if(cur1==NULL)
            {
            tail->next=cur2;
            tail=tail->next;
        }
        else
            {
            tail->next=cur1;
            tail=tail->next;
        }
        return NewHead;
    }

2.递归方式
递归的问题就是考虑两件事情:一是递归结束条件,二是子问题的解决。
这里直接给出代码:

 ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1==pHead2)
            return pHead1;
        if(pHead1==NULL)
            return pHead2;
        if(pHead2==NULL)
            return pHead1;

        if(pHead1->val < pHead2->val)
            {
            pHead1->next=Merge(pHead1->next,pHead2);
            return pHead1;
        }
        else         // pHead1->val >= pHead2->val
            {
            pHead2->next=Merge(pHead1,pHead2->next);    
            return pHead2;
        }
作者:qq_29503203 发表于2017/4/27 17:03:47 原文链接
阅读:124 评论:0 查看评论

今日头条屡传负面消息 曾经的神话将破灭?

$
0
0

  在五六年前,当时恰逢中国互联网开始步入高速发展的阶段,每天都有大量的信息在互联网上进行传播,而在当时微博、微信等等社交应用还没有像现在这么活跃和全面,所以对于新闻资讯的主要来源还是基于一些传统的新闻媒体的网站或者一些门户网站的推送消息。新闻聚合类的应用推出之后,迅速获得了用户们的青睐甚至改变了不少用户的阅读习惯,而在众多的新闻聚合类的应用当中属《今日头条》发展作为迅速,不过最近《今日头条》的日子过的可不太顺。


  央视记者经过3个多月的跟踪调查发现,从2016年下半年开始,今日头条开始不定期地推送一些直播秀链接,点开链接之后用户会进入一个直播模块中,在该平台上有大量的女主播穿着性感暴露进行大尺度表演,在完成调查之后央视用了大篇幅对这一情况进行了报道。而这只是一系列负面新闻的开始。腾讯、搜狐向北京市海淀区人民法院提交诉讼,将《今日头条》以涉嫌侵犯作品版权和约稿版权为由告上法庭。一时间这个聚合新闻应用又再一次被推上了风口浪尖。

  其实这并不是《今日头条》第一次因为版权问题而被起诉,自从2013年底开始,新京报网、搜狐网、享有《广州日报》网络传播权的广州市交互式信息网络有限公司、《长沙晚报》旗下星辰在线、《楚天都市报》等各类媒体机构都曾经与今日头条发生过版权方面的纠纷,凤凰新闻还曾以恶意劫持凤凰新闻客户端流量为由向《今日头条》索赔2000万,这些都是在当年引起了非常大轰动的事情。

  不过虽然发展的过程中出现了许多的争议,但是这并没有阻碍《今日头条》疯狂发展的势头,在不少新闻聚合类应用渐渐的从网友们的视线中淡去的时候,《今日头条》发展却异常迅猛。在去年的世界互联网大会上,《今日头条》创始人、CEO张一鸣透露截止至10月底,《今日头条》已经累积有6亿激活用户,而活跃用户更是达到了惊人的1.4亿,而到了11月底,已有超过39万个个人、组织开设头条号,拥有了如此大的用户数对于如今的互联网从业者来说那可是一笔巨大的财富。


  新闻聚合类的应用收入主要分为两个类型,一种是向用户收取一定的费用,类似于会员费一类的,而另外一种则是通过销售广告获得收入,《今日头条》在这里就属于后者,在去年《今日头条》曾经放话要靠广告挣60亿,据透露这个目标去年已经实现,而且实际数额可能会达到100亿元,若是按照这个数字计算,那么其在单一电视频道中也能紧跟中央一台与湖南卫视排名第三。

  既然有了如此多的收入,自然不会放慢发展的步伐,在去年9月份,《今日头条》宣布将投资10亿补贴短视频创作,正式加入短视频市场,而在今年2月份,《今日头条》全资收购了美国短视频应用Flipagram,可见其已经不再满足于新闻聚合了,而就在这个突飞猛进的重要关头却接连受到打击,不免让人为《今日头条》的前景表示堪忧。

  《今日头条》因为能够根据用户自己的喜好个性化的选择推送的消息新闻而获得用户们的喜爱,但是其消息来源往往是其他传统新闻媒体或者门户网站,在以前版权意识还尚未提升的时候或许能够在灰色地带生存一段时间,但是如今版权问题越来越多的受到各界的重视,这些争议不免要被放到桌面上说。此外,由于其广告是重要的收入来源,不免让人猜测《今日头条》是否为了一味的追求利益而忽视对于广告内容的监管,甚至故意放出一些“擦边球”广告博取用户的眼球。不管怎么说《今日头条》现在已经被推到了风口浪尖上,接下来或许将面临更大的挑战。

阅读全文


通过 PWA 我们让新用户转化率提升了 104%

$
0
0
文章关键词:PWA、Google、Android、Mobile Site;
阅读本文大约需要 10 分钟。

过去的2016年里,你一定不下数次听到 PWA 这个词,但它究竟是什么呢?

PWA 其实是一种渐进式的的无需用户安装、可被随时唤起的 Web App , 融合了 Native App 的结构、交互、降级方案等的更优体验技术,使得用户通过浏览器打开网站时,获得有如原生 App 般的顺滑体验。

Google 的官方文档中是这样介绍的:

PWA(Progressive Web App), Progressive Web Applications take advantage of new technologies to bring the best of mobile sites and native applications to users.

利用最新的技术带给用户最好的无线体验。按照官方文档,PWA具有这些特性:Reliable, Fast, Engaging。

可靠的(Reliable):

瞬间加载,即使在不稳定的网络下也不会显示 downasaur(小恐龙页面),通过预加载缓存关键资源,消除对于网络的依赖,确认用户在无网络或者网络情况较差情况下的即时可靠体验


快速的(Fast)


快速响应并带给用户平滑的动画体验,没有卡顿

参与感(Engaging) 原生 App 一般的体验,具有沉浸式的用户体验,可以将 Progressive Web App 安装在用户的主频幕上,甚至没有浏览器的头部,给用户提供一种如原生 App 的全屏体验。


但说了这么多,关于 Progressive Web App 我们应该关注些什么呢?

  • 独立的 Logo
  • 快速加载 App Shell 模型
  • 添加到主屏幕,从主屏幕启动
  • App-like 启动画面
  • Web App Manifest
  • Service Worker 离线缓存
  • 推送通知 Push Notification

简单概括描述一下,PWA 是一种给用户提供无需下载、快速启动、顺滑体验的一项移动端技术。

独特的识别性,快速与 Native App 进行区分

PWA 可以拥有一个桌面图标。Flipkart不仅重新设计了 icon,还将 PWA 改名为 Flipkart Lite。


使用App Shell 快速加载页面

App Shell 预加载页面的框架结构和基础 UI 元素,渐进式缓存,动态加载内容。

很多网站在加载页面时都会遇 loading 等待的问题,这段时间里其实服务器在紧张的传输数据给客户端,再由客户端将结构渲染出来、内容填充进去。可惜用户并没有耐心等待,大家极有可能在这短短几秒内就关闭页面走掉了。

通过 App Shell 快速加载,可以减少用户界面中的白屏感受,让用户知道数据正在填充回页面。


无需下载,随时唤起

PWA 让 Web 应用能够像原生应用一样添加到主屏,无需通过应用商店下载,减少了用户安装原生 App 的成本。

用户可以通过 Chrome 等浏览器的侧边栏添加(下图左)或者通过 Chrome 弹出的 Add to Home Screen Banner 选择同意添加(右图)。可将 PWA 自带的 icon 生成一个桌面 App 图标,方便用户随时唤起。

友好的启动页

在无线页面中,所有的元素都是实时渲染的。所以 PWA 增加了类似 Splash screen的等待画面,显示品牌logo和品牌名,以便在这个等待时间内,预加载第一个页面所需的资源,缓和用户的等待时间。


沉浸式的的全屏体验 Web App Manifest

用户点击被保存在桌面的 PWA icon ,进入全屏的 PWA 界面。没有了浏览器头部,Progressive Web App 和 Navtive App 几乎没有界面上的差别。用户在浏览和使用页面的过程中,完全感知不到卡顿,所有的动效和反馈都是接近原生体验的,这对于像欧美这些应用下载意愿较低的用户而言无疑是更进一步提升了在手机端的使用体验。


无网络也能访问

当你处于无网络时,可以继续使用PWA,会自动加载你已经缓存的内容得以在离线时继续访问。这对于像巴西这类流量资费偏贵的用户来说可以节省很多不必要的浪费。

不同网站对于 Service Worker 的使用也不同:

  • Flipkart 在无网络状态下,置灰展示所有页面信息,所有已经加载的内容可以继续浏览,但是给到用户的感觉过于消极;
  • Digikala 在无网络状态下,仅使用灰色提示用户已经离线状态;
  • Alibaba 在无网络状态下,通过品牌定义的报错颜色提示用户出于离线状态,但是由于整体页面都比较明亮,用户感知不到处于离线状态;
  • Currency Converter 在无网络状态下,缓存的汇率比例可以继续离线使用;可以看到不同产品对于自己产品诉求的不同,使用离线提示的方案也会各有不同。


不安装也能推送通知 Push Notification

Push 是如今的产品必不可少的一个营销工具,我们需要通过它传递信息、唤醒用户。但通常的 Mobile Site 是不具备 Push 能力的。但 PWA 却可以,它能像 App 一样接收网站讯息,通知用户网站产生了新的内容或者和用户相关的通知,促使用户的回访。


与很多喜欢尝鲜的团队一样,2016 年初我们开始与 Google 团队合作,推动 PWA 技术在 AliExpress 上的落地。希望通过“新设计带来新体验;新技术推动新变革” 来看看 PWA 对于电商平台的意义。

结果是非常令人惊奇和满意的。AliExpress 发现新用户的转换率增加了 104%。在 Safari 的转化率也上升了82%。现在用户每次访问的页面数量是原先的两倍,也大大提升了用户浏览页面的时间。


AliExpress 的 Mobile Site 存在已经有些年头了,逻辑也不必 App 简单,想要重新开工并不是件容易的事情。而对于设计团队,我们的精力主要放在了以下几个方面:

无线“格式化”

抛弃了原有 PC 年代的 Header 和 Footer 的形式,以更符合 Web App 的特性。

优化交互结构

使用了类 App 的头部导航结构,整合页面中的功能,更合理的利用首屏的高度,更充分的展示有效内容。

界面升级

不仅在 UI 界面层遵循了 Material Design ,也在功能动效的还原上接近原生体验。

结果喜人,但过程同样也是坎坷不易的。有些 tips 给到大家。

01. 为你的 PWA 设计一个有识别性的 icon ,区别于 Native App icon(如果你有的话)。

由于市面上有较多投放尺寸,所以桌面启动icon需要适配各种场景,包含的内容:

  • Android Chrome 桌面图标及开机画面
  • iOS Safari 桌面图标及全屏开机画面
  • 浏览器 favicon Windows Phone 图标
  • 其他配置

推荐使用 Favicon Generator for all platforms: iOS, Android, PC/Mac... 来生成各种尺寸的图标及配置。

02.梳理原有交互框架结构,使得页面层级更轻量,流程更简洁。确认 App Shell 首先预加载的框架结构和 UI 元素,为每个页面设计渲染加载图片。

扩展阅读

感谢大家的阅读,希望本文对你理解 PWA 有所帮助。

--------- 招聘分割线 ---------

国际 UED 是阿里唯一一支国际化设计团队, 为集团的国际及国内业务提供设计支持。我们在杭州、深圳、北京、香港、美国、俄罗斯都有 Office,如果你对我们的工作感兴趣欢迎加入我们。

目前团队开放职位:

  1. 资深前端开发工程师
  2. 资深交互设计师
  3. 用户研究专家
  4. 资深视觉设计师
  5. Content Manager

简历投递邮箱: key.chent@alibaba-inc.com

如果你是无线开发工程师,我们这里也同样非常欢迎。来邮件吧!



来源:知乎 www.zhihu.com
作者: 阿里国际UED

【知乎日报】千万用户的选择,做朋友圈里的新鲜事分享大牛。 点击下载

Gen8折腾记:ESXi

$
0
0

什么是ESXi

ESXi是Vmware推出的一款免费虚拟化系统。可以为服务器创建多个虚拟系统。与 VirtualBox、QEMU、Hyper-V 等方案不同,ESXi 是 Type-1 Hypervisor,它直接运行于裸机上,不需要先安装一个操作系统,因此性能开销小,本身占地也很小,很适合安装到 TF 卡或 U 盘等小型存储器中。正巧,Gen8 的机箱内主板上有一个 USB 和一个 TF 卡接口(据说TF卡安装ESXi有时会读取不到),可以把 ESXi 安装进去。由于 ESXi 启动完成之后就在内存中运行了,不用担心 U 盘和 TF 卡的读写性能问题。

ESXi的安装

安装镜像下载:

免费版本的ESXi目前已经改名为vSphere Hypervisor。这里我选择了定制版的vSphere 6.5 Nov 2016。

注意:定制版本自带了Gen8的驱动程序,但是部分网友报定制版本存在与RDM兼容文理,会导致虚拟机卡死。

安装过程非常的方便,在主板上插上1个不用的USB硬盘,然后直接使用iLO进行安装。

  • 重启ILO
  • 在系统自检后的POST阶段,按住F11,进入Boot设置阶段
  • 选择从CD ROM启动
  • 在虚拟驱动器中挂载下载的镜像文件

安装完成后会在界面中出现IP地址。

在浏览器中输入IP地址,打开页面后输入安装时设置的帐号和密码即可登录。

ESXi虽然免费,但是还是需要输入许可证号方可免费使用,许可证的申请只需在ESXi官网上注册一个帐号就会分配。

虚拟机的创建与管理

在创建虚拟机前,需要先存储。流程:存储->创建数据存储->创建新的VMFS数据存储,将SSD盘全部创建,用于当作系统盘,后期专门负责安装系统。

创建虚拟机和管理虚拟机的流程和Vmware Workstation的流程完全一致,另外也可以通过本地电脑中的Vmware Workstation创建和管理局域网中服务器上的虚拟机。

创建磁盘映射(RDM)

Gen8的最核心用途是作为NAS,考虑到后期搭建的NAS系统跑在虚拟机里,如果将数据也存储在虚拟机里会对后期的数据迁移记恢复造成较大的麻烦,合理的方式是NAS系统安装在虚拟机里,数据存自己存储在物理磁盘中,而不是再隔一层文件系统。RDM (VMware 开发的开放虚拟磁盘格式,原理是创建一个的特殊 .vmdk 文件(一个文本文件)映射到一块物理磁盘,当虚拟机向这块 VMDK 写入时,实际写入的是后面的物理磁盘。

那就用 RDM 吧。具体流程:

  • 在ESXi 的设置中打开 SSH 访问

具体操作为,在管理 -> 服务 中开启TSM(ESXi Shell)和TSM-SSH(SSH)这两个服务。

  • 通过Putty连接ESXi

一开始使用了xshell。发现连接不进去,于是改为了Putty。

3、寻找需要直连的磁盘路径

在WEB管理后台的磁盘管理中,找到需要想要直连的磁盘,可在页面下方获取到磁盘所在路径

我这里找到的是

  • /vmfs/devices/disks/naa.600508b1001ce379ba83424700f52501
  • /vmfs/devices/disks/naa.600508b1001c6fccee4ef9201d10fd2a

4、寻找已经创建好的SSD虚拟磁盘位置

同样在WEB管理后台就可以查看到,这里的路径是:

  • /vmfs/volumes/5638afb3-daa60169-4788-941882388144
  • 创建RDM文件

创建RDM需要使用ssh中才能进行,具体的指令为:

vmkfstools -z /vmfs/devices/disks/naa.600508b1001ce379ba83424700f52501 /vmfs/volumes/5638afb3-daa60169-4788-941882388144/work.vmdk
vmkfstools -z /vmfs/devices/disks/naa.600508b1001c6fccee4ef9201d10fd2a /vmfs/volumes/5638afb3-daa60169-4788-941882388144/life.vmdk

具体含义:

  • 第一个参数:创建模式,可选参数-z或-r。两种模式的差别很小,正常使用2种模式都可以。
    • -r 参数创建的是 Virtual Compabilitiy Mode RDM,即 ESXi 会截获除 READ / WRITE 之外所有 SATA 指令
    • -z 参数则是创建 Physical Compability Mode RDM,即 ESXi 除了 LUN REPORT 指令,其他全部原样传递给物理磁盘。
  • 第二个参数:物理磁盘路径
  • 第三个参数:创建的.vmdk 文件的保存路径,文件名可自定义。

执行完上述命令后就会生成RDM文件了,想知道文件中到底有那些信息,可以使用:

[root@localhost:~] cat /vmfs/volumes/5638afb3-daa60169-4788-941882388144/work.vmdk
# Disk DescriptorFile
version=1
encoding="UTF-8"
CID=fffffffe
parentCID=ffffffff
isNativeSnapshot="no"
createType="vmfsPassthroughRawDeviceMap"

# Extent description
RW 7813971632 VMFSRDM "work-rdmp.vmdk"

# The Disk Data Base
#DDB

ddb.adapterType = "lsilogic"
ddb.geometry.cylinders = "486397"
ddb.geometry.heads = "255"
ddb.geometry.sectors = "63"
ddb.longContentID = "3a4251a9a640535171363678fffffffe"
ddb.uuid = "60 00 C2 9a 3f 7c ba f2-fd 62 45 f5 cd 51 0e 26"
ddb.virtualHWVersion = "13"
[root@localhost:~]

将这样创建得到的特殊 .vmdk 文件分配给虚拟机,便可在虚拟机中访问到外层 ESXi 的硬盘。如果你已经装了Windows的虚拟机,你会发现新增了磁盘以后,硬盘并没有在系统中出现,原因是新增的磁盘还没有被格式化。

下载磁盘工具格式化以后即可使用。

EXSi的磁盘性能问题

SSD测试结果:

HDD测试结果:

从上图可知,随机读写的速度非常的差。经查询是Gen8 使用的Raid卡驱动导致的。解决办法是要门装低版本的ESXi5.5(VMware ESXi™ 5.5 Update 2 | 09 SEP 2014 | Build 2068190),要么把B120i的驱动刷到较低版本。这里使用的是后一种。

  • 下载驱动: scsi-hpvsa-5.5.0-88OEM.550.0.0.1331820.x86_64.vib
  • 在Web管理后台上传下载下来的vib文件到ESXi磁盘上
  • 使用ssh登录ESXi,并转到文件所在目录:cd /vmfs/volumes/5638afb3-daa60169-4788-941882388144
  • 将vib文件复制到系统目录/var/log/vmware:cp scsi-hpvsa-5.5.0-88OEM.550.0.0.1331820.x86_64.vib /var/log/vmware/
  • 开启维护模式:esxcli system maintenanceMode set –enable true
  • 卸载原来的驱动:esxcli software vib remove -n scsi-hpvsa -f
  • 安装上传的驱动:esxcli software vib install -v file:scsi-hpvsa-5.5.0-88OEM.550.0.0.1331820.x86_64.vib –force –no-sig-check –maintenance-mode

完成后重启ESXi,退出维护模式,开启虚拟机测试一下性能。如需恢复对应的版本为:scsi-hpvsa–5.5.0.102-1OEM.550.0.0.1331820

按照上述流程完成后,重启设备,再次打开后台发现硬盘没了(T_T)。

在此基础上有

  • 查看一下加载的驱动和驱动版本:esxcli software vib list | grep ahci

若加载了vmw-ahci,形如:

sata-ahci                      3.0-22vmw.650.0.0.4564106             VMW              VMwareCertified   2015-11-02
vmw-ahci                       1.0.0-32vmw.650.0.0.4564106           VMW              VMwareCertified   2015-11-02

另外的解决方案是:强制禁用通用驱动:

esxcli system module set --enabled=false --module="vmw_ahci"

再次重启,丢失的硬盘终于恢复了,开启虚拟机发现无法启动,原因是还在维护模式。关闭维护模式:esxcli system maintenanceMode set –enable false

参考链接: http://www.nxhut.com/2016/11/fix-slow-disk-performance-vmwahci.html

进入Windows 测试磁盘性能:

SSD的测试情况:

HDD的测试情况:

可以看到:SSD的性能有较大的提升,仅在4K小文件时的性能较差。HHD的性能在顺序读写的时候还OK,但是在小文件读取的时候还是非常的慢。

MySQL explain执行计划详细解释

$
0
0

一. explain 语法

explain < table_name >

例如: explain select * from t3 where id=3952602;

二.explain输出解释

+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys     | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+

id

我的理解是SQL执行的顺利的标识,SQL从大到小的执行. 例如:

mysql> explain select * from (select * from ( select * from t3 where id=3952602) a) b;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| id | select_type | table      | type   | possible_keys     | key     | key_len | ref  | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
|  1 | PRIMARY     |  | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  2 | DERIVED     |  | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  3 | DERIVED     | t3         | const  | PRIMARY,idx_t3_id | PRIMARY | 4       |      |    1 |       |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+

很显然这条SQL是从里向外的执行,就是从id=3 向上执行.

select_type

就是select类型,可以有以下几种

(1) SIMPLE

简单SELECT(不使用UNION或子查询等) 例如:

mysql> explain select * from t3 where id=3952602;
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys     | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | t3    | const | PRIMARY,idx_t3_id | PRIMARY | 4       | const |    1 |       |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+

(2). PRIMARY

我的理解是最外层的select.例如:

mysql> explain select * from (select * from t3 where id=3952602) a ;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| id | select_type | table      | type   | possible_keys     | key     | key_len | ref  | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
|  1 | PRIMARY     |  | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  2 | DERIVED     | t3         | const  | PRIMARY,idx_t3_id | PRIMARY | 4       |      |    1 |       |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+

(3).UNION

UNION中的第二个或后面的SELECT语句.例如

mysql> explain select * from t3 where id=3952602 union all select * from t3 ;
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type  | table      | type  | possible_keys     | key     | key_len | ref   | rows | Extra |
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+
|  1 | PRIMARY      | t3         | const | PRIMARY,idx_t3_id | PRIMARY | 4       | const |    1 |       |
|  2 | UNION        | t3         | ALL   | NULL              | NULL    | NULL    | NULL  | 1000 |       |
|NULL | UNION RESULT | <union1,2> | ALL   | NULL              | NULL    | NULL    | NULL  | NULL |       |
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+

(4).DEPENDENT UNION

UNION中的第二个或后面的SELECT语句,取决于外面的查询

mysql> explain select * from t3 where id in (select id from t3 where id=3952602 union all select id from t3)  ;
+----+--------------------+------------+--------+-------------------+---------+---------+-------+------+--------------------------+
| id | select_type        | table      | type   | possible_keys     | key     | key_len | ref   | rows | Extra                    |
+----+--------------------+------------+--------+-------------------+---------+---------+-------+------+--------------------------+
|  1 | PRIMARY            | t3         | ALL    | NULL              | NULL    | NULL    | NULL  | 1000 | Using where              |
|  2 | DEPENDENT SUBQUERY | t3         | const  | PRIMARY,idx_t3_id | PRIMARY | 4       | const |    1 | Using index              |
|  3 | DEPENDENT UNION    | t3         | eq_ref | PRIMARY,idx_t3_id | PRIMARY | 4       | func  |    1 | Using where; Using index |
|NULL | UNION RESULT       | <union2,3> | ALL    | NULL              | NULL    | NULL    | NULL  | NULL |                          |
+----+--------------------+------------+--------+-------------------+---------+---------+-------+------+--------------------------+

(4).UNION RESULT

UNION的结果。

mysql> explain select * from t3 where id=3952602 union all select * from t3 ;
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type  | table      | type  | possible_keys     | key     | key_len | ref   | rows | Extra |
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+
|  1 | PRIMARY      | t3         | const | PRIMARY,idx_t3_id | PRIMARY | 4       | const |    1 |       |
|  2 | UNION        | t3         | ALL   | NULL              | NULL    | NULL    | NULL  | 1000 |       |
|NULL | UNION RESULT | <union1,2> | ALL   | NULL              | NULL    | NULL    | NULL  | NULL |       |
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+

(5).SUBQUERY

子查询中的第一个SELECT.

mysql> explain select * from t3 where id = (select id from t3 where id=3952602 )  ;
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------------+
| id | select_type | table | type  | possible_keys     | key     | key_len | ref   | rows | Extra       |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------------+
|  1 | PRIMARY     | t3    | const | PRIMARY,idx_t3_id | PRIMARY | 4       | const |    1 |             |
|  2 | SUBQUERY    | t3    | const | PRIMARY,idx_t3_id | PRIMARY | 4       |       |    1 | Using index |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------------+

(6). DEPENDENT SUBQUERY

子查询中的第一个SELECT,取决于外面的查询

mysql> explain select id from t3 where id in (select id from t3 where id=3952602 )  ;
+----+--------------------+-------+-------+-------------------+---------+---------+-------+------+--------------------------+
| id | select_type        | table | type  | possible_keys     | key     | key_len | ref   | rows | Extra                    |
+----+--------------------+-------+-------+-------------------+---------+---------+-------+------+--------------------------+
|  1 | PRIMARY            | t3    | index | NULL              | PRIMARY | 4       | NULL  | 1000 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | t3    | const | PRIMARY,idx_t3_id | PRIMARY | 4       | const |    1 | Using index              |
+----+--------------------+-------+-------+-------------------+---------+---------+-------+------+--------------------------+

(7).DERIVED

派生表的SELECT(FROM子句的子查询)

mysql> explain select * from (select * from t3 where id=3952602) a ;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| id | select_type | table      | type   | possible_keys     | key     | key_len | ref  | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
|  1 | PRIMARY     |  | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  2 | DERIVED     | t3         | const  | PRIMARY,idx_t3_id | PRIMARY | 4       |      |    1 |       |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+

3.table

显示这一行的数据是关于哪张表的. 有时不是真实的表名字,看到的是derivedx(x是个数字,我的理解是第几步执行的结果)

mysql> explain select * from (select * from ( select * from t3 where id=3952602) a) b;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| id | select_type | table      | type   | possible_keys     | key     | key_len | ref  | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
|  1 | PRIMARY     |  | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  2 | DERIVED     |  | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  3 | DERIVED     | t3         | const  | PRIMARY,idx_t3_id | PRIMARY | 4       |      |    1 |       |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+

4.type

这列很重要,显示了连接使用了哪种类别,有无使用索引. 从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL

(1).system

这是const联接类型的一个特例。表仅有一行满足条件.如下(t3表上的id是 primary key)

mysql> explain select * from (select * from t3 where id=3952602) a ;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| id | select_type | table      | type   | possible_keys     | key     | key_len | ref  | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
|  1 | PRIMARY     |  | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  2 | DERIVED     | t3         | const  | PRIMARY,idx_t3_id | PRIMARY | 4       |      |    1 |       |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+

(2).const

表最多有一个匹配行,它将在查询开始时被读取。因为仅有一行,在这行的列值可被优化器剩余部分认为是常数。const表很快,因为它们只读取一次!

const用于用常数值比较PRIMARY KEY或UNIQUE索引的所有部分时。在下面的查询中,tbl_name可以用于const表:

SELECT * from tbl_name WHERE primary_key=1;
SELECT * from tbl_name WHERE primary_key_part1=1和 primary_key_part2=2;

例如:

mysql> explain select * from t3 where id=3952602;
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys     | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | t3    | const | PRIMARY,idx_t3_id | PRIMARY | 4       | const |    1 |       |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+

(3). eq_ref

对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型。它用在一个索引的所有部分被联接使用并且索引是UNIQUE或PRIMARY KEY。

eq_ref可以用于使用= 操作符比较的带索引的列。比较值可以为常量或一个使用在该表前面所读取的表的列的表达式。

在下面的例子中,MySQL可以使用eq_ref联接来处理ref_tables:

SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column=other_table.column;

SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column_part1=other_table.column
    AND ref_table.key_column_part2=1;

例如

mysql> create unique index  idx_t3_id on t3(id) ;
Query OK, 1000 rows affected (0.03 sec)
Records: 1000  Duplicates: 0  Warnings: 0

mysql> explain select * from t3,t4 where t3.id=t4.accountid;
+----+-------------+-------+--------+-------------------+-----------+---------+----------------------+------+-------+
| id | select_type | table | type   | possible_keys     | key       | key_len | ref                  | rows | Extra |
+----+-------------+-------+--------+-------------------+-----------+---------+----------------------+------+-------+
|  1 | SIMPLE      | t4    | ALL    | NULL              | NULL      | NULL    | NULL                 | 1000 |       |
|  1 | SIMPLE      | t3    | eq_ref | PRIMARY,idx_t3_id | idx_t3_id | 4       | dbatest.t4.accountid |    1 |       |
+----+-------------+-------+--------+-------------------+-----------+---------+----------------------+------+-------+

(4).ref

对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。如果联接只使用键的最左边的前缀,或如果键不是UNIQUE或PRIMARY KEY(换句话说,如果联接不能基于关键字选择单个行的话),则使用ref。如果使用的键仅仅匹配少量行,该联接类型是不错的。

ref可以用于使用=或<=>操作符的带索引的列。

在下面的例子中,MySQL可以使用ref联接来处理ref_tables:

SELECT * FROM ref_table WHERE key_column=expr;

SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column=other_table.column;

SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column_part1=other_table.column
    AND ref_table.key_column_part2=1;   

例如:

mysql> drop index idx_t3_id on t3;
Query OK, 1000 rows affected (0.03 sec)
Records: 1000  Duplicates: 0  Warnings: 0

mysql> create index idx_t3_id on t3(id) ;
Query OK, 1000 rows affected (0.04 sec)
Records: 1000  Duplicates: 0  Warnings: 0

mysql> explain select * from t3,t4 where t3.id=t4.accountid;
+----+-------------+-------+------+-------------------+-----------+---------+----------------------+------+-------+
| id | select_type | table | type | possible_keys     | key       | key_len | ref                  | rows | Extra |
+----+-------------+-------+------+-------------------+-----------+---------+----------------------+------+-------+
|  1 | SIMPLE      | t4    | ALL  | NULL              | NULL      | NULL    | NULL                 | 1000 |       |
|  1 | SIMPLE      | t3    | ref  | PRIMARY,idx_t3_id | idx_t3_id | 4       | dbatest.t4.accountid |    1 |       |
+----+-------------+-------+------+-------------------+-----------+---------+----------------------+------+-------+
2 rows in set (0.00 sec)

(5). ref_or_null

该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行。在解决子查询中经常使用该联接类型的优化。

在下面的例子中,MySQL可以使用ref_or_null联接来处理ref_tables:

SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL;

(6). index_merge

该联接类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素。 例如:

mysql> explain select * from t4 where id=3952602 or accountid=31754306 ;
+----+-------------+-------+-------------+----------------------------+----------------------------+---------+------+------+------------------------------------------------------+
| id | select_type | table | type        | possible_keys              | key                        | key_len | ref  | rows | Extra                                                |
+----+-------------+-------+-------------+----------------------------+----------------------------+---------+------+------+------------------------------------------------------+
|  1 | SIMPLE      | t4    | index_merge | idx_t4_id,idx_t4_accountid | idx_t4_id,idx_t4_accountid | 4,4     | NULL |    2 | Using union(idx_t4_id,idx_t4_accountid); Using where |
+----+-------------+-------+-------------+----------------------------+----------------------------+---------+------+------+------------------------------------------------------+
1 row in set (0.00 sec)

(7). unique_subquery

该类型替换了下面形式的IN子查询的ref:

value IN (SELECT primary_key FROM single_table WHERE some_expr)

unique_subquery是一个索引查找函数,可以完全替换子查询,效率更高。

(8).index_subquery

该联接类型类似于unique_subquery。可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引:

value IN (SELECT key_column FROM single_table WHERE some_expr)

(9).range

只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。key_len包含所使用索引的最长关键元素。在该类型中ref列为NULL。 当使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操作符,用常量比较关键字列时,可以使用range

mysql> explain select * from t3 where id=3952602 or id=3952603 ;
+----+-------------+-------+-------+-------------------+-----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys     | key       | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+-------------------+-----------+---------+------+------+-------------+
|  1 | SIMPLE      | t3    | range | PRIMARY,idx_t3_id | idx_t3_id | 4       | NULL |    2 | Using where |
+----+-------------+-------+-------+-------------------+-----------+---------+------+------+-------------+
1 row in set (0.02 sec)

(10).index

该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小。

当查询只使用作为单索引一部分的列时,MySQL可以使用该联接类型。

(11). ALL

对于每个来自于先前的表的行组合,进行完整的表扫描。如果表是第一个没标记const的表,这通常不好,并且通常在它情况下很差。通常可以增加更多的索引而不要使用ALL,使得行能基于前面的表中的常数值或列值被检索出。

5.possible_keys

possible_keys列指出MySQL能使用哪个索引在该表中找到行。注意,该列完全独立于EXPLAIN输出所示的表的次序。这意味着在possible_keys中的某些键实际上不能按生成的表次序使用。

如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用EXPLAIN检查查询

6. key

key列显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

7.key_len

key_len列显示MySQL决定使用的键长度。如果键是NULL,则长度为NULL。 使用的索引的长度。在不损失精确性的情况下,长度越短越好

8. ref

ref列显示使用哪个列或常数与key一起从表中选择行。

9. rows

rows列显示MySQL认为它执行查询时必须检查的行数。

10. Extra

该列包含MySQL解决查询的详细信息,下面详细.

(1).Distinct

一旦MYSQL找到了与行相联合匹配的行,就不再搜索了

(2).Not exists

MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行, 就不再搜索了

(3).Range checked for each Record(index map:#)

没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一

(4).Using filesort

看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行

(5).Using index

列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候

(6).Using temporary

看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上

(7).Using where

使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题

简要解释版本

EXPLAIN列的解释:

描述
table显示这一行的数据是关于哪张表的。
type这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为 const、eq_reg、ref、range、index和ALL。
possible_keys显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句。
key实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MySQL会选择优化不足的索引。这种情况下,可以在SELECT语句中使用USE INDEX(indexname) 来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MySQL忽略索引。
key_len使用的索引的长度。在不损失精确性的情况下,长度越短越好。
ref显示索引的哪一列被使用了,如果可能的话,是一个常数。
rowsMySQL认为必须检查的用来返回请求数据的行数。
Extra关于MySQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MySQL根本不能使用索引,结果是检索会很慢。

extra列返回的描述的意义:

意义
Distinct一旦MySQL找到了与行相联合匹配的行,就不再搜索了。
Not existsMySQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了。
Range checked for each Record(index map:#)没有找到理想的索引,因此对于从前面表中来的每一个行组合,MySQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一。
Using filesort看到这个的时候,查询就需要优化了。MySQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行。
Using index列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候。
Using temporary看到这个的时候,查询需要优化了。这里,MySQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上。
Where used使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题不同连接类型的解释(按照效率高低的顺序排序)。
system表只有一行 system 表。这是const连接类型的特殊情况 。
const表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为MySQL先读这个值然后把它当做常数来对待。
eq_ref在连接中,MySQL在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用。
ref这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好。
range这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况。
index这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更好,因为索引一般小于表数据)。
ALL这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免。

热点题材公告股战法

$
0
0

股市里的成功者大多有自己固定的几个招式,只要玩好了,都能有稳定的盈利模式,但有些招式有一定的门槛,有些招式却只需要勤奋与积累。公告,是上市公司的公开信息,是市场上的明牌,但由于每天的公告多而繁杂,少有人愿意长久的研究这块机会。

做任何事,都要讲究方法论,应该用最短的时间做到80%,去超过大多数人,再用大量的时间去攻克剩下的20%,成为顶尖玩家。

我用这篇短文,带大家做到前面的80%,剩下的20%,就靠自身的努力与智慧了。

要入门做到80%其实非常简单,我把方法化繁为简,主要是以下4个步骤:

1. 收藏这两个网址

a) 中证网:http://ggjd.cnstock.com/gglist/search/ggkx

b) 巨潮资讯网:http://www.cninfo.com.cn/cninfo-new/index


2. 学会懒人思维,公告三千,只取三瓢

每天的公告都有成百上千,该如何减少工作量,但又不落下重要的公告呢?如何能够用最短的时间筛选并分析呢?

a) 明日复牌股的公告必看

(在巨潮资讯网里看,把每个复牌股的原委弄清楚,再看下停牌日期,估算补跌或者补涨的幅度)

b) 中证网的重点公告必看

(步骤1里我给的网址)

c) 自选股的公告必看

(自己持有的股票或者关注的股票,每天都要去看一下公告,看看会不会对基本面有较大影响)


3. 分清楚利好公告与利空公告的类型。

需要注意的是,利好不一定买进能赚钱,利空不一定会让股票跌,灵活分析才是关键,灵活分析的额前提是长期的归纳总结与试错。

利好主要有:

a) 资本举牌

b) 大股东增持

c) 更换实控人

d) 并购重组

e) 定增

f) 大订单

g) 高送转

h) 业绩大增

利空公告里我主要关注的只有两类:

a) 重组终止

b) 收到行政处罚书


4. 新手该如何分析一个公告

格式如下,如果一开始每个都这么分析,无需多久,就能做到80%。

案例:

a) 公告简介

(写这个公告的内容和相关数据)

b) 公司基本面

(搞清楚这个公司的基本面如何,本身的业绩如何,本身的题材是什么,公告后的业绩如何,公告后的题材是什么)

c) 解禁与减持

(解禁和减持对股票短期的影响非常明显,看复牌公告的时候,一定要排查这两项)

d) 停牌影响

(停牌多久?期间指数情况?是否需要补涨?)

e) 总结

做出综合分析,给出预判,做好一个交易预案,然后就是总结和归纳了

Tangram Android 的设计说明

$
0
0

前言

前段时间开源了团队内的 vlayout项目,从 Github上反馈来看,还是深受欢迎。 但如果仅仅是采用 vlayout 搭建页面,使用起来还不是特别灵活,在此基础之上,我们封装了一套动态化调整界面的模块,命名为 Tangram,现在同样已开源—— Tangram AndroidTangram iOS。我们希望将它打造成某一特定领域的解决方案,提速业务开发。更多关于 Tangram 的介绍可以阅读文章 《页面动态化的基础 —— Tangram》《Tangram是我们对界面动态化的态度》去了解,本文假设你对 Tangram 已经有了大致的理解,然后针对 Android 端的设计做一说明。

基本模型

在前面的文章 《页面动态化的基础 —— Tangram》里,已经重点介绍了 Tangram 里涉及的几个主要概念和模型设计,这里简单重复一下,将页面拆分成三个层次:页面——卡片——组件。页面指的就是整体可滑动页面实体;卡片指的是页面内可按行划分的一个一个独立区块,组件指的是卡片内部一个独立的、业务级别的单元,它可以是一张图,也可以是文字 + 图的组合。因此整体整个页面可以这样描述:一个页面嵌套了多个卡片,一个卡片嵌套了多个组件。Tangram 体系就是规范了这三层的数据结构描述,将其渲染出一张页面。

架构

整体的层次结构如下,我们自底向上进行说明:

  1. 整个 Tangram 框架的页面UI搭建基于 vlayout 和 UltraViewPager。vlayout 之前已经提到过了,用来构建多类型、多布局类型的 RecyclerView; UltraViewPager是 ViewPager 的一种扩展,整合了多种特性,比如横向滑动,竖向滑动,循环滚动等,用来实现 Tangram 内部所需要的轮播滚动卡片;这两个都比较独立,因此独立成库先行开源了,即使不使用 Tangram,也可以单独使用它们。
  2. vlayout 主要提供了一个自定义的 LayoutManager,因此 Tangram 还需要提供一个 RecyclerView 和 Adapter 来才能配合 vlayout 运行。这里的 RecyclerView 可以由外部业务方通过 TangramEngine 注入,也可以内部默认构造。GroupBasicAdapter 则封装了 vlayout 所需要的 Adapter 的逻辑,组件的创建、组件数据的绑定、组件类型的定义等都由它负责。
  3. 在 UI 基础之上的便是各种功能逻辑模块:
    • TangramEngine 是核心类,它负责绑定 RecyclerView 到底层 vlayout,绑定页面数据,操作页面数据(包括增、删、改),还提供注册外部服务的接口。
    • ServiceManager 是服务管理模块,不轮是内部还是外部功能模块,都可以注册到这里,一方面能被 Tangram 内部的其他模块访问到使用,另一方面解耦了框架与业务模块。
    • Bus 是事件总线,它在内部也被注册到 ServieManager,内部模块和业务使用方都可以使用它进行通信,解耦业务代码。
    • DataParser 负责解析数据,它将原始数据解析成卡片、组件的 model 对象。框架里提供的是解析 JSON 数据的解析器,也支持扩展解析其他类型的数据。
    • DataResolver 负责识别卡片、组件并构建对象,解析器解析数据的时候,需要依赖这些 Resolver去识别数据中的卡片或者组件是否合法,Resolver 识别的方式就是去组件库或者卡片库里寻找这些组件是否已经注册过。
  4. 与业务相关性较大的就是组件库、卡片库以及相关业务接口。TangramBuilder 是业务方构建 TangramEngine 的入口。组件库里注册了业务方所需有的组件,Tangram 的实例是一个页面一份,因此每个业务方可以分别注册各自所需要的组件,当业务方使用 Tangram 进行业务开发的时候,主要工作可能就在组件的开发上。卡片库注册的是卡片类型,框架里已经内置了一系列卡片,如果业务方有需要可以单独再注册特殊类型的卡片。而 ClickSupport、ExposureSupport 等都是辅助业务开发的功能模块,前者定义了组件点击处理的接口,后者定义了组件曝光处理的接口。它们都被注册到 ServiceManager 里,业务方在组件或者页面内都可以使用它们。

初始化流程

初始化 Tangram 的流程其实比较简单,无非就是构造 TangramEngine 对象,注册业务组件或者卡片,注册服务模块,绑定 RecyclerView,我们在接入文档—— 接入Tangram代码里详细说明了步骤。这里不一一赘述,在初始化过程中,各个核心模块也都完成了初始化。

运行流程

基本流程图

页面数据示例

[
  {"id": "banner1","type": 1,"style": {"aspectRatio": 3.223
    },"items": [
      {"bizId":"item1","type": 110,"msg": "info1"
      },
      {"bizId":"item2","type": 110,"msg": "info2"
      }
    ]
  },
  {"type": 1,"style": {"aspectRatio": 3.223
    },"items": [
      {"type": 10,"imgUrl": "https://gw.alicdn.com/tfs/TB1pdJFQpXXXXbUXpXXXXXXXXXX-750-243.png"
      },
      {"type": 10,"imgUrl": "https://gw.alicdn.com/tfs/TB1pdJFQpXXXXbUXpXXXXXXXXXX-750-243.png"
      }
    ]
  }
]
  1. 整个 Tangram 对界面的动态调整是通过数据来驱动的,所以首先要将原始数据传递给 TangramEngine,由于集团体系内的接口都采用 JSON 数据,Tangram 框架的默认设计也是接收 JSON 格式的数据,不过也支持通过自定义 DataParser 提前将其他格式的数据解析好之后再传给 TangramEngine。以上述的示例的 JSON 数据为例,一个页面下挂载了一个卡片数组,每个卡片都定义了 id、type、style、items节点;items 内部的数组定义的是组件数据,组件也有type、bizId 等业务字段数据。
  2. 不论是传递原始 JSON 数据给 TangramEngine还是通过直接解析原始数据,都是通过 DataParser 来完成的,它会按照树型结构解析出对应的卡片和组件的 model 对象,解析过程依赖于相应的卡片 Resolver 和组件 Resolver 来识别卡片、组件是否已注册,关键点就是识别 type 字段。若碰到无法识别的 type,则不会解析出对应的 model 对象。
  3. 解析完成之后会得到一个卡片列表,每个列表的卡片 model 元素里持有它所包含的组件列表。
  4. model 列表交给 GroupBasicAdapter 进行处理,首先提取卡片列表,将包含空组件列表的卡片过滤掉,因为它没有东西可以渲染展示,然后创建出 vlayout 所需要的 LayoutHelper 列表,设置它们的样式属性,这样就打通了通过 JSON 数据最终控制布局排版的流程。
  5. 同时将所有的组件 model 提取出来成为一个独立的列表,真正交给 GroupBasicAdapter 去渲染数据,组件 model 列表的大小就是 GroupBasicAdapter 的 item 的大小, RecyclerView 也就直接加载组件视图,卡片相对于只负责了布局逻辑的控制,并没有 UI 实体的承载。
  6. 数据都准备完毕之后,RecyclerView 就驱动 vlayout 里的 LayoutManager 进行渲染和布局。
  7. LayoutManager 首先回调 RecyclerView 内部获取 ViewHolder,若复用池里存在复用的对象,就回调 GroupBasicAdapter 进行数据绑定,否则先回调 GroupBasicAdapter 进行组件 ViewHolder 的创建,然后进行数据绑定。ViewHolder 的创建也是通过 Resolver 内部创建 UI 的模块进行构造。
  8. 这就是 Tangram 渲染页面的整体流程,本身并没有特别复杂的逻辑。而整个框架里的其他模块比如事件总线、ServiceManager 等都是在组件自身绑定数据,处理业务逻辑过程中会使用到,对于这些功能模块的使用,我们也编写了相应的教程文档供参考。

相关文章

本文简要地对 Tangram Android 内部的设计和工作流程进行了说明,有助于对阅读源码的理解。但是建议读者在对 Tangram 有一定了解的基础之上再阅读此文,否则可能有云里雾里的感觉,因为这里涉及到大量的知识点没法完全展开,可以参考以下文章进行了解。

web图像的常见应用策略与技巧

$
0
0

本文介绍一些关于响应式图像的适配应用策略,回退原理,SVG的换色技巧,雪碧图的百分比定位计算公式等相关的一些小知识点,目的在于帮助一部分同学快速的理清图像应用思路,以及一些web图像的应用技巧。

1.响应式图像的应用与回退

特点:应用简单,上手容易,性能表现良好
难点:lazyload实现

根据不同设备,不同分辨率,不同像素比使用的响应式图像,常用的有两种场景:

1.1固定尺寸图像

基于设备像素比选择,很多网站logo就是固定宽度图像的一个例子,不管viewport的宽度如何,始终保持相同的宽度。

在dom里图像与在css里的图像写法如下面的例子

<img srcset="test.jpg 1.5x, test2.jpg 2x" src="test.jpg" alt="" />
background-image: image-set(url(test.jpg) 1x,url(test2.jpg) 2x);

1.2不固定尺寸图像

与内容相关的图片,在需要响应式的时候,它们的大小往往并不是不变的,会随viewport改变,对于这类图像,也有两种常用的处理方式

1.2.1 我们使用srcset搭配w描述符以及sizes属性 。

w描述符告诉浏览器列表中的每个图象的质量。sizes属性是一个包含两个值的,由逗号分隔的列表。根据最新规范,如果srcset中任何图像使用了w描述符,那么必须要设置sizes属性。

sizes属性有两个值:第一个是媒体条件;第二个是源图尺寸值,在特定媒体条件下,此值决定了图片的宽度。

比如:

<img srcset="360.jpg 360w,
            768.jpg 768w,
            1200.jpg 1200w,
            1920.jpg 1920w"
     sizes="
     (max-width: 360px) 100vw,
     (max-width: 768px) 90vw,
     (max-width: 1980px) 80vw,
     768px"
     src="360.jpg" alt="">

我们来逐条读这一个img标签的信息

srcset,我们给浏览器准备了四个质量的图像,分别为360 768 1200 1920
sizes,我们来告诉浏览器,在不同的环境下图像的宽度
当视口不大于360的时候,图像显示宽度为100vw,当视口不大于768的时候,图像显示宽度为90vw,以此类推。
最后一个src作为默认图像url引入,并且是天然的回退方案,当浏览器不认识以上属性的时候,直接读取src渲染。

这样说不够直观,我们看个demo

在iphone4(320)下,图像宽度和我们设置的100vw一致,但是为什么浏览器选择了768的图像而没有选择360的?因为4的dpr是2呀^_^,浏览器很智能的选择了质量最合适的768.

再看一下6p(414),很听话的按照我们的设置,显示了90vw。因为他的dpr更高,浏览器聪明的选择了1200质量的图像。

这里我们可以欺骗一下浏览器:

360.jpg 1200w,
1200.jpg 9999w

我们把360的图像,骗浏览器说这是1200的,然后把原本1200的扔天上去

浏览器果然上当了,他把360的图当成1200的来用了。这里可能有些疑问,图像的宽度为什么不是90vw了哪?因为浏览器被骗了但是自己却不知道,他依然按照1200的图像,去适配dpr。414*90%*(360/1200)约等于111.7。这种方式很智能,浏览器去根据你的sizes,从w列表里选择最适合的图像来调用显示。正因为他太智能了,在实际操作中可控性较差,有些我们想精确控制的图像显示,有时候并不能如意。而且在做lazyload的时候要处理的东西也比较复杂。

这个时候可以考虑另外一种方式。

1.2.2.picture元素,可精确把控

picture元素就像是图像和其源的容器。浏览器仍然需要img元素,用来表明需要加载图片,如果没有img,那么什么都不会渲染。source为浏览器提供了要显示图像的供选版本。

适用场景为:在一个精确特定的转效点(breakpoint)需要显示一个特定的图像时。使用picture元素选择图像,不会有歧义,理解起来也更直观。

<picture><source media="(min-width: 960px)" srcset=960.jpg"><source media="(min-width: 768px)" srcset="768.jpg"><img src="360.jpg" alt=""></picture>

在本例中,当viewport大于960像素时,会加载图像960的图像。当viewport宽度大于768像素时,浏览器会加载768的。而当宽度小于768像素时,加载默认图像360。

而且这个写法的懒加载非常好处理,只需要在传统的lazyload策略上稍加改进

data-src
data-srcset
在加载到的时候更换为
src
srcset

就轻松解决了。

http://snghr.tencent.com  里面使用较多

他也不需要去特意做回退处理,当浏览器不支持的时候就直接读取img标签。对于懒加载的回退......我选择判断IE 7-8...直接塞url给他.....。

2.特殊格式的图像应用与回退

特点:体积优化效果显著
难点:兼容性掌控

上面picture元素还可以提供  基于图片格式选择。

有一些图像格式在较小的文件大小情况下保证了较好的图片质量。听起来还不错,但残酷的事实是没有一个新格式被所有浏览器支持。谷歌的WebP表现不错,但只有Chrome和Opera原声支持 JPEG-XR,最初被称为高清照片,是微软发布的一个专有图像格式,仅Internet Explorer支持

<picture><source type="image/vnd.ms-photo" src="test.jxr"><source type="image/jp2" src="test.jp2"><source type="image/webp" src="test.webp"><img src="test.png" alt="test"></picture>

source的type属性用来指定每个图像的MIME类型,浏览器会选择第一个含有其支持的MIME类型的源。源的顺序是至关重要的,如果浏览器无法识别所有的图象类型,它会回退至原来的img元素。

但是目前这些格式的支持多数不会直接这么做,因为代码会有些冗余难看,有判断浏览器ua输出不同dom或者样式的,也有服务端直接输出的。服务端直接输出,或者CDN做特殊处理,进行无感知格式切换,同时预留url和拒绝的接口,处理起来更灵活,省时省力,例如我们的:

 

服务端根据浏览器的请求头,返回不同的图像格式,对于X5内核还可以支持sharpP。

3.SVG应用

难点:变色方案,响应式定位计算

上面这个source的type属性 还支持另一种我们更常用的图像格式,SVG。

说起SVG,这是个出现频率比webp更高的图像格式了,他有着比iconfont更多的优点,所以现在web上正在大量的应用。

优点:

1.SVG提供的功能集涵盖了嵌套转换、裁剪路径、Alpha通道、滤镜效果等能力,它还具备了传统图片没有的矢量功能,可适配任何高清屏。

2.可读性好,有利于SEO与无障碍

与iconfont对比

1.渲染方式不同

关于渲染方式,之前欧文同学的文章已经讲述的很清楚,这里不多做叙述( https://isux.tencent.com/svg-icon-part-one.html),无论 黑白渲染,灰度渲染,次像素渲染,还是DirectWrite 或  GDI 渲染, 既然iconfont他是一个字体,就难逃出现锯齿的命运,特别是在一倍屏幕下的渲染。

2.icon font只能支持单色

icon font做为字体无法支持多色图形,这就对设计造成了许多限制,因此这也成为了icon font的一个瓶颈。

3.icon font可读性不好

icon font主要在页面用Unicode符号调用对应的图标,这种方式不管是浏览器,搜索引擎和对无障碍方面的能力都没有SVG好

在对比完之前,可能有同学就会问,SVG和iconfont对比有个致命的缺点,就是换色.

比如hover换色,iconfont只要写个颜色就好了,SVG是不是需要做两个颜色的图?这也是SVG图像应用我们解决的一个难点之一

SVG换色,最初我试过三个方案

一是mask-image属性,他的优点是简单粗暴,直接用css来mask这个svg图形来进行换色,缺点很明显就是兼容性了,除去兼容性,还是很好用的。

demo:

background: #ff6600;
-webkit-mask:center no-repeat;
-webkit-mask-image: url(qq-logo.svg);

查看demo

二是通过SVG滤镜来实现,优点是效果更好,缺点除了兼容性,还需要额外的脚本配合。关于滤镜换色的详细说明在我上一篇文章里有详细介绍以及demo

查看文章

三是我们最终选择的底层无感知换色的方案,把修改颜色的脚本集成到了我们的工作流里,我们在写css的时候,遇到svg需要换色的地方,只需要

background-image: url(test.svg?fill=#ffffff)

加一句换色参数,工作流在底层会自动生成你所需要的svg图片并合并到雪碧图里。

SVG应用的另一个难点,就是作为背景图响应式渲染,雪碧图的background-position和background-size 的计算,这个其实也是其他图像都会存在的一个难点。

我的导师 wenju 之前发过这个计算公式相关的文章:

百分比值()是背景图相对于背景定位区(background positioning area)的百分比,可以控制在容器元素内仅显示Sprites图的部分内容。比如下图中,Sprites图是由四张图像拼成的,要想在容器内仅显示第一张图像,background-size的值应该多少呢?

我们仅需要Sprites图的1/4显示在容器内,那么Sprites图与容器的比例应该是4:1,计算公式为: background-size : ( Sprites width / image width) (Sprites height / image height)

如何计算background-position

我们已知的信息如下:

容器元素的尺寸:elW * elH
单张图片的尺寸:imgW * imgH
Sprites图片的尺寸:spritesW * spritesH
单张图片在Sprites图上的位置:imgPosX, imgPosY

我们假设:

点的位置为 (x, y)
容器上的(x, y)点与容器左上角的距离为 cX, cY
Sprites图上的(x, y)点与本张图片左上角的距离为 sX, sY

如果要把某张图片完全显示在容器元素内,我们可以推导出:

elW = imgW, elH = imgH
cX = sX, cY = sY

根据上面的信息,我就可以计算出具体的(x, y)值了,下面以 x% 为例:

cX = elW * x
sX = spritesW * x - imgPosX
elW * x = spritesW * x - imgPosX

解方程后就得到计算公式了:

x = imgPosX / (spritesW - elW) = imgPosX / (spritesW - imgW)
y = imgPosY / (spritesH - elH) = imgPosY / (spritesH - imgH)

如果你每次都手动计算的话会被累死吧?所以这一步我们还是集成到了工作流里,在所有合并雪碧图的地方用这个公式自动计算出位置。

而关于SVG的回退方案,已经是老生常谈

比如

<svg width="200" height="200"><image xlink:href="svg.svg" src="svg.png" width="200" height="200" /></svg>

svg标签方式,缺点必须指定宽高,没有图片的保持款高比例特性,优点兼容性好,兼容所有主流浏览器

或者

<picture><source type="image/svg+xml" srcset="svg.svg"><img src="svg.png" alt=""></picture>

在支持的浏览器里使用SVG,在不支持的浏览器里显示PNG,优点是type灵活,可用于SVG,WEBP等,而且保持了img标签的特性,方便做布局操作。缺点兼容性要求高,ios9+,安卓5+,微软Edge+
当然这个兼容性说的是source type的兼容,并不是SVG本身的兼容。

对于css里的SVG 的应用与回退策略,比较简单,也已经成熟,一般情况下都是这种用法

 background-image: url(fallback.png);
 background-image: url(image.svg), none;

利用的技术是CSS3多背景,浏览器只要支持了多背景,几乎无一例外支持SVG

再或者

background-image: url(fallback.png);
background-image: image-set( "test.png" 1x, "test-2x.png" 2x,"test-print.png" 600dpi );

通过image-set来筛选和回退。

F-Droid 将迎来 UI 大更新

$
0
0
无广告无需注册的 Android 应用商店 F-Droid 只提供开源和自由软件 Android 应用,自 2010 年发布至今,它的 UI 几乎没有发生变化,仍然保持了极简的风格,只展示应用列表和简单的应用介绍、权限需求,源代码链接等等。但即将发布的 v0.103 将迎来 UI 大更新。新版将带来用户期待已久的特性,其中包括屏幕截图和功能图形演示,批量下载和安装,改进通知功能,转化应用元数据,等等。开发者正在进行测试以在正式发布前识别出可用性问题,他们邀请用户试用反馈信息。

高考志愿填报软件值得托付吗?看这篇数据测评就知道了。

$
0
0

高考,作为中国一年一度的「地震级」大事件,不仅与 960 万考生息息相关,更与 960 个家庭紧密相连。在巨大的利益驱动之下,「高考」在商人眼中和「诱人而庞大的市场」划上了等号。高三一年,让人眼花缭乱的教辅书籍、课外辅导机构攫取了大部分利益;到了 6 月中下旬,利用家长和考生们「病急乱投医」的心理,高考志愿填报领域又成为新的「掘金圣地」。

志愿填报咨询师可以说是最早出现的志愿填报服务,但由于高昂的收费、始终未被国家法律承认的行业资质,近年来引起了大量高考家庭的反感,甚至央视在 2016 年也出面声讨「天价志愿填报服务」。

随着科技的发展,市场上出现了大量集合全国大学录取数据,为家长和教师们提供志愿填报帮助的软件。家长们也意识到,使用工具更加方便快捷,能帮孩子发现更多选择的同时,也有自主性,不是完全把孩子的命运交到他人手上。

然而,有利也有弊。大量质量低劣、数据错漏百出的志愿填报系统由于「尝到甜头」纷纷涌入市场,这样的黑心的教育 APP 严重伤害了付出信任的家庭,而且由于志愿填报的特殊性,这样的伤害甚至可能持续影响孩子的一生。

志愿填报软件的首要任务,就是保障数据的准确性,为家长和孩子提供准确的参考依据。为了让各位家长选择真正能帮助自己的志愿填报辅助工具,我们选取了目前市场上最权威、最多好评的四款志愿填报软件,分别是:完美志愿、优志愿、来进行数据准确度的对比,看看目前市场上志愿填报产品数据的优劣情况。

为了让结果更权威、更全面,我们提取了各软件中 1000 所学校在广东省的录取数据(最低分、平均分、录取人数),并与广东省教育厅编写的《2015 年高考志愿填报指南》进行严格对照,反复检验,以此得出结论。(以下简称软件 A\B\C\D)

第一步,我们进行了 697 所本科学校的数据对比。

从图表中,我们可以清晰地看到:

从图表中,我们可以清晰地看到:

1. 最低分:完美志愿、升学 E 网通和第一高考网都比较精准,达到了 98% 以上;而优志愿在这个数据上表现不佳,准确率只有 76.76%。

2. 平均分:升学 E 网通没有此项数据,优志愿反超第一高考网正确率达到 96.84%,完美志愿以 99.57% 继续排在榜首。

3. 录取人数:录取人数也是填报的参考关键,这一项数据完美志愿和优志愿不相伯仲,第一高考网和升学 E 网通则表现平平,准确率都没有达到 90%。

第二步,我们进行了 1000 所本、专科院校对比

由于部分本科院校也包含专科专业,所以我们将本科和专科院校结合在一起讨论,以体现各个软件专科录取数据的准确度。

观察上图,我们发现,由于第一高考网缺失专科数据,其数据准确度大幅下降 30%,直接由第二名掉到第三名。

第三步,总结对比:

我们向大家随即抽取 10 所学校在广东省的文科录取原始数据对比,相信各位感受会更深。

优志愿:最低分数据错误率较高,总体表现不错;

第一高考网:平均分和录取人数错误率较高,且缺少专科 B 类学校的数据

升学 e 网通:录取人数错误率较高,且缺少平均分数据。

完美志愿:数据最全面、准确度最高;

综合发现,目前市场上的志愿填报软件在数据质量上可谓优劣分明,最优秀的软件数据准确率可达 99.14%,而最劣的软件数据准确率仅有 64.71%。将近一半的数据出现错误或缺失,令人寒心!假如考生轻信了不靠谱的数据,在志愿填报这件「终身大事」上出现失误,后果不堪设想。在下载量最多,口碑最佳的几款软件还是酝酿着数据准确度的巨大风险,更遑论其他的小作坊软件了。

为什么志愿填报软件优劣之间差距这么大?一方面,市场混乱,缺少监管,黑心的志愿填报软件有恃无恐;另一方面,竞争品牌之间相互抄袭,反而导致认真监控数据质量的软件生存空间越来越小。

希望通过此次数据对比,家长和学生们能提高警惕,在甄选高考志愿填报工具时更要注重数据的准确性。同时,我们强烈呼吁整肃高考志愿填报市场,让众多高考志愿填报软件得到有效的监管;优胜劣汰,彻底消灭牺牲孩子前程来牟取利益的黑心志愿填报软件,让考生十年的努力不会被白白浪费。

OpenVAS开源风险评估系统部署方案

$
0
0

OpenVAS,即开放式漏洞评估系统,是一个用于评估目标漏洞的杰出框架。功能十分强大,最重要的是,它是“开源”的——就是免费的意思啦~

它与著名的Nessus“本是同根生”,在Nessus商业化之后仍然坚持开源,号称“当前最好用的开源漏洞扫描工具”。最新版的Kali Linux(kali 3.0)不再自带OpenVAS了,所以我们要自己部署OpenVAS漏洞检测系统。其核心部件是一个服务器,包括一套网络漏洞测试程序,可以检测远程系统和应用程序中的安全问题。

但是它的最常用用途是检测目标网络或主机的安全性。它的评估能力来源于数万个漏洞测试程序,这些程序都是以插件的形式存在。openvas是基于C/S(客户端/服务器),B/S(浏览器/服务器)架构进行工作,用户通过浏览器或者专用客户端程序来下达扫描任务,服务器端负责授权,执行扫描操作并提供扫描结果。

本文档属于部署方案文档,详细介绍了部署方法,搭建部署过程中有很多踩过的坑,这里整理出来供参考。

OpenVAS系统架构

一套完整的openvas系统包括服务器端和客户端的多个组件,如下图所示:

image001.jpg

服务器层组件(建议都安装)客户层组件(任选其一安装即可)
OpenVAS-scanner(扫描器) 负责调用各种漏洞检测插件,完成实际的扫描操作。 OpenVAS-cli(命令行接口) 负责提供从命令行访问OpenVAS服务层程序。
OpenVAS-manager(管理器) 负责分配扫描任务,并根据扫描结果生产评估报告。 Greenbone-security-assistant(安全助手) 负责提供访问OpenVAS服务层的Web接口,便于通过浏览器来建立扫描任务,是使用最简便的客户层组件。
OpenVAS-administrator(管理者) 负责管理配置信息,用户授权等相关工作。 Greenbone-Desktop-Suite(桌面套件) 负责提供访问OpenVAS服务层的图形程序界面,主要在windows系统中使用。

kali Linux安装openvas

我这里是在kali linux系统上安装的openvas,最新版本的kalilinux系统是不带openvas的

注意:openvas服务器端仅支持安装在linux操作系统中,客户端安装在windows和Linux系统均可。

我的linux系统版本号如下:

image002.jpg

image003.jpg

虚拟机的网络连接方式如下,设置为桥接模式,设置为NAT。

image004.jpg

安装openvas过程

1. 更新软件包列表:

         apt-get update

image005.jpg

2. 获取到最新的软件包:

         apt-get dist-upgrade

image006.jpg

3. 安装openvas

         apt-get install openvas

执行以上命令后,如果没有报错,说明已经成功安装openvas。

kali Linux配置openvas

4.下载并更新OpenVAS库

openvas-setup

image007.jpg

在更新openvas库过程中创立了证书,下载及更新了一切扫描插件。

5.在更新OpenVAS库时,自动为admin用户创建了一个密码,只是该密码比较长,不容易记忆,我们使用如下命令将admin密码修改为容易记忆的密码,以kali123为例:

          image008.jpg

另外,我们新增一个普通用户wdl1,如下图:

image009.png

6.openvas-check setup

这个命令用于查错并用来确认OpenVAS是否成功安装,用apt-get安装总会出现这样那样的错误,我们可以用openvas-check-setup查看安装到哪步出错了,以及缺少什么东西。

image010.jpg

当出现如下结果时,表示安装成功:

image011.jpg

然后输入openvasmd –rebuild:rebuild the openvasmd database

image012.png

安装完成后,在应用程序—〉漏洞分析中会出现openvas这个应用,如下图:

image013.png

注意:openvas安装好之后并没有openvas-restart命令和openvas-status命令,该命令需要自定义安装。

在/usr/bin目录下,新建两个文件夹,分别为openvas-restart和openvas-status,然后赋予这两个文件可执行的权限:

image014.jpg

openvas-restart和openvas-status内容分别如下:

image015.png

之后就能够使用openvas-restart命令重启系统,使用openvas-status察看openvas系统状态了:

image016.jpg

image017.jpg

可以看到openvas目前处于运行状态。

kali linux启动openvas

由于OpenVAS是基于C/S,B/S架构进行工作的,所以,如果要使用该漏洞检测系统,必须先将OpenVAS服务启动,客户端才能连接进行测试。

双击应用程序中的openvas start启动openvas服务,出现如下界面:

image018.png

或者输入命令行程序openvas-start来启动openvas服务

image019.jpg

使用客户端访问openvas服务器

当openvas服务成功启动后,用户就可以连接openvas服务器并进行扫描了。根据前面的介绍,可知openvas有三种不同的客户端,分别是:OpenVAS命令行接口,Greenbone安全助手和Greenbone桌面套件。而且客户端能够用于各种操作系统。在kali linux中,默认安装的是Greenbone安全助手。

 本部署方案中使用最简单的浏览器客户端方式访问OpenVAS服务。因为,这种使用方式不仅简单,而且不需要客户额外安装应用程序,避免了枯燥的命令行方式,用户在任何操作系统中只要通过浏览器就可以在本地或远程连接OpenVAS服务器来对目标主机或网络进行漏洞检测。

通过使用浏览器客户端访问openvas服务器进行漏洞检测

然后我们使用客户层组件Greenbone-security-assistant访问openvas服务器,通过浏览器来建立扫描任务。

image020.jpg

 

image021.jpg

绿骨安全助手 GSA( Greenbone Security Assistant)是开放漏洞评估系统 OpenVAS(OpenVulnerability Assessment System)的基于网页的用户图形界面。 GSA 通过 OpenVAS Management Protocol (OMP) 连接 OpenVAS Manager。 通过实现完整的 OMP 特性集合,GSA 提供了一个直接了当的、非常强力的途径以管理网络漏洞扫描。

配置外部访问

安装完成后,openvas默认设置的监听地址为127.0.0.1,每次使用都只能用linux虚拟机打开浏览器通过 https://127.0.0.1:9392来进行登录扫描,不如通过自己的电脑浏览器连接到openvas服务器直接进行扫描来的方便。

如果openvas安装在远程服务器或者虚拟机里面,则必须用服务器或者虚拟机打开浏览器来扫描,这样比较麻烦。用户更加希望,通过自己的电脑浏览器连接到openvas服务器,直接进行扫描。下面介绍配置外部访问的方法:

openvas新版本有两种方式控制openvas的开关,一种是服务的方式,一种是脚本的方式。

1.服务的方式

这种方式是通过openvas-start/openvas-stop脚本启动和关闭的,这两个脚本里调用的是service指令。启动openvas服务的脚本都存放在/lib/systemd/system下。

修改三个配置文件openvas-manager.service,openvas-scanner.service和greenbone-security-assistant.service,将配置文件中的监听IP由127.0.0.1改为0.0.0.0(相比于更改为openvas服务器的实际IP地址,改为0.0.0.0会更好,因为0.0.0.0代表本机的任意地址,适用于服务器有多个IP或者服务器IP有变动的情况)。修改后的三个配置文件内容如下:

image022.jpg

image023.jpg

image024.jpg

2.脚本的方式

需要三个脚本控制开启和关闭openvas,

/etc/init.d/openvas-manager  //管理manager服务

/etc/init.d/openvas-scanner  //管理scanner服务

/etc/init.d/greenbone-security-assistant //管理gsad服务

这三个脚本对应了三个配置文件,分别为:

/etc/default/openvas-manager

/etc/default/openvas-scanner

/etc/default/greenbone-security-assistant

image025.jpg

分别修改配置文件中的监听ip,由127.0.0.1改为0.0.0.0,保存。修改后三个配置文件的内容分别如下:

image026.jpg

image027.jpg

image028.jpg

察看openvas的监听地址,如下图:

image029.jpg

image030.jpg

或者使用ps aux | less命令查看系统目前正在运行的进程。

可以看到,openvas监听地址已由127.0.0.1变为0.0.0.0。

察看linux虚拟机的IP地址为192.168.9.208:

image031.jpg

在不登录linux服务器的情况下,即可在本机windows系统的客户端浏览器中输入 https://192.168.9.208:9392/login/login.html连接openvas服务器进行漏洞检测。

注意:要保证主机和虚拟机间能通信。经过在多台台式机测试,测试者都能通过自己台式机浏览器连接我部署的openvas漏洞检测系统进行主机和网络安全测试。

image032.jpg

 *本文作者:魅影儿,转载请注明来自FreeBuf.COM

PM如何深度思考需求?这里有十步法

$
0
0

对于需求认知过程,其实就是考察一个产品经理自我修养。

最近作者正好在公司分享了自己关于了产品需求管理方面的内容,也是作者在产品经理职业道路上80%的做产品经理价值所在。近而又开始重新反思产品需求是什么?

不同的产品经理,对于需求的理解也是不一样的。然后造成了对于需求洞察能力也是不一样的。所以造成了1-3年的产品经理根据成长环境不同,自身的价值不同。有些产品经理可以3年不到成为产品总监。 原因就在对于需求理解的程度不同。

而产品经理最大能力毫不夸张的来说,就是对于需求理解层次。如果一个产品经理只懂交互,那么他在理解需求时的逻辑性会很差。而一个懂得财务的产品经理,对于理解需求上的商业行为会有更深层次的理解。

这里引用猎豹CEO傅盛的认知三要素: 就是信息的输入,思维模式的训练,最后判断。其实理解需求过程,就是认知需求过程。你对信息的广度和深度认知的不足,会造成当下处境和形式看不透。在认知上,一定学会倾听。而下一步思维模式是要靠后天训练获得的。在思维模式训练中,有一个非常重要的点叫自我挑战。最后做判断,必须基于深度思考。 所以在思考需求过程,其实就是认知需求的过程。

那么如何深度思考需求?这里作者试图用十步法则去解释并做好需求……

作者将产品需求管理分为了三个阶段。三个阶段分别是第一是获取需求阶段, 第二是需求管理阶段,第三是需求决策阶段。

一、需求从哪里来?

首先先要思考这个需求是从哪里来的?从大的抽象方面,可以分为产品经理自我挖掘需求、用户研究、竞品分析。从小的具象方面,可以是用户反馈、老板的需求、同事的需求、业务方的需求等等。

先说大的抽象方面:用户研究。它分为定性分析和定量分析。从定性分析方法有:用户访谈、焦点小组、实地调查、可用性测试、眼动实验、用户博客/评论等。最后用户博客/评论这种方法是来源于 腾讯1000/100/10原则。从定量分析方法有:行业调查报告、调查问卷、数据分析、A/B测试。

再说竞品分析:有市场分析、用户分析、功能分析、数据分析等方向。其实真正的指导思想可以按 《用户体验五要素》那本书去分层方式:战略层、范围层、框架层、结构层、表现层、作为分析方法的指导方向。

从小的具象方面:用户反馈大部分都是通过我们的用户研究得到的。而老板的需求、同事的需求都是他们基于自己的思考角度得来的。还有业务方的需求也是基于他们自己需要提出的。

回到需求提出方式上来,大致有如下几类

  • A:我希望产品上加上XXX功能,这样就能解决XXX问题了……(用户)
  • B:我习惯性这样使用产品……(同事)
  • C:我觉得他这个产品这个功能很好,我们能不能做……(老板)
  • D:我要做这个需求,因为这个功能很棒……(业务方)

基于以上几类需求解决思路,在后文中会有解答。这些只是罗列几种提出方式。

其实最后总结来说,需求来源通过不同的方式汇总到一起,有些是我们自己主动发起的,但还有些就是被动接受的。所以思考需求来源很重要,因为需求来源决定了我们判别需求的重要性。 另外,我们判断需求方式总共有三步:分为是来源、了解需求背景,最后是重要性。

二、思考需求面对的用户是哪些?

在一个产品成立之初,就是有特定的用户群定位。比如小米手机的初期的定位用户就是手机发烧友,打的就是性价比。而这些用户就是小米的种子用户。但是种子用户并不是KOL用户。KOL用户可能不是使用你的产品人,但是他们的消息是可以覆盖你的种子用户。比如美食产品,找一些新浪微博上的旅游吃货达人,让他们帮助传播一些美食产品的消息,这样就可以覆盖到我们的种子用户。

其实思考你面对的用户时,就是在对于你的产品用户进行一次初期画像过程。比如你的用户年龄、从事职业、爱好、特征等等。 而你的产品在某些产品需求上是可以覆盖他们的。

在用户画像时,其实已经在需求管理阶段了。为什么呢?其实在产品生命周期内用户人数会不断进行增长,而我们的定位用户人群也会发生变化。仔细思考小米的用户群,从发烧友到普通爱玩手机用户一个过程。其实用户画像是根据产品不同阶段进行不断调整的过程。

在这里深度思考要引入俞军提出一个观点, 就是用户不是人,而是需求集合。其实对于这点是这样理解的,比如在用户画像中的用户存在屌丝这个特点,其实就是用户群比较年轻化。但是这个特征可能是学生,可能是普通上班族。而他们又有年轻化的特性。比如学生可能是在18-22。而普通上班族可能工作不到3年的人,在22-25岁。那么这2个特点构成一个需求集合。其实这2个需求集合恰巧对应了小米手机的主要用户群。

三、思考需求发生的场景

在需求来源中,作者提到了一种需求发现方式是自我挖掘方式。其实就是人、行为、场景三者重合的过程。其实相对人而言,场景还是更简单些。比如在PC时代就是简单的固定式场景。 且我们做的服务都是功能导向式。特别是iMAC电脑。而进入手机阶段的时候,就是在移动场景下进行产品设计。这是需要考虑大环境,

然后特定的情景下产生需求场景。

其实大环境比较好考虑,比如在马路上,在地铁里。而情景考虑是基于人而产生的。比如我做在地铁上,需要看《人民的名义》这个电视剧。那么情景产生就在看电视剧,那么此时需要产生行为。选择那些视频播放器,最后可能选择了XX播放器。在这里场景下,其实行为是基于人的想法后再做出的,而场景只是我们符合相应的需求而做的考虑。

像打车软件需要考虑的情景就是:位置、确认、联系。

四、人与场景的需要与需求

在这里要注意的一点,是关于人性的思考,究竟这个需要发生需求是什么?这里就要回到著名的 马斯洛需求层次理论上来了,究竟是生理需求,还是安全需求,还是情感归属的需求,还是尊重的需求,还是自我实现的需求……无论在哪个层次上,都是需求发起的本质。人的行为都是通过我们脑子想的过程而进行神经行为反应的。

其实用户在地铁上看电视剧,经历了几个心理过程。首先,打开手机是为了打发无聊的时间。本质是落在情感归属上的需求。然后其实有看新闻、聊天、看电视剧等多种选择。然后,如果有事情要说,就聊天。但是好像今天没有想说的话,恰巧我坐的地铁时间需要1个多小时。那么不如看电视剧吧,打开XX视频,开始看视频了。

在这个过程中,第一个情感上归属,第二个是根据心理、时间、空间等多种条件决定的结果。在这个过程中,其实像微信、新闻类产品、视频类产品都是泛竞品关系, 而第三个是需要看电视剧时候,那就是视频类产品竞争的关系。究竟是爱奇艺,还是腾讯视频、优酷土豆、乐视视频、芒果TV……

五、思考需求来源的动机

在人与场景的需要和需求连接过程中,我们需要思考需求来源的动机是什么?实际上就是要思考:

需求的本质是什么?

需求的本质是动机、而不是需要。思考用户的动机、而不是他说要什么,因为他想要的可能是伪需求,并不能真正解决他的问题。

这里比较著名的例子就是:我想要的更快的马。而其实表现需求是要马,而当时造出了第一辆汽车,然后汽车比马 更快。所以后面汽车是开始广泛使用了。

还有例子就是:当一个男同事说:能不能帮我介绍个女朋友,可他真正的问题是:太不修边幅了,所以他真正的需求是:有人能帮他打扮的帅帅的。

所以介绍女朋友是需要,并不是他的需求。

回到第一段提到4种问题类型。

  • A:我希望产品上加上XXX功能,这样就能解决XXX问题了……(用户)
  • B:我习惯性这样使用产品……(同事)
  • C:我觉得他这个产品这个功能很好,我们能不能做……(老板)
  • D:我要做这个需求,因为这个功能很棒……(业务方)

A类问题需求解决方案是:这问题的本质是什么?是更快,还是更好。或者是解决什么问题。比如在前端产品中,用户希望可以分享,希望有个分享功能。实际上他想更多人评论他的看法,所以加上评论反而更好。所以A类需要了解用户本质需求点,转化一种交互实现方式。

B类问题则是同事站在他自己的习惯上,所以解决方案就是多找几个用户或者竞品去分析不同的用户行为, 从而找到一个合理的解决方案。

C类问题是比较棘手,因为竞争对手的功能与我们产品不在一个维度上。所以别从竞争对手功能出发。 而是要理解竞争对手的功能出发点,最后做出决定。因为我们不会比他们想的更深,因为我们是从表层去考虑他们,而他们是从本质去理解。跟随他们容易掉坑里。

D类问题其实要从自身的产品说,不能从业务方的战略出发。而是要思考具体需求。潮流决定方向,这才是战略,用户决定需求, 具体需求要结合场景。这里引用张小龙一句话:潮流是什么?潮流是为了让人不落伍,是比性还重要。

六、要有用户同理心

如果你只懂自己是不行的,要试图多理解他人。

1、如果你懂自己,自己的需求,你最感同身受。

记得曾经有个美团的朋友告诉我,猫眼电影初期要做的时候,他们的CEO亲自去电影院看了1万小时的电影,这就是要做感同身受,理解用户。这里也证明了一万小时的理论重要性。

2、群体用户思维简单,冲动,情绪化跟风。

读过《乌合之众》《大众心理学》的朋友并不陌生这样的话。群体思维是有从众心理的,就跟很多事情为什么火起来道理都是一样的。

3、试图多理解他人,产品经验最重要的是同理心。

这是产品经理最重要的条件。

4、如果你连朝夕相处的女朋友都搞不定,何况用户。

从身边最亲密的人开始了解,这是产品经理最基本的。

七、用户潜在心理

用户潜在心理,是对于一个事物的认知过程。从时间上,有一个认知过程,就像我们对于AI人工智能认知是不同的。加上我们自身的经验和角度问题,造成认知的不同。我们往往与用户不在一个认知层面上,那就导致需求理解的偏离。从物理空间上,我们的本身能力决定了购买力和生活方式,而这些限制用户心理上的思考程度。所以用户潜在心理并不是简单的无聊这么简单。而人的7宗罪都是认知上的一部分。而是要从时间和空间上理解,这样思考是符合用户心理变化过程。

其实很多例子很明显都可以证明这一点。女生在学生时代可能只能用几十元的化妆品,但是你毕业之后开始工作,你会用几百上千的化妆品。女生在学生时代不会打扮,但是毕业之后开始工作,都打扮的漂漂亮亮的。这些都对产品使用人群都是变化的过程。

所以用户潜在心理是在一个变化过程。

八、产品定位的需求

产品定位的需求要活在未来。基于未来的需求做产品设计。把缺失有趣的东西做出来。这些产品需求才是兴奋型的需求。

在做产品定位时,在第二阶段我们已经有了产品需求池。这里要做就是第三阶段,需求决策阶段。这个需求我们是否考虑要进入决策阶段,需要考虑可实现性,核心诉求点是什么?是否满足我们的战略目标?而我们最好做成长中的市场与需求。

在产品用户体验方面,我们一定要学会反问自己:如果不做这些功能具体问题出在哪里?核心在于这个用户体验是否真的缺失。

在做移动端产品需求时,一定不是简洁,而是简单。一个界面,一个主题。回归本质:不要让我思考。这是我们做界面简洁的时候一个思考方向。

九、核心功能的内在联系

解剖一个伟大的产品,做产品正确的思维顺序是:骨骼:产品的节后来源于对受众的理解。肌肉:最重要的几个功能分别是什么?血液:其他功能与核心功能的关系如何串?皮毛:功能和使用流程每一处的细节。举例:微信的定位,最重要的功能,朋友圈,已经它的细节。

产品经理核心价值一定在于产品的抽象能力,这是作者作为产品经理以来最深的体会。产品的抽象能力决定了你未来的前途和发展。特别是作者对于产品、技术、运营都有理解的产品经理来说,特别能感受这点。

其实做简单的秘诀就是抽象分类。找出各个功能的共性,寻找用户的认知G点,感受用户的文化水平。特别在需求决策阶段我们考虑SMART原则,考虑具体、可度量、可实现性、相关性、有时限。

这里列出业内大拿提出的2个公式:

X(超预期)>Y1(用户预期)+Y2(转移成本)

做的事情有价值>新版体验-(原用户体验+成本)

十、产品亮点往往是对于一件事深入思考

首先,我们需要引入四个思考法则:

紧急重要四象限法则,对于判断重要性来说:

  1. 不做,会造成严重的问题和恶劣的影响。
  2. 做了,会产生巨大好处和极佳效果。
  3. 跟核心用户利益有关。
  4. 跟大部分用户利益有关。
  5. 跟效率或成本有关。
  6. 跟用户体验有关。

对于判断紧急性来说:

  1. 不做,错误会持续发生并造成严重影响。
  2. 在一定时间内可控但长期会有糟糕的影响。
  3. 做了,立刻能解决很多问题,产生正面的影响。
  4. 做了,在一段时间后可以有良好的效果。

KANO模型(满意度、具备度):

  1. 必备型需求
  2. 期望型需求
  3. 魅力型需求
  4. 无差异型需求
  5. 反向型需求

需求性价比:需求优先级分为P1\P2\P3,开发成本优先级D1\D2\D3,

最后形成需求性比矩阵:需求顺序为:

  1. P1D1
  2. P1D2
  3. P2D1
  4. P2D2
  5. P1D3
  6. P3D1
  7. P2D3
  8. P3D2
  9. P3D3

(来自刘飞的从点子到产品)

可行性的MVP产品找到PMF的契合点,2点思考:商业价值和用户需求。

名词解释:MVP:小而试行型的产品。PMF:产品和市场的契合点。

再次,我们需要学会反问自己: 比人做的成,你做的成吗?你凭什么觉得自己做的成?

特别重要的一点警告:千万别算计用户,最好不要不计一切地展示聪明!要选择善良。

最后,怎么让用户记住你?有趣。

产品、功能、设计三个之中至少一个层面能够用用户能够熟悉的东西来帮助理解。举例:产品层面上,约炮能记得陌陌。功能层面上,朋友圈能记得微信。设计层面上,美观的游戏上能记得王者荣耀。

对于一件事的深入思考,除了付出大量的时间,还有空间上深入你的用户之外,别无他法。

对于需求认知过程,其实就是考察一个产品经理自我修养。

这里引用几句话:

  1. 我就是我,是不一样的烟火。

  2. 不避免装逼,不刻意去装逼。
  3. 产品发布会,难得机会,要将故事。

#专栏作家#

晓翼,微信公众号:上海人在北京。人人都是产品经理专栏作家。专注于电商、O2O的产品经理。会IOS开发、会P图、会运营的逗比一个。常关注社交、旅行类产品。

本文原创发布于人人都是产品经理,未经许可,不得转载。

kafka数据可靠性深度解读

$
0
0

1 概述

Kakfa起初是由LinkedIn公司开发的一个分布式的消息系统,后成为Apache的一部分,它使用Scala编写,以可水平扩展和高吞吐率而被广泛使用。目前越来越多的开源分布式处理系统如Cloudera、Apache Storm、Spark等都支持与Kafka集成。

Kafka凭借着自身的优势,越来越受到互联网企业的青睐,唯品会也采用Kafka作为其内部核心消息引擎之一。Kafka作为一个商业级消息中间件,消息可靠性的重要性可想而知。如何确保消息的精确传输?如何确保消息的准确存储?如何确保消息的正确消费?这些都是需要考虑的问题。本文首先从Kafka的架构着手,先了解下Kafka的基本原理,然后通过对kakfa的存储机制、复制原理、同步原理、可靠性和持久性保证等等一步步对其可靠性进行分析,最后通过benchmark来增强对Kafka高可靠性的认知。


2 Kafka体系架构

如上图所示,一个典型的Kafka体系架构包括若干Producer(可以是服务器日志,业务数据,页面前端产生的page view等等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干Consumer (Group),以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在consumer group发生变化时进行rebalance。Producer使用push(推)模式将消息发布到broker,Consumer使用pull(拉)模式从broker订阅并消费消息。

名词解释:

名称解释
Broker消息中间件处理节点,一个Kafka节点就是一个broker,一个或者多个Broker可以组成一个Kafka集群
TopicKafka根据topic对消息进行归类,发布到Kafka集群的每条消息都需要指定一个topic
Producer消息生产者,向Broker发送消息的客户端
Consumer消息消费者,从Broker读取消息的客户端
ConsumerGroup每个Consumer属于一个特定的Consumer Group,一条消息可以发送到多个不同的Consumer Group,但是一个Consumer Group中只能有一个Consumer能够消费该消息
Partition物理上的概念,一个topic可以分为多个partition,每个partition内部是有序的

2.1 Topic & Partition

一个topic可以认为一个一类消息,每个topic将被分成多个partition,每个partition在存储层面是append log文件。任何发布到此partition的消息都会被追加到log文件的尾部,每条消息在文件中的位置称为offset(偏移量),offset为一个long型的数字,它唯一标记一条消息。每条消息都被append到partition中,是顺序写磁盘,因此效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证)。

每一条消息被发送到broker中,会根据partition规则选择被存储到哪一个partition。如果partition规则设置的合理,所有消息可以均匀分布到不同的partition里,这样就实现了水平扩展。(如果一个topic对应一个文件,那这个文件所在的机器I/O将会成为这个topic的性能瓶颈,而partition解决了这个问题)。在创建topic时可以在$KAFKA_HOME/config/server.properties中指定这个partition的数量(如下所示),当然可以在topic创建之后去修改partition的数量。

# The default number of log partitions per topic. More partitions allow greater
# parallelism for consumption, but this will also result in more files across
# the brokers.
num.partitions=3

在发送一条消息时,可以指定这个消息的key,producer根据这个key和partition机制来判断这个消息发送到哪个partition。partition机制可以通过指定producer的partition.class这一参数来指定,该class必须实现kafka.producer.Partitioner接口。

有关Topic与Partition的更多细节,可以参考下面的“Kafka文件存储机制”这一节。


3 高可靠性存储分析

Kafka的高可靠性的保障来源于其健壮的副本(replication)策略。通过调节其副本相关参数,可以使得Kafka在性能和可靠性之间运转的游刃有余。Kafka从0.8.x版本开始提供partition级别的复制,replication的数量可以在$KAFKA_HOME/config/server.properties中配置(default.replication.refactor)。

这里先从Kafka文件存储机制入手,从最底层了解Kafka的存储细节,进而对其的存储有个微观的认知。之后通过Kafka复制原理和同步方式来阐述宏观层面的概念。最后从ISR,HW,leader选举以及数据可靠性和持久性保证等等各个维度来丰富对Kafka相关知识点的认知。

3.1 Kafka文件存储机制

Kafka中消息是以topic进行分类的,生产者通过topic向Kafka broker发送消息,消费者通过topic读取数据。然而topic在物理层面又能以partition为分组,一个topic可以分成若干个partition,那么topic以及partition又是怎么存储的呢?partition还可以细分为segment,一个partition物理上由多个segment组成,那么这些segment又是什么呢?下面我们来一一揭晓。

为了便于说明问题,假设这里只有一个Kafka集群,且这个集群只有一个Kafka broker,即只有一台物理机。在这个Kafka broker中配置($KAFKA_HOME/config/server.properties中)log.dirs=/tmp/kafka-logs,以此来设置Kafka消息文件存储目录,与此同时创建一个topic:topic_zzh_test,partition的数量为4($KAFKA_HOME/bin/kafka-topics.sh –create –zookeeper localhost:2181 –partitions 4 –topic topic_vms_test –replication-factor 4)。那么我们此时可以在/tmp/kafka-logs目录中可以看到生成了4个目录:

drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-0
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-1
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-2
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-3

在Kafka文件存储中,同一个topic下有多个不同的partition,每个partiton为一个目录,partition的名称规则为:topic名称+有序序号,第一个序号从0开始计,最大的序号为partition数量减1,partition是实际物理上的概念,而topic是逻辑上的概念。

上面提到partition还可以细分为segment,这个segment又是什么?如果就以partition为最小存储单位,我们可以想象当Kafka producer不断发送消息,必然会引起partition文件的无限扩张,这样对于消息文件的维护以及已经被消费的消息的清理带来严重的影响,所以这里以segment为单位又将partition细分。每个partition(目录)相当于一个巨型文件被平均分配到多个大小相等的segment(段)数据文件中(每个segment 文件中消息数量不一定相等)这种特性也方便old segment的删除,即方便已被消费的消息的清理,提高磁盘的利用率。每个partition只需要支持顺序读写就行,segment的文件生命周期由服务端配置参数(log.segment.bytes,log.roll.{ms,hours}等若干参数)决定。

segment文件由两部分组成,分别为“.index”文件和“.log”文件,分别表示为segment索引文件和数据文件。这两个文件的命令规则为:partition全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值,数值大小为64位,20位数字字符长度,没有数字用0填充,如下:

00000000000000000000.index
00000000000000000000.log
00000000000000170410.index
00000000000000170410.log
00000000000000239430.index
00000000000000239430.log

以上面的segment文件为例,展示出segment:00000000000000170410的“.index”文件和“.log”文件的对应的关系,如下图:

如上图,“.index”索引文件存储大量的元数据,“.log”数据文件存储大量的消息,索引文件中的元数据指向对应数据文件中message的物理偏移地址。其中以“.index”索引文件中的元数据[3, 348]为例,在“.log”数据文件表示第3个消息,即在全局partition中表示170410+3=170413个消息,该消息的物理偏移地址为348。

那么如何从partition中通过offset查找message呢?
以上图为例,读取offset=170418的消息,首先查找segment文件,其中00000000000000000000.index为最开始的文件,第二个文件为00000000000000170410.index(起始偏移为170410+1=170411),而第三个文件为00000000000000239430.index(起始偏移为239430+1=239431),所以这个offset=170418就落到了第二个文件之中。其他后续文件可以依次类推,以其实偏移量命名并排列这些文件,然后根据二分查找法就可以快速定位到具体文件位置。其次根据00000000000000170410.index文件中的[8,1325]定位到00000000000000170410.log文件中的1325的位置进行读取。

要是读取offset=170418的消息,从00000000000000170410.log文件中的1325的位置进行读取,那么怎么知道何时读完本条消息,否则就读到下一条消息的内容了?
这个就需要联系到消息的物理结构了,消息都具有固定的物理结构,包括:offset(8 Bytes)、消息体的大小(4 Bytes)、crc32(4 Bytes)、magic(1 Byte)、attributes(1 Byte)、key length(4 Bytes)、key(K Bytes)、payload(N Bytes)等等字段,可以确定一条消息的大小,即读取到哪里截止。

3.2 复制原理和同步方式

Kafka中topic的每个partition有一个预写式的日志文件,虽然partition可以继续细分为若干个segment文件,但是对于上层应用来说可以将partition看成最小的存储单元(一个有多个segment文件拼接的“巨型”文件),每个partition都由一些列有序的、不可变的消息组成,这些消息被连续的追加到partition中。

上图中有两个新名词:HW和LEO。这里先介绍下LEO,LogEndOffset的缩写,表示每个partition的log最后一条Message的位置。HW是HighWatermark的缩写,是指consumer能够看到的此partition的位置,这个涉及到多副本的概念,这里先提及一下,下节再详表。

言归正传,为了提高消息的可靠性,Kafka每个topic的partition有N个副本(replicas),其中N(大于等于1)是topic的复制因子(replica fator)的个数。Kafka通过多副本机制实现故障自动转移,当Kafka集群中一个broker失效情况下仍然保证服务可用。在Kafka中发生复制时确保partition的日志能有序地写到其他节点上,N个replicas中,其中一个replica为leader,其他都为follower, leader处理partition的所有读写请求,与此同时,follower会被动定期地去复制leader上的数据。

如下图所示,Kafka集群中有4个broker, 某topic有3个partition,且复制因子即副本个数也为3:

Kafka提供了数据复制算法保证,如果leader发生故障或挂掉,一个新leader被选举并被接受客户端的消息成功写入。Kafka确保从同步副本列表中选举一个副本为leader,或者说follower追赶leader数据。leader负责维护和跟踪ISR(In-Sync Replicas的缩写,表示副本同步队列,具体可参考下节)中所有follower滞后的状态。当producer发送一条消息到broker后,leader写入消息并复制到所有follower。消息提交之后才被成功复制到所有的同步副本。消息复制延迟受最慢的follower限制,重要的是快速检测慢副本,如果follower“落后”太多或者失效,leader将会把它从ISR中删除。

3.3 ISR

上节我们涉及到ISR (In-Sync Replicas),这个是指副本同步队列。副本数对Kafka的吞吐率是有一定的影响,但极大的增强了可用性。默认情况下Kafka的replica数量为1,即每个partition都有一个唯一的leader,为了确保消息的可靠性,通常应用中将其值(由broker的参数offsets.topic.replication.factor指定)大小设置为大于1,比如3。 所有的副本(replicas)统称为Assigned Replicas,即AR。ISR是AR中的一个子集,由leader维护ISR列表,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟条数replica.lag.max.messages两个维度, 当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度),任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。

Kafka 0.10.x版本后移除了replica.lag.max.messages参数,只保留了replica.lag.time.max.ms作为ISR中副本管理的参数。为什么这样做呢?replica.lag.max.messages表示当前某个副本落后leaeder的消息数量超过了这个参数的值,那么leader就会把follower从ISR中删除。假设设置replica.lag.max.messages=4,那么如果producer一次传送至broker的消息数量都小于4条时,因为在leader接受到producer发送的消息之后而follower副本开始拉取这些消息之前,follower落后leader的消息数不会超过4条消息,故此没有follower移出ISR,所以这时候replica.lag.max.message的设置似乎是合理的。但是producer发起瞬时高峰流量,producer一次发送的消息超过4条时,也就是超过replica.lag.max.messages,此时follower都会被认为是与leader副本不同步了,从而被踢出了ISR。但实际上这些follower都是存活状态的且没有性能问题。那么在之后追上leader,并被重新加入了ISR。于是就会出现它们不断地剔出ISR然后重新回归ISR,这无疑增加了无谓的性能损耗。而且这个参数是broker全局的。设置太大了,影响真正“落后”follower的移除;设置的太小了,导致follower的频繁进出。无法给定一个合适的replica.lag.max.messages的值,故此,新版本的Kafka移除了这个参数。

注:ISR中包括:leader和follower。

上面一节还涉及到一个概念,即HW。HW俗称高水位,HighWatermark的缩写,取一个partition对应的ISR中最小的LEO作为HW,consumer最多只能消费到HW所在的位置。另外每个replica都有HW,leader和follower各自负责更新自己的HW的状态。对于leader新写入的消息,consumer不能立刻消费,leader会等待该消息被所有ISR中的replicas同步后更新HW,此时消息才能被consumer消费。这样就保证了如果leader所在的broker失效,该消息仍然可以从新选举的leader中获取。对于来自内部broKer的读取请求,没有HW的限制。

下图详细的说明了当producer生产消息至broker后,ISR以及HW和LEO的流转过程:

由此可见,Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。事实上,同步复制要求所有能工作的follower都复制完,这条消息才会被commit,这种复制方式极大的影响了吞吐率。而异步复制方式下,follower异步的从leader复制数据,数据只要被leader写入log就被认为已经commit,这种情况下如果follower都还没有复制完,落后于leader时,突然leader宕机,则会丢失数据。而Kafka的这种使用ISR的方式则很好的均衡了确保数据不丢失以及吞吐率。

Kafka的ISR的管理最终都会反馈到Zookeeper节点上。具体位置为:/brokers/topics/[topic]/partitions/[partition]/state。目前有两个地方会对这个Zookeeper的节点进行维护:

  1. Controller来维护:Kafka集群中的其中一个Broker会被选举为Controller,主要负责Partition管理和副本状态管理,也会执行类似于重分配partition之类的管理任务。在符合某些特定条件下,Controller下的LeaderSelector会选举新的leader,ISR和新的leader_epoch及controller_epoch写入Zookeeper的相关节点中。同时发起LeaderAndIsrRequest通知所有的replicas。
  2. leader来维护:leader有单独的线程定期检测ISR中follower是否脱离ISR, 如果发现ISR变化,则会将新的ISR的信息返回到Zookeeper的相关节点中。

3.4 数据可靠性和持久性保证

当producer向leader发送数据时,可以通过request.required.acks参数来设置数据可靠性的级别:

  • 1(默认):这意味着producer在ISR中的leader已成功收到的数据并得到确认后发送下一条message。如果leader宕机了,则会丢失数据。
  • 0:这意味着producer无需等待来自broker的确认而继续发送下一批消息。这种情况下数据传输效率最高,但是数据可靠性确是最低的。
  • -1:producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。但是这样也不能保证数据不丢失,比如当ISR中只有leader时(前面ISR那一节讲到,ISR中的成员由于某些情况会增加也会减少,最少就只剩一个leader),这样就变成了acks=1的情况。

如果要提高数据的可靠性,在设置request.required.acks=-1的同时,也要min.insync.replicas这个参数(可以在broker或者topic层面进行设置)的配合,这样才能发挥最大的功效。min.insync.replicas这个参数设定ISR中的最小副本数是多少,默认值为1,当且仅当request.required.acks参数设置为-1时,此参数才生效。如果ISR中的副本数少于min.insync.replicas配置的数量时,客户端会返回异常:org.apache.kafka.common.errors.NotEnoughReplicasExceptoin: Messages are rejected since there are fewer in-sync replicas than required。

接下来对acks=1和-1的两种情况进行详细分析:

1. request.required.acks=1

producer发送数据到leader,leader写本地日志成功,返回客户端成功;此时ISR中的副本还没有来得及拉取该消息,leader就宕机了,那么此次发送的消息就会丢失。

2. request.required.acks=-1

同步(Kafka默认为同步,即producer.type=sync)的发送模式,replication.factor>=2且min.insync.replicas>=2的情况下,不会丢失数据。

有两种典型情况。acks=-1的情况下(如无特殊说明,以下acks都表示为参数request.required.acks),数据发送到leader, ISR的follower全部完成数据同步后,leader此时挂掉,那么会选举出新的leader,数据不会丢失。

acks=-1的情况下,数据发送到leader后 ,部分ISR的副本同步,leader此时挂掉。比如follower1h和follower2都有可能变成新的leader, producer端会得到返回异常,producer端会重新发送数据,数据可能会重复。

当然上图中如果在leader crash的时候,follower2还没有同步到任何数据,而且follower2被选举为新的leader的话,这样消息就不会重复。

注:Kafka只处理fail/recover问题,不处理Byzantine问题。

3.5 关于HW的进一步探讨

考虑上图(即acks=-1,部分ISR副本同步)中的另一种情况,如果在Leader挂掉的时候,follower1同步了消息4,5,follower2同步了消息4,与此同时follower2被选举为leader,那么此时follower1中的多出的消息5该做如何处理呢?

这里就需要HW的协同配合了。如前所述,一个partition中的ISR列表中,leader的HW是所有ISR列表里副本中最小的那个的LEO。类似于木桶原理,水位取决于最低那块短板。

如上图,某个topic的某partition有三个副本,分别为A、B、C。A作为leader肯定是LEO最高,B紧随其后,C机器由于配置比较低,网络比较差,故而同步最慢。这个时候A机器宕机,这时候如果B成为leader,假如没有HW,在A重新恢复之后会做同步(makeFollower)操作,在宕机时log文件之后直接做追加操作,而假如B的LEO已经达到了A的LEO,会产生数据不一致的情况,所以使用HW来避免这种情况。
A在做同步操作的时候,先将log文件截断到之前自己的HW的位置,即3,之后再从B中拉取消息进行同步。

如果失败的follower恢复过来,它首先将自己的log文件截断到上次checkpointed时刻的HW的位置,之后再从leader中同步消息。leader挂掉会重新选举,新的leader会发送“指令”让其余的follower截断至自身的HW的位置然后再拉取新的消息。

当ISR中的个副本的LEO不一致时,如果此时leader挂掉,选举新的leader时并不是按照LEO的高低进行选举,而是按照ISR中的顺序选举。

3.6 Leader选举

一条消息只有被ISR中的所有follower都从leader复制过去才会被认为已提交。这样就避免了部分数据被写进了leader,还没来得及被任何follower复制就宕机了,而造成数据丢失。而对于producer而言,它可以选择是否等待消息commit,这可以通过request.required.acks来设置。这种机制确保了只要ISR中有一个或者以上的follower,一条被commit的消息就不会丢失。

有一个很重要的问题是当leader宕机了,怎样在follower中选举出新的leader,因为follower可能落后很多或者直接crash了,所以必须确保选择“最新”的follower作为新的leader。一个基本的原则就是,如果leader不在了,新的leader必须拥有原来的leader commit的所有消息。这就需要做一个折中,如果leader在表名一个消息被commit前等待更多的follower确认,那么在它挂掉之后就有更多的follower可以成为新的leader,但这也会造成吞吐率的下降。

一种非常常用的选举leader的方式是“少数服从多数”,Kafka并不是采用这种方式。这种模式下,如果我们有2f+1个副本,那么在commit之前必须保证有f+1个replica复制完消息,同时为了保证能正确选举出新的leader,失败的副本数不能超过f个。这种方式有个很大的优势,系统的延迟取决于最快的几台机器,也就是说比如副本数为3,那么延迟就取决于最快的那个follower而不是最慢的那个。“少数服从多数”的方式也有一些劣势,为了保证leader选举的正常进行,它所能容忍的失败的follower数比较少,如果要容忍1个follower挂掉,那么至少要3个以上的副本,如果要容忍2个follower挂掉,必须要有5个以上的副本。也就是说,在生产环境下为了保证较高的容错率,必须要有大量的副本,而大量的副本又会在大数据量下导致性能的急剧下降。这种算法更多用在Zookeeper这种共享集群配置的系统中而很少在需要大量数据的系统中使用的原因。HDFS的HA功能也是基于“少数服从多数”的方式,但是其数据存储并不是采用这样的方式。

实际上,leader选举的算法非常多,比如Zookeeper的 ZabRaft以及 Viewstamped Replication。而Kafka所使用的leader选举算法更像是微软的 PacificA算法。

Kafka在Zookeeper中为每一个partition动态的维护了一个ISR,这个ISR里的所有replica都跟上了leader,只有ISR里的成员才能有被选为leader的可能(unclean.leader.election.enable=false)。在这种模式下,对于f+1个副本,一个Kafka topic能在保证不丢失已经commit消息的前提下容忍f个副本的失败,在大多数使用场景下,这种模式是十分有利的。事实上,为了容忍f个副本的失败,“少数服从多数”的方式和ISR在commit前需要等待的副本的数量是一样的,但是ISR需要的总的副本的个数几乎是“少数服从多数”的方式的一半。

上文提到,在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失,但如果某一个partition的所有replica都挂了,就无法保证数据不丢失了。这种情况下有两种可行的方案:

  1. 等待ISR中任意一个replica“活”过来,并且选它作为leader
  2. 选择第一个“活”过来的replica(并不一定是在ISR中)作为leader

这就需要在可用性和一致性当中作出一个简单的抉择。如果一定要等待ISR中的replica“活”过来,那不可用的时间就可能会相对较长。而且如果ISR中所有的replica都无法“活”过来了,或者数据丢失了,这个partition将永远不可用。选择第一个“活”过来的replica作为leader,而这个replica不是ISR中的replica,那即使它并不保障已经包含了所有已commit的消息,它也会成为leader而作为consumer的数据源。默认情况下,Kafka采用第二种策略,即unclean.leader.election.enable=true,也可以将此参数设置为false来启用第一种策略。

unclean.leader.election.enable这个参数对于leader的选举、系统的可用性以及数据的可靠性都有至关重要的影响。下面我们来分析下几种典型的场景。

如果上图所示,假设某个partition中的副本数为3,replica-0, replica-1, replica-2分别存放在broker0, broker1和broker2中。AR=(0,1,2),ISR=(0,1)。
设置request.required.acks=-1, min.insync.replicas=2,unclean.leader.election.enable=false。这里讲broker0中的副本也称之为broker0起初broker0为leader,broker1为follower。

  • 当ISR中的replica-0出现crash的情况时,broker1选举为新的leader[ISR=(1)],因为受min.insync.replicas=2影响,write不能服务,但是read能继续正常服务。此种情况恢复方案:

    1. 尝试恢复(重启)replica-0,如果能起来,系统正常;
    2. 如果replica-0不能恢复,需要将min.insync.replicas设置为1,恢复write功能。
  • 当ISR中的replica-0出现crash,紧接着replica-1也出现了crash, 此时[ISR=(1),leader=-1],不能对外提供服务,此种情况恢复方案:

    1. 尝试恢复replica-0和replica-1,如果都能起来,则系统恢复正常;
    2. 如果replica-0起来,而replica-1不能起来,这时候仍然不能选出leader,因为当设置unclean.leader.election.enable=false时,leader只能从ISR中选举,当ISR中所有副本都失效之后,需要ISR中最后失效的那个副本能恢复之后才能选举leader, 即replica-0先失效,replica-1后失效,需要replica-1恢复后才能选举leader。保守的方案建议把unclean.leader.election.enable设置为true,但是这样会有丢失数据的情况发生,这样可以恢复read服务。同样需要将min.insync.replicas设置为1,恢复write功能;
    3. replica-1恢复,replica-0不能恢复,这个情况上面遇到过,read服务可用,需要将min.insync.replicas设置为1,恢复write功能;
    4. replica-0和replica-1都不能恢复,这种情况可以参考情形2.
  • 当ISR中的replica-0, replica-1同时宕机,此时[ISR=(0,1)],不能对外提供服务,此种情况恢复方案:尝试恢复replica-0和replica-1,当其中任意一个副本恢复正常时,对外可以提供read服务。直到2个副本恢复正常,write功能才能恢复,或者将将min.insync.replicas设置为1。

3.7 Kafka的发送模式

Kafka的发送模式由producer端的配置参数producer.type来设置,这个参数指定了在后台线程中消息的发送方式是同步的还是异步的,默认是同步的方式,即producer.type=sync。如果设置成异步的模式,即producer.type=async,可以是producer以batch的形式push数据,这样会极大的提高broker的性能,但是这样会增加丢失数据的风险。如果需要确保消息的可靠性,必须要将producer.type设置为sync。

对于异步模式,还有4个配套的参数,如下:

PropertyDescription
queue.buffering.max.ms默认值:5000。启用异步模式时,producer缓存消息的时间。比如我们设置成1000时,它会缓存1s的数据再一次发送出去,这样可以极大的增加broker吞吐量,但也会造成时效性的降低。
queue.buffering.max.messages默认值:10000。启用异步模式时,producer缓存队列里最大缓存的消息数量,如果超过这个值,producer就会阻塞或者丢掉消息。
queue.enqueue.timeout.ms默认值:-1。当达到上面参数时producer会阻塞等待的时间。如果设置为0,buffer队列满时producer不会阻塞,消息直接被丢掉;若设置为-1,producer会被阻塞,不会丢消息。
batch.num.messages默认值:200。启用异步模式时,一个batch缓存的消息数量。达到这个数值时,producer才会发送消息。(每次批量发送的数量)

以batch的方式推送数据可以极大的提高处理效率,kafka producer可以将消息在内存中累计到一定数量后作为一个batch发送请求。batch的数量大小可以通过producer的参数(batch.num.messages)控制。通过增加batch的大小,可以减少网络请求和磁盘IO的次数,当然具体参数设置需要在效率和时效性方面做一个权衡。在比较新的版本中还有batch.size这个参数。


4 高可靠性使用分析

4.1 消息传输保障

前面已经介绍了Kafka如何进行有效的存储,以及了解了producer和consumer如何工作。接下来讨论的是Kafka如何确保消息在producer和consumer之间传输。有以下三种可能的传输保障(delivery guarantee):

  • At most once: 消息可能会丢,但绝不会重复传输
  • At least once:消息绝不会丢,但可能会重复传输
  • Exactly once:每条消息肯定会被传输一次且仅传输一次

Kafka的消息传输保障机制非常直观。当producer向broker发送消息时,一旦这条消息被commit,由于副本机制(replication)的存在,它就不会丢失。但是如果producer发送数据给broker后,遇到的网络问题而造成通信中断,那producer就无法判断该条消息是否已经提交(commit)。虽然Kafka无法确定网络故障期间发生了什么,但是producer可以retry多次,确保消息已经正确传输到broker中,所以目前Kafka实现的是at least once。

consumer从broker中读取消息后,可以选择commit,该操作会在Zookeeper中存下该consumer在该partition下读取的消息的offset。该consumer下一次再读该partition时会从下一条开始读取。如未commit,下一次读取的开始位置会跟上一次commit之后的开始位置相同。当然也可以将consumer设置为autocommit,即consumer一旦读取到数据立即自动commit。如果只讨论这一读取消息的过程,那Kafka是确保了exactly once, 但是如果由于前面producer与broker之间的某种原因导致消息的重复,那么这里就是at least once。

考虑这样一种情况,当consumer读完消息之后先commit再处理消息,在这种模式下,如果consumer在commit后还没来得及处理消息就crash了,下次重新开始工作后就无法读到刚刚已提交而未处理的消息,这就对应于at most once了。

读完消息先处理再commit。这种模式下,如果处理完了消息在commit之前consumer crash了,下次重新开始工作时还会处理刚刚未commit的消息,实际上该消息已经被处理过了,这就对应于at least once。

要做到exactly once就需要引入消息去重机制。

4.2 消息去重

如上一节所述,Kafka在producer端和consumer端都会出现消息的重复,这就需要去重处理。

Kafka文档中提及GUID(Globally Unique Identifier)的概念,通过客户端生成算法得到每个消息的unique id,同时可映射至broker上存储的地址,即通过GUID便可查询提取消息内容,也便于发送方的幂等性保证,需要在broker上提供此去重处理模块,目前版本尚不支持。

针对GUID, 如果从客户端的角度去重,那么需要引入集中式缓存,必然会增加依赖复杂度,另外缓存的大小难以界定。

不只是Kafka, 类似RabbitMQ以及RocketMQ这类商业级中间件也只保障at least once, 且也无法从自身去进行消息去重。所以我们建议业务方根据自身的业务特点进行去重,比如业务消息本身具备幂等性,或者借助Redis等其他产品进行去重处理。

4.3 高可靠性配置

Kafka提供了很高的数据冗余弹性,对于需要数据高可靠性的场景,我们可以增加数据冗余备份数(replication.factor),调高最小写入副本数的个数(min.insync.replicas)等等,但是这样会影响性能。反之,性能提高而可靠性则降低,用户需要自身业务特性在彼此之间做一些权衡性选择。

要保证数据写入到Kafka是安全的,高可靠的,需要如下的配置:

  • topic的配置:replication.factor>=3,即副本数至少是3个;2<=min.insync.replicas<=replication.factor
  • broker的配置:leader的选举条件unclean.leader.election.enable=false
  • producer的配置:request.required.acks=-1(all),producer.type=sync

5 BenchMark

Kafka在唯品会有着很深的历史渊源,根据唯品会消息中间件团队(VMS团队)所掌握的资料显示,在VMS团队运转的Kafka集群中所支撑的topic数已接近2000,每天的请求量也已达千亿级。这里就以Kafka的高可靠性为基准点来探究几种不同场景下的行为表现,以此来加深对Kafka的认知,为大家在以后高效的使用Kafka时提供一份依据。

5.1 测试环境

Kafka broker用到了4台机器,分别为broker[0/1/2/3]配置如下:

  • CPU: 24core/2.6GHZ
  • Memory: 62G
  • Network: 4000Mb
  • OS/kernel: CentOs release 6.6 (Final)
  • Disk: 1089G
  • Kafka版本:0.10.1.0

broker端JVM参数设置:
-Xmx8G -Xms8G -server -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark -XX:+DisableExplicitGC -Djava.awt.headless=true -Xloggc:/apps/service/kafka/bin/../logs/kafkaServer-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=9999

客户端机器配置:

  • CPU: 24core/2.6GHZ
  • Memory: 3G
  • Network: 1000Mb
  • OS/kernel: CentOs release 6.3 (Final)
  • Disk: 240G

5.2 不同场景测试

场景1:测试不同的副本数、min.insync.replicas策略以及request.required.acks策略(以下简称acks策略)对于发送速度(TPS)的影响。

具体配置:一个producer;发送方式为sync;消息体大小为1kB;partition数为12。副本数为:1/2/4;min.insync.replicas分别为1/2/4;acks分别为-1(all)/1/0。

具体测试数据如下表(min.insync.replicas只在acks=-1时有效):

acksreplicasmin.insync.replicasretriesTPS
-111028511.3
-121022359.5
-122022927.4
-141016193.9
-142016599.9
-144016680.3
01N/A045353.8
02N/A046426.5
04N/A046764.2
11N/A033950.3
12N/A032192.2
14N/A032275.9

测试结果分析:

  • 客户端的acks策略对发送的TPS有较大的影响,TPS:acks_0 > acks_1 > ack_-1;
  • 副本数越高,TPS越低;副本数一致时,min.insync.replicas不影响TPS;
  • acks=0/1时,TPS与min.insync.replicas参数以及副本数无关,仅受acks策略的影响。

下面将partition的个数设置为1,来进一步确认下不同的acks策略、不同的min.insync.replicas策略以及不同的副本数对于发送速度的影响,详细请看情景2和情景3。

场景2:在partition个数固定为1,测试不同的副本数和min.insync.replicas策略对发送速度的影响。

具体配置:一个producer;发送方式为sync;消息体大小为1kB;producer端acks=-1(all)。变换副本数:2/3/4; min.insync.replicas设置为:1/2/4。

测试结果如下:

replicasmin.insync.replicasTPS
219738.8
229701.6
318999.7
329243.1
419005.8
428216.9
449092.4

测试结果分析:副本数越高,TPS越低(这点与场景1的测试结论吻合),但是当partition数为1时差距甚微。min.insync.replicas不影响TPS。

场景3:在partition个数固定为1,测试不同的acks策略和副本数对发送速度的影响。

具体配置:一个producer;发送方式为sync;消息体大小为1kB;min.insync.replicas=1。topic副本数为:1/2/4;acks: 0/1/-1。

测试结果如下:

replicasacksTPS
1076696
2057503
4059367
1119489
2120404
4118365
1-118641
2-19739
4-19006

测试结果分析(与情景1一致):

  • 副本数越多,TPS越低;
  • 客户端的acks策略对发送的TPS有较大的影响,TPS:acks_0 > acks_1 > ack_-1。

场景4:测试不同partition数对发送速率的影响

具体配置:一个producer;消息体大小为1KB;发送方式为sync;topic副本数为2;min.insync.replicas=2;acks=-1。partition数量设置为1/2/4/8/12。

测试结果:

测试结果分析:partition的不同会影响TPS,随着partition的个数的增长TPS会有所增长,但并不是一直成正比关系,到达一定临界值时,partition数量的增加反而会使TPS略微降低。

场景5:通过将集群中部分broker设置成不可服务状态,测试对客户端以及消息落盘的影响。

具体配置:一个producer;消息体大小1KB;发送方式为sync;topic副本数为4;min.insync.replicas设置为2;acks=-1;retries=0/100000000;partition数为12。

具体测试数据如下表:

acksreplicasmin.insync.replicasretries测试方法TPS数据落盘出现错误
-1420发送过程中kill两台broker12840一致(部分数据可落盘,部分失败)错误1
-142100000000发送过程中kill两台broker13870一致(消息有重复落盘)错误2
-142100000000发送过程中kill三台broker,之后重启N/A一致(消息有重复落盘)错误2、3、4

出错信息:

  • 错误1:客户端返回异常,部分数据可落盘,部分失败:org.apache.kafka.common.errors.NetworkException: The server disconnected before a response was received.
  • 错误2:[WARN]internals.Sender - Got error produce response with correlation id 19369 on topic-partition default_channel_replicas_4_1-3, retrying (999999999 attempts left). Error: NETWORK_EXCEPTION
  • 错误3: [WARN]internals.Sender - Got error produce response with correlation id 77890 on topic-partition default_channel_replicas_4_1-8, retrying (999999859 attempts left). Error: NOT_ENOUGH_REPLICAS
  • 错误4: [WARN]internals.Sender - Got error produce response with correlation id 77705 on topic-partition default_channel_replicas_4_1-3, retrying (999999999 attempts left). Error: NOT_ENOUGH_REPLICAS_AFTER_APPEND

测试结果分析:

  • kill两台broker后,客户端可以继续发送。broker减少后,partition的leader分布在剩余的两台broker上,造成了TPS的减小;
  • kill三台broker后,客户端无法继续发送。Kafka的自动重试功能开始起作用,当大于等于min.insync.replicas数量的broker恢复后,可以继续发送;
  • 当retries不为0时,消息有重复落盘;客户端成功返回的消息都成功落盘,异常时部分消息可以落盘。

场景6:测试单个producer的发送延迟,以及端到端的延迟。

具体配置::一个producer;消息体大小1KB;发送方式为sync;topic副本数为4;min.insync.replicas设置为2;acks=-1;partition数为12。

测试数据及结果(单位为ms):

发送端(avg)发送端(min)发送端(max)发送端(99%)发送端(99.99%)消费端(avg)消费端(min)消费端(max)消费端(99%)消费端(99.99%)
1.71511573291.6461288472

各场景测试总结

  • 当acks=-1时,Kafka发送端的TPS受限于topic的副本数量(ISR中),副本越多TPS越低;
  • acks=0时,TPS最高,其次为1,最差为-1,即TPS:acks_0 > acks_1 > ack_-1;
  • min.insync.replicas参数不影响TPS;
  • partition的不同会影响TPS,随着partition的个数的增长TPS会有所增长,但并不是一直成正比关系,到达一定临界值时,partition数量的增加反而会使TPS略微降低;
  • Kafka在acks=-1,min.insync.replicas>=1时,具有高可靠性,所有成功返回的消息都可以落盘。

关于本文

本文内容由我组(唯品会消息中间件团队)成员收集,本人整理撰稿,首先在唯技术上发表。
ps:团队招人,欲来可私信~

作者:u013256816 发表于2017/5/2 19:19:32 原文链接
阅读:3 评论:0 查看评论

买买买的最终,不过是在交智商税

$
0
0

图片发自简书App

《极简》这本书的作者是乔舒亚.贝克尔,他是一个消费主义的反对者,极简主义生活的提倡着。

如今极简这个词很多都不陌生,"断舍离"这三个字也被众人所知,此类书籍、文章更是数不胜数,但是这本书之所以如此畅销,在我看来,是其从心理学的角度对"消费主义"这种心理的准确分析。

网络上一直有两个很火爆的问题,一个是:你买过最成功的东西是什么?

另一个:你买过最后悔的东西是什么?

有趣的是,亚马逊的kindle在这两个排行榜中都名列前茅,它被很多人评价为最成功的物品,又被不少人评为买过最后悔的物品,甚至,我甚至都怀疑做出这两种回答的是同一波人。

生活中不止kindle,许多物品我们买的时候都觉得无比成功,待到过一段时间,都成了可有可无的东西。

记得有次去上海玩,遇到一个多年不见的同学,刚到他租的房子没一会,他就给我展示自己各种各样的电子产品,印象很深的就是他有两台电脑,一个笔记本,一台台式,还都是苹果的,加起来近了2W块。展示完,我就问他,这笔记本你平时用吗。

他毫不犹豫的说:"用个毛,我是写程序的,台式明显比笔记本好用,买这东西除了看和在你面前装逼,就没什么作用了,还有这台式,我平时根本不玩大型游戏,要那配置也是完全浪费。"

我相信在我们生活中人人都有这样的经历,不少东西买回来,除了压箱底和向朋友展示基本没太大作用,而且这些东西的价格都不便宜。

那么我们为什么会去买这些东西呢?

根据马斯洛理论,人的发展分为五个阶段:生存需求、安全需求、社交需求、自我实现、自我超越。

父母一辈喜欢囤积东西,是因为他们经历过饥荒的年代,他们意识中一直有对生存的恐惧,总想着留着所有东西,说不定哪天就会用到,因此东西越积越多。

但是现在这个时代,只要是个人稍微勤奋点,生存都不是问题,囤积那么多的东西真有必要吗?

答案毋庸置疑是没有。

买买买的本质是因为社交需求,科技发展,让大多人摆脱了生存需求,年轻人也不必过早忧虑安全需求,人就进入第三阶段——社交需求。

社交需求是人和人打交道,人与人相交都想赢得认同、尊重,在交往中占据优势。这其中必然会牵扯到物质比较,这个过程中物质比较"输"的一方会觉得羞耻。我们古话讲"知耻而后勇",因此一定要拥有更好的物质比过别人才行。

就好比看一部最新电影,你正给人谈的眉飞色舞,忽然有个家伙说她/他早看过了。然后呢?下次刚出新电影,你为了比过他,立马去看,她/他也如此,你们这次交手,又是不分胜负,还弄的你们都很气愤,甚至最后两人绝交。买东西也是一样,你有多少次是看着别人拥有,不想被比下去才买的?

虚荣、嫉妒、忧虑……等等一切影响幸福负面因素就在人与人的社交中产生。这些可以统称为"需求",有了需求,必然就会有满足你需求的东西出现。

各个产品的制造商,广告商都是心理学的牛人,更是知识储备过硬的专家。他们利用人的这种社交需求心理,制造出一件件看似"高逼格"产品,再通过广告,让你觉得这些都是你的必需品。

他们用进口化妆品、进口奶粉、打折、超实惠这种广告语来刺激着你的消费需求,让你产生购买的欲望。

实际上这些真的是生活必需品吗?

毫无疑问,买买买的最终结局,大部分都是交智商税。这些都是广告商和制造商,他们利用信息的不对等性和人的社交需求的心理,做出的稀缺假象。

很多人往往都觉得自己很睿智,没有被广告诱导,这说明不了你聪明,这只能说明广告商越成功。

简单的就像饥饿营销的方法,复杂点的就如商场布置,更是穷尽心思。商场主食一般都在入口的对角线,当你进入时,必然要穿越整个商场;最贵重的产品,往往都放在最显眼的位置,甚至在这些地方的地面都有一定坡度,来延长你的存留时间,这些费尽心思的方法数不胜数……

再如,这种广告语《不要让这座城市就住你的青春,却留不住你》、《我最害怕读书的人》。说句不好听的话,即使你把这两篇文章全看完,你也发现不了前一个是卖房子的广告,后一个是卖书的广告。

再如,中国某学生用娃哈哈矿泉水做出韩国天价护肤品的效果,而且经过分析两者效果真差不多,唯一区别一个一百多块,一个只用两块钱。

看到这里,你肯定记住了娃哈哈矿泉水和想看上边两篇文章,那么,你就落入我这个广告的陷阱。当然,这里只是开个玩笑。

《极简》这本书中对"消费主义"心理的分析的确精彩,可以说是本书最值得看的部分。

消费主义提倡的是"消费主义=幸福",而极简主义者正好相反,他们认为消费主义不等于幸福。

在我们消费时,智商税是必须交的税,也不必羞愧,只是别滥交。最起码交了智商税你得感觉到快乐。引用书中一句精彩的话

购买是给你带来自由,而不是带来负担,比如房贷。

如果你严重拥有这种"消费=幸福"的心理,你的确该看下《极简》这本书。看看极简主义的生活,试着将你认为的"必需品"一点一点清理出去。

这本书中不仅给了清理方式,还给了清理渠道,比如最好的就是做慈善。别以为做慈善是有钱人的专利,现在,各大城市都有为贫困地区捐衣、捐物的服务站,精简出去的东西可以捐出去,还能帮你赢得一个慈善家的美名。

《极简》这本书讲了什么是极简,怎么极简,无论他写的如何精彩,这些都只是过程,最终的目的是极简带来的好处。

极简主义最大的好处就是让你少做无用功,把时间用在有用的地方。

如果坚持极简主义,随着物品和物品需求减少,我们购物和维护物品的时间会相应减少,为了购买物品工作的时间也会减少,与此同时,自己自由支配的时间会增多。

就像精简衣服鞋子,你会减少逛淘宝的时间;精简了房子、车子等奢侈品的压力,你会减少更多的工作时间。你会空余出更多的时间去陪父母、陪孩子、陪亲近的人,最重要的是陪自己。

这也是极简的本质所在,把空余出来的时间留给自己,找到自己的价值,并将其发扬光大,这才是人生的追求。而不是极简出来的时间,无所事事,无病呻吟。

最后,客观来讲,《极简》这本书本质上还是写给消费主义者的书,而且是拥有一定物质基础的人,作者也是针对美国。

实际上,在国内大多人根本都没达到需要极简的地步。他们拥有大量自由的时间,却无所事事,不知如何支配。

反而是消费主义的观念,消费主义=物质的观念,能让他们进步。

借用别人的一句话:大多人努力程度之低,根本没到拼运气的地步。

这里我想说的是:大多人拥有物质之少,根本每到极简的条件,还是好好努力,好好奋斗,先成为一个消费主义者。

Viewing all 15896 articles
Browse latest View live


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