注册
环信即时通讯云

环信即时通讯云

单聊、群聊、聊天室...
环信开发文档

环信开发文档

环信FAQ

环信FAQ

集成常见问题及答案
RTE开发者社区

RTE开发者社区

汇聚音视频领域技术干货,分享行业资讯
技术讨论区

技术讨论区

技术交流、答疑
资源下载

资源下载

收集了海量宝藏开发资源
iOS Library

iOS Library

不需要辛辛苦苦的去找轮子, 这里都有
Android Library

Android Library

不需要辛辛苦苦的去找轮子, 这里都有

环信移动客服助力58到家打造一站式客服体验闭环

近日,环信B轮融资及移动客服新品发布会在北京顺利举行。发布会上,环信CEO刘俊彦宣布,环信成功获得B轮1250万美元融资,同时环信CTO马晓宇宣布环信移动客服3.0产品正式上线,主打移动、智能、全渠道。而环信将以“连接人与商业”为愿景,开启万亿级企业云服务市场...
继续阅读 »
近日,环信B轮融资及移动客服新品发布会在北京顺利举行。发布会上,环信CEO刘俊彦宣布,环信成功获得B轮1250万美元融资,同时环信CTO马晓宇宣布环信移动客服3.0产品正式上线,主打移动、智能、全渠道。而环信将以“连接人与商业”为愿景,开启万亿级企业云服务市场。


113R92H4-0.png



58到家客服总监王辉现场解读环信移动客服是怎么服务客户的

58到家客服总监王辉表示:“58到家原来只有呼叫中心一条服务渠道,对及时响应问题做得非常不好,虽然我们服务水平一直维持在85%以上,但是我们仍有客户打不进来电话,这些客户意味着订单的损失。O2O行业特点就是客户要求及时响应,快速解决,同时要满足任何下单需求。接入环信移动客服以后解决了58到家业务量暴增导致客服资源严重不足的问题,同时环信移动客服支持全渠道接入把服务渠道统一,把客户引流到在线客服上,提供了一站式的客户服务体验解决方案。同时帮助58到家把客服部门完成了从成本中心向盈利和营销中心的变革。”


113R95c2-1.png



据悉,环信移动客服产品已经有国美在线、58到家、楚楚街9块9等大批互联网巨头企业签约,累计签约客服座席已经达4万个,据艾媒咨询《2015年中国移动客服市场发展研究报告》显示,环信移动客服在新兴的移动客服市场占有率第一。

环信CTO马晓宇介绍,环信之所以受到市场如此欢迎,是因为环信移动客服是有别于传统客服的划时代产品。在环信CTO马晓宇看来,客服经历了三个时代传统callcenter的时代,互联网尚未普及,电话客服客户等待时间长,客户服务人员压力大,工作重复内容多;到了PC网页客服时代,随着互联网的普及,网页客服的成本明显小于电话客服,客户服务人员工作量较小,客户满意度有所提高,但相应依然不能及时有效;当今,随着智能手机用户的快速增长,客户要求能够随时在移动端获取咨询服务。移动端客服的重要性将不断上升。移动客服时代随之到来。

然而在环信基于IM长连接技术推出移动客服之前,业界并没有一款真正适合移动端和社交化时代的可靠的云端移动客服产品。此前,业界普遍存在的是两种移动客服产品,一类是工单客服,可以被视为移动客服1.0,基于工单方式和客服交互,技术实现简单,但无法做到实时交互,沟通效率低,用户体验差。另一类是轮询技术客服,可以被视为移动客服2.0,基于HTTP轮询技术,通过不断向服务器发送查询请求来准实时的收取消息。不能做到实时收取消息,流量和电量消耗高,APP在后台时无法接收消息,容易丢失客户。

而环信推出的移动客服产品是业界唯一一款具有IM长连接技术的移动客服产品,这被视为更适合移动端的客服产品。这一类型的移动客服可以定义为“交互型智能移动客服”,可以被视为移动客服3.0,以环信为代表的这一类新型移动客服是基于亿级IM长连接,系统稳定;智能机器人技术可处理大部分重复问题。相较环信移动客服的能力,传统的电话、工单、轮询客服在移动端和社交趋势下就显得捉襟见肘,有些OUT了。

环信移动客服实现了跨平台多渠道接入:支持 App、微信公众账号、微博、网页等,均可以快速统一接入客户服务后台管理。

其次,满足了开放性与自定义信息:这种新型的移动客服实现了代码开源、UI开源,还提供多套UI模版,便于与APP快速集成,平滑接入。同时第三方集成功能也很强大,可与第三方工单、知识库、CRM 系统等进行扩展集成。

第三,富媒体消息交互体验:移动客服平台提供了基于IM技术的类似微信体验的友好富媒体消息交互,不仅可以实时收发文字、表情,还可以即时收发图片、位置、实时语音、还可自定义消息,大大方便了与客户的沟通交流。

第四,精准客户画像功能:移动客服的IM沟通帮助企业辅助判断客户需求与诉求,通过自定义客户分类标签,客户再次访问时可获知客户的类型。并且还支持自定义会话小结,根据会话小结统计会话的分类,通过会话小结追踪客户诉求。通过轨迹分析功能,即通过发送客户访问页面的轨迹,判断客户意图,获取客户个性化细节,了解客户基础信息,分析客户行为,可以极大提高订单效率。

第五,“智能机器人+智能知识库”:作为一套智能化的客服系统,让企业建立基于业务的智能知识库,智能辅助归结业务信息和应答客户信息,历史常见问题系统梳理,提高客服效率。“智能机器人+智能知识库”组合目前可自动回复80%常见问题,随着智能知识库的不断训练,这一比例能够提高到90%。 收起阅读 »

SDK浪潮来袭:阿里百川VS环信VS京东万象VS腾讯SDK

 几大互联网巨头在加紧企业级的布局,他们的进入让企业级更加热闹。谁说他们没有企业级的基因?谁说抢占企业级市场一定需要有企业级的基因呢?基因本来就是一个扯淡的话题,或许是BAT他们明修栈道,暗渡陈仓的“阴谋”呢? 随 着互联网时代的不断发展,定会有越来越多...
继续阅读 »


38121439790295.jpg


 几大互联网巨头在加紧企业级的布局,他们的进入让企业级更加热闹。谁说他们没有企业级的基因?谁说抢占企业级市场一定需要有企业级的基因呢?基因本来就是一个扯淡的话题,或许是BAT他们明修栈道,暗渡陈仓的“阴谋”呢?

随 着互联网时代的不断发展,定会有越来越多的企业会开放自己的核心能力,BAT也不会例外!但这次腾讯、阿里和京东相继推出重磅的开放策略,一切都是为了连 接。只不过阿里的百川和京东的万象互联已经发布面向公众。而腾讯的SDK开放还没有公之于众,但是腾讯已经邀请众开发合作伙伴正在测试,相信不久将会发 布,这将是颗核弹,杀伤力十足。

接下来,我们就来一一剖析大佬们的“阴谋”:
微信将开放IM的SDK


先 来看腾讯旗下的微信是如何通过开放IM的SDK丰富企业号的功能,赋予企业号更多的想象空间。之前企业号是以消息的驱动模式,一个流程进来,只能机械地顺 着消息来完成工作,这也是很多人诟病的地方,一是认为企业号的入口太深;二是觉得企业号是消息架构而非应用架构,从此认为企业号并不足以承载企业业务应 用。

但是随着 SDK的开放,企业号内部就拥有了im的能力,开发者也会进一步挖掘SDK的潜力,以支持企业在处理流程的任何节点可以与相关的人发起互动和协作,此大招 祭出之后,相信会有不少im厂商会立马紧张起来。我也不仅要担心专业的im厂商,他们接下来如何应对?也许我的担心是多余的。

随着腾讯全系列的im SDK的开放,势必有很多APP会选择与腾讯的SDK相连接,让自己的APP瞬间具有微信一样的通讯能力,微信也将无处不在,渗透到企业内部。通过Im 与企业和APP的相连,完成以im为基础应用布局企业级大业。

当然这也不是所有的人都会使用腾讯所开放的SDK,至少竞争对手是不会使用的。一些感受到腾讯威胁的创业者或者企业也不太会用。大型企业目前还是更喜欢私 有部署的方式,通过自己的App来完成一站式的沟通和协作,但是随着微信im SDK的开放,这样的情况应该会有所改变。

阿里百川即时通讯VS环信即时通讯云

自阿里推出阿里百川计划,试图打造一个开放的移动互联网应用开发及商业化平台之后,会对互联网行业有着什么样的影响呢?

1. 大幅度降低了互联网应用开发的门槛,通过提供整理的SDK包,让整个产业链流程标准化、透明化,分工合理,专业的人做专业的事。

2. 阿里百川平台是一个集成技术、商业优势资源的开放平台、创业平台,提供一个完整的解决方案,覆盖技术、数据服务、商业化,还将依托淘宝平台,授权APP导入淘宝O2O链条,实现淘宝账号登陆,接入交易体系,打通推广、支付、后台管理的全链条。

3. 瓦解现有移动互联网入口格局。毋庸置疑,百川将依托阿里大电商平台,通过扶持APP市场,分流垂直应用,瓦解过去的移动互联网入口格局。

而成立于2013年在上线一年就完成4轮融资的环信即时通讯云无疑也是阿里百川强有力的竞争对手。

环信的优势在于:

1. 环信在即时通讯云领域有绝对优势和深度积累,能够保障海量移动设备长连接的高并发,同时提供良好的社交交互体验。

2. 环信一直就是在做开放平台,建设生态体系,环信能够像zendesk一样,引入全产业链优质资源支持客户的未来无限扩展需求

京东的万象互联API PaaS

在互联网+大背景的影响下,各行各业的企业对于自身数据的开放已有了全新的认识,逐渐从数据封闭向数据开放合作进行创新性的尝试与转型,借助全维度、全生 命周期的大数据体系来创造巨大价值。数据时代也已从单一的业务数据收集和数据分析,逐步跨入数据开放、数据共享的数据2.0时代。

而京东万象正是京东云在已有的云计算平台基础上围绕数据提供方、数据需求方、数据服务方等多方,构建了以数据开放、数据共享、数据分析为核心的综合性数据开放平台。

京东万象将帮助数据的提供方与需求方进行数据对接,解决企业之间的数据缺失问题,完善数据价值,提升企业效率。平台本身会对接多维度的丰富数据, 保证数据的安全性与接入效率,是企业数据输出与流入的最佳渠道。与此同时,京东万象还将京东内部的企业总线服务云化并对外提供,帮助企业实现内部各应用系 统之间的数据互联互通,解决企业内部数据孤岛以及多系统之间的数据整合问题。

企业的数据通过API的形式接入京东万象,在数据安全的基础上,通过数据API的共享和交易实现双方的数据价值交换。同时京东万象平台提供完善的数据交易和数据管理功能,充分满足数据上下游客户的需求,保障数据共享的安全性和流畅性。

京东万象目前主推的是金融行业的相关数据,现已覆盖了包括个人和企业征信报告,黑名单数据,失信数据等金融数据,此类数据给互联网金融创新企业带来巨大的数据共享价值。

京东万象正在逐步开放和引入电商和政府相关数据,凭借京东自身品牌和丰富的电商数据,势必会成为行业权威大数据的开放共享平台。

相信在不久的将来,我们就能看到这些互联网大佬们为我们带来的企业级的布局pk饕餮盛宴! 收起阅读 »

环信ONE SDK架构介绍

环信即时通讯SDK自2014年6月正式发布2.0版本至今已走过一个年头,从基本的单聊功能,到群聊功能,再到聊天室的实现,SDK不管是功能,稳定性,还是易集成性都在一步一步的走向完善与稳定,感谢开发者们给与提供的反馈与帮助,使我们的SDK迅速的在诸多方面得到提高...
继续阅读 »
环信即时通讯SDK自2014年6月正式发布2.0版本至今已走过一个年头,从基本的单聊功能,到群聊功能,再到聊天室的实现,SDK不管是功能,稳定性,还是易集成性都在一步一步的走向完善与稳定,感谢开发者们给与提供的反馈与帮助,使我们的SDK迅速的在诸多方面得到提高与改进。

随着现在物联网的兴起,环信现在的SDK的架构对应对未来物联网平台还是略有不足,使我们不得不在今年年初就考虑到如何去改善我们的架构,使之能够较容易的适配到各个主流平台和物联网平台。

现在的Android, IOS, SDK都是各自维护了自己逻辑,给2.0开发带来了很多不便,相同的功能需要维护两份代码,同一个bug要在不同的平台修复两次,由于不同平台的实现,导致Android,IOS架构设计不统一,API不一致,再加上在不同的平台开发的工程师之间由于不同的实现导致沟通问题诸多,这样导致的问题就是,不同平台的开发进度大多数情况下都不相同。

所以针对以上的问题,我们今年年初就计划我们SDK3.0的开发,我们称之为ONE SDK,基本的理念就是我们实现共同IM 内核,使代码可被不同的平台最大化的重用,但是设计和实现却是面临着巨大的挑战,如何适配到不同的平台,如何最大化的重用代码都是要面临的问题。

ONE SDK 架构设计
总体设计


20150806175739849.png



ONE SDK的设计总体分为3层,平台层-Platfrom layer,适配层-Porting layer,核心层-Common layer.

平台层会根据各平台的不同,实现不同的接口,开放符合平台层的API给第三方开发者。

适配层主要要适配到ONE SDK 核心层所需要的一些接口,起到承上启下的作用。

核心层主要提供IM 功能和业务逻辑,保证最大化的代码重用。

考虑到跨平台共享代码,我们ONE SDK主要是用C++这种跨平台的语言来实现。

平台层

我们计划要支持的平台为Android,IOS,MAC OS,Windows,Windows phone, Linux,Embedded Linux, 还有较为广泛应用的物联网平台-IOT OS。

我们会针对不同平台提供给开发者,和平台一致API规范,使各平台的开发者无难度的集成SDK,例如我们会提供JAVA给Android,Objective-C 给IOS和MAC, C++ 给Linux, IOT平台。


适配层


20150806181806647.png



适配层主要就是各平台需要实现核心层所需要的一些接口类例如,上图给出的线程模型,定时器模型,数据库模型,还有HTTP模型。

这些模型都是和平台相关的,例如有的平台提供sqllite的访问,有的没有,有的线程模型和定时器模型都有自己的实现方式例如物联网IOT OS,所以实现了上述的模型,就可以使核心层可以工作,不过也可能会遇到一些问题,这都会在具体的实现中会具体的应对,但结构是清晰的。

其实例如Android,Linux, IOS, Mac OS,Windows都是支持C++11,也就是说都是支持C++11所提供的线程模型,所以这几个操作系统的线程模型的实现应该是同样的,所以代码是可以共享的, 但是数据库Anroid NDK是不支持sqlite访问,所以这部分Android有两种策略一个是集成sqlite源代码,二是回调给JAVA层,前一种策略是代码逻辑清晰,但坏处就是增加了代码量。

还有就是HTTP模型,默认的android,iOS,Mac OS,windows都有自己的API提供,所以ONE SDK策略就是如果有平台有原生的HTTP API 支持我们就会尽量用平台的,但是类似linux,就需要第三方库的支持例如libcurl.

所以综上所述适配层的意义重大,保证我们ONE SDK代码共享最大化的目的。

核心层

核心层是具体实现环信相关功能的模块,它里面也包含的几个部分,核心业务逻辑API实现,抽象协议层,和独立实时音视频模块。

Core Common


20150806191008517.png



业务逻辑层,负责提供基本的IM功能。

会话管理
消息收发
登录鉴权
连接管理

Audio/Video Call

实时音视频模块是一个独立的模块,可以单独存在,这样可以灵活处理,不需要时不用加载此模块。


20150806191418690.png



实时音视频
多人语音

Abstract Protocol Layer

协议抽象层,主要用来处理具体的IM底层协议,这层是比较独立的主要实现环信定义的IM传输协议,也为为日后协议改造,扩展提供承上启下的作用。


20150806192326297.png



  • 定义抽象消息载体

  • 基本的消息发送

  • 基本的消息回调

  • 定义基本的通知


上述ONE SDK架构是我们计划要实现的,现在一些基本的设计已经在Linux SDK上得到了实现,下面让我们继续了解下Linux SDK。

Linux SDK

经过几个月的开发,我们已经推出了Linux SDK测试版,有兴趣的开发者可以小试一下去我们的官网 http://www.easemob.com/downloads 下载。

通过EMChatClient Facade类开发者可以访问到任何IM停供的功能

  • 登录注册

  • 消息收发,支持TXT, IMAGE, VIDEO, AUDIO, FILE, LOCATION, 类型的message

  • 会话管理

  • 联系人管理



在开发linux SDK的时候我们利用了C++11提供的很多优良feature,例如lambda,shared ptr,thread等较新的功能,通过这些功能的使用,让我们能够迅速并且高效的开发出Linux SDK测试版。

Lambda是我们的代码逻辑阅读起来更清楚,shared ptr使我们对内存的管理更为简单,thread的使用使我们能迅速的建立好线程模型,加快开发的速度。

我们在Linux SDK porting layer实现了线程模型,数据库模型,定时器模型和HTTP模型

  • 线程模型,利用C++11的thread进行封装

  • 数据库模型,使用Linux的sqlite

  • HTTP模型,我们使用了Libcurl进行了封装

  • 定时器模型,我们使用了C++11进行了封装



我们对Linux SDK还在紧锣密鼓,夜以继日的紧张开发中,群组和实时音视频功能还在开发中,但是在开发过程中我们也遇到了些的问题,例如不linux平台对库的支持也不尽相同,所以势必会对我们的架构有些改变,有些重构的工作也在进行中,我们希望8月底能够实现大部分的功能。

选择linux SDK为开始主要是因为大多数的嵌入式智能平台还是以linux为主,所以首先开发Linux 版SDK也是必先的一步,感谢我们的工程师,我么的Linux SDK已经能够支持树莓派的开发环境,这对于很多开发者来说确实是个好消息。

如果想了解如何集成Linux SDK请参考环信IM Linux SDK 集成说明

展望

Linux SDK 只是实现我们ONE SDK 的第一步,我们下半年还要实现基于ONE SDK 的Android ,IOS,MAC OS, 还有主流物联网平台的SDK,通过借鉴Linux SDK的实现方式,我们认为实现ONE SDK是可以做到的。

我们还会进一步和主要IOT平台供应商合作,实现某个具体IOT平台的环信SDK,使环信即时通讯平台生态圈更加壮大。 收起阅读 »

环信Linux SDK测试版正式发布

万物互联时代,物联网创新方兴未艾。今日环信即时通讯云宣布环信Linux SDK测试版正式发布,作为环信生态圈重要的布局,环信Linux SDK测试版使用C++开发,原生支持Linux操作系统,适用于基于Linux系统的智能设备和硬件,在各种智能硬件设备和嵌入式...
继续阅读 »
万物互联时代,物联网创新方兴未艾。今日环信即时通讯云宣布环信Linux SDK测试版正式发布,作为环信生态圈重要的布局,环信Linux SDK测试版使用C++开发,原生支持Linux操作系统,适用于基于Linux系统的智能设备和硬件,在各种智能硬件设备和嵌入式系统上都可以快速移植和接入环信IM的通讯能力,全面对接物联网


165_150805154310_1.jpg



根据环信CEO刘俊彦描述的愿景:未来,当一台物联网时代的电冰箱坏了后,你还需要打开网页搜索厂家联系方式,然后打400电话解决问题吗?当然不!你只需要按冰箱上的一个红色小按钮,就可以立即接通厂家的客服,而且是视频的,客服可以一键将修复后的设置推送到冰箱上来。

不仅如此,客服还能够根据电冰箱存储食品提醒用户是否购买食材,保存和处理食材。如果和其他健康产品联系起来,还能提醒用户是否注意饮食健康等,成为“监测”和“服务”的前端设备。

这并不是很遥远的未来,万物互联正在成为现实。回到物联网冰箱的场景。当消费者按动红色按钮时,在按钮的背后是一个“环信Inside”芯片,烧制了环信的Linux版SDK。当消费者和客服视频通话时,使用的是环信提供的基于IP网络的视频通讯能力,而客服使用的工作后台,则是环信移动客服提供的。

今日,环信的Linux版移动客服SDK已经正式发布。“环信Inside”芯片也已经在实验室中紧张调试,生态圈也正在进一步建立中。环信已经在“连接人与商业”的愿景下又迈出了坚实的一步。


165_150805154519_1_lit.jpg



目前环信Linux SDK测试版版本支持单聊,各种富媒体消息,如文本消息,图片消息,语音/视频片段等,好友管理和黑名单管理等功能,下一个版本将全面支持群组,聊天室和实时音视频,敬请期待! 收起阅读 »

Ubuntu实现树莓派交叉编译

一、交叉编译 在一个平台上生成另一个平台上的可执行代码。为什么要大费周折的进行交叉编译呢?一句话:不得已而为之。有时是因为目的平台上不允许或不能够安装所需要的编译器,而又需要这个编译器的某些特征;有时是因为目的平台上的资源贫乏,无法运行所需要的编译器;有...
继续阅读 »
一、交叉编译

在一个平台上生成另一个平台上的可执行代码。为什么要大费周折的进行交叉编译呢?一句话:不得已而为之。有时是因为目的平台上不允许或不能够安装所需要的编译器,而又需要这个编译器的某些特征;有时是因为目的平台上的资源贫乏,无法运行所需要的编译器;有时又是因为目的平台还没有建立,连操作系统都没有,根本谈不上运行什么编译器。

要进行交叉编译,我们需要在主机平台上安装对应的交叉编译工具链(cross compilation tool chain),然后用这个交叉编译工具链编译源代码,最终生成可在目标平台上运行的代码。     
常见的交叉编译例子如下:
  1. 在Windows PC上,利用ADS(ARM 开发环境),使用armcc编译器,则可编译出针对ARM CPU的可执行代码。
  2. 在Linux PC上,利用arm-linux-gcc编译器,可编译出针对Linux ARM平台的可执行代码。
  3. 在Windows PC上,利用cygwin环境,运行arm-elf-gcc编译器,可编译出针对ARM CPU的可执行代码。

 
二、名词解释
 
Linux下的大多数软件包都使用Autoconf/Automake工具自动生成Makefile,只要使用“./configure”,“make”,“make install”就可以把程序安装到Linux系统中去了。编译第三方源代码时,可以看下工程中的readme和install文件,一般情况下都会写编译步骤。
 
 1、./configure 常用参数  [--build] | [--host] | [--target] | [--prefix] | [--help]

注意:host和--host不是一个意思,host是指宿主机,即编辑和编译程序的平台,是个名词;--host是设置执行文件所运行的主机,是个动词。
 
>> ./configure: 用来生成对应的 Makefile;
 
>> --build: 执行代码编译的主机,正常的话就是你的主机系统。若无指定使用host的值;
>> --host: 编译出来的二进制程序所执行的主机, 交叉编译工具链的前缀。因为绝大多数是如果本机编译就本机执行,所以这个值就等于build。但是交叉编译的时候build和host需要设置不同值,用host指定运行主机,即host != build的时候编译才是交叉编译。若无指定将会运行`config.guess'来检测;
 
>> --prefix: 安装目录,比如 --prefix=/usr 意思是将该软件安装在 /usr 下面,执行文件就会安装在 /usr/bin (而不是默认的 /usr/local/bin),资源文件就会安装在 /usr/share(而不是默认的/usr/local/share);
 
>> --help: 查看参数;
 
>> --target: 这个参数比较特殊,表示需要处理的目标平台名称,主要在程序语言工具如编译器和汇编器上下文中起作用,若无指定使用host的值。一般用来编译工具,比如给arm开发板编译一个可以处理mips程序的gcc,那么--target=mips;
 
>>>> 举例说明:编译gcc
 
>> ./configure --build=i386-linux --host=arm-linux --target=mipsel-linux --prefix=$(pwd)/_install
 
用i386-linux的编译器进行gcc的编译,编译出的gcc运行在arm-linux, 编译结果存放到$(pwd)/_install路径下,编译出的gcc用来编译能够在mipsel-linux下运行的代码。
 
2、Makefile包含了一些基本的预先定义的操作: 

  • >>make: 根据Makefile编译源代码,连接,生成目标文件,可执行文件;

  • >>make clean: 清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件;

  • >>make distclean: 类似make clean,但同时也将configure生成的文件全部删除掉,包括Makefile;

  • >>make test / make check: 检查make,确保make没有出错,一般在make install之前执行;

  • >>make install: 将编译成功的可执行文件安装到指定目录中,一般为/usr/local/bin目录;

  • >>make dist: 产生发布软件包文件(即distribution package)。这个命令将会将可执行文件及相关文件打包成一个tar.gz压缩的文件用来作为发布软件的软件包。它会在当前目录下生成一个名字类似“PACKAGE-VERSION.tar.gz”的文件。PACKAGE和VERSION,是我们在configure.in中定义的AM_INIT_AUTOMAKE(PACKAGE, VERSION);

  • >>make distcheck: 生成发布软件包并对其进行测试检查,以确定发布包的正确性。这个操作将自动把压缩包文件解开,然后执行configure命令,并且执行make,来确认编译不出现错误,最后提示你软件包已经准备好,可以发布了; 



三、交叉编译源代码

1、环境
Ubuntu
 
2、树莓派交叉编译工具安装step
 

  • step1.下载树莓派交叉编译工具https://github.com/raspberrypi/tools

  • step2. 将源码放到各用户都能share的文件夹下,如/usr/tools

  • step3. 将交叉编译工具的路径加到环境变量中,为了以后启动不用再设置,我加到了bashrc中


1 $nano ~/.bashrc
2 #在文件的末尾加上: export PATH=$PATH:/usr/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
3 $source .bashrc


  • step4. 检测是否安装成功


way1: 
$arm #双tab
显示以下内容


031616177676048.jpg

 
way2:
$arm-linux-gnueabihf-gcc -v 
#能显示正确信息

$arm-linux-gnueabihf-g++ -v
#能显示正确信息
注意:交叉编译时,如果出现arm-linux-gnueabihf-XXX找不到,确定arm-linux-gnueabihf-XXX -v 是否能输出正确信息,如果能,可以切到root下进行编译

三、编译源代码

1、在写编译脚本时,一定要确保编译器写的是交叉编译的编译器。比如比较常用的Makefile,
1 demo: $(obj)
2 $(CXX) -o $@ $^ $(LDFLAGS)
其中的CXX必须是arm-linux-gnueabihf-g++才能编译出正确的在树莓派上的可执行文件。

2、编译第三方库

如果你想设置全局的CC和CXX变量,在每次打开一个新的Terminal时,输入以下命令:
1 $export CC=arm-linux-gnueabihf-gcc  
2 $export CXX=arm-linux-gnueabihf-g++
其他的全局变量同上。

以下列几个常用的第三方库交叉编译步骤

1>> sqlite3 http://www.sqlite.org/download.html sqlite-autoconf-3081002.tar.gz

  • step1:  make clean

  • step2:  ./configure --host=arm-linux-gnueabihf --prefix=/usr/local/tools/sqlite3

  • step3:  make

  • step4:  make install



 

2>>curl http://curl.haxx.se/download.html  curl-7.43.0.tar.gz

  • step1:  make clean

  • step2:  ./configure --host=arm-linux-gnueabihf --prefix=/usr/local/tools/curl

  • step3:  make

  • step4:  make install



3>> openssl: http://www.openssl.org/source/ openssl-1.0.1p.tar.gz

  • step1: ./config no-asm shared --prefix=/usr/local/tools/openssl

  • step2:  a、修改Makefile CC=arm-linux-gnueabihf-gcc


           b、找到有-m64的地方,将-m64删除。  

  • step3: make

  • step4: make install


收起阅读 »

经验分享:PHP 安全编程建议

要提供互联网服务,当你在开发代码的时候必须时刻保持安全意识。可能大部分 PHP 脚本都对安全问题都不在意,这很大程度上是因为有大量的无经验程序员在使用这门语言。但是,没有理由让你因为对你的代码的不确定性而导致不一致的安全策略。当你在服务器上放任何涉及到钱的东西...
继续阅读 »
要提供互联网服务,当你在开发代码的时候必须时刻保持安全意识。可能大部分 PHP 脚本都对安全问题都不在意,这很大程度上是因为有大量的无经验程序员在使用这门语言。但是,没有理由让你因为对你的代码的不确定性而导致不一致的安全策略。当你在服务器上放任何涉及到钱的东西时,就有可能会有人尝试破解它。创建一个论坛程序或者任何形式的购物车,被攻击的可能性就上升到了无穷大。
为了确保你的 web 内容安全,这里有一些常规的安全准则:
  • 别相信表单
攻击表单很简单。通过使用一个简单的 JavaScript 技巧,你可以限制你的表单只允许在评分域中填写 1 到 5 的数字。如果有人关闭了他们浏览器的 JavaScript 功能或者提交自定义的表单数据,你客户端的验证就失败了。用户主要通过表单参数和你的脚本交互,因此他们是最大的安全风险。你应该学到什么呢?在 PHP 脚本中,总是要验证 传递给任何 PHP 脚本的数据。在本文中,我们向你演示了如何分析和防范跨站脚本(XSS)攻击,它可能会劫持用户凭据(甚至更严重)。你也会看到如何防止会玷污或毁坏你数据的 MySQL 注入攻击。
  • 别相信用户
假定你网站获取的每一份数据都充满了有害的代码。清理每一部分,即便你相信没有人会尝试攻击你的站点。
  • 关闭全局变量
你可能会有的最大安全漏洞是启用了 register_globals 配置参数。幸运的是,PHP 4.2 及以后版本默认关闭了这个配置。如果打开了 register_globals,你可以在你的 php.ini 文件中通过改变 register_globals 变量为 Off 关闭该功能:
register_globals = Off 
新手程序员觉得注册全局变量很方便,但他们不会意识到这个设置有多么危险。一个启用了全局变量的服务器会自动为全局变量赋任何形式的参数。为了了解它如何工作以及为什么有危险,让我们来看一个例子。假设你有一个称为 process.php 的脚本,它会向你的数据库插入表单数据。初始的表单像下面这样:
运行 process.php 的时候,启用了注册全局变量的 PHP 会将该参数赋值到 $username 变量。这会比通过 $_POST['username']或 $_GET['username'] 访问它节省击键次数。不幸的是,这也会给你留下安全问题,因为 PHP 会设置该变量的值为通过 GET 或 POST 的参数发送到脚本的任何值,如果你没有显示地初始化该变量并且你不希望任何人去操作它,这就会有一个大问题。看下面的脚本,假如 $authorized 变量的值为 true,它会给用户显示通过验证的数据。正常情况下,只有当用户正确通过了这个假想的 authenticated_user() 函数验证,$authorized 变量的值才会被设置为真。但是如果你启用了 register_globals,任何人都可以发送一个 GET 参数,例如 authorized=1 去覆盖它:
这个故事的寓意是,你应该从预定义的服务器变量中获取表单数据。所有通过 post 表单传递到你 web 页面的数据都会自动保存到一个称为 $_POST 的大数组中,所有的 GET 数据都保存在 $_GET 大数组中。文件上传信息保存在一个称为 $_FILES 的特殊数据中。另外,还有一个称为 $_REQUEST 的复合变量。要从一个 POST 方法表单中访问 username 字段,可以使用 $_POST['username']。如果 username 在 URL 中就使用$_GET['username']。如果你不确定值来自哪里,用 $_REQUEST['username']。
['post_value'];$get_value = $_GET['get_value'];$some_variable = $_REQUEST['some_value']; ?>  
$_REQUEST 是 $_GET、$_POST、和 $_COOKIE 数组的结合。如果你有两个或多个值有相同的参数名称,注意 PHP 会使用哪个。默认的顺序是 cookie、POST、然后是 GET。 推荐安全配置选项 这里有几个会影响安全功能的 PHP 配置设置。下面是一些显然应该用于生产服务器的:
  • register_globals 设置为 off
  • safe_mode 设置为 off
  • error_reporting 设置为 off。如果出现错误了,这会向用户浏览器发送可见的错误报告信息。对于生产服务器,使用错误日志代替。开发服务器如果在防火墙后面就可以启用错误日志。(LCTT 译注:此处据原文逻辑和常识,应该是“开发服务器如果在防火墙后面就可以启用错误报告,即 on。”)
  • 停用这些函数:system()、exec()、passthru()、shell_exec()、proc_open()、和 popen()。
  • open_basedir 为 /tmp(以便保存会话信息)目录和 web 根目录,以便脚本不能访问这些选定区域外的文件。
  • expose_php 设置为 off。该功能会向 Apache 头添加包含版本号的 PHP 签名。
  • allow_url_fopen 设置为 off。如果你能够注意你代码中访问文件的方式-也就是你验证所有输入参数,这并不严格需要。
  • allow_url_include 设置为 off。对于任何人来说,实在没有明智的理由会想要访问通过 HTTP 包含的文件。
  •  
  • 一般来说,如果你发现想要使用这些功能的代码,你就不应该相信它。尤其要小心会使用类似 system() 函数的代码-它几乎肯定有缺陷。
  • 启用了这些设置后,让我们来看看一些特定的攻击以及能帮助你保护你服务器的方法。
 SQL 注入攻击 由于 PHP 传递到 MySQL 数据库的查询语句是用强大的 SQL 编程语言编写的,就有了某些人通过在 web 查询参数中使用 MySQL 语句尝试 SQL 注入攻击的风险。通过在参数中插入有害的 SQL 代码片段,攻击者会尝试进入(或破坏)你的服务器。假如说你有一个最终会放入变量 $product 的表单参数,你使用了类似下面的 SQL 语句:
$sql = "select * from pinfo where product = '$product'";
如果参数是直接从表单中获得的,应该使用 PHP 自带的数据库特定转义函数,类似:
$sql = 'Select * from pinfo where product = '"'        mysql_real_escape_string($product) . '"';
如果不这样做的话,有人也许会把下面的代码段放到表单参数中:
39'; DROP pinfo; SELECT 'FOO 
那么 $sql 的结果就是:
select product from pinfo where product = '39'; DROP pinfo; SELECT 'FOO' 
由于分号是 MySQL 的语句分隔符,数据库会运行下面三条语句:
select * from pinfo where product = '39'DROP pinfoSELECT 'FOO' 
好了,你丢失了你的表。注意实际上 PHP 和 MySQL 不会运行这种特殊语法,因为 mysql_query() 函数只允许每个请求处理一个语句。但是,一个子查询仍然会生效。要防止 SQL 注入攻击,做这两件事:
  • 总是验证所有参数。例如,如果需要一个数字,就要确保它是一个数字。
  • 总是对数据使用 mysql_real_escape_string() 函数转义数据中的任何引号和双引号。
注意:要自动转义任何表单数据,可以启用魔术引号(Magic Quotes)。一些 MySQL 破坏可以通过限制 MySQL 用户权限避免。任何 MySQL 账户可以限制为只允许对选定的表进行特定类型的查询。例如,你可以创建只能选择行的 MySQL 用户。但是,这对于动态数据并不十分有用,另外,如果你有敏感的用户信息,可能某些人能访问其中一些数据,但你并不希望如此。例如,一个访问账户数据的用户可能会尝试注入访问另一个人的账户号码的代码,而不是为当前会话指定的号码。 防止基本的 XSS 攻击 XSS 表示跨站脚本。不像大部分攻击,该漏洞发生在客户端。XSS 最常见的基本形式是在用户提交的内容中放入 JavaScript 以便偷取用户 cookie 中的数据。由于大部分站点使用 cookie 和 session 验证访客,偷取的数据可用于模拟该用户-如果是一个常见的用户账户就会深受麻烦,如果是管理员账户甚至是彻底的惨败。如果你不在站点中使用 cookie 和 session ID,你的用户就不容易被攻击,但你仍然应该明白这种攻击是如何工作的。不像 MySQL 注入攻击,XSS 攻击很难预防。Yahoo、eBay、Apple、以及 Microsoft 都曾经受 XSS 影响。尽管攻击不包含 PHP,但你可以使用 PHP 来剥离用户数据以防止攻击。为了防止 XSS 攻击,你应该限制和过滤用户提交给你站点的数据。正是因为这个原因,大部分在线公告板都不允许在提交的数据中使用 HTML 标签,而是用自定义的标签格式代替,例如   和 [linkto]。让我们来看一个如何防止这类攻击的简单脚本。对于更完善的解决办法,可以使用 SafeHTML,本文的后面部分会讨论到。
function transform_HTML($string, $length = null) {// Helps prevent XSS attacks    // Remove dead space.    $string = trim($string);    // Prevent potential Unicode codec problems.    $string = utf8_decode($string);    // HTMLize HTML-specific characters.    $string = htmlentities($string, ENT_NOQUOTES);    $string = str_replace("#", "#", $string);    $string = str_replace("%", "%", $string);    $length = intval($length);    if ($length > 0) {        $string = substr($string, 0, $length);    }    return $string;} 
这个函数将 HTML 特定的字符转换为 HTML 字面字符。一个浏览器对任何通过这个脚本的 HTML 以非标记的文本呈现。例如,考虑下面的 HTML 字符串:
Bold Text
一般情况下,HTML 会显示为:Bold Text但是,通过 transform_HTML() 后,它就像原始输入一样呈现。原因是处理的字符串中的标签字符串转换为 HTML 实体。transform_HTML() 的结果字符串的纯文本看起来像下面这样:
Bold Text 
该函数的实质是 htmlentities() 函数调用,它会将 <、>、和 & 转换为 <、>、和 &。尽管这会处理大部分的普通攻击,但有经验的 XSS 攻击者有另一种把戏:用十六进制或 UTF-8 编码恶意脚本,而不是采用普通的 ASCII 文本,从而希望能绕过你的过滤器。他们可以在 URL 的 GET 变量中发送代码,告诉浏览器,“这是十六进制代码,你能帮我运行吗?” 一个十六进制例子看起来像这样:
 
浏览器渲染这个信息的时候,结果就是:
 
为了防止这种情况,transform_HTML() 采用额外的步骤把 # 和 % 符号转换为它们的实体,从而避免十六进制攻击,并转换 UTF-8 编码的数据。最后,为了防止某些人用很长的输入超载字符串从而导致某些东西崩溃,你可以添加一个可选的 $length 参数来截取你指定最大长度的字符串。 [b]使用 SafeHTML
 之前脚本的问题比较简单,它不允许任何类型的用户标记。不幸的是,这里有上百种方法能使 JavaScript 跳过用户的过滤器,并且要从用户输入中剥离全部 HTML,还没有方法可以防止这种情况。当前,没有任何一个脚本能保证无法被破解,尽管有一些确实比大部分要好。有白名单和黑名单两种方法加固安全,白名单比较简单而且更加有效。一个白名单解决方案是 PixelApes 的 SafeHTML 反跨站脚本解析器。SafeHTML 能识别有效 HTML,能追踪并剥离任何危险标签。它用另一个称为 HTMLSax 的软件包进行解析。按照下面步骤安装和使用 SafeHTML:
  • 到 http://pixel-apes.com/safehtml/?page=safehtml 下载最新版本的 SafeHTML。
  • 把文件放到你服务器的类文件夹。该文件夹包括 SafeHTML 和 HTMLSax 功能所需的所有东西。
  • 在脚本中 include SafeHTML 类文件(safehtml.php)。
  • 创建一个名为 $safehtml 的新 SafeHTML 对象。
  • 用 $safehtml->parse() 方法清理你的数据。
这是一个完整的例子:
alert('XSS Attack')";// Create a safehtml object.$safehtml = new safehtml();// Parse and sanitize the data.$safe_data = $safehtml->parse($data);// Display result.echo 'The sanitized data is 
' . $safe_data;?>
如果你想清理脚本中的任何其它数据,你不需要创建一个新的对象;在你的整个脚本中只需要使用 $safehtml->parse() 方法。 什么可能会出现问题? 你可能犯的最大错误是假设这个类能完全避免 XSS 攻击。SafeHTML 是一个相当复杂的脚本,几乎能检查所有事情,但没有什么是能保证的。你仍然需要对你的站点做参数验证。例如,该类不能检查给定变量的长度以确保能适应数据库的字段。它也不检查缓冲溢出问题。XSS 攻击者很有创造力,他们使用各种各样的方法来尝试达到他们的目标。可以阅读 RSnake 的 XSS 教程http://ha.ckers.org/xss.html ,看一下这里有多少种方法尝试使代码跳过过滤器。SafeHTML 项目有很好的程序员一直在尝试阻止 XSS 攻击,但无法保证某些人不会想起一些奇怪和新奇的方法来跳过过滤器。注意:XSS 攻击严重影响的一个例子 http://namb.la/popular/tech.html,其中显示了如何一步一步创建一个让 MySpace 服务器过载的 JavaScript XSS 蠕虫。 用单向哈希保护数据 该脚本对输入的数据进行单向转换,换句话说,它能对某人的密码产生哈希签名,但不能解码获得原始密码。为什么你希望这样呢?应用程序会存储密码。一个管理员不需要知道用户的密码,事实上,只有用户知道他/她自己的密码是个好主意。系统(也仅有系统)应该能识别一个正确的密码;这是 Unix 多年来的密码安全模型。单向密码安全按照下面的方式工作:
  • 当一个用户或管理员创建或更改一个账户密码时,系统对密码进行哈希并保存结果。主机系统会丢弃明文密码。
  • 当用户通过任何方式登录到系统时,再次对输入的密码进行哈希。
  • 主机系统丢弃输入的明文密码。
  • 当前新哈希的密码和之前保存的哈希相比较。
  • 如果哈希的密码相匹配,系统就会授予访问权限。
主机系统完成这些并不需要知道原始密码;事实上,原始密码完全无所谓。一个副作用是,如果某人侵入系统并盗取了密码数据库,入侵者会获得很多哈希后的密码,但无法把它们反向转换为原始密码。当然,给足够时间、计算能力,以及弱用户密码,一个攻击者还是有可能采用字典攻击找出密码。因此,别轻易让人碰你的密码数据库,如果确实有人这样做了,让每个用户更改他们的密码。 加密 Vs 哈希 技术上来来说,哈希过程并不是加密。哈希和加密是不同的,这有两个理由:不像加密,哈希数据不能被解密。是有可能(但非常罕见)两个不同的字符串会产生相同的哈希。并不能保证哈希是唯一的,因此别像数据库中的唯一键那样使用哈希。
function hash_ish($string) {    return md5($string);}
上面的 md5() 函数基于 RSA 数据安全公司的消息摘要算法(即 MD5)返回一个由 32 个字符组成的十六进制串。然后你可以将那个 32 位字符串插入到数据库中和另一个 md5 字符串相比较,或者直接用这 32 个字符。 破解脚本 几乎不可能解密 MD5 数据。或者说很难。但是,你仍然需要好的密码,因为用一整个字典生成哈希数据库仍然很简单。有一些在线 MD5 字典,当你输入 06d80eb0c50b49a509b49f2424e8c805 后会得到结果 “dog”。因此,尽管技术上 MD5 不能被解密,这里仍然有漏洞,如果某人获得了你的密码数据库,你可以肯定他们肯定会使用 MD5 字典破译。因此,当你创建基于密码的系统的时候尤其要注意密码长度(最小 6 个字符,8 个或许会更好)和包括字母和数字。并确保这个密码不在字典中。 用 Mcrypt 加密数据 如果你不需要以可阅读形式查看密码,采用 MD5 就足够了。不幸的是,这里并不总是有可选项,如果你提供以加密形式存储某人的信用卡信息,你可能需要在后面的某个地方进行解密。最早的一个解决方案是 Mcrypt 模块,这是一个用于允许 PHP 高速加密的插件。Mcrypt 库提供了超过 30 种用于加密的计算方法,并且提供口令确保只有你(或者你的用户)可以解密数据。让我们来看看使用方法。下面的脚本包含了使用 Mcrypt 加密和解密数据的函数:
mcrypt() 函数需要几个信息:
  • 需要加密的数据
  • 用于加密和解锁数据的口令,也称为键。
  • 用于加密数据的计算方法,也就是用于加密数据的算法。该脚本使用了 MCRYPT_SERPENT_256,但你可以从很多算法中选择,包括 MCRYPT_TWOFISH192、MCRYPT_RC2、MCRYPT_DES、和 MCRYPT_LOKI97。
  • 加密数据的模式。这里有几个你可以使用的模式,包括电子密码本(Electronic Codebook) 和加密反馈(Cipher Feedback)。该脚本使用 MCRYPT_MODE_CBC 密码块链接。
  • 一个 初始化向量-也称为 IV 或者种子,用于为加密算法设置种子的额外二进制位。也就是使算法更难于破解的额外信息。
  • 键和 IV 字符串的长度,这可能随着加密和块而不同。使用 mcrypt_get_key_size() 和 mcrypt_get_block_size() 函数获取合适的长度;然后用 substr() 函数将键的值截取为合适的长度。(如果键的长度比要求的短,别担心,Mcrypt 会用 0 填充。)
如果有人窃取了你的数据和短语,他们只能一个个尝试加密算法直到找到正确的那一个。因此,在使用它之前我们通过对键使用md5() 函数增加安全,就算他们获取了数据和短语,入侵者也不能获得想要的东西。入侵者同时需要函数,数据和口令,如果真是如此,他们可能获得了对你服务器的完整访问,你只能大清洗了。这里还有一个数据存储格式的小问题。Mcrypt 以难懂的二进制形式返回加密后的数据,这使得当你将其存储到 MySQL 字段的时候可能出现可怕错误。因此,我们使用 base64encode() 和 base64decode() 函数转换为和 SQL 兼容的字母格式和可检索行。 破解脚本 除了实验多种加密方法,你还可以在脚本中添加一些便利。例如,不用每次都提供键和模式,而是在包含的文件中声明为全局常量。 生成随机密码 随机(但难以猜测)字符串在用户安全中很重要。例如,如果某人丢失了密码并且你使用 MD5 哈希,你不可能,也不希望查找回来。而是应该生成一个安全的随机密码并发送给用户。为了访问你站点的服务,另外一个用于生成随机数字的应用程序会创建有效链接。下面是创建密码的一个函数:
 0) &&        (! is_null($num_chars))) {        $password = '';        $accepted_chars = 'abcdefghijklmnopqrstuvwxyz1234567890';        // Seed the generator if necessary.        srand(((int)((double)microtime()*1000003)) );        for ($i=0; $i<=$num_chars; $i++) {            $random_number = rand(0, (strlen($accepted_chars) -1));            $password .= $accepted_chars[$random_number] ;        }        return $password;     }}?> 
使用脚本 make_password() 函数返回一个字符串,因此你需要做的就是提供字符串的长度作为参数:
 
函数按照下面步骤工作:
  • 函数确保 $num_chars 是非零的正整数。
  • 函数初始化 $accepted_chars 变量为密码可能包含的字符列表。该脚本使用所有小写字母和数字 0 到 9,但你可以使用你喜欢的任何字符集合。(LCTT 译注:有时候为了便于肉眼识别,你可以将其中的 0 和 O,1 和 l 之类的都去掉。)
  • 随机数生成器需要一个种子,从而获得一系列类随机值(PHP 4.2 及之后版本中并不需要,会自动播种)。
  • 函数循环 $num_chars 次,每次迭代生成密码中的一个字符。
  • 对于每个新字符,脚本查看 $accepted_chars 的长度,选择 0 和长度之间的一个数字,然后添加 $accepted_chars中该数字为索引值的字符到 $password。
  • 循环结束后,函数返回 $password。

作者:SamarRizvi 译者:ictlyh来源:http://www.codeproject.com/Articles/363897/PHP-Security
收起阅读 »

开发经验:如何优雅的进行页面间的跳转

在你的开发过程中,是否遇到过如下的需求: 在tableView类型的展示列表中,点击每个cell中人物头像都可以跳转到人物详情,可参见微博中的头像,同理包括转发、评论按钮、各种链接及linkcard。跳转到任意页面 产品要求,某个页面的不同banner图,点击...
继续阅读 »
在你的开发过程中,是否遇到过如下的需求:
  1. 在tableView类型的展示列表中,点击每个cell中人物头像都可以跳转到人物详情,可参见微博中的头像,同理包括转发、评论按钮、各种链接及linkcard。
  2. 跳转到任意页面

  • 产品要求,某个页面的不同banner图,点击可以跳转到任何一个页面,可能是原生的页面A、页面B,或者是web页C。
  • 在web页面,可以跳转到任何一个原生页面。
  • 在远程推送中跳转到任意指定的页面。
以上2种需求,我想大多数开发者都遇到过,并且可以实现这种功能。毕竟,这是比较基础的功能。但是代码未必那么优雅。一般处理办法针对1.,一般初学者会用target或者block等方法在tableView的代理方法拿到事件,并把要执行的跳转写到controller里。功能是可以实现的,但问题是这种cell及相似的cell(布局有些变化,或者多几个少几个控件)一般出现在多个页面。这样的话相同的代码就会出现在多个地方。就算把跳转方法抽取出来写成category,但是target或者block总是每个地方都要写的。针对2.,初级的方法是每个地方写一坨判断及跳转,高级一些是抽取出来写在基类或者category。优雅的解决办法纵观上面各种情况,总结起来就是一句话,在任意的地方触发事件(可以是推送,拦截的js跳转,各种控件的点击事件)可以跳转到任意界面。所以我们可以统一用一个控制跳转的manager来完成跳转。
  • 与后端约定好数据结构,例如:
NSDictionary *target = @{@"action" : @"user",                         @"data"   : @{@"user_id" : @(123456)}};
就是跳转到id为123456的用户页面,如果跳转需要更多的数据,可以在data的value里面继续添加。
  • 创建一个用来跳转的类,可以起的牛x的名字,XXCoreActionManager。
创建一个类方法:
+ (BOOL)doActionForTarget:(NSDictionary *)target{    //根据你的app结构,来取得你当前的controller,由它来进行跳转    UIApplication *application = [UIApplication sharedApplication];    AppDelegate *myAppDelegate = (AppDelegate *)[application delegate];    UIViewController *viewController;    if ([myAppDelegate getTabbarSelectedView]) {        viewController = [myAppDelegate getTabbarSelectedView].visibleViewController;    }else {        return NO;    }    if([json[@"action"] isEqualToString:@"film_view"]){        MFMaterialListViewController *materialListVC = [[MFMaterialListViewController alloc] initWithNibName:@"MFMaterialListViewController" bundle:nil];        materialListVC.hidesBottomBarWhenPushed = YES;        materialListVC.filmId = json[@"data"][@"film_id"];        [viewController.navigationController pushViewController:materialListVC animated:YES];        return YES;    }else if([json[@"action"] isEqualToString:@"home"]){        [myAppDelegate getTabbar].selectedIndex = 0;        [viewController.navigationController popToRootViewControllerAnimated:YES];        return YES;    }    return NO;}
在刚才定义的数据结构中,action的值为需要跳转的页面,data的值跳转所需要要的参数,比如id、type等。服务器只需要传入相应的数据就可以用
[XXCoreActionManager doActionForTarget:target];
  • 针对头像、评论、转发等多处使用的子控件,可以把事件由自己接收,通过XXCoreActionManager来进行跳转。即做到了代码分离,使之成为真正独立的控件,便于复用。
下面是一个简单例子:
implementation MFUserHeadButton-(void)awakeFromNib{    [self addTarget:self action:@selector(toUserDetail) forControlEvents:UIControlEventTouchUpInside];}- (void)toUserDetail{    NSDictionary *target = @{@"action":@"user",                           @"data":@{@"user_id":@(self.tag)}};    [XXCoreActionManager doActionForTarget:target];}
这就是一个简单的实现跳转到个人页功能的头像控件实现。如果你问user_id的值怎么来的?当然是configCell的时候传给view的tag的。你可能会说,我的跳转需要多个参数啊,你一个tag不够用啊亲。可以定义一个NSDictionary属性接收啊。如果你用原生的UI控件,那让强大的runtime给你加喽
- (void)setDict:(NSDictionary *)dict {        objc_setAssociatedObject(self, dictKey, dict, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (NSDictionary *)dict {        return objc_getAssociatedObject(self, dictKey);}
总结[list=1]
  • 通过XXCoreActionManager,你可以做到无论你身在何处(哪一个controller),要到何处去(跳转到哪一个controller),你只需要叫来XXCoreActionManager,告诉它你的目的地(target)。你就可以做到想跳就跳。真正的write once,use anywhere。
  • 针对各种有跳转功能的控件,可以做到真的解耦。只需要把它放到他需要显示的位置。告诉它对应的参数就可以了。方便复用和后期的维护。
  • 这次想说的只是一个思路,代码很简单。其实可以使用的地方还有很多。不只是跳转各位童鞋可以自己思考。有什么不明白的可以留言,如果觉得那里不合适更欢迎留言指教或交流。


  • 写在后面的废话

    从读大学接触到计算机技术这一领域,我就被这个圈子的氛围深深的触动了,这是一个如此自由、分享、开放的圈子。我从无数人的的分享中汲取着知识(开源的代码,分享的博客),我受益于这种环境,我就想对它做出回馈,写出这篇文章,即希望能帮助一些人,也希望能与大家有更多的交流。最后,谢谢那些无私的做出各种奉献的人。
     
    来源:csdn 收起阅读 »

    环信移动客服“智能知识库+智能机器人”技术开启3.0时代

    7月28日,环信B轮融资及移动客服新品发布会在北京JW万豪酒店举行。发布会上,环信CEO刘俊彦宣布环信成功获得B轮融资1250万美元。同时环信CTO马晓宇宣布环信移动客服3.0产品也正式上线,而环信移动客服凭借IM长连接垄断技术结合“智能知识库+智能机器人” ...
    继续阅读 »
    7月28日,环信B轮融资及移动客服新品发布会在北京JW万豪酒店举行。发布会上,环信CEO刘俊彦宣布环信成功获得B轮融资1250万美元。同时环信CTO马晓宇宣布环信移动客服3.0产品也正式上线,而环信移动客服凭借IM长连接垄断技术结合“智能知识库+智能机器人” 组合技术将开启移动客服3.0新时代。


    1.png



    环信CTO马晓宇现场解读环信移动客服3.0

    传统的客服行业是一个劳动密集型企业,因为人力成本的不断提高,导致传统呼叫中心已经从北上广转移到大连、成都等地,未来甚至将要迁往新疆、西藏、青海等人力成本更低的地方。环信CEO马晓宇表示环信希望通过技术来解决人力成本不断升高的问题,所以环信适时推出了智能的移动客服系统。


    2.png



    智能的两个要素
    • 一个是智能知识库;
    • 一个智能机器人。

    智能知识库环信移动客服3.0版本会内置几个标准的行业知识库,基于人工智能深度挖掘技术,比较容易维护这个知识库,大家如果用得越多,知识库会更敏感,更能够快速回答问题。如果有一个用户说我要重置密码,智能知识库系统自动在客服工作端显示出重置密码的流程,客服就可以根据标准流程走下来。还有一个智能机器人技术,环信移动客服将智能机器人技术和知识库结合,能够解决回答80%的常见问题,剩下的20%才需要人工来解决

    另外,环信在移动客服里开始逐渐部署了大数据技术,环信会提供15个新的数据统计报表,从最简单的客服当前工作状态,客服每天工作量,客服会话小结等。在这基础上提供渠道统计报表,就可以知道你的客服用户需求,比如是来自微信还是移动APP还是网页,做一些渠道分析。另外我们还提供用户满意度报表,这样不光能提高整个用户满意度,同时我们希望这个产品是能帮助APP运营者,开发者能快速迭代,能真正尽快改进产品,不断满足客户需求。帮助客户将客服系统从成本中心向营销中心和盈利中心转换

    最后马晓宇表示环信移动客服是一个开放平台,整个内部是平台插件化可扩展化体系,通过插件形式打造可扩展的平台,希望基于这个满足SaaS服务成千上万客户不同需求,可以进行二次开发。同时在这个基础上环信还有一个生态圈部署。现在环信有几个合作伙伴,私有云部署有两家私有云授权认证集成商,移动客服领域有两家做增值服务的授权认证集成商,我们希望环信能继续发展下去,发展过程中和合作伙伴一起把市场做好,让我们的合作伙伴也的确能挣到钱。

    据悉,环信移动客服典型用户包括国美在线、58到家、楚楚街9块9等上百家互联网巨头企业客户,累计签约客服座席已经达4万个。根据艾媒咨询现场发布的《2015年中国移动客服市场发展研究报告》显示环信在新兴的移动客服市场占有率第一,APP客户规模最大。不难预料,有着连接“人与商业”愿景的环信凭借惊人的成长速度将最有希望成为下一个“独角兽”。 收起阅读 »

    环信移动客服3.0上线签约国美在线、58到家、楚楚街9块9等龙头企业

    7月28日,环信B轮融资及移动客服新品发布会在北京JW万豪酒店举行。发布会上,环信CEO刘俊彦宣布,环信成功获得B轮融资,同时环信移动客服3.0产品也正式上线,而环信将以“连接人与商业”为愿景,开启万亿级企业云服务市场。 据悉,环信移动客服产品已...
    继续阅读 »
    7月28日,环信B轮融资及移动客服新品发布会在北京JW万豪酒店举行。发布会上,环信CEO刘俊彦宣布,环信成功获得B轮融资,同时环信移动客服3.0产品也正式上线,而环信将以“连接人与商业”为愿景,开启万亿级企业云服务市场。


    5.png



    据悉,环信移动客服产品已经有国美在线、58到家、楚楚街等大批龙头企业签约,累计签约客服座席已经达4万个,而按艾媒咨询的报告显示,环信已占据市场份额第一的宝座

    发布会现场,据启动环信移动客服3.0产品上线的环信CTO马晓宇介绍,环信之所以受到市场如此欢迎,是因为环信移动客服是有别于传统客服的划时代产品。在环信CTO马晓宇看来,客服经历了三个时代——
    1. 传统 call center的时代: 互联网尚未普及,电话客服客户等待时间长,客户服务人员压力大,工作重复内容多;
    2. 到了PC网页客服时代,随着互联网的普及,网页客服的成本明显小于电话客服,客户服务人员工作量较小,客户满意度有所提高,但相应依然不能及时有效;
    3. 当今,随着智能手机用户的快速增长,客户要求能够随时在移动端获取咨询服务。移动端客服的重要性将不断上升。移动客服时代随之到来。



    9.png


    然而在环信基于IM长连接技术推出移动客服之前,业界并没有一款真正适合移动端和社交化时代的可靠的云端移动客服产品。此前,业界普遍存在的是两种移动客服产品
    1. 一类是工单客服,可以被视为移动客服1.0,基于工单方式和客服交互,技术实现简单,但无法做到实时交互,沟通效率低,用户体验差。
    2. 另一类是轮询技术客服,可以被视为移动客服2.0,基于HTTP轮询技术,通过不断向服务器发送查询请求来准实时的收取消息。不能做到实时收取消息,流量和电量消耗高,APP在后台时无法接收消息,容易丢失客户。

    而环信推出的移动客服产品是业界唯一一款具有IM长连接技术的移动客服产品,这被视为更适合移动端的客服产品。

    这一类型的移动客服可以定义为“交互型智能移动客服”,可以被视为移动客服3.0,以环信为代表的这一类新型移动客服是基于亿级IM长连接,系统稳定;智能机器人技术可处理大部分重复问题。相较环信移动客服的能力,传统的电话、工单、轮询客服在移动端和社交趋势下就显得捉襟见肘,有些OUT了。
     
    • 环信移动客服实现了跨平台多渠道接入:支持 App、微信公众账号、微博、网页等,均可以快速统一接入客户服务后台管理。

    8.png

    • 其次,满足了开放性与自定义信息:这种新型的移动客服实现了代码开源、UI开源,还提供多套UI模版,便于与APP快速集成,平滑接入。同时第三方集成功能也很强大,可与第三方工单、知识库、CRM 系统等进行扩展集成。
    • 第三,富媒体消息交互体验:移动客服平台提供了基于IM技术的类似微信体验的友好富媒体消息交互,不仅可以实时收发文字、表情,还可以即时收发图片、位置、实时语音、还可自定义消息,大大方便了与客户的沟通交流。
    • 第四,精准客户画像功能:移动客服的IM沟通帮助企业辅助判断客户需求与诉求,通过自定义客户分类标签,客户再次访问时可获知客户的类型。并且还支持自定义会话小结,根据会话小结统计会话的分类,通过会话小结追踪客户诉求。通过轨迹分析功能,即通过发送客户访问页面的轨迹,判断客户意图,获取客户个性化细节,了解客户基础信息,分析客户行为,可以极大提高订单效率。
    • 第五,“智能机器人+智能知识库”:作为一套智能化的客服系统,让企业建立基于业务的智能知识库,智能辅助归结业务信息和应答客户信息,历史常见问题系统梳理,提高客服效率。“智能机器人+智能知识库”组合目前可自动回复80%常见问题,随着智能知识库的不断训练,这一比例能够提高到90%。
    • 第六、真正实现不丢订单:环信是中国乃至全球领先的即时通讯云服务提供商,上线1年来,已成为唯一一家经历了亿级长连接真实稳定运行的即时通讯云平台,中国主流的采用第三方即时通讯云服务的App几乎都是环信的客户,每日环信平台的消息量达到数亿条,有着即时通讯云领域亿级长连接的绝对垄断优势。环信基于IM长连接技术,帮助企业实现超线拉取排队会话,让等待时间过长或有订单需求的客户得到优先服务,提升客服效率,即使客户关闭会话,退出App,甚至关闭手机,基于IM长连接技术依然能找回客户会话,提升客服效率,不丢客户订单。同时,基于IM长连接技术,即使在断网或复杂网络切换时也依然能找回客户会话,不丢客户订单。
    • 第七、实现了质检系统透明管理:移动客服自带实时监控系统,可实时监控当前所有会话,管理员在线质检,在线查看会话信息,还提供了历史会话查询、质检考核。通过实时与历史查看,可以统计出客服代表的接入量、在线时间及应答等待时间。



    6.png


    环信CTO Johnson向大家介绍客服产品

    总之,通过部署环信移动客服,较于传统客服产品可以节省成本约60%,在移动端省电省流量高达80%,同时留住70%会话客户,订单量和客单价均可以获得不同幅度提升。 收起阅读 »

    环信移动客服受电商青睐背后:社交经济时代的全渠道营销变革

    2015年7月28日,环信B轮融资发布会如期召开了。据了解,此轮环信共获得1250万美元融资。至此,环信已经在1年内完成了4轮融资,前三轮分别是2014年5月经纬中国天使伦融资500万人民币,2014年8月SIG A轮融资500万美金,2014年10红杉300...
    继续阅读 »
    2015年7月28日,环信B轮融资发布会如期召开了。据了解,此轮环信共获得1250万美元融资。至此,环信已经在1年内完成了4轮融资,前三轮分别是2014年5月经纬中国天使伦融资500万人民币,2014年8月SIG A轮融资500万美金,2014年10红杉300万美金A+轮融资。


    QQ截图20150729110432_conew1.png



    这一轮融资依然被前几轮老股东“三巨头”内部瓜分,领头的是红杉资本、经纬中国和SIG跟投,诸多投资机构欲进无门,望而兴叹。发布会现场,依然有VC代表赶来想碰碰运气,追着环信的人问未来还有没有进入的可能。


    3_conew1.png



    经纬副总裁 熊飞 给环信刘俊彦 颁发融资支票

    这现象的不同寻常之处在于,这是一个做2B领域的创业项目,此前受到业界如此猛烈追捧的一般都是2C的企业。为何环信一个做2B业务的企业会猛然间冒出来受到如此关注?
     
    据了解,让环信炙手可热的是其背后蕴含的巨大商机。而支撑环信估值暴涨的商机正是近来被业界关注的万亿级企业云服务市场前景,而且环信的“即时通讯云+移动客服”业务切中的恰是企业云服务市场王冠上的宝钻。
     
    2015年将成为企业云服务市场的爆发元年,这一端倪在企业云服务市场率先爆发的美国已经十分清晰。据有关专家分析,在过去的1年时间里美国的企业服务市场,已经有超过15家企业IPO,他们的募集资金达到70亿美元,总市值超过400亿美元,其中有Zendesk、Hubspot、New Relic、Hortonworks、Box等不少明星企业。而且,美国目前这样规模的企业云市场还正处在从青年走向壮年的阶段。据分析,从市值排名前十的云计算企业服务公司的市值总和看,2008年市值总和是250亿美元,到2015年这一数字已经是1800亿美元了,7年里增长了超过7倍。而预计到2016年,美国才会出现第一个云服务市场占有率超过传统软件的垂直领域,就是CRM。而“客服”是CRM四大细分市场之一。而且,客服是CRM细分领域中最大的一个,占市场总额的37%。在北美,2015年客服软件市场采购总额高达96亿美元。这个市场中已经出现了两家“独角兽”公司,就是近来受到各界关注和追捧的Zendesk和Freshdesk。


    1_conew1.png


    环信北美对标公司
     
    中国的市场机会可能比美国更令人兴奋,在中国企业云服务市场还是一片空白。相交美国,为企业2700万家企业客户,提供云服务的三家大的领军公司Oracle、SAP、Salesforce市值总和在3500亿美金左右。而中国,目前有2200万企业,但中国至今还没有百亿美元身价的公司甚至还没有基于SaaS的10亿美元身价的公司。这也许正是最先能嗅到风信的VC们挤破头的想投环信的缘由。

    有意思的是,据记者发现,由于无法进入环信的投资圈,VC们并没有放弃这一领域的赌胜机会和投资热情。因而,自从环信变得越来越炙手可热但却望而兴叹后,业界“类环信”项目在连带效应下成为VC们宣泄相对过剩资金的一个突破口。近来有不少作为环信影子的公司,或者学环信的公司获得了融资,甚至有创业者表示:环信成功之前,“类环信”公司想拿融资,得跟投资人费一番口舌,而环信成功后,“类环信”只要拿换新的概念改一改说自己做的跟环信一样就通过了。

    更有意思的是,在环信的IM云+客服模式被证明获得成功之后,百度、阿里、腾讯、金蝶、个推等相继跟随,推出了自己的IM业务,虽然,被大平台追逐,但在环信CEO刘俊彦看来这并没有什么可怕的,环信积累的技术优势和市场优势不是一两天能追得上的。据了解,环信的根基——即时通讯云能达到亿级稳定长连接并不容易,以环信网罗的中国IT界最顶尖的IM技术精英,需要至少1年时间了解服务器架构,真实稳定积累千万、亿万系统的维护经验。但一家100人的创业企业得到BAT带头的互联网巨头公司的特别“关照”,还是人感到兴奋的。

    还有值得关注的是,环信也引起了美国Zendesk和layer的足够重视,在环信公布“连接人与商业”的愿景后,我们可以看到Zendesk的官网增加了一句类似的话:Zendesk brings companies and their customers closer together.

    同时,也传闻Zendesk正在收购整合提供IM服务的公司,而layer也有被传闻称将推类似环信的业务。 收起阅读 »

    一个双网卡导致的网络故障

    内网有个机器有2个网卡,并且是不同的网段和网关。 其中的B服务器有2个网卡。这个时候我们就只有默认网关为10.1.1.1 那查看路由表就是如下[root@localhost ~]# ip route show table all 10.1.1.0/...
    继续阅读 »
    内网有个机器有2个网卡,并且是不同的网段和网关。


    network.png


    其中的B服务器有2个网卡。这个时候我们就只有默认网关为10.1.1.1 那查看路由表就是如下
    [root@localhost ~]# ip route show table all
    10.1.1.0/24 dev eth0 proto kernel scope link src 10.1.1.247
    10.1.2.0/24 dev eth1 proto kernel scope link src 10.1.2.239
    169.254.0.0/16 dev eth0 scope link metric 1002
    169.254.0.0/16 dev eth1 scope link metric 1003
    default via 10.1.1.1 dev eth0

    [root@localhost ~]# ip rule show
    0: from all lookup local
    32766: from all lookup main
    32767: from all lookup default

    这个时候我们可以发现,从1网段到1网段来回都没有问题,2网段来回也没有问题。但是从server A到server B的2网段是不通的。 因为你去到2网段后,server B的默认路由是10.1.1.1。
    所以我们需要设置server B上,来自哪个网卡的路由就从哪个网卡出去。这样server A到server B的2网段就没有问题了。

    首先添加2个route table
    $ cat /etc/iproute2/rt_tables
    #
    # reserved values
    #
    255 local
    254 main
    253 default
    252 lan1
    251 lan2
    0 unspec

    然后再添加ip route和ip rule
    ip route flush table lan1
    ip route add default via 10.1.1.1 dev eth0 src 10.1.1.247 table lan1
    ip rule add from 10.1.1.247 table lan1

    ip route flush table lan2
    ip route add default via 10.1.2.1 dev eth1 src 10.1.2.239 table lan2
    ip rule add from 10.1.2.239 table lan2

    这个时候我们再查看路由表如下
    [root@localhost ~]# ip route show all
    10.1.1.0/24 dev eth0 proto kernel scope link src 10.1.1.247
    10.1.2.0/24 dev eth1 proto kernel scope link src 10.1.2.239
    169.254.0.0/16 dev eth0 scope link metric 1002
    169.254.0.0/16 dev eth1 scope link metric 1003
    default via 10.1.1.1 dev eth0

    [root@localhost ~]# ip rule show
    0: from all lookup local
    32764: from 10.1.2.239 lookup lan2
    32765: from 10.1.1.247 lookup lan1
    32766: from all lookup main
    32767: from all lookup default

    这个时候从表面上似乎解决了问题,从server A访问server B的2网段也能正常返回,从server C访问server B的1网段也可以正常返回。
    但是我们发现,这个时候从server B访问server A的1网段的时候,一直网络状态在SYN的状态。


    half-tcpdump.png


    上面tcpdump的结果我们发现是有很多的TCP重传。这个时候我们发现,上面的ip rule只是设定了,来自2网段的走lan2(又设定了src为自己), 来自1网段的走lan1(又设定了src 为自己的IP)。而没有设定如果主动出去是怎么样的。
    因此我们把上面的ip rule加了2条.
    ip route flush table lan1
    ip route add default via 10.1.1.1 dev eth0 src 10.1.1.247 table lan1
    ip rule add from 10.1.1.247 table lan1
    ip rule add from 10.1.1.247 to 10.1.1.0/24 table main

    ip route flush table lan2
    ip route add default via 10.1.2.1 dev eth1 src 10.1.2.239 table lan2
    ip rule add from 10.1.2.239 table lan2
    ip rule add from 10.1.2.239 to 10.1.2.0/24 table main

    然后我们查看路由表如下:
    [root@localhost ~]# ip route show table all
    default via 10.1.1.1 dev eth0 table lan1 src 10.1.1.247
    10.1.1.0/24 dev eth0 proto kernel scope link src 10.1.1.247
    10.1.2.0/24 dev eth1 proto kernel scope link src 10.1.2.239
    169.254.0.0/16 dev eth0 scope link metric 1002
    169.254.0.0/16 dev eth1 scope link metric 1003
    default via 10.1.1.1 dev eth0
    default via 10.1.2.1 dev eth1 table lan2 src 10.1.2.239

    [root@localhost ~]# ip rule show
    0: from all lookup local
    32762: from 10.1.2.239 to 10.1.2.0/24 lookup main
    32763: from 10.1.2.239 lookup lan2
    32764: from 10.1.1.247 to 10.1.1.0/24 lookup main
    32765: from 10.1.1.247 lookup lan1
    32766: from all lookup main
    32767: from all lookup default

    从上面这个例子中可以窥见,平时我们用netstat -rn这样来查看路由是没有问题的,但是当出现自定义route table的时候,我们需要注意的一些东西,一个是route table本身,还有是ip rule去定义使用哪个table。
    作者:崔永强
      收起阅读 »

    luckyOne - HTML5&CSS3抽奖小程序

    程序可用于各类活动,如年会、发布会等。 支持回车和空格键控制。   附件中是环信本次发布会使用的版本。 空格/回车控制抽奖的启动和开始,由于中奖者大图弹出是现场第二轮抽奖时临时加的,不完善。每次启动停止后需F5刷新重新开始新的一轮。最后按键盘"o"键有...
    继续阅读 »
    程序可用于各类活动,如年会、发布会等。
    支持回车和空格键控制。
     
    附件中是环信本次发布会使用的版本。
    空格/回车控制抽奖的启动和开始,由于中奖者大图弹出是现场第二轮抽奖时临时加的,不完善。每次启动停止后需F5刷新重新开始新的一轮。最后按键盘"o"键有个结束特效幻灯片。

    通用版请访问Github库:https://github.com/haozki/luckyOne
    欢迎大家下载使用和交流。 收起阅读 »

    wordpress静态化

    下面这个是结合了很多参考加上自己实践最终确认能用的。不过这个还是跟主题有很大关系。不是所有主题都是可以完全转成静态的。下面这个配置文件是nginx的,apache的用户就没有这样的烦恼了。 大部分主题都是可以使用的,但是有些加了一些动态验证这样代码的,要那样的...
    继续阅读 »
    下面这个是结合了很多参考加上自己实践最终确认能用的。不过这个还是跟主题有很大关系。不是所有主题都是可以完全转成静态的。下面这个配置文件是nginx的,apache的用户就没有这样的烦恼了。
    大部分主题都是可以使用的,但是有些加了一些动态验证这样代码的,要那样的就只能用fastcgi cache来进行处理了。
    这里使用的是wordpress的supercache模块。
    upstream php-fpm {
    server unix:/var/run/phpfpm.sock;
    }

    server {
    listen 80;
    server_name timo.piqiu.me;

    root /opt/web/wordpress;
    index index.php;

    access_log logs/timo.piqiu.me.access.log proxy;
    error_log logs/timo.piqiu.me.error.log;

    location = /favicon.ico {
    log_not_found off;
    access_log off;
    }

    location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
    }

    location ~ /\.svn/* {
    deny all;
    }

    location ~ /\.git/* {
    deny all;
    }

    location /nginx_status {
    stub_status on;
    access_log off;
    }

    set $cache_uri $request_uri;

    # POST requests and urls with a query string should always go to PHP
    if ($request_method = POST) {
    set $cache_uri 'null cache';
    }
    if ($query_string != "") {
    set $cache_uri 'null cache';
    }

    # Don't cache uris containing the following segments
    if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
    set $cache_uri 'null cache';
    }

    # Don't use the cache for logged in users or recent commenters
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
    set $cache_uri 'null cache';
    }

    # Use cached or actual file if they exists, otherwise pass request to WordPress
    location / {
    try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html $uri $uri/ /index.php ;
    }
    location ~ \.php$ {
    try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html $uri $uri/ /index.php ;
    fastcgi_pass php-fpm;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /opt/web/wordpress$fastcgi_script_name;
    fastcgi_intercept_errors on;
    include fastcgi_params;
    #
    # fastcgi_cache cache_fastcgi;
    #
    # fastcgi_cache_valid 200 302 301 24h;
    # fastcgi_cache_valid any 1m;
    #
    # fastcgi_cache_min_uses 1;
    #
    # fastcgi_cache_use_stale error timeout invalid_header http_500;
    # fastcgi_cache_key $request_method://$host$request_uri;
    }

    # Cache static files for as long as possible
    location ~* .(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
    expires max; log_not_found off; access_log off;
    }

    location ~ ^/(status|ping)$ {
    include /opt/server/nginx/conf/fastcgi_params;
    fastcgi_pass unix:/var/run/phpfpm.sock;
    fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
    allow 127.0.0.1;
    deny all;
    }
    }
    收起阅读 »

    redis replication问题一解

    公司有个redis比较大,同时又是跨IDC同步,但是最近发现一旦连接断了之后,好久都不能完全同步。 查看了一下log。[12826] 01 Apr 16:54:37.675 # I/O error trying to sync with MASTER: co...
    继续阅读 »
    公司有个redis比较大,同时又是跨IDC同步,但是最近发现一旦连接断了之后,好久都不能完全同步。

    查看了一下log。
    [12826] 01 Apr 16:54:37.675 # I/O error trying to sync with MASTER: connection lost
    [12826] 01 Apr 16:54:38.555 * Connecting to MASTER 10.x.x.x:6379
    [12826] 01 Apr 16:54:38.555 * MASTER <-> SLAVE sync started
    [12826] 01 Apr 16:54:38.621 * Non blocking connect for SYNC fired the event.
    [12826] 01 Apr 16:54:38.692 * Master replied to PING, replication can continue...
    [12826] 01 Apr 16:54:45.229 * MASTER <-> SLAVE sync: receiving 390598473 bytes from master

     
    通过info观察也是一直去master上同步,有时候看着马上就完成了,就报以上的错误。 以前使用redis很老的版本2.4一直都没有这样的问题,自从换了2.6以后才出现这样的问题的。
    放狗查了下原来是 client-output-buffer-limit  这个参数导致的。默认是:
    client-output-buffer-limit slave 256mb 64mb 60

    但是不是很明白这个参数的含义,看了redis文档还正有说这个的。

    大体意思就是如下:

    hard limit是一旦redis到达这个值后会马上关闭client连接。
    soft limit是一种依赖于时间的。 比如一个soft limit被设置为32MB 10s, 那就意味着当client的output buffer超过32MB,并且持续10秒钟,那这个连接就会被断开。

    默认值就是hard 为256M, soft为 32M 60秒

    普通客户端的默认limit为0, 就是任何时候都没有limit,因为普通的client使用阻塞来实现发送命令和接收完整的返回,在发送下一个命令之前,所以在普通的client的情况下关闭连接是不合适的。

    但是要特别注意pub/sub客户端,这种方式一次处理和输出的数据都会特别大。

    我们可以使用config set 来进行设置,但是注意使用config set的时候不支持MB,GB 这样的单位。
    下面这个是redis官网的一个具体说明:

    http://redis.io/topics/clients
    Output buffers limits

    Redis needs to handle a variable-length output buffer for every client, since a command can produce a big amount of data that needs to be transferred to the client.

    However it is possible that a client sends more commands producing more output to serve at a faster rate at which Redis can send the existing output to the client. This is especially true with Pub/Sub clients in case a client is not able to process new messages fast enough.

    Both the conditions will cause the client output buffer to grow and consume more and more memory. For this reason by default Redis sets limits to the output buffer size for different kind of clients. When the limit is reached the client connection is closed and the event logged in the Redis log file.

    There are two kind of limits Redis uses:
    • The hard limit is a fixed limit that when reached will make Redis closing the client connection as soon as possible.
    • The soft limit instead is a limit that depends on the time, for instance a soft limit of 32 megabytes per 10 seconds means that if the client has an output buffer bigger than 32 megabytes for, continuously, 10 seconds, the connection gets closed.
    Different kind of clients have different default limits:
    • Normal clients have a default limit of 0, that means, no limit at all, because most normal clients use blocking implementations sending a single command and waiting for the reply to be completely read before sending the next command, so it is always not desirable to close the connection in case of a normal client.
    • Pub/Sub clients have a default hard limit of 32 megabytes and a soft limit of 8 megabytes per 60 seconds.
    • Slaves have a default hard limit of 256 megabytes and a soft limit of 64 megabyte per 60 second.


    It is possible to change the limit at runtime using the CONFIG SET command or in a permanent way using the Redis configuration file redis.conf. See the exampleredis.conf in the Redis distribution for more information about how to set the limit.
    作者:戚俊奇 收起阅读 »

    gcm推送

    一.gcm前期准备   Apple有apns推送,Google有gcm推送,iOS接收通知调用系统通知栏提示,Android接收通知启动应用调用通知栏提示。 相对于apns,gcm则多了一些限制,需要一些必备条件达到才可以使用。 1.在国内,首先就...
    继续阅读 »
    一.gcm前期准备


    20150727113000901_(1).jpg


     
    Apple有apns推送,Google有gcm推送,iOS接收通知调用系统通知栏提示,Android接收通知启动应用调用通知栏提示。
    相对于apns,gcm则多了一些限制,需要一些必备条件达到才可以使用。
    1.在国内,首先就是Google被墙,无法连接到Google服务器,需要你走VPN或者其它方式可以连接到Google服务器。
    2.在你的开发环境下,需要通过Android SDK Manager—>Extras下安装Google Play services,成功之后在你的SDK文件/sdk/extras/google/google_play_services/libproject下会看到google-play-services_lib类库,可以从里面直接复制jar包到你的项目libs下
    3.Android客户端,要求安装了Google核心服务Google Play服务,Google Play 商店才能使用gcm


    20150727113107623.jpg


    以上是Google官方文档描述:
    大意就是gcm要求设备运行在Android系统2.2或更高版本并且安装了Google play商店应用,或者虚拟机运行在Android系统2.2版本的并且支持Google API,但是并不限制你的应用必须部署在Google play 商店。
    然而,如果你想要使用gcm 新的API就需要设备运行在Android系统2.3或更高版本,或者使用虚拟机运行在Android系统2.3并且支持Google API
    在一个现有连接Google服务的设备上,对于前置3.0的设备,就要求在Android设备上设置Google账号,4.0.4或者更高版本则不需要设置Google账号
    注:gcm只是简单的推送一个通知到客户端,其消息内容应小于4Kb,Google服务器存储时间为4个星期。在客户端,gcm是通过客户端设备注册一个系统广播来唤醒应用并发出通知提示,所以这个时候客户端不需要一直运行来接收消息,gcm一次发送最多可发送100个用户
     
    二.官网创建项目获取信息

    去Google Developers Console(https://console.developers.google.com/project)创建项目,输入你的project name和project id,创建成功如下图:


    6.jpg


    在应用中我们会用到这个project number,再去Credentials下创建一个server key,得到的api key会在自己的服务器端用到,如图

    7.jpg


    顺便说一下,默认Cloud Messaging for Android是可用的,如果不能用,就需要去API & auth—> API下查看Cloud Messaging for Android是否disable

    三.创建一个客户端app

    首先要在AndroidManifest.xml文件配置gcm权限等信息
      
    //当GCM需要谷歌账户(设备版本低于4.0.4时需要)


    android:protectionLevel="signature" />

    applicationPackage要改成自己的报名

    android:minSdkVersion="8"不得低于8


    8.jpg


    根据官网文档介绍:

    1.我们应该在配置文件注册一个系统广播
    
               android:name="com.google.android.gms.gcm.GcmReceiver"  
    android:exported="true"
    android:permission="com.google.android.c2dm.permission.SEND" >





    2.注册监听通知服务(在app中创建一个服务类并继承GcmListenerService类)
    
                android:name="com.example.MyGcmListenerService"  
    android:exported="false" >





    public class MyGcmListenerService extends GcmListenerService {

    @Override
    public void onMessageReceived(String from, Bundle data) {
    String message = data.getString("message");
    /**
    * Production applications would usually process the message here.
    * Eg: - Syncing with server.
    * - Store message in local database.
    * - Update UI.
    */

    /**
    * In some cases it may be useful to show a notification indicating to the user
    * that a message was received.
    */
    sendNotification(message);
    }
    // [END receive_message]

    /**
    * Create and show a simple notification containing the received GCM message.
    *
    * @param message GCM message received.
    */
    private void sendNotification(String message) {
    Intent intent = new Intent(this, MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
    PendingIntent.FLAG_ONE_SHOT);

    Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
    .setSmallIcon(R.drawable.ic_stat_ic_notification)
    .setContentTitle("this is a new new GCM Message")
    .setContentText(message)
    .setAutoCancel(true)
    .setSound(defaultSoundUri)
    .setContentIntent(pendingIntent);

    NotificationManager notificationManager =
    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
    }
    }

     
    3.注册监听token改变的服务(在app中创建一个服务类继承InstanceIDListenerService类,本服务存在于新的Google play服务版本上)
     android:name="com.example.MyInstanceIDListenerService"  
    android:exported="false">





    public class MyInstanceIDListenerService extends InstanceIDListenerService {

    @Override
    public void onTokenRefresh() {
    // Fetch updated Instance ID token and notify our app's server of any changes (if applicable).
    }
    }

     
    gcm通过注册的系统广播可以自行启动GcmListenerService类,所以不需要在app创建一个广播类

    注册token

    App发送 project number到GCM Server注册接收推送信息。 

    GCM Server 向App返回token(token是GCM服务器自行生产的,能够保证某一终端设备上的某一个应用,很重要)。 

    在设备上应该先判断是否安装了Google play 服务
    int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);  
    if (resultCode != ConnectionResult.SUCCESS) {
    if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
    GooglePlayServicesUtil.getErrorDialog(resultCode, this, PLAY_SERVICES_RESOLUTION_REQUEST).show();//会提示错误对话框,其他错误信息可参考官网API
    } else {
    Log.i(TAG, "This device is not supported.”);
    }

     
     
    Google play服务存在,那接下来就需要去注册token了,在上边创建项目得到的project number在这里就需要用它来注册token


    10.jpg




    9.jpg


     
    InstanceID instanceID = InstanceID.getInstance(this);  
    String token = instanceID.getToken(PROJECT_NUMBER,
    GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);

     
    对于旧版本还是要使用以下方式来注册token(用于Google play 服务7.5版本之前)
    GoogleCloudMessaging.getInstance(Context).register(PROJECT_NUMBER);  

     
    在自己的服务器端注册api key

    App向我们自己服务器发送token(推送消息的时候要使用token,GCM服务器是使用token来确定某一终端设备上的某一个应用接收消息的,所以第三方服务器需要保存它,需要注意的是token很长,存数据库时需要注意字段长度) ,将我们在上边create new key 得到的apikey添加到自己的服务器端保存,我们的服务器向GCM Server发送消息,传递apikey和token ,GCM Server把消息推送给App 
    最后注意一下,关于app卸载出现的问题,看一下官方的介绍


    11.jpg



    app会发生卸载过程并不能很快完成,卸载app需要花时间从GCM移除当前关联的token,这个过程就会出现发送消息是成功的,但是不能到达app客户端,在最后,token被移除了,服务器端发送的消息失败,得到一个NotRegistered错误,这个时候就应该在服务器端删除相对应的token 收起阅读 »

    凡信(超仿微信Android版)后台代码

    不知道什么鬼的看这个帖子http://www.imgeek.org/question/763?notification_id=226&column=log。   关于该项目的讨论群为437758366;   我的QQ:84543217.。。。。欢迎交流和一起学...
    继续阅读 »
    不知道什么鬼的看这个帖子http://www.imgeek.org/question/763?notification_id=226&column=log。
      关于该项目的讨论群为437758366;
      我的QQ:84543217.。。。。欢迎交流和一起学习。
    服务器端代码,回复可见。帮忙顶一下。
    另外需要朋友圈的朋友可以找我,做了套独立系统,支持嵌入任何有好友关系的android APP.
      收起阅读 »

    开发实战:Android自定义标题栏

    本文要讲自己定义一个标题栏,能加事件。然后可以移值到不同的手机上,基本上不用改什么,调用也很简单 在layout文件夹下,新建一个XML。名字叫做layout_title_bar.xml然后来看看布局: ...
    继续阅读 »
    本文要讲自己定义一个标题栏,能加事件。然后可以移值到不同的手机上,基本上不用改什么,调用也很简单

    在layout文件夹下,新建一个XML。名字叫做layout_title_bar.xml然后来看看布局:
      
    android:layout_width="fill_parent"
    android:layout_height="45.0dip"
    android:background="@drawable/bg_title_bar"
    android:gravity="top" >

    android:id="@+id/title_bar_menu_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_marginLeft="3.0dip"
    android:layout_marginRight="3.0dip"
    android:layout_marginTop="3.0dip"
    android:gravity="center"
    android:src="@drawable/ic_top_bar_category" />

    android:layout_width="wrap_content"
    android:layout_height="fill_parent"
    android:layout_toRightOf="@id/title_bar_menu_btn"
    android:background="@drawable/ic_top_divider" />

    android:id="@+id/title_bar_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:ellipsize="end"
    android:gravity="center"
    android:paddingLeft="75.0dip"
    android:paddingRight="75.0dip"
    android:singleLine="true"
    android:text="Java学习宝典"
    android:textColor="#ffffff"
    android:textSize="22sp" />

    看下效果:


    20150220004910963.png


    接下要就是要用了,在要用到的地方:我这里是activity_main.xml文件中:

    加上一句:  这样就行了,

    然后我们要给标题栏上的按钮添加事件,这个更加简单了:

    在MainActivity.java(对应activity_main.xml)中,onCreate函数中添加:事件可以自己改,我这里是让它控制左右滑动的功能。
    ImageView menuImg = (ImageView) findViewById(R.id.title_bar_menu_btn);  
    menuImg.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View arg0) {
    if (!menuIsShow)
    showMenu();
    else {
    hideMenu();
    }

    }

    });
    这样就可以了:

    我们来看看效果


    20150220004942958.gif



    这就是效果了,很简单吧,想用直接把上面的布局复制过去就OK了!
     
    作者:林炳文Evankaka 收起阅读 »

    中国互联网大会感受:技术人员最好的创业时代

    “年年岁岁花相似,岁岁年年人不同”,用这句千古名句来形容中国互联网大会的发展历程可以说再贴切不过了。在经历了互联网的1.0时代(PC互联网)、2.0时代(移动互联网)后,互联网的发展正快速步入下一个迭代周期,你可以认为是3.0时代,也可以给这个时代打上一个互联...
    继续阅读 »
    “年年岁岁花相似,岁岁年年人不同”,用这句千古名句来形容中国互联网大会的发展历程可以说再贴切不过了。在经历了互联网的1.0时代(PC互联网)、2.0时代(移动互联网)后,互联网的发展正快速步入下一个迭代周期,你可以认为是3.0时代,也可以给这个时代打上一个互联网+的标签。总之,当下这个时代互联网已不再是某个或某些企业的专属,谈全民互联网甚至也不觉得夸张。 在日前举办的第十四届中国互联网大会上,笔者也选取了三个非常有代表性的企业进行采访报道,看看透过它们是否能觉察出一些互联网+的典型特征。这三家企业分别是传统互联网的代表百度、传统制造业的代表联想,以及新兴的科技型创业企业环信。要说本次中国互联网大会上三家企业的所讲所展,有一个共同点是它们都在做云相关的内容,当然不同就多了去了,这里也不细说了,毕竟是三家几乎不搭杠的企业。 

    首先,我们来聊聊环信。 

    作为一家刚刚成立不过2年多的创业型企业,可能很多人还不太熟悉,这里先做个介绍。环信即时通讯云成立于2013年4月,是一家提供移动即时通讯能力的云计算 PaaS (Platform as a Service, 平台即服务) 平台服务商。 

    这么听起来可能还是有些难懂,我们举个例子来进一步说明一下,即时通讯相信大家并不陌生,电话、QQ、微信之类的都算是即时通讯工具,即时通讯云简单理解就是用于承载这些工具的平台。而基于该平台,环信有自己开发的即时通讯工具移动客服。


    lij5o8EpD9NFA_600.jpg


     
    环信CEO刘俊彦
     
    据环信CEO刘俊彦介绍,目前其即时通讯云平台上线已一年多时间,SDK(Software Development Kit,软件开发工具包)覆盖用户近1个亿,有3万余个APP集成了该平台的即时通讯能力,终端用户覆盖超3个亿。而另一款拳头产品移动客服上线仅3个月,已签约数百家企业,包括国美在线、58到家在内的众多龙头企业都已是环信的用户。 

    关于即时通讯云前文已有相应的解释,这里不再赘述,而是介绍下何谓移动客服顾名思义,移动客服就是主要面向移动终端的客服,更准确地讲就是针对各种APP打造的客服工具。相信大家都打过400、800之类的客服电话,估计也用过PC端的网页客服,但随着移动互联网的兴起,这些方式在应对移动端客户的需求时已变得捉襟见肘。 

    为什么呢?
     
    • 一是移动端的量大,一个APP同时在线的人数可能在数十上百万;
    • 二是一旦APP打开,与客服之间的连接就是一直长期保持的关系。而在PC端,网页一旦关闭,与客服之间的对话就已经中断。无疑,移动客服后台数据中心所承载的压力和成本是要远高于传统客服方式的。刘俊彦表示,这也是移动客服市场的主要门槛之一。 


    显然,作为国内较早进入该行业的企业,环信无疑已经占领了先机,前文已经提到了环信目前的用户情况,可以说非常不错,而更关键的是环信基本已经实现了收支平衡,要知道这在创业一两年的企业中可是为数不多的。 

    放眼未来,环信也有着明确的规划。当前,其B轮融资已经到位,至于具体数额会于下周公布,但这些钱用在什么地方,他们早就想清楚了。刘俊彦表示,公司下一步的拓展方向主要是两个方面,一是由现在更多面向服务电商、O2O、互联网金融、互联网教育、医疗、旅游行业的移动客服向覆盖传统客服和移动客服的全领域扩张,二则是向物联网领域发展。 

    具体来说,一是为了向用户提供客服的整体解决方案,而这么做的规划其实也是客户需求促使。据刘俊彦介绍,现在就有客户问他是不是能提供一整套完整的解决方案,而不需要移动客服找你们,网页客服找另一家,电话客服再找一家。二则是面向未来广阔的物联网市场,用不了几年,冰箱、洗衣机坏了的时候你已经无需再查找厂商的客服电话,而可能通过电器上的一个按钮就能实现报修,想一想是不是很美好。 

    但话说回来,作为一个具有广阔市场前景的行业,环信所不可避免的要面临竞争,而且随着市场的不断扩大,竞争也会愈发剧烈。不过在笔者将这个问题抛给刘俊彦时,他倒是相当坦然。要说技术出身的人总是那么的质朴,可能是被问的多了回答的也多了,他很平淡地说,“市场这么大(美国大概是100亿美金,中国市场比这个还要大),我们能拿下个位数的份额,已经是足够大的体量了。现在更重要的是有更多的人进来,把这个行业先做大,做好用户服务。”

    在写有关环信内容的最后,加一点刘俊彦的个人履历,17年研发,先后任职IONA、RedHat,专注于高并发消息中间件,实时消息系统,异构分布式企业系统集成,应用服务器。联想到最近一两年兴起的一批批创业公司,包括很多都在本次互联网大会上亮了相,笔者只想说,这是技术人员最好的创业时代。  收起阅读 »

    iOS开发生涯的初恋:详解Objective-C多项改进

    对于许多iOS开发者而言,Objective-C就是开发生涯中的初恋。如今,Swift正在以迅雷不及掩耳之势碾压Objective-C的份额,但Objective-C多项性能改进依然让开发者非常激动,本文作者对OC的提升进行了详解。   The Setup ...
    继续阅读 »
    对于许多iOS开发者而言,Objective-C就是开发生涯中的初恋。如今,Swift正在以迅雷不及掩耳之势碾压Objective-C的份额,但Objective-C多项性能改进依然让开发者非常激动,本文作者对OC的提升进行了详解。
     
    The Setup

    下面的代码你们一定再熟悉不过了,我们来重温一下吧:
    @property (strong, nonatomic) NSArray *someViews;  
    这绝对符合Objective-C完美主义开发者的标准。对它表示的属性,不同人有不同观点。但是,其中仍然存在着一些难以察觉的缺陷。

    是否可能返回nil?

    除非有现成的文件,或开发者全程都在一旁,否则光凭看是无法获取信息的。

    除了UIView之外还有什么?

    还是那句话——不确定。也许答案是reflection? 或许问题可以改成:除了UIView,有可能出现UIView子类吗?

    看样子会出现诸多转换(casting)

    因为是一队列……东西,知道那东西是什么之后,经过cast后才能利用。

    会弱化Swift代码和可读性

    很遗憾,Swift支持泛型(generics)就意味着(Objective-C )只会以optional的AnyObject集合的形式出现。如此一来,开发者要使用该属性就必须在Swift和Objective-C之间进行转换。

    Nullability Annotations

    单单一个属性就引发了这么多担忧,还挺让人不安的。如果代码本身引发很多质疑,出现error的可能性就大大增加,更别提在广为熟知的Objective-C和语言新秀Swift之间相互调用(interoperability)了。现在有了nullability annotations——我最爱的Objective-C新功能之一,问题就简单多了,编程也会省下很多麻烦。

    intent.

    现在谈到API,(intent.)可能会,也可能不会返回nil。简而言之,终于不用花费数小时来排除漏洞了。以下有三个选项:
    • nullable — Think UIView?
    • nonnull — Think UIView
    • null_unspecified — Think UIView!


    再回到实例属性。假设在运行时迭代这个属性来创建某个用户界面,在相应的位置应该有UIButton和UIView。

    但是,天哪!——不论怎么样它们也不应该是nil啊。现在出现如下的信息:
    @property (strong, nonatomic, nonnull) NSArray *someViews; 
    intent.大大提升了Objective-C,而且这个属性也不会在Swift里满满都是optional了。

    开发者看看代码就知道有没有nil pointer了,太棒了!

    计算机的静态检验和Swift的可用性都得到了提升,最重要的是实现了API的intent通讯。

    泛型

    ……Objective-C开发者们举国欢庆。呜呼,泛型的恩泽终于笼罩大地,这无疑是那些开发者勇士们的功劳。

    如果把Cocoa Touch比作孩子们的睡前故事,那么Objective-C就好比是主演,故事书肯定是以上面那段话结尾的。泛型的缺席一直以来是Objective-C开发者心头之痛,而诞生32年之后,Objective-C终于也支持泛型了。2015 WWDC上Swift 2成为了镁光灯下的宠儿,而Objective-C这一巨大的跨越却被忽视了,实在委屈。支持泛型将带来诸多改变,而且都是积极的改变。

    现在可以定义属性,下指令给编译器来显示所有UIView:
    @property (strong, nonatomic, nonnull) NSArray *someViews;  
    向属性强加UIView之外的东西时,编译器会报错。而且如今不用做大量头痛的转换(cast)了。

    Objective-C支持泛型对Swift而言也是好消息。上次更新时,我们让Swift知道对象不应该是optional的,现在Swift还知道它们是UIViews,如此一来含混不清的AnyObject声明就不需要了。如今的Objective-C可以像C#、C++、Swift等语言一样通过<>括号来表示类型了。虽然通常是对协议表示一致性(conformance),但编译器知道何时、何地以及如何运用它们,且运用是经过推理的。

    再进一步,可以用参数来表示扩展(extensions)、类别(categories)和类(classes),好处不仅仅体现在集合(collections)上。泛型的强大体现在整个Objective-C之中,集合仅仅是结果而已。举个例子,看看NSDictionary,开发者肯定会偷着乐吧:
    @interface NSDictionary (Lookup)  
    - (nullable ObjectType)objectForKey:(KeyType)aKey;
    @end
    刚开始知道类型擦除(type erasure)是为了这个的时候,我有点儿不满意,但考虑到老旧的Objective-C程序堆积在一起的问题,也就释怀了。

    类型擦除(type erasure)不但能实现二进制兼容,而且不改变Objective-C的执行时间。所以亲爱的开发者们,C#的泛型的确胜过其他语言,皱皱眉头,发几句牢骚就算了,日子还得继续呢。

    KindOf Types

    啊,这是最后一部分重要内容。再次调用之前定义的属性,就会显示UIView。判断里面包含着views和buttons是再正常不过的事。

    这种情况下,添加如下代码会发生什么呢?
    [self.someViews[0] addTarget:self action:selector(aMethod:) forControlEvents:UIControlEventTouchUpInside];  
    啊,编译器警告。

    这就对啦,因为即便可以在这个属性里插入一个button,就算可以假设是个UIView,button也不一定没有经过转换。

    新的KindOf特性能够轻松解决这种始料未及的情况。我们再回到实例属性上:
    @property (strong, nonatomic, nonnull) NSArray<__kindof UIView *> *someViews;  
    实际上我们已经告诉编译器:属性及其集合会出现一些UIView。这样在类型协议里显示更多我们之前看不到的信息。其本质向下转型(downcasting)。

    这意味着上述代码编译没什么问题,因为编译器知道集合里肯定会出现一个button。

    现在那些担忧就都解释得清了。

    虽然不喜欢Swift的人可能会刻意夸大Objective-C的优点,但如今两种语言实现了互相调用,这是Objective-C所有提升的最大价值所在,我们应该心存感激。

    毋庸置疑,Objective-C的确比以往更加强大。

    总结

    对我来说,Objective-C是开发生涯中的初恋,相比其他语言,它是那么与众不同——直到今天都是如此,它的好、它的坏都让我欲罢不能。虽然如今Swift正以迅雷不及掩耳之势征服着我的心,我还是希望Objective-C陪伴在身边。

    Objective-C的提升能够帮助开发者写出更好的代码,这是好事。而且这些优势已经在Foundation中随处可见了。
     
    来源:csdn 收起阅读 »

    招人不易留人更难 创业团队要闯哪些关?

    嘉宾简介:马晓宇,环信CTO,18年的老程序员, 先后从事过 IC设计软件,短信网关、电信网管、中间件、手机操作系统和手机App的研发。从2004年开始从事开源软件的开发,参与了Apache,Eclipse,Symbian fundation等开源社区。在创办...
    继续阅读 »
    嘉宾简介:马晓宇,环信CTO,18年的老程序员, 先后从事过 IC设计软件,短信网关、电信网管、中间件、手机操作系统和手机App的研发。从2004年开始从事开源软件的开发,参与了Apache,Eclipse,Symbian fundation等开源社区。在创办环信之前,先后在Symbian、Nokia、微软等公司工作。
    公司简介:环信即时通讯云是移动即时通讯能力的云计算PaaS (Platform as a Service, 平台即服务) 平台服务商。环信将基于移动互联网的即时通讯能力,如单聊、群聊、发语音、发图片、发位置、实时音频、实时视频等,通过云端开放的Rest API 和客户端SDK 包的方式提供给开发者和企业。让App内置聊天功能和以前网页中嵌入分享功能一样简单。环信全面支持Android、iOS、Web等多种平台,在流量、电量、长连接、语音、位置、安全等能力做了极致的优化,让移动开发者摆脱繁重的移动IM通讯底层开发,极大限度地缩短产品开发周期,极短的时间内让App拥有移动IM能力。
    现场速记:
    马晓宇:大家好,我是环信的马晓宇 Johnson, 很高兴这个机会和大家交流。
    18年的老程序员,先后做过IC软件,电信系统,中间件,手机系统等;重度开源参与者,从2004年从事开源软件开发,参与了Apache,Eclipse,Symbian Foundation 等社区。创办环信前,在Iona,Nokia,Symbian,Microsoft 等公司工作,2001到2004年在美国工作,见证了第一次的互联网泡沫。

    个人是从何时开始的创业之旅,请分享下创业心得。

    马晓宇:2013年初看到移动互联网的爆发,结合我们在服务器端的长期积累,开始做一个移动互联网的BaaS平台,到最后聚焦在IM云平台创立环信。我们做的是面向企业的SaaS,确切的说是toD,对企业的SaaS服务.

    体会是两个:
    • 一是市场巨大:这两年在环信平台上,我们见证了新兴移动互联网app的爆发式增长,有些客户已经开始准备IPO,更多的公司经过各轮融资得到了快速发展。20%+的app都有付费能力,另外,大量的传统企业,像国美,链家等也开始使用我们的saas 服务,市场”钱景”广阔。
    • 第二个体会是过程刺激。心脏不好的没法做企业SaaS。尤其是像我们的IM 云服务和移动客服产品,都是和客户的业务系统紧耦合的,是他们服务的关键一环。对稳定性要求比人力资源评测,后台数据分析等服务要求高的多。从上线以来,用户每月增长100%造成的容量压力;PaaS 服务商的宕机;DDoS攻击;DNS 域名污染,甚至线上运维错误操作。
    介绍下环信目前的情况以及团队构成。在创业方向上环信是如何选择的呢?马晓宇:环信平台2014年5月上线,目前服务2万多App,日活几千多万,每天消息超过1亿条。公司现有100人,其中研发团队50多人。分为:移动端,IM后台,运维,音视频,大数据,移动客服几个team。起步是做移动互联网的BaaS(backend as service),在上面又做了企业IM产品,但发展不顺利,13年年底几个创始人闭门开会,决定聚焦在 IM 云服务。在即时通讯云发展起来之后,发现很多我们的客户都有移动客服的需求,去年年底开始开发移动客服saas 服务。您怎么看移动即时通讯平台技术的现状及发展趋势?马晓宇:两个趋势:融合通信和连接平台。即时通讯从只提供发送短信,图片到实时语音,实时视频。虽然现在还有牌照限制,最逐渐终会取代传统的移动网络。基于此技术的公共平台,现在是微信和Facebook主推的业务。不光是一个IM服务,而是用来连接生活的方方面面。环信的创新产品及重点项目有哪些?产品竞争力体现在哪些方面?下一步重点发展方向是什么?马晓宇:环信是第一个即时通讯云平台。但我们认为,接入IM,提供即时通讯服务只是第一步。我们在定义一个社交模型,通过定量定性分析,来帮助客户增加用户黏性,更好的做社交。同时我们也在重点推荐移动客服平台的开发。我们是开放的即时通讯云平台,支持千万级并发。环信下一步重点在IM方面是提供增值服务,包括反垃圾,数据挖掘等。同时,我们也在重点开发移动客服云服务。环信在开发移动即时通讯平台中经历过哪些经验教训?马晓宇:最主要是经验教训是用户爆发性增长和平台容量的矛盾,和由此推动的平台架构快速演进和扩容。从去年6月上线以来,每月用户量环比增长100%,推动平台快速演进。一年时间,后台架构演化到第6个版本。从教训角度来看,架构演化是无法一步到位的,一个比较实际的措施是压力测试。应该尽早的,经常性的做系统的压力测试,并且要保持比线上环境高几倍的压力。请您谈谈环信与开源技术的渊源,环信技术中都涉及哪些开源技术?马晓宇:环信的三个创始人都是开源社区的重度参与者,我们的平台也用到了大量优先的开源软件,从Kafka 消息队列,Storm 实时处理,到Spark 大数据挖掘。我们在开源方面的目标是在即时通信领域,结合我们的海量用户,打造一个开源的基础软件项目,并逐渐成为一个流行的开源项目,回馈技术社区。技术团队是什么样的氛围?工作模式是怎样的?马晓宇:总结一下就是压力、乐观、坦诚。创业团队事比人多,每个人都有足够的技术挑战。虽然遇到各种大大小小的困难,但大家每天工作气氛是乐观而幽默的。我们研发团队50多人,没有专门的管理人员,我是CTO,但每天主要大部分时间也是写程序。基本是工程师文化,没有KPI,讲究code wins。举几个具体了例子:有些同事一般下午才出现在公司,有的同事喜欢跑步,一看天气好,就去奥森跑步去了,当然跑完再继续工作。我们希望打造自组织的高效团队。下半年开始周三,周六work from home。希望到明年,team 更加成熟,实现有些同事期待在海南、普吉岛等地remote 办公,每年会北京开两次会。在培养技术人才方面,环信有哪些举措?马晓宇:我们有三个措施:
    • 第一内部交流:再忙也要做每周的技术分享,而且要高质量的,充分准备的。这个对年轻工程师帮助很大;
    • 第二外部交流。我们自己主办 技术沙龙meetup. 也鼓励技术人员多参加各种会议和线下活动,和同行交流,和比自己更优秀的人交流,这个有助于核心工程师的技术提高;这块其实一直有人提醒我,小心优秀的工程师被挖走。但帮助他们更优秀,在社区更认同是我的责任,如果留不来了,也是我工作中的不足了。
    • 第三是梯队培养:对重点的苗子,要越级使用,给比较大的技术挑战和压力,同时由技术负责人对他重点给予相关的指导和帮助。对年轻工程师,我们允许失败,但不允许犯同一个错误。
    现在创业公司大批量招人,在招聘过程中您遇到过怎样的瓶颈吗?如何找到优秀的技术人才,有什么好的建议?马晓宇:招聘上我们走了一些弯路。现在总结看,通过社招,通过猎头效果都不够好。团队本身就人手紧张,每周安排几个面试,但没有收获,比较浪费时间。我总结通过内部推荐、介绍和线下交流长期跟进最有效。比如我们移动端团队,就是symbian,nokai 同事逐渐互相推荐来的。对出色的技术人才,要长期跟进。最近我们有两个都是跟进了1年多,有了机会,邀请加盟的。另外,我们看好的候选人,都是优秀的技术人才,很容易得到 BAT的offer。这种情况下,就要靠工作内容、技术挑战打动他们,一起打造一个业界领先的SaaS平台。还有一个细节的地方是:JD中对要求和岗位责任描述要尽量清楚。每个岗位需要写一个专门的JD。创业团队该如何留住人?(换血问题)马晓宇:工作的吸引力主要有三点,不同工程师内心排序不同。
    • 第一是待遇。兄弟们没日没夜的跟你干几年,工资要above market rate。即便没时间花,但给家里有个交待。期权更要多给,公司如果能成功,主要原因是团队每天的拼搏和贡献的积累,而不是因为创始人或投资人。
    • 第二是发展。员工在环信的这2,3年技术水平等能不能有比较大的发展,跟上甚至超过公司的发展速度。我们是要求技术团队每人都得有一个6个月的发展目标,根据此目标,我和 team lead 来具体看怎么安排相关工作,并经常给予指导。
    • 第三是快乐。个人觉得最重要。团队成员每天能不能高兴的来上班,enjoy 工作中的各种挑战。另外,我一直尽量创造轻松幽默的工作氛围,team内部提倡简单直接的沟通方式。
    在公司发展过程中,“换血”也无法避免。不少创业公司都有类似问题,起步阶段创始人和各个team技术比较强,但整个团队技术水平和经验比较欠缺。环信2年前从车库咖啡起步,那时候还没有高大上的创业大街。水平不错的应聘者一来,一看是在一个咖啡馆办公,一般转身就走。再加上经费有限,那个阶段,我们只能招到大专毕业,甚至培训班毕业的员工。这样的团队结构,只能把产品做出来。公司发展起来后,对人员标准的目标变成要能做出技术领先的产品,并且能服务世界各地的用户,能用英语和海外开发者流利交流。为了解决人才瓶颈,最近一年我们逐渐对开发团队进行了“换血”。比如移动端team,开发的主力变成了 计算机专业研究生毕业工作10多年的经验丰富的工程师为主力。这样的中间力量才能支撑我们今后2,3年的快速发展。在团队逐渐调整过程中,早期的技术人员也没用流失,而是在更合适自身经验和能力的岗位成长。还是我们移动端的例子,核心的sdk core部分是几位10多年经验的Linux C++背景的同事负责,其他工程师负责在此基础上开发Android,iOS sdk 和App,也做的很出色。创业团队如何创造条件来更好的协调工作和家庭?马晓宇:环信团队平均年龄30多岁,很有几个40几岁的老程序员。大家都是上有老下有小,更好的协调工作和家庭是决定大家每天能不能来公司开心工作的关键。我们的经验有几点事业要获得家庭的理解和支持。让家里人理解我们做的事情,感受到整个团队的拼搏,也能看到我们未来3年的目标和可期待的收获。我多次给核心技术团队的家人们打电话,一打就是1,2个小时,主要是让他们理解外人开来一群酷爱编程的疯子每天在干的事情,汇报我们的发展和我们的目标。切实关心团队的家庭。家庭遇到什么具体问题,我们能怎么帮助。比如父亲病重,可以安排回老家远程工作1,2个月,边工作边照顾家人;买房首付不够,我们帮助借些钱周转;虽然平时我们没时间照顾小孩,环信工作时间比较灵活,孩子开家长会什么的,倒是爸爸去的比较多。推行work from home。我们是一周6天工作,但周六在家办公。下半年开始进一步推进周三也在家办公。一方面能节省路上的时间,另一方面能让大家在家的时间多一些,帮帮忙。要招聘合适创业团队的员工。我们自己的经验看,女生有小孩的,不适合创业公司的开发岗位。但能在销售、市场、人事等岗位做得很好。不过这些都是对团队来说的。做为创始人,根本无法协调工作和家庭了。我每天晚上11,12点下班,除了周日,都见不到小孩。互动环节: 
    • 最近腾讯云也推出了即时通讯产品,环信的产品和他们产品对比,有哪些不同?
    马晓宇:对,阿里去年年底开始搞openim,腾讯云最近也推出了类似服务,我们的区别主要开放和专注。他们的产品是云平台下面的一个小team 在搞,我们是一个整个公司focus 在上面。 
    • 请问一下没有KPI,那么你们怎么做绩效考核或者说激励的呢?
    马晓宇:我们每月会统计代码提交,但那是做为reference。team 每个人的效率和output,其实不需要领导打分,大家都知道。奖励是 team lead 给input,由管理层商量。 
    • 环信做为im平台,会切入移动办公类的工具或系统开发吗,类似阿里钉钉之类的。

     马晓宇:不会,我们只做平台,不做产品。我们希望借鉴开源团队的管理方式。每个人完成的feature,做的技术分享,checkin 的代码质量,其实team 都在看着。 收起阅读 »

    2015中国SaaS生态“元素周期表”

    去年以来,SaaS市场持续火爆,吸引了无数创业者或者投资机构的关注,为此我们特别策划了这期2015中国SaaS生态“元素周期表”的专题,希望从一个比较直观的角度勾勒出2015年中国SaaS大生态,共谱中国SaaS大势。以SaaS为代表的企业云端应用,正在改变着...
    继续阅读 »

    去年以来,SaaS市场持续火爆,吸引了无数创业者或者投资机构的关注,为此我们特别策划了这期2015中国SaaS生态“元素周期表”的专题,希望从一个比较直观的角度勾勒出2015年中国SaaS大生态,共谱中国SaaS大势。以SaaS为代表的企业云端应用,正在改变着整个IT的格局。各种各类的SaaS的应用,这两年就从来没有离开过人们的眼球。今年以来,融资超过千万美金的SaaS企业比比皆是,包括今目标、销售易、雅座等,而主打企业SaaS应用的众多初创企业也纷纷拿到数额巨大的融资,可以看到,中国SaaS市场从来没有像今天一样火热。其实,深究SaaS为什么可以得到投资人和用户的青睐,这和SaaS本身的特点是分不开的,由于其资费、用户体验等方面相对传统应用而言,具有不可比拟的优势,使得SaaS应用的用户积累速度要远远超过传统应用的拓展速度。

    SaaS市场投融资市场的火爆也催生了众多不同类型的SaaS企业的出现、包括OA协同、CRM、HR、ERP、安全等等,另外,我们也看到SaaS模式在物流交通、医疗、教育、农业等领域同样得到越来越多的应用。因此,我们特别策划了2015中国SaaS生态“元素周期表”,希望从一个比较直观的角度勾勒出2015年中国SaaS大生态,共谱中国SaaS大势。


    1.jpg


    通过前后一个月左右的时间,我们绘制了这张中国SaaS生态“元素周期表”,我们征求过很多业界专家的意见,反复修改,终于成型,在此一并谢过。当然,国内SaaS企业众多,无法全部囊括,我们只是筛选出其中的典型代表,后续我们会继续完善。

    当然绘制这张SaaS生态“元素周期表”并不是我们的全部,我们还从时下最火的SaaS企业中邀请到一些CTO给我们分享了他们企业自己在SaaS实践过程中的一些实战经验以及爬过的坑。 当然,无论企业级市场有多火,我们的用户才是真正有发言权的,所以我们还特别邀请到一线的SaaS用户,让他们谈谈自己在使用SaaS过程中的体验,实践经验和面临的一些问题。我想,这些干货是很多沙龙和meetup上看不到的,而这正是目前SaaS企业的领导们和技术客们喜闻乐见的,这就是价值。
     
    实战解读

    团队协作工具Worktile技术架构揭秘: Worktile自上线两年多以来,以良好的用户体验和稳定的服务,获得了用户的认可和喜爱。目前,已经有超过10万家团队在使用Worktile。作为团队协作工具,从技术上分析首先要解决如下几个问题:

    • 基于Web的跨平台设计,让用户在任何地方都可以随时通过浏览器访问

    • Web形态的产品要具有原生客户端的体验,如任务的拖拽等

    • 具有高效的实时消息系统,每个团队成员在Worktile中所做的任何操作,都要实时在其他成员的客户端中自动刷新

    • 服务要稳定,稳定压倒一切那么Worktile是如何做到这几点的?点击上面蓝色字体,有你想知道的。


    让餐厅放心的云服务-雅座CRM技术解密:雅座CRM历经9年的成长,累计为超过15000家餐饮门店提供精准营销服务,管理超过5000万会员数据,日交易流水超百万笔,年交易额达200亿以上。 雅座提供专业的企业级SaaS云服务,对系统的安全性、稳定性、可靠性要求极高,这是企业的基本诉求。同时,基于用户交易和消费行为的数据分析,是精准营销的核心,如何快速处理海量数据,进行多维度、低延时的统计分析、数据挖掘,对系统性能和可伸缩性提出了更高的挑战。CRM的目标是数据营销,需要提供各种纬度的灵活的查询分析。举例来说,餐厅一个简单的需求,希望根据消费时间、性别、消费金额筛选出目标客户,随着数据量增大,仅靠关系数据库分库分表和索引优化已经难以满足企业的效率要求。高弹性、易扩展的大数据处理能力,在CRM云服务中日趋重要。本文雅座CTO对其CRM系统架构做了深度分享。
     
    以下内容节选自《程序员》电子刊:

    移动端企业IM系统优化:imo在PC端IM领域有很强的积累,但在做移动端时遇到了不少的挑战。在移动端相对恶劣的运行环境加上企业IM的特殊性(高及时性,大数据量),使得许多之前行之有效的经验水土不服,引发了若干问题,通过一些系统重构以及针对性的定位处理,问题得到了解决,本文重点介绍imo遇到的这些问题以及相应的处理经验。

    聚焦用户体验,dayHR云存储技术背后思考:dayHR是理才网公司的核心产品,是基于移动互联、云计算和 SaaS服务的人才资本管理云平台。作为一家典型的SaaS供应商,并且是提供企业级应用的SaaS供应商,主机是理才网最重要的核心资产,肩负着向用户提供接入服务,存储用户关键业务数据的使命,其重要性不言而喻。在众多的主机部署方案中,主机托管和云主机租用是众多SaaS供应商最主流的两种选择。到底是主机托管还是云主机租用呢?本文分享了dayHR是如何抉择的,并提出了自己的思考。

    基于公有云平台打造TB级海量文件备份系统:企业业务稍微上点规模的,IT系统产生的数据很容易就超过TB级,并且资料文档等很容易超过亿级别的规模,如果用手动复制的方案来备份,基本是非常困难的;这种情况下,即使购买一些专业系统,随着数据量日益增大,跑起来也非常吃力。本文重点讨论如何基于云平台来实现对应的解决方案。

    SaaS用户心声

    泛OA,2B-SaaS的主场:OA系统基本上是国内产品的天下,这在IT领域非常少见,大多数领先的IT产品和IT技术都源自美国,学自美国。不知是不是因为没有美国榜样,总之中国的OA系统市场巨大,没有霸主,需求广泛,成功罕见。 作为一个企业的信息化负责人,想找到一款满意的OA系统非常困难。不论是百里挑一,还是一见钟情,选择了OA之后,总会发现实施难度比计划中大很多,使用效果比想象中差很多,难堪大用,游走在用和停用的边缘。虽说信息系统项目实施本就困难重重,需要一把手支持,需要打破部门墙,但我认为根本原因其实是OA产品不够好。 OA系统梦想很大,现实很小,说OA的痛点,其实就是说OA的方向。能解决这些痛点的OA,将是管理系统领域的王者,不论是私有部署的传统OA还是SaaS版泛OA。以上节选自金山软件CIO的分享。

    传统企业SaaS应用的五个误区:随着互联网快速普及和发展,进一步加速了企业的数字化建设,作为较慢拥抱互联网的传统企业也越来越多的加入到企业数字化升级转型行列。提升效率,降低成本,增强软实力,成为吸引众多传统企业实施 SaaS应用的重要因素,然而不同企业的实施结果却大相径庭,很多传统企业走入了SaaS应用的五个重要误区。本文来自味多美集团味多美集团首席电子商务官的深度分享。

    结语

    中国的中小企业的运营情况和美国并不一样。在美国,很多企业即使只有很小规模,也都可以稳定的运行几十年,因此他们有能力持续购买相应的SaaS服务。但是在中国,短期的(1-3年的)企业占据了中小企业数量的绝大多数,而这就造成了SaaS软件繁荣的假象,虽然前期会花很大的成本获取了客户,但是由于这些客户很短的生命周期,不见得能够在较长时间之内获得稳定的收入,甚至不足以平衡早期的客户获取成本。

    虽然进入2015年以来,各种企业级SaaS应用的投资热潮一浪高过一浪,但是我们还是坚持认为,国内的SaaS目前还是早期阶段,需要更多的培育和成长引导。在资本的驱动下,未来几年内会有更多的传统软件厂商以及新的SaaS厂商进入这个领域,为企业级应用市场带来百花齐放的变革,真正能够为客户提供更专业、更全面的企业信息化服务。
     
    内容来源:csdn 收起阅读 »

    WorktileCTO揭秘:团队协作工具Worktile技术架构

    Worktile自上线两年多以来,以良好的用户体验和稳定的服务,获得了用户的认可和喜爱。截止笔者写这篇文章的时候,已经有超过10万家团队在使用Worktile。作为团队协作工具,从技术上分析首先要解决如下几个问题: 基于Web的跨平台设计,让用户在任何地方...
    继续阅读 »
    Worktile自上线两年多以来,以良好的用户体验和稳定的服务,获得了用户的认可和喜爱。截止笔者写这篇文章的时候,已经有超过10万家团队在使用Worktile。作为团队协作工具,从技术上分析首先要解决如下几个问题:

    • 基于Web的跨平台设计,让用户在任何地方都可以随时通过浏览器访问

    • Web形态的产品要具有原生客户端的体验,如任务的拖拽等

    • 具有高效的实时消息系统,每个团队成员在Worktile中所做的任何操作,都要实时在其他成员的客户端中自动刷新

    • 服务要稳定,稳定压倒一切



    那么Worktile是如何做到这几点的?今天笔者在这篇文章里一一为大家揭秘。

    SPA设计

    先来说说Worktile中SPA(单页应用程序)设计,作为团队协作工具,需要尽可能减少用户在不同页面之间的跳转,所以从一开始我们就决定Worktile必须是单页应用程序,当时面临的选择有很多,首先我们考虑使用大名鼎鼎的Backbone.js,但是很快又抛弃了,因为在实际使用中Backbone.js太复杂,另一方面开发效率太低,最终我们选择了Google出品的AngularJs,下面这幅图是AngularJS的结构图:


    1.jpg


    选择它主要基于以下几点考虑:

    1. 自动化双向数据绑定功能,这一点在Worktile中非常重要,如任务的状态变化都要实时变更到其他成员,如果具有自动化双向数据绑定功能,只需要绑定到UI的数据源发生变化,UI会自动发生改变,不需要工程师再通过代码去修改UI元素的改变,如下面这段代码:
    ng-class="{1:'task-completed-style'}[task.completed]">
    id="task_check_{{ task.tid }}"
    wt-click="js_complete_task($event, entry, task)">


    {{task.name}}
    2. 语义化标签,AngularJS在设计之初信奉的理念就是:当编写UI的同时又需要编写业务逻辑时,声明式的代码远比命令式代码要好,命令式的代码更适合写业务逻辑,AngularJS在设计上就通过语义化的标签把对DOM元素的操作和逻辑代码分离,如我们需要展现一个任务列表,只需要下面这段代码即可:

    class="slide-trigger"
    hide_action="true"
    ng-click="locator.openTask(task.pid, task.tid)">

    3. 模块化设计,AngularJS堪称模块化设计方面的典范,通过模块化设计我们可以非常好的实现Worktile的工程化,在Worktile中涉及的元素非常多,如有项目、任务、日程、文件、话题、文档等等,而这每一个元素都可以设计为一个模块,如下所示:
    (function () {
    'use strict';
    angular.module('wtApp', [
    'wt.project.ctrl',
    'wt.team.ctrl',
    'wt.task.ctrl',
    'wt.event.ctrl',
    'wt.post.ctrl',
    'wt.file.ctrl',
    'wt.page.ctrl',
    'wt.mail.ctrl'
    ]);
    }());
    4. 引入依赖注入,依赖注入是面向对象中比较成熟的设计模式之一,为了解决面向对象中依赖问题,得到了广泛的应用,AngularJS中大胆使用了依赖注入,极大的减少了各个模块之间的依赖问题:
    taskListCtrl.$inject = ['$scope', '$stateParams', 
    '$rootScope', '$popbox',
    '$location', '$timeout',
    'bus', 'globalDataContext',
    'locator'];
    结合以上特点,我们最终决定了前端框架使用AngularJS来实现,从Worktile上线两年多的表现来看,我们的选择无疑是正确的。当然AngularJS也有一些缺点,在实际使用中还是要根据具体的产品类型来选择使用,另外AngularJS 2.0也已经初见端倪,和AngularJS 1.0有很大的不同,感兴趣的同学可以先去尝鲜一下。
     
    服务设计

    我们再来看看Worktile的后台服务设计,Worktile的整体服务架构设计如下图所示:


    2.jpg



    其中前端部分在上面的SPA一节中我们已经说过了,下面一一分析下其他的服务:

    1.  API服务,包括Web API、Mobile API、Open API,这些都运行于NodeJS之上,选用NodeJS的原因主要是它的异步事件驱动,对于高并发的支持比较好,另外一个原因是使用简单,对于前后端可以使用同一门语言去开发。

    2.  缓存和队列服务,Worktile中的缓存和队列服务都是基于Redis来实现,Redis是一款非常优秀的开源缓存服务,并且可以选择基于内存还是进行数据持久化,它提供的pub/sub模型对于Worktile来说非常重要,对于一些实时性要求不高的处理,我们都是在Redis中pub一条消息,告知其他服务有数据发生了变化,那些服务在接收到Redis中的消息后,根据消息的类型决定应该如何做出处理。

    3.  数据库服务,Worktile产品本身的特点决定了它是一个对实时性和性能的要求,远超过对事务性要求的产品,所以在选择数据库时,我们选用了MongoDB数据库,性能高,集群方便,数据以BSON结构存储,和Node.js天生完美结合。

    4.  文件预览服务,使用Worktile的同学肯定知道在Worktile中所有的文件都可以做到无需下载到本地,而直接在线查看,这一切都是预览服务的功劳,因为文件类型的各种各样,在实现文件预览时也要根据文件的类型做出不同的处理,针对txt、pdf、代码片段等文本型的文件,我们只需要读取文件中的内容,然后再前端用相应的视图展现出来即可,相对比较简单。但是对于Office类型的文件,如ppt、doc、xls等文件,就不能这么简单的处理,我们希望文件在Worktile中查看的效果和用户在本地使用Word、Excel、PowerPoint查看的效果差不多,经过我们的调研,最终选用了微软官方提供的Office Web App服务。

    消息推送

    消息推送服务是Worktile最核心的服务之一,前面提到过作为一款团队协作工具,要能够实现非常好的实时性,任何数据的变化都需要及时变更到团队所有成员当前所在的视图,如下面这幅图,是一个典型的任务看板,团队所有成员可能同时在操作当前项目中的任务,每个操作引起看板的变化都会实时更新,不需要用户做任何刷新操作:


    3.jpg


    为了达到这种效果,需要在Web客户端和服务器之间维持一个长连接,当有任何改变发生时,给客户端发送不同的消息,告知客户端哪些数据发生了变化,如下面是我们为任务定义的消息中的其中几个:

    实现实时消息推送,有以下几种方式可供选择:
    on_task_trash            : "on_task_trash",
    on_task_complete : "on_task_complete",
    on_task_move : "on_task_move",
    on_task_update : "on_task_update",
    on_task_comment : "on_task_comment",
    on_task_badges_file : "on_task_badges_file",
    on_task_unarchived : "on_task_unarchived",
    on_task_badges_check : "on_task_badges_check"
    1.  短轮询,页面端通过js定时异步刷新,这种方式优点在于实现简单,但实时效果较差。

    2.  长轮询。页面端通过js异步请求服务端,服务端在接收到请求后,如果该次请求没有数据,则挂起这次请求,直到有数据到达或时间片(服务端设定)到,则返回本次请求,客户端接着下一次请求,这种方式对于服务的要求较高,尤其在并发量很大的情况下,对服务端的压力很大。

    3.  Websocket。浏览器通过websocket协议连接服务端,实现了浏览器和服务器端的全双工通信。需要服务端和浏览器都支持websocket协议。

    在Worktile一开始我们选用了Socket.IO作为消息服务,但是随着访问量的增大,需要做集群化的时候感觉到力不从心,尤其对于Socket.IO状态数据的存储,由于并没有官方的解决方案,当时我们采用了一个第三方的开源项目,使用Redis来存储,引起了一些性能上的问题,在后来重构时选用了基于Erlang语言的开源XMPP服务ejabberd作为我们的消息服务。

    ejabberd是xmpp协议的一种实现, xmpp广泛应用于即时通信领域。Xmpp协议的实现有很多种,比如java的openfire,但相较其他实现,ejabberd的并发性能无疑使最优秀的。Xmpp协议的前身是jabber协议,早期的jabber协议主要包括在线状态(presence)、好友花名册(roster)、IQ(Info/Query)几个部分。现在jabber已经成为rfc的官方标准,如rfc2799, rfc4622, rfc6121,以及xmpp的扩展协议(xep)。Worktile就是基于XEP-0124、XEP-0206定义的BOSH扩展协议。

    由于自身业务的需要,我们对ejabberd的用户认证和好友列表模块的源码进行修改,通过redis保存用户的在线状态,而不是mnesia和mysql。另外好友这块我们是从已有的数据库中(mongodb)中获取Worktile中项目或团队的成员。Web端通过strophe.js来连接(http-bind),strophe.js可以以长轮询和websocket两种方式来连接,由于ejabberd还没有好的websocket的实现,就采用了BOSH的方式模拟长连接。整个系统的结构如下:


    4.jpg


     
    作者:李会军 收起阅读 »

    值得推荐:理解 JavaScript 的原型链和继承

    instanceof 运算符可以用来判断某个构造函数的prototype属性是否存在另外一个要检测对象的原型链上1   什么意思呢? 来个题Function instanceof Object;用高中数学的话就是把x,y代入公式得: insta...
    继续阅读 »
    instanceof 运算符可以用来判断某个构造函数的prototype属性是否存在另外一个要检测对象的原型链上1
     
    什么意思呢?

    来个题Function instanceof Object;用高中数学的话就是把x,y代入公式得:

    instanceof 运算符可以用来判断Object的 prototype属性 是否存在Function的 原型链 上。
    等等,斜体字的这俩到底是什么鬼意思?

    prototype属性是原型链吗?

    JS是基于原型链面向对象语言,也就是说所有对象都是以对象为模板创建实例的。如果是其他oo语言的背景比如Java或Ruby,都习惯于创建一个class模板,class创建object实例。比如ruby:
      class A
    def initialize name @name = name end
    def to_s
    @name
    end
    endputs A.new('hehe') # => hehe
    这里的类A就是所有 A.new 创建出来的实例的模板而已。而对于原型链语言JS来说,同意的事情要这样做
    function A(name){  this.name = name
    }
    A.prototype.toString = function(){ return this.name
    }var a = new A('hehe')
    console.log('object name is:' + new A ('hehe')) // => object name is: hehe


    • 这里的怪怪的函数其实就是constructor,相当于ruby例子里的initialize

    • 而prototype上的方法toString也就是类似class模板上的方法。


    为什么要把方法绑到prototype上?直接 A.toString…= 不行吗?
    在解释prototype之前,先解释一下 new A 到底发生了什么2:
    1: // var a = new A('hehe') =>2: var a = new Object();3: a.__proto__ = A.prototype; (proto)4: A.call(a, 'hehe');
    其中 A.call 的意思是先把A的this设置为a,然后执行A的body也就是this.name=name

    但是 __proto__ 又是什么

    __proto__ 才是原型链

    __proto__ 是内部 [ [Prototype ]] (说了半天原型链这就是牛逼闪闪的 原型链, 指向对象或者null)的getter和setter方法(已加入ES6规范3,但是还是建议只使用Object.getPrototypeOf())

    JS对象能使用它原型链对象的所有方法,比如所有的对象的原型链(的原型链的原型链的原型链…)都最终会指向Object(或null)。因此,所有的对象都能使用Object.prototype上的方法,比如我之前覆盖掉的 toString 本身就是Object.prototype上的方法,如果没有覆盖,它是可以拿到所有Object上的方法的:
    a.toString === A.prototype.toString // truea.toLocalString === Object.prototype.toLocalString // truea.__proto__ === A.prototype // true
    所以,现在是否可以理解这句话了呢
    instanceof 运算符可以用来判断Object的 prototype属性 是否存在Function的 原型链 上。

    所以instanceof其实就是
    Function.__proto__ === Object.prototype// false
    擦,假设失败了呢,让我们来看看为什么不对,Function.__proto__到底指哪去了
    Function.__proto__ === Function.prototype//true
    原来指向自己的prototype了呢,那就意味着…
    Function instanceof Function//true
    yes,然而 Function instanceof Object似乎也能解释了
    Function.__proto__ === Function.prototypeFunction.__proto__.__proto__ === Object.prototype
    所以如果我们让
    Function.__proto__.__proto__ = nullFunction instanceof Object//false
    这回知道为什么不要用 __proto__ 了吧,一不小心重写了会导致所有继承自它的对象都受影响。
    为了养成良好的习惯,实际项目最好使用 getPrototypeOf 取原型链,这里只是为了方便我采用__proto__
    下面来看第二个题
    Object instanceof Function
    难道可以互相链吗?这意味着
    Object.__proto__ === Function.prototype// true// 但是Firefox取不到Object.__proto__, 看来做了保护,必须要用// Object.getPrototypeOf(Object) === Function.prototype
    要晕了, 忍不住要画个图


    1.png

    多简单呢,一共就分别有两类:

    原型链指向Function.prototype的函数们

    原型链指向Object.propotype的对象们

    而原型链顶端的Object.prototype就再没有原型链了,所以是空

    现在再回头看题目是不是so easy了。

    也没什么卵用得 contructor

    如果你好奇的在FireFox Console中看一下 a 除了刚才那些玩意,还有一个奇怪的东西


    2.png

    话说 A 里面这个constructor是个什么鬼,我们来玩它一下
    a.constructor === A.prototype.constructor
    A.prototype.constructor === A
    A.prototype.constructor = nulla.constructor // => nulla instanceof A // true
    这只是函数都有的一个玩意而已, 由于js的函数可以作为构造器,也就是可以 new ,所以所有的 函数的prototype.constructor都指向自己,因此所有的 new 出来的对象也都有一个reference能找到自己的构造器。

    然而除了这个功能也并没有什么卵用嘛。

    真的是这样吗?
    .

    ..



    ….

    …..

    ……

    …….
    恩,真的!

    Bonus 继承

    下面这个是babel 从es6 class
    class A{
    constructor(name) { this.name= name
    }
    toString() { return this.name
    }
    }class B extends A {
    toString(){ return this.name + 'b'
    }
    }
    编译出来的ES5继承
    function _inherits(subClass, superClass) { 
    // 密}var A = (function () { function A(name) { this.name = name;
    }

    A.prototype.toString = function toString() { return this.name;
    }; return A;
    })();var B = (function (_A) { function B() { if (_A != null) {
    _A.apply(this, arguments);
    }
    }

    _inherits(B, _A);

    B.prototype.toString = function toString() { return this.name + 'b';
    }; return B;
    })(A);
    其他地方都不用看了,inherits 函数用到了之前学到的所有玩意,要求实现要满足下列所有的cases,就当是课后练习了:
    var a= new A('A');var b= new B('B');
    a.constructor === A &&
    b.constructor === B &&
    a instanceof A &&
    b instanceof A &&
    b instanceof B


    • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/instanceof

    • 这里只是意思,但是如果真的改变 __proto__ 是非常低效的https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

    • http://www.ecma-international.org/ecma-262/6.0/#sec-additional-properties-of-the-object.prototype-object



    Author: Jichao Ouyang 收起阅读 »

    不算“真正的语言”?详说Swift 2.0中的错误处理

    苹果公司在今年的全球开发者大会(Worldwide Developers Conference, WWDC)上宣布推出Swift2.0,该语言的首席架构师Chris Lattner表示,Swift 2.0主要在语言基本语法、安全性和格式美观度这三方面进行了改进...
    继续阅读 »
    苹果公司在今年的全球开发者大会(Worldwide Developers Conference, WWDC)上宣布推出Swift2.0,该语言的首席架构师Chris Lattner表示,Swift 2.0主要在语言基本语法、安全性和格式美观度这三方面进行了改进。除了这些新的功能特性,还有对语法的优化、修饰及美化,最后是Swift 1.x中最具影响力的错误处理机制。
     
    历史一瞬:不起眼的开端

    我们都知道,Swift语言作为Objective-C当前替代语言被推出,是OS X和iOS应用程序开发的“通用语”。在最初的版本中,Objective-C没有原生的异常处理机制。后来通过添加NSException类,还有 NS_DURING, NS_HANDLER和 NS_ENDHANDLER宏才有了异常处理。这种方案现在被称为“经典的异常处理”,还有这些宏都是基于setjmp()和longjmp()这两个C语言函数的。

    异常捕获(exception-catching)看起来如下所示,在NS_DURING和NS_HANDLER宏之间抛出的任何异常都将会导致在NS_HANDLER和NS_ENDHANDLER宏之间执行相应的代码。
    NS_DURING  
    2. // Call a dangerous method or function that raises an exception:
    3. [obj someRiskyMethod];
    4.NS_HANDLER
    5. NSLog(@"Oh no!");
    6. [anotherObj makeItRight];
    7.NS_ENDHANDLER
    下面是立刻能触发抛出异常的方法(现在仍然可用):

    [cpp] view plaincopy
    1.- (void)someRiskyMethod
    2.{
    3. [NSException raise:@"Kablam"
    4. format:@"This method is not implemented yet. Do not call!"];
    5.}
    可以想象,这种手工处理异常的方式戏弄的是早期Cocoa框架程序开发人员。但是这些程序员还不至于到这份儿上,因为他们很少使用这种方式。无论在Cocoa还是Cocoa Touch框架下,异常通常都被归为灾难性的,不可恢复的错误,比如程序员造成的错误。上面的-someRiskyMethod就是很好的例子,由于实现部分没有准备好而引发了异常。在Cocoa和Cocoa Touch框架中,可恢复的错误由稍后讨论的NSError类来处理。

    原生的异常处理

    我想由于Objective-C中的经典异常处理机制对应的手工处理方式让人感觉闹心,于是苹果公司在Mac OS X 10.3(2003年10月)中发布了原生的异常处理机制,彼时还没有iOS系统。这本质上是将C++的异常处理嫁接到了Objective-C。异常处理的结构目前看起来是这样的:
    @try {  
    2. [obj someRiskyMethod];
    3.}
    4.@catch (SomeClass *exception) {
    5. // Handle the error.
    6. // Can use the exception object to gather information.
    7.}
    8.@catch (SomeOtherClass *exception) {
    9. // ...
    10.}
    11.@catch (id allTheRest) {
    12. // ...
    13.}
    14.@finally {
    15. // Code that is executed whether an exception is thrown or not.
    16. // Use for cleanup.
    17.}
    原生的异常处理使你有机会为每个异常类型指定不同@catch部分。无论@try结果如何,@finally都要执行其对应的代码。

    尽管原生的异常处理如所预期的那样抛出一个NSException异常,但是最明确的方法还是“@throw ;”语句。通常你抛出的是NSException实例,但说不定什么对象会被抛出。

    NSError

    尽管Objective-C原生与经典的异常处理有许多优点,但Cocoa和Cocoa Touch框架应用程序开发人员仍然很少使用异常,而是限制程序出现程序员所导致的不可恢复的错误。使用NSError类处理可恢复的错误,这种方法早于使用异常处理。Swift 1.x也继承了NSError的样式。

    在Swift 1.x中,Cocoa和Cocoa Touch的方法和函数可能不会返回一个布尔类型的false或者nil来表示一个失败(failure)的对象。另外,NSErrorPointer对象会被当作一个参数返回特定的失败信息。下面是个典型的例子:

    cpp] view plaincopy
    1.// A local variable to store an error object if one comes back:
    2.var error: NSError?
    3.// success is a Bool:
    4.let success = someString.writeToURL(someURL,
    5. atomically: true,
    6. encoding: NSUTF8StringEncoding,
    7. error: &error)
    8.if !success {
    9. // Log information about the error:
    10. println("Error writing to URL: \(error!)")
    11.}
    程序员所导致的错误可以用Swift标准库(Swift Standard Library)函数fatalError("Error message”)来标记,将其在控制台记录为错误消息并无条件中止执行。还可以使用assert(), assertionFailure(), precondition()和preconditionFailure()这些函数。

    Swift第一次发布时,一些非苹果平台开发人员已经准备好了火把和干草叉。他们声称Swift不能算是“真正的语言”,因为它缺乏异常处理。但是,Cocoa和Cocoa Touch社区对此不予理睬,我们知道NSError和NSException那个时候就存在了。就我个人而言,我相信苹果公司仍然在思考实现错误和异常处理的正确方式。我还认为直到问题解决了,苹果公司才会公开Swift源码。这一切问题在Swift 2.0中全被扫清了。 
     
    Swift 2.0中的错误处理

    在Swift 2.0中,如果想要抛出错误,那么抛出的对象必须符合ErrorType协议。可能正如你所愿,NSError就符合该协议。枚举在这里用来给错误进行分类。
    enum AwfulError: ErrorType {  
    2. case Bad
    3. case Worse
    4. case Terrible
    5.}
    然后如果一个可能抛出一个或多个错误的函数或方法会被throws关键字标记:
    func doDangerousStuff() throws -> SomeObject {  
    2. // If something bad happens throw the error:
    3. throw AwfulError.Bad
    4.
    5. // If something worse happens, throw another error:
    6. throw AwfulError.Worse
    7.
    8. // If something terrible happens, you know what to do:
    9. throw AwfulError.Terrible
    10.
    11. // If you made it here, you can return:
    12. return SomeObject()
    13.}
    为了捕获错误,新型的do-catch语句出现了:
    do {  
    2. let theResult = try obj.doDangerousStuff()
    3.}
    4.catch AwfulError.Bad {
    5. // Deal with badness.
    6.}
    7.catch AwfulError.Worse {
    8. // Deal with worseness.
    9.}
    10.catch AwfulError.Terrible {
    11. // Deal with terribleness.
    12.}
    13.catch ErrorType {
    14. // Unexpected error!
    15.}
    这个do-catch语句和switch语句有一些相似之处,被捕获的错误详尽无遗,因此你可以使用这种样式来捕获抛出的错误。还要注意关键字try的使用。它是为了明确地标示抛出的代码行,因此当阅读代码的时候,你能够立刻找到错误在哪里。

    关键字try的变体是“try!”。这个关键字大概也适用于那些程序员导致的错误。如果使用“try!”标记一个被调用的抛出对象中的方法,你等于告诉编译器这个错误永远不会发生,并且你也不需要捕获它。如果该语句本身产生了错误(error),应用程序会停止执行,那么你就要开始调试了。
    let theResult = try! obj.doDangerousStuff()  
    与Cocoa和Cocoa Touch框架间的交互

    现在的问题是,你如何在Swift 2.0中处理爷爷级的NSError API呢?苹果公司已经在Swift 2.0中为统一代码行为作了大量工作,并且已经为未来写入Swift的框架准备方法。Cocoa和Cocoa Touch中可以产生NSError实例的方法和函数有苹果公司的签名( signature),可以自动转换为Swift新的错误处理方式。

    例如,这个NSString的构造器( initializer)在Swift 1.x中就有以下签名:
    convenience init?(contentsOfFile path: String,  
    2. encoding enc: UInt,
    3. error error: NSErrorPointer)
    注意:在Swift 2.0中,构造器不再被标记为failable,它并不需要NSErrorPointer来做参数,而是使用抛出异常的方式显式地指示潜在的失败。

    下面的例子使用了这种新的签名:
    do {  
    2. let str = try NSString(contentsOfFile: "Foo.bar",
    3. encoding: NSUTF8StringEncoding)
    4.}
    5.catch let error as NSError {
    6. print(error.localizedDescription)
    7.}
    注意错误是如何被捕获的,并且如何被转换成了一个NSError实例,这样你就可以获取与其相似API的信息了。事实上,任何ErrorType类型的实例都可以转换成NSError类型。

    最后说说@finally

    细心的读者可能已经注意到,Swift 2.0引入了一个新的do-catch语句,而不是do-catch-finally。不管是否捕捉到错误的情况下,你如何指定必须运行的代码呢?为此,现在可以使用defer语句,用来推迟代码块的执行直到当前的作用域结束。
    // Some scope:  
    2.{
    3. // Get some resource.
    4.
    5. defer {
    6. // Release resource.
    7. }
    8.
    9. // Do things with the resource.
    10. // Possibly return early if an error occurs.
    11.
    12.} // Deferred code is executed at the end of the scope.
    Swift 2.0将Cocoa和Cocoa Touch的错误处理机制凝聚为具有现代风格的用法,这是一项伟大的工作,也会使许多程序员倍感亲切。统一行为是不错的定位,会使Swift语言和其所继承的框架逐步发展。
     
    文章来源:Big Nerd Ranch 收起阅读 »

    大家怎么实现自定义表情的 有谁说说吗 我是这么实现的 不完美啊

    但是不完美  有人有更好得方法吗  求分享
    但是不完美  有人有更好得方法吗  求分享

    全球扫货指南集成环信移动客服:订单量客单价双丰收

    移动互联网时代,同时迎来了全民创业的时代,虽然这两者不能划上等号,但不能否认,移动互联网的普及,给全民创业提供了更多的创新灵感,也赋予了电商更多活力。笔者了解的一些电商,已完全摒弃PC端接入口,而只从移动端进行接入。 在传统大型平台电商的窗口已经被...
    继续阅读 »
    移动互联网时代,同时迎来了全民创业的时代,虽然这两者不能划上等号,但不能否认,移动互联网的普及,给全民创业提供了更多的创新灵感,也赋予了电商更多活力。笔者了解的一些电商,已完全摒弃PC端接入口,而只从移动端进行接入。


    1.jpg


    在传统大型平台电商的窗口已经被逐渐关闭的时候,新入场的玩家必须借助移动端和社交实现弯道超车,移动电子商务公司“全球扫货指南”便是中国移动互联网发展的其中一家受益企业,其市场总监左小禛便认为:电子商务的未来是移动电商,而移动电商必须重视移动客服。

    移动电商的三个关键点

    的确,百度无线数据报告显示,移动互联网创业者开发经验不足1年的人数比例为38.3%,个人开发者比例明显上升,同时11—20人团队开发比例下降,移动开发者生态从量变向质变迁移。此外,移动互联网创业者对商业模式的探索和创新尚无特定模式可以借鉴。上述状况下,移动电商创业者如何实现弯道超车?笔者认为,以下三个关键点需要重点把握:

    1、转变思维,认清客户在哪?

    移动互联网时代用户数的迅猛增长,已是必然趋势。用户数的急速增长,意味着巨大的机会,同时也意味着巨大的挑战,终端的小型化、多样化,接入方式的多样化都意味着我们处在全新的互联网生态环境中。

    Gartner预测,到2017年年底,超过70%的交易将来自于移动端。2014年阿里双十一的现场监控显示,总成交额571亿其中移动端贡献了243亿,移动端交易量比例将有超过PC端的趋势。同时,京东2014年第四季度财报显示移动端的订单量占比接近40%。

    因此,移动互联网时代,电商行业应转变思维,认清客户来源趋势,应在移动端的各个接口做好整体布局,无论是营销、服务还是流程,应进行相应梳理。

    2、移动互联网的营销,社交为王!

    但在移动互联时代,营销渠道越来越多。过去的营销是通过电视、报纸等渠道跟消费者沟通,但移动互联网时代则是分享的时代,免费思维已成为主流,为了让用户喜欢,愿意分享,消费者和商家会直接面对面进行免费试用与体验,通过各种活动进行赠予、使用,增强体验。这也使移动互联网时代的电商营销成本非常巨大。

    移动互联网时代的营销除了普通的宣传、引导、试用、免费等传统方式外,更多的具有社交化属性,用户体验变得如此重要,即时通讯所具有的实时性、互动社交性让移动电商更具活力。甚至很多商家都搭建了基于兴趣社交的板块辅助自身电商平台,通过社区导购来降低用户的购买决策成本,在非标品等方面与传统大电商平台竞争获取优势实现弯道超车。

    3、移动互联网的客户服务,体验为王!

    营销的成功,只能带来一定的流量,但真正的交易与再次销售,则一定是产品本身具有的价值及客户服务所带给客户的体验。我们经常说,客户在哪,客户服务就要跟到哪。客户在移动端,移动端的客户服务如果不到位,不但订单丢失,客户丢失,甚至公司也可能被那些更重视移动端的用户服务体验的公司,更能迅速把握时代大趋势的公司所颠覆。一项研究报告指出,如果做不好客服,辛辛苦苦通过各种渠道引流到自己App、网站、平台、账号的客户,95%都会流失掉。

    因此,电商已从PC端转战到了移动端,移动端有其特定的消费属性,无论是营销与服务,应从移动端的消费属性来研究。

    移动电商的客服趋势

    如果说电商的移动性趋势已得到行业认可,各个大、中型电子商务公司正在奋力奔跑在移动端,正在全力争夺消费者手机上不多的APP安装上。但笔者需要提醒大家的是:太多企业过于重视营销,而对客户服务有所忽视。

    客户服务的重要性已不需要赘述,笔者认为移动电商客服有几个趋势将带动电商企业的发展:

    1、 多渠道融合,尤其以移动端为主的客服请求将占主导

    客户服务请求来源包括传统呼叫中心的语音、基于网页的实时在线聊天系统、工单系统、微博、微信等等。客户来源的多样性,让多渠道融合客服成为必须。另一方面,正如上文所述,如今的消费者正在远离PC和电话,消费者都去了移动端,所以客户服务将以移动端客服请求为主导。

    2、 即时通讯(IM)将是最适合移动设备的客服服务形式

    微信已成为我们最重要的日常沟通形式,界面简单易操作,不需要任何培训,人人都会使用,最重要的是,这是最适合移动设备的沟通方式。互联网女皇发布的《2015互联网趋势报告》也认为: IM不仅是世界上标准化程度最高的互联网产品,而且是世界上下载和使用量最大的互联网产品,也是用户交流体验最好的产品。异步又实时、个性化又主流、表达能力很强但又迅速等等。

    传统400电话虽然也方便迅速,但笔者也经常因为400电话的无尽等待而挂机。虽然对12306这样的订票刚需企业影响不大,但如是电商企业,那客户流失率就太高了。因此,即时通讯式的客服形式非常适合移动设备,一个发送就能即时接送信息,按一个按钮就得到服务,可以秒级接通,并且可以得到7X24小时随时随地的解答。不用去填写工单,也不用在APP中遇到问题的时还要跳出APP外通过电话,邮件,微信,QQ等方式才能后被解决。

    3、智能客服技术将得到大量应用

    上文提到了即时通讯式的客服形式非常适合移动设备,移动互联网时代,客户手机24 小时随身携带的属性决定了传统5 X8小时的服务形式早已满足不了客户需求。客户需要实现一个发送就能即时接送信息,可以秒级接通,并且可以得到7X24小时随时随地的解答。我们可以想象的是,这个7X24小时随时随地的服务并不是企业派了足够多的人工座席在为客户提供服务,而是智能客服技术的一项应用。

    智能客服技术是实时聊天的一种技术应用,因为即时通讯沟通方式最大的优势是可以一对多,即一个客服坐席可以同时和多个人聊天,使用自动菜单导航,语义分析,机器人智能应答,智能知识库等技术的大面积应用,“智能机器人+知识库”可以帮助回答80%的常见问题,可大量减少传统座席人员数。

    移动电商客服的三大难点透析

    通过笔者对移动电商客服的几个趋势的梳理,我们看到,移动电商的消费群体的特定性,让移动客服与传统客服具有很多的不一致性,移动客服如需要做精做透,以下流程是难点:
     1、 集中的质检系统

    传统语音客服因为可以通过录音、检索等方式进行客户服务质量检测,移动客服的即时通讯性、多渠道接入等属性,使客服的质量把控具有一定的难度。

    2、 交易行业的大数据积累与分析

    大量的移动端客服数据与传统来自于呼叫中心、Web端、微博、微信的客服数据,如何进行统一接入、积累与分析,进行数据挖掘实现商业转化,这是较大的难点也是最重要的商机,客户趋动才是需求的起点,是创新的动力。

    3、 即时客服需要即时的信息管理

    移动互联网时代,服务的即时性也对管理的即时性提出了很高的要求。移动电子商务公司全球扫货指南市场总监左小禛便深有体会:“移动电商经常会定期或者不定期举行一些活动,活动举办期间,服务请求较平常可能会有十倍甚至更多倍的增长,这样的环境下,如何去调配座席人员?如果知道何种渠道来源的增长比例?服务过程中出现了哪些问题?哪些是共性问题等等。移动环境下,即时客服要求有即时的信息管理。我们采用了环信移动客服,解决了我们客服过程中的很多难题。“

    全球扫货指南的移动客服初体验

    全球扫货指南是全球首家时尚买手制购物平台。平台上有各类买手推荐的奢侈单品,产品的价格搜索引擎可以帮助用户一键比价,比完之后觉得划算就可以找靠谱买手下单购买。全球扫货指南作为交易平台,实行第三方担保机制,若买卖双方发生纠纷,提供先行赔付。在CEO肖宇眼里,随着这几年电商的蓬勃发展,用户线上消费习惯正在快速被培养。目前客单价千元以上的电商玩家并不多,另一方面用户的消费升级不断加速,奢侈品购买呈年轻化,因此在手机上消费的客单价越来越高,将会给轻奢市场带来巨大的机会。
     
    据左小禛介绍,全球扫货指南接入了这种智能移动客服产品后,可以实现快速注册、根据体验指南一两分钟内即可对接商家完成会话体验,同时实现了跨平台多渠道接入:支持 App、微信公众账号、微博、网页等,均可以快速统一接入客户服务后台管理。

    其次,满足了开放性与自定义信息:这种新型的移动客服实现了代码开源、UI开源,还提供多套UI模版,这样便于与全球扫货指南自身的APP快速集成,平滑接入。同时第三方集成功能也很强大,可与第三方工单、知识库、CRM 系统等进行扩展集成。

    第三,富媒体消息交互体验:移动客服平台提供了基于IM技术的类似微信体验的友好富媒体消息交互,不仅可以实时收发文字、表情,还可以即时收发图片、位置、实时语音、还可自定义消息,大大方便了与客户的沟通交流。

    第四,精准客户画像功能:移动客服的IM沟通帮助全球扫货指南辅助判断客户需求与诉求,通过自定义客户分类标签,客户再次访问时可获知客户的类型。并且还支持自定义会话小结,根据会话小结统计会话的分类,通过会话小结追踪客户诉求。通过轨迹分析功能,即通过发送客户访问页面的轨迹,判断客户意图,获取客户个性化细节,了解客户基础信息,分析客户行为,可以极大提高订单效率。

    第五,“智能机器人+智能知识库”:作为一套智能化的客服系统,全球扫货指南建立了基于业务的智能知识库,智能辅助归结业务信息和应答客户信息,历史常见问题系统梳理,提高客服效率。“智能机器人+智能知识库”组合目前可自动回复80%常见问题,随着智能知识库的不断训练,这一比例能够提高到90%。

    第六、真正实现不丢订单:环信基于IM长连接技术,帮助全球扫货指南实现超线拉取排队会话,让等待时间过长或有订单需求的客户得到优先服务,提升客服效率,即使客户关闭会话,退出App,甚至关闭手机,基于IM长连接技术依然能找回客户会话,提升客服效率,不丢客户订单。同时,基于IM长连接技术,即使在断网或复杂网络切换时也依然能找回客户会话,不丢客户订单。

    第七、实现了质检系统透明管理:移动客服自带实时监控系统,可实时监控当前所有会话,管理员在线质检,在线查看会话信息,还提供了历史会话查询、质检考核。通过实时与历史查看,可以统计出客服代表的接入量、在线时间及应答等待时间。

    通过销售部和技术部返回的数据报告显示,左小禛判断,通过部署这种新型智能移动云客服,较于传统客服产品节省成本约60%,省电省流量高达80%,同时留住了70%会话客户,订单量和客单价均获得了不同幅度提升。目前全球扫货指南覆盖全球100个顶级奢侈品品牌,超过10万个热门单品。根据贝恩咨询的数据,中国人去年一年消费奢侈品3800亿元,其中70%在海外消费,仅代购市场就超过750亿元,奢侈品的海淘电商无疑是块很诱人的蛋糕。

    移动客服的魅力,从上述数据的发布可看出,已超出了大家的想象!原因在于:传统客服体系下,客服只是成本中心,辅助部门;而在移动互联网时候下,客服俨然已成为核心流程之一,竞争力关键要素之一。

    一位客服领域大佬曾这样总结这个时代:“伴随着客户互动的革命,从呼叫中心演变到的客户中心作为不断扩展的客户交互平台概念一直经历着深刻的变革。运营日益规模化,精准化、多通路化、智能化和普适化。作为集语音和非语音渠道为一体,人工与智能服务共协同,物理与数字的通路大融合,随着以客户理念,人文为中心时代的来临,客户服务中心正从企业价值链的一测走向价值传递的中央,与惊艳的产品研发一起发挥着客户体验创造的核心角色。”

    移动客服,你已不能再等待! 收起阅读 »

    环信CEO:环信移动客服技术全解析

    摘要:《近匠》第92期,在专注于为开发者提供移动端即时通讯解决方案的同时,环信也开始了在连接人与商业、物与物版块的IM生态布局。环信CEO刘俊彦深剖环信移动客服的核心技术,以及对开源与未来无所不在的客服场景畅想。上线一年多的时间,环信的团队已经扩大到一百多人,...
    继续阅读 »
    摘要:《近匠》第92期,在专注于为开发者提供移动端即时通讯解决方案的同时,环信也开始了在连接人与商业、物与物版块的IM生态布局。环信CEO刘俊彦深剖环信移动客服的核心技术,以及对开源与未来无所不在的客服场景畅想。上线一年多的时间,环信的团队已经扩大到一百多人,在专注于为开发者提供移动端即时通讯解决方案的同时,环信开始了在连接人与商业、物与物版块的IM生态布局。并于近期推出专为移动端打造的客服平台,与环信IM SDK共享核心代码。本期《近匠》专访环信CEO 刘俊彦,听他讲述打造环信移动客服的核心技术与挑战,以及环信对开源与未来无所不在的客服场景畅想。


    1.jpg


    图:环信CEO 刘俊彦

    CSDN:最初决定打造这样一个移动客服平台有着哪些初衷和考虑?

    刘俊彦:首先从现实说起,从去年上线之后,就不断地有海淘、生活O2O等领域的开发者表示,使用环信IM SDK来做手机App的内置客服通道还是很好用的,但希望环信同时还能够提供一个具备客服工作台、工单分配、KPI考核等功能的客服平台。其实我的第一反应是拒绝的,因为这好像与即时通讯本身并没有太大关系,但随着这方面用户需求的增长,我们便开始思考这个问题。

    其次,与战略方面也有所契合,我们所希望的是能够通过环信将所有人、物都连接起来,但之前却只拘泥于连接人与人的社交,在环信的版图中缺少着连接人与商业、人和客户的版块。因此,我们决定开发移动客服系统,让用户和商家可以直接在应用中聊起来,打造出一个完整的IM生态蓝图。

    CSDN:在环信的产品线中,移动客服平台有着什么样的定位?

    刘俊彦:移动客服平台是一个在IM基础上的行业应用,在IM已经覆盖几亿用户之后,我们就在思考下一步做什么?如果一款产品或某项功能能够真真切切地帮助到我们的IM用户,体验更爽,更有粘性,我们都会进行尝试。其实我们做了很多类似的工作,比如最近上线的关键字检测功能,就是为用户提供IM之上的最后一公里的额外的服务。移动客服同样如此,移动客服的核心还是IM,但我们同时还提供全套的完整的客服工作后台,有客户服务支持需求的用户不需要任何开发就可以直接使用,因此,我们将移动客服定位为IM在客户服务行业的垂直应用。

    CSDN:云客服模式其实早有应用,相比其他服务提供商,环信移动客服平台有着哪些特色及技术优势?

    刘俊彦:当下的客服软件市场分为两部分,一是传统电话呼叫中心的客服,他们的沟通方式以语音沟通为主,第二类是非语音类的客服,一般归纳为新媒体客服。新媒体客服包含微信公众号、微博、网页、App等渠道来源。对网页端的客服渠道来说,其对客服服务器的并发技术要求并不高,因为通常是使用基于轮询的短连接技术。比如每天有十万人找客服咨询聊天,每人聊五分钟,那么平均到每秒钟也就最多2-3个人同时在聊天,服务器只需要支持每秒钟2到3个请求的并发,对服务器压力很小。但手机App客服就不一样了。从技术上来讲,好用的手机App客服技术要求手机与客服服务器之间保持着7x24的长连接。任何时候,只要用户不卸载你的App,客服就永远可以主动的去找到消费者,向他主动发送消息。举个例子,我们有的用户用这个技术做外呼营销。他先在自己的CRM系统里对他的用户挖掘打上标签,比如找到所有的2-4岁小孩的妈妈用户群体。然后向这类妈妈群体群发消息。发出去的促销消息在消费者的App端看来是一条推送过来的聊天消息,首先这个消息不会被错过。然后点击这个推送后就会进入到客服的一对一聊天页面。这样的促销消息发送方式因为是和真人客服之间一对一的双向交互,所以效果比那种单向的广告推送好很多,转化率非常高。类似这样的外呼营销方式,让客服部门从以前的成本中心变成了现在的营收中心。而这些营销方式,如果没有手机APP和客服服务器之间的IM长连接,是不可能做到的。

    当然,IM长连接技术对服务器的技术能力以及成本压力也增大许多,因为哪怕一个用户从来没找过客服聊天,我们也要为这个用户在他的手机和服务器端之间维护一个长连接,这个长连接会7X24小时的运行着。通常有100万的月活,就意味着要维护100万的长连接。但这却是环信的优势所在。环信移动客服平台与其他客服提供商最大的区别就是在移动端的IM长连接技术。在这点上,环信基本是垄断地位。

    CSDN:可否详解环信移动客服平台的智能应答系统在面对海量请求时,如何做到真正智能地屏蔽与提炼?

    刘俊彦:全球有100亿设备24小时连在网上,他们随时随地都能通过移动设备内置的客服通道找到客服,所以移动端的客服要应对的请求可能是海量的。对客服部门来说,利用原本的人工客服团队再加上新型的智能机器人客服系统,可以让问题解决效率呈指数性增长。传统的聊天机器人技术是基于搜索的问答匹配,而环信的智能客服是最新的基于深度机器学习的技术,不仅可以根据上下文进行推理,还能对人工座席和客户的交流进行实时学习和训练,极大降低知识库的维护成本。

    目前,智能聊天机器人技术最实际的应用场景是基于文字交互的客服系统,包括短信、微信公众号、App内置客服、网页在线客服等,不仅降低了原本一年四季24小时在线的客服中心成本,更让拥有极致用户体验的新一代客服中心成为可能。

    但是,相比之下,人工服务永远是最智能、最好的服务,在产品设计上,应实现人工与智能服务的无缝切换,当智能客服回答比较勉为其难的时候,就直接切换到人工服务,这样才能为客户提供更好的体验。所以客服系统中的聊天机器人技术最重要的是做好人工座席和机器人座席之间的无缝高效的切换,而不用刻意追求智能机器人技术的智能程度。客服系统中的智能聊天机器人只需解决特定领域的知识问题,无需像微软小冰那样陪用户聊人生聊理想。

    CSDN:在移动客服平台开发的过程中,遇到过哪些难点与挑战?如何解决的?

    刘俊彦:对移动客服来说,有两个技术难点。首当其冲便是在手机上为客户提供服务,IM技术是最适合用来在手机上连接客户和商家的。IM长连接技术能保障客服随时随地主动联系到客户、断网或复杂网络切换时不丢客户,并对电量流量进行优化。同时,也必须让客服人员、商家使用起来非常爽,即客服工作台好不好用、能不能提高客服效率、是否能进行全方位的KPI考核等。

    对客服而言,即时通讯可谓是重中之重,在纯社交领域,消息丢失或晚到可能不是太大问题,但客服不一样,如果一条消息丢失或延迟,可能就意味着会发生丢单、客户流失情况,商业损失会非常大。所以对于选购移动客服的商家来说,他们希望移动端的IM技术和支撑客服工作人员的客服后台技术能够由一个厂商来统一提供。而不是说需要客服后台用这家,即时通讯买那家,这样在使用移动客服产品过程中遇到任何问题,都可以通过一个厂商一次性地及时解决。

    CSDN:在移动网络环境下,弱网络、丢消息、丢订单等问题天然存在,环信采取了哪些技术手段来解决这一难题?通过哪些机制确保做到真正的不丢消息、不丢客户订单,还能省电省流量?

    刘俊彦:这是环信过去一年多一直在做的事情,其实大多数IM厂商也能保证做到,但这的确还存在一些技术难点。首先,如何保证不丢消息?就必须通过一个好的协议来实现,必须保证协议本身是完全可靠,一定不会丢消息的,而在电量、流量的节约方面则包含协议、实现等诸多层面的优化。当然,无论是电量、流量的节约还是保证消息及时到达,除了最根本的协议、架构等,还包括许多细节方面的长时间打磨,才能不断地进步。
    举个例子,环信在去年针对用户需求进行了许多优化,其中有一点便是针对来自三四线城市使用2G网络的用户,在弱网络环境下发送图片速度非常慢的问题,进行了切片上传的优化,将一个50K大小的图片切成10片,开10个线程,同时上传,极大地提高了传送的成功率。

    CSDN:将UI模板开源让开发者100%自定制是环信一直以来的做法,之前也上线了“更极客 更开源”的IM Geek开发者社区,并将SDK开源,请问环信在开源方面有着哪些具体的计划和措施?

    刘俊彦:环信的创始成员都是做开源出身,现在有些人对开源软件存在误解,而我们从不认为开源软件就意味着不好、不安全、低质量,这点从Linux就可以显而易见。我曾在Redhat担任高级开发工程师的工作,现在,许多公司都在使用Linux,也没有人会给予Linux不安全、不好用等差评。我们所希望的,不是自己重新去发明轮子,而是站在前人的肩膀上,取其精华去其糟粕,来进行自己的开发,比如最初在设计环信的通讯协议时,我们就借鉴了一部分XMPP的核心模块精华。

    但XMPP协议并非为移动端设计,XMPP出现时还没有移动设备。在PC时代,用户不需要考虑电量、流量等问题,并且也不存在弱网络导致消息丢失的问题。这也就表明,我们必须将其中不好的部分推倒,进行重新设计或重写,开发出适用于移动端的IM协议。当然,XMPP也拥有很多优点,它存在十年,有许多插件,并充分考虑到协议的扩展,它是经过许多人不断摸索制定出来的协议,拥有着非常高的价值。

    在使用开源软件的同时,我们也会反哺回馈开源社区,环信公司内的技术团队现在至少有十多个人都是一些知名的开源软件的committer,一直都在为开源软件贡献力量。现在有些公司可能会担心用开源软件出了问题后不可控制,这说明他们并没有用真正开源人的精神去使用开源软件,对于存在瑕疵的地方,就应该去改写代码,让它变得更好,而不是直接弃之如敝履。在开发过程中,如果使用到存在问题的开源软件,我们要做的事情就是去进行修改完善,然后将代码提交回去,让整个开源社区都能收益,这是环信对每个工程师的要求。

    无论是将UI模板100%开源还是上线IM Geek开发者社区,我们的真正目的就是为开源社区做贡献,而不只是环信的产品。其实在当下的国内外开源社区方面,IM和移动社交还是空白的存在。现在,很多人都在做朋友圈、附近的人、聊天、匿名聊天或群组等功能,但归纳起来大概只有二三十种模块,剩下的工作便是各种模块的组合及编组。所以我们的想法就是开发者不用自己再去劳心劳力地重新开发一些基础和常见模块,而是通过社区的力量将这些模块都开发并开源出来,甚至包括基于这些模块的完整的应用,统统开源。这样不仅能节省创业团队大量的时间精力,也能降低开发成本,将资金投入到更好的用户体验、设计及运营上。

    要经营好开源社区就必须以身作则,环信自身也在不断地贡献开源模块。当然,开源社区光靠一时的热情是远远不够的,未来,环信会为乐于开源的committer提供经济上的资助,只要开源软件做得好,并将其回馈给社区,提供开放下载。我们非常欢迎有开源情怀的开发者投身到开源事业中。

    CSDN:环信移动客服在安全及保护用户隐私数据等方面有着哪些具体措施?

    刘俊彦:隐私一直是很多公司一直在意的问题,会担心商业数据、用户信息会不会被窃取?环信移动客服对这方面是非常注意的,不会以任何形式碰触用户的聊天记录,用户可以100%随时导出或删除。而在系统保障方面,环信到目前已经趟过了许多雷点,并且内部还有一个每月演习的硬性规定,以此来保障即使发生系统挂掉的情况,核心功能依然可用。此外,环信正在着手“异地多活”,以此来提供更可靠、安全的服务。

    CSDN:移动客服系统如何收费?

    刘俊彦:IM已成红海,而移动客服则是商业化的企业级服务,需要为企业出现丢单等情况负责。环信移动客服系统的收费标准分为免费和收费用户数量级,2个座席以内可免费使用,超过2个座席的公司,需按每年每座席1500元进行收费。


    2.jpg


    图:环信移动客服收费标准

    CSDN:现在大部分客服请求均来自于移动端,环信对于未来的移动客户服务还有着哪些畅想?

    刘俊彦:当下,客户服务软件行业正在经历一场变革,用户获得服务的渠道和行为在发生巨变。随着环信移动客服这样的产品的普及,用户的服务体验将有极大的提升,而依托于技术进步,商家为此投入的人力成本却呈下降趋势。环信已经实现了人与人、人与商业的连接,但其生态布局依然缺少一块,就是人与物、物与物的连接。物联网正在临近,客户服务更将呈现完全不同的场景。我们需要做什么?首先就是物与物之间实现通讯功能的实时数据交换,其次,人如何控制设备?第三,在物与物之上,人和人之间需要沟通。

    比如,一个物联网冰箱发生故障后,用户肯定不会再打400电话了。用户只需直接点击冰箱内嵌的客服按钮,即可一键接通,不仅可以和客服视频聊天得到帮助,还可以将冰箱的相关参数发送给客服,快速解决问题。同时,未来的客服一定是内嵌型的,将无处不在,无论是App、智能家居还是车联网,一个按钮所有问题都能搞定。目前,环信已经在着手物联网IM产品研发。 收起阅读 »

    App崩溃分析:如何监控http请求并做出优化?

    移动互联网时代,移动App与服务器之间的交互越来越频繁,数据量也越来越大,伴随而来的各种网络连接问题也在影响着各App的留存率。如何保证http请求的质量成为开发者们需要解决的一大问题。本文着重讲述http请求遇到的问题以及该如何监控和优化。 http请...
    继续阅读 »
    移动互联网时代,移动App与服务器之间的交互越来越频繁,数据量也越来越大,伴随而来的各种网络连接问题也在影响着各App的留存率。如何保证http请求的质量成为开发者们需要解决的一大问题。本文着重讲述http请求遇到的问题以及该如何监控和优化。

    http请求遇到的问题

    在App开发过程中,通常是用无线网络去做测试,这样网络的连通率、速度以及响应时间都是处在一个相对理想的情况下,但是在App发布后,用户使用场景往往是2G、3G,以及一些网络水平初级的地区,网速和连通率都在一个不稳定甚至极低的水平,这时,一系列的问题随之而来,其中我们主要讨论两点:

    • 响应时间:由于网络速度的下降, 响应时间开始变长,同一个url的访问时间可能会成倍增长,增加用户的等待时间;

    • 错误率:由于网络质量的下降,丢包错包概率成倍增长,由于请求的错误也会导致服务器端处理错误率的提高,可能会造成返回数据为空或者错误,致使用户增加使用成本。



    如何去优化?

    应用向服务器发送http请求,一般都是调用的系统接口或者第三方接口(比如OKHttp):    

    1.jpg

    这个时候,我们可以在应用调用接口时,加一个收集模块来采集http信息,如图:


    2.jpg

    Collection module可以用不同的实现方式:iOS利用Runtime通过代码注入的方式去获取相关信息;Android通过自定义URLStreamHandler去获取http信息,之后通过一些统计工具可以直观地去分析和优化应用的网络模块。

    如何利用网络监控去做优化?

    那么,究竟该如何利用好网络监控来进行应用优化?开发者可以从http响应时间、http错误率、请求量(rpm)和Data I/O四个维度监控http请求,并监测每个url的运营商、终端设备、错误码。同时,地理定位功能可以提供每个地区网络状况的平均值,方便开发者去分析和优化。

    • http响应时间



    通过响应时间的长短,可以判断哪个url去优化,如果响应时间长,是不是后台查询过慢?还是因为运营商基站建的少网络信号差?

    • http错误率



    通过错误率,可以知道访问某个url时最多的错误码是多少,根据错误码去确定是请求错误还是服务器错误,从而缩小问题的查找范围。

    • 请求量(rpm)



    通过查看url的请求量,可以去推断用户的喜好,从而做一些定制化的服务,也可以通过这个指标去确定App端发请求频率是否正常。

    • Data I/O



    通过Data I/O,可以知道某个url的请求数据大小是否正常,是否流量过大可以进行压缩从而节省用户的费用。


    3.jpg


    当然,每个指标不是独立的,而是应该综合来判断。举一个例子,做一个在线图库的应用,用户反映一直刷不出图,那就可以这样推断一下:看http响应时间,如果正常,有可能是App收到数据后显示有问题,如果过长,那么可能是服务器端问题;再看错误率,如果很高,那么有可能是服务器返回结果有问题,如果不高,那么有可能是因为网络信号差导致的。

    这个时候,再看一下请求量和Data I/O,如果请求量很小,但是Data I/O很高,那么是否可以优化http请求策略?如果改成每次请求一屏的数据,根据用户的翻页情况发送多次请求,而不是一次去请求几页数据,那么这样数据总量没变,但是用户看到的效果却是每翻一页,只要很少的时间就可以显示出图片,这样就可以提升用户体验。

    简单论述了网络问题对于移动App产生的影响,以及如何去监控http请求并做出优化。当然,实现监控的方式有很多,也可以有很多技术上的玩法,但无论技术实现得有多好,如何优化App?提升用户体验和留存率,才是开发者最该关心的问题。
      收起阅读 »

    一个社交App是如何构建高伸缩性的交互式系统

    一个社交App需实现的功能 用户关注的常规社交功能、活动、地理位置、探索功能、新鲜事、视频照片分享等等,需要提供的功能不胜枚举,所以从技术角度来说,开发者需要解决的问题也是异常复杂的。 当一款社交App发布之初,用户访问量比较小,使用一台服务器就能...
    继续阅读 »
    一个社交App需实现的功能

    用户关注的常规社交功能、活动、地理位置、探索功能、新鲜事、视频照片分享等等,需要提供的功能不胜枚举,所以从技术角度来说,开发者需要解决的问题也是异常复杂的。

    当一款社交App发布之初,用户访问量比较小,使用一台服务器就能够支撑全部的访问压力和数据存储需求,但是互联网应用具有病毒式的传播特点。一款App很可能会面临一夜爆红的现象,访问量和数据量在短时间内呈现爆发式增长,这时候会面临的局面是每天上亿PV、数百万新增用户和活跃用户、流量飙升至每秒数百兆。这些对于一个只部署了简单后端架构的应用来讲是无法支撑的,会直接导致服务器响应缓慢甚至超时,以及在高峰期时服务呈现瘫痪状态,使得后端的服务完全无法使用,用户体验急剧下降。本文将会通过一个真实的案例来分享一个社交应用如何构建一个具备高伸缩性的后端系统。

    社交App最初部署的后端架构解析

    社交App在最初的时候,后端架构相对比较简单,最初是部署在基础网络之上。最前面放置一台绑定了公网IP的nginx服务器作负载均衡,后面放置3台应用服务器来负责处理所有业务上的请求,最后面搭建一台MySQL Database数据库。


    1.jpg


     
    构建私有网络

    随着产品的不断迭代、用户数的持续增长、数据量的积累,App就需要改进自己的后端架构,即开始构建私有网络。用户可以使用私有网络构建自己的网络拓扑——创建路由器和私有网络,将后续加入的用于运行内部服务的主机放置在私用网络中,可以有效地和云平台其他用户主机,在网络上实现100%二层隔离。主机对外开放的仅仅只有80端口,这样系统安全性上多了一层保障。


    2.jpg



    在上面的架构图中,最前面的是防火墙,后面接负载均衡器,然后接路由器和私有网络,很多互联网应用都存在读多写少的情况,这个比例有时可以达到8:2,所以我们首先通过引入缓存分摊数据库读压力。其次,引入负载均衡器,替换最初架构中的nginx proxy,负责均衡器在这里其主要用于分发请求到后端多台应用服务器,,当其中一台应用服务器挂掉,负载均衡器可以进行自动隔离。

    业务分区与扩展

    App随着并发访问量和数据量不断增大,首先想到横向扩容Web服务。水平扩容业务服务器的前提是要保证每台服务器都是无状态的,将session信息下放到缓存或数据库中存储,保证请求被负载到任何一台服务器可以正常处理。


    3.jpg



    从上图中看到,在前一步「构建私有网络」之后,增加了一个新的私有网络来扩展网络层,这里可以利用自有映像功能,将原有的应用服务器制作成模板,后续就可以基于这个模板快速启动新的主机。另外可以利用Auto-scaling(自动横向扩展)功能,根据后端服务器的负载请求,动态调整服务器的数量。

    一个社交应用的后端会提供很多服务请求接口,比如添加好友、刷新新鲜事、浏览页面等,可以通过日志分析每一个接口的耗时,将耗时长但非重要业务的请求分到单独的Web服务器上进行处理,从而给主Web服务器留出更多资源去处理关键业务的请求。

    面向服务的架构

    随着产品功能的不断迭代,业务代码会越来越复杂,出现故障的可能性也在加大,当一个局部功能出现问题时,都会影响整个服务的可用性。此时可以构建面向服务的架构,将一个完整且庞大的服务拆分为一个个的子服务,服务之间通过接口交互。如下图所示:


    4.jpg



    社交App的服务被拆分成了四个子服务——新鲜事(News Feed)、用户资料(Profile)、广告(Ads)和探索(Explore),不同的服务之间通过消息通信框架(例如ZeroMQ)来进行交互。把一个大服务拆分为几个小的子服务的好处不言而喻,主要是:

    • 故障隔离:子服务出现故障不会影响全局,比如广告业务出现问题并不会让整个App不能使用,依然可以查看新鲜事等;

    • 独立扩展:每一个被拆分出的子服务有着不同的访问压力,比如新鲜事的调用相比一些二级页面的用户资料要高很多,所以前者会被分配更多的Web 服务器;

    • 独立部署:一个大服务的配置因功能过多会异常复杂,一旦被拆分就可根据不同的特性需求定制配置项,从而提高可管理性;

    • 团队协作开发:开发者都有着自己精通的方向,从而提高开发效率;

    • 抽象出数据访问:在后续进行数据层面(数据库、缓存)扩展时,可通过修改子服务的Data Service,实现对下层数据的透明。



    数据库Replication

    业务增长也会给数据库带来诸多问题,当最初架构中单台数据库(数据库同时提供读和写)不足已支撑起App访问压力时,首先需要做数据副本Replication。市面上常见的MySQL、MongoDB等数据库都提供Replication功能,以MySQL为例,从高层来看,Replication可分成三步:

    • Master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events);

    • Slave将Master的binary log events拷贝到它的中继日志(relay log);

    • Slave重做中继日志中的事件,将改变反映它自己的数据。



    具体实现该过程的第一部分就是Master记录二进制日志。在每个事务更新数据完成之前,Master在二进制日志记录这些改变。MySQL将事务串行的写入二进制日志,即使事务中的语句都是交叉执行的。在事件写入二进制日志完成后,Master通知存储引擎提交事务。

    下一步就是Slave将Master的binary log拷贝到它自己的中继日志。首先,Slave开始一个工作线程——I/O线程。I/O线程在Master上打开一个普通的连接,然后开始binlog dump process。Binlog dump process从Master的二进制日志中读取事件,如果已经跟上Master,它会睡眠并等待Master产生新的事件。I/O线程将这些事件写入中继日志。

    SQL slave thread处理该过程的最后一步。SQL线程从中继日志读取事件,更新Slave的数据,使其与Master中的数据一致。只要该线程与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小。

    此外,在Master中也有一个工作线程:和其它MySQL的连接一样,Slave在Master中打开一个连接也会使得Master开始一个线程。复制过程有一个很重要的限制——复制在Slave上是串行化的,也就是说Master上的并行更新操作不能在Slave上并行操作。

    对于云计算使用者来说,只需要知道数据库的IP和端口即可进行使用。具体实现见下图:


    5.jpg

    第一步要做的是扩充Slave,将单机Master变成Master+3台Slave的架构,而在其中的Slave上搭建一个内网的负载均衡器(Load Balancer),对于最上层的Data Service来说,只要配置一个MySQL Master节点和一个LB节点即可,今后因业务变化进行增减Slave对上层来说完全是透明的。

    此做法可以带来两个好处,第一是提高可用性,若是一台Master出现错误,则可以提升某一台的Slave作为Master继续提供服务,从而保证数据可用性;第二个是分摊读压力,对于一个社交App来说,读写分离是在数据层优化第一步要做的事情,利用上面的架构可以很轻易地做到将读的请求分担到MySQL Slave上进行查询,而写留给Master。但是读写分离时会有数据库一致性的问题,即在数据写至Master之后同步到Slave有一个延迟的时间,对于社交应用来说,这是可以接受的,只要保证数据的最终一致性即可。

    在上图的最下面有一个Snapshot,即定期对数据进行冷备份,这不同于单纯对MySQL Master进行复制的Slave,因为线上bug或误操作会删除Master上的数据,这时会立即同步到slave上造成数据丢失这时冷备份Snapshot就会起到数据保护作用。

    运行过程中肯定需要监控,用户可以利用Linux上的工具进行统计分析top / iotop / df / free / netstat等工具去监控系统里的各个服务和组件是否正常运行,以及通过日志的信息(http access log / application log / database slow log )分析各个服务的性能瓶颈。

    数据分区与扩容

    下一步业务的调整要进行数据库的分区和扩容。第一,构建缓存集群,在开始的架构中引用了Memcached缓存,是单机数据库缓存。当数据量增长,,需要把数据分散到多台缓存服务器上,常用的是HashRing算法,好处在于不管是添加结点还是删除结点时,只会使得少部分数据失效。还可以引用NoSQL数据库,这里用到了Redis把社交数据里对于关系要求不强但对查询效率要求很高的数据从MySQL里拿到Redis里存。Redis尤其适合存储列表类数据,比如好友关系列表、排行榜数据等。


    6.jpg

    除此以外可以考虑做数据分区对于MySQL第一步是垂直拆分,把原来单独的数据库按照功能模块分别拆分成:好友新鲜事、用户资料、广告数据以及探索数据。对于Redis也同样,将原来的单台Redis按照功能模块拆成四个,分别为:排行榜数据、好友、广告数据、探索数据。

    接下来会遇到的瓶颈是单表过大的问题,这时候我们需要做水平拆分——把一个表拆分成多个表,需要选取一个分区Key,比如对用户表做拆分时,通常选取User ID。分区key的选择主要是看所有的查询语句频繁使用哪个查询字段,就选择那个字段作为分区key这样能保证大部分的查询可以落在单个数据表上,少量没有带分区Key的查询语句,可能要遍历一遍所有切分后的数据表。

    构建完整的测试环境

    构建完整测试服务器时需要创建新的路由器和私有网络、独立的网络环境和带宽资源、内网GRE隧道打通路由器、VPN拨入网络和SSH密钥管理。


    8.jpg

    这个过程你可以创建一个包含所有系统服务的all-in-one的环境,将其制作成自有映像。如果后续你的团队来新的人,需要独立的完整开发环境,只需基于自有镜像快速创建主机即可;还可以利用User Data定制化功能,在主机启动执行一段你上传的脚本,来初始化环境。你可以将这两个功能结合起来用,把所有你所需要用的服务全部安装部署完毕后做成映像,并用User Data脚本从代码库里更新代码。因为代码的变动相对于环境的更新更加频繁,不可能每次代码的更新都要构建一个新的自有镜像。通过这种方式构建起一个完整的测试服务器,让每个工程师都可以有自己独立的测试服务器。

    在App发布上线时需要连到线上环境怎么办?这两个网络本身完全100%隔离,可利用GRE隧道的功能,把两个路由器打通,实现测试环境网络和线上生产环境网络的完全连通。

    多机房部署与混合组网

    为了让后端架构更可靠和业务更稳定,就需要实施多机房部署和混合组网。具体原因有以下三点:

    • 异地容灾:在复杂的网络环境下,机房可能会出现网络状况,导致一些比较关键性的业务的可用性降低,备份机房后可保证服务不会出现明显的长时间中断;

    • 负载分摊:单独一个机房可能不足以支撑全部的请求,这时可以把一部分的请求压力分担到另一个机房;

    • 加速区域访问:在国内网络环境下,南方和北方相互之间网络访问时有较高的延迟。通过做多机房部署实现加速区域用户的访问。




    9.jpg


    如上所示,有三个机房,中间是QingCloud北京1区机房,负责主营业务。左边是亚太1区机房,主要服务亚太和海外的客户。这两个机房都使用了QingCloud私有网络部署,利用路由器,通过GRE隧道或者IPsec加密隧道的方式进行互通。如果对数据传输过程的安全性要求较高,可以用IPsec的方式把两个机房相互打通,这时的访问只能通过内网IP进行访问。右边是办公室机房,工程师在这个环境下进行开发。

    在实现混合组网时,只要机房路由器或者网宽设备支持标准的GRE隧道协议、IP隧道协议,就可以将传统物理世界的机房与路由器连通,并最终打通公有云环境。多机房部署通常见的方案有这些:

    • 异地冷备份



    把主机房全套业务在异地重新构建一遍,且不需要提供线上服务,只有在主机房出现故障的时候才切换到备用机房,部署相对要简单一些。但有两方面缺点,一是成本比较高,需要双倍的费用且只是用来做冷备份,平时完全用不上;另外,当主机房突然挂掉时,备用机房再起动起来提供服务,数据需要预热,这是非常缓慢的过程,可能会出现服务响应慢,甚至不能正常提供服务。

    • 异地多活



    从易到难有三阶段:第一,反向代理,用户请求到第二个机房,但不做任何处理被转向第一个机房这样会对两地的延时有一定的要求。第二,在第二个机房部署应用服务器和缓存,大部分的数据请求可以从缓存中读取,不用进行跨机房请求,但当缓存失效时,依然落到第一个机房的数据库去查询。所以,这个方式不太彻底;第三,全套服务的部署,包括HTTP服务器、业务服务器、缓存和数据库的 slave。此方式使得进入第二个机房的请求,只需要在机房内就可以完成请求处理,速度更快,但会遇到数据一致性和缓存一致性的问题,针对这点也会有一些解决方法。除了数据同步过程中的不一致问题,还需要面对缓存。

    好的系统架构不是设计出来的,而是进化而来的

    构建稳定可靠的业务系统需要注意以下这些:

    • 分析用户行为,理解你的业务,如社交、电商、视频;



    不同的业务有不同的行业属性和特点,对于社交来讲,比较典型的特点是数据量庞大、数据查询维度多,比如查询6月11日-7月15日在xx咖啡厅我所有好友里拍过照片的人,查询条件包括好友维度、照片维度、地点维度、隐私状态维度等,这时就需要合理的做数据层面的扩展。

    • 电商的特点是定期举办大促销活动,届时会需要大量的计算资源、应用服务器来扛流量峰值,此时可利用云计算平台的弹性实现快速扩展业务,而在自己业务压力、促销来临时调用API接口,及AutoScaling扩展后端计算资源。视频业务有非常明显的流量高峰期和低峰期,流量高峰期通常是白天或者大家晚上下班回家那段时间,晚上2点到早上6点是流量非常低的时候,可利用云计算弹性优势,来调用API方式调整业务带宽资源,从而达到节省成本目的。

    • 合理规划系统,预估系统容量,如 10w / 100w / 1000w PV(DAU):不同的系统容量有可能对应不同架构的部署方式,找到最适合自己的那一个;

    • 系统是可横向扩展的 scalable;

    • 不遗余力地解决单点问题;

    • 为出错而设计design for failure:App的后端架构在开发支出就要为可能出现的各种问题进行准备,比如异地备份等;

    • 设计面向服务的架构,拆分子系统,API交互,异步处理;

    • 构建无处不在的缓存:页面缓存、接口缓存、对象缓存、数据库缓存;

    • 避免过度设计,好的系统架构不是设计出来的,而是进化而来的。


     来源:csdn 收起阅读 »

    帮助开源社区解决冲突

    在今年于波特兰召开的OSCON开源大会上,唐娜·本杰明(DonnaBenjamin)和吉娜·利金斯(GinaLikins)强强联合,探讨一个有时容易被人忽略的话题:冲突解决。考虑到处理科技冲突的需求日渐增加,甚至连像LinuxKernel这样流行的项目都采用了...
    继续阅读 »
    在今年于波特兰召开的OSCON开源大会上,唐娜·本杰明(DonnaBenjamin)和吉娜·利金斯(GinaLikins)强强联合,探讨一个有时容易被人忽略的话题:冲突解决。考虑到处理科技冲突的需求日渐增加,甚至连像LinuxKernel这样流行的项目都采用了规范准则,因此,会议注重探讨人际互动也就不足为奇了。

    在本次采访中,唐娜和吉娜回答了许多难题,比如在开源社区冲突解决看起来是怎样的,如何与工程师们在这方面开展合作,以及如何鼓励社区把尊重和同情视为解决问题的基础。

    吉娜和唐娜,你们都一直在讲冲突解决,我对这个话题也很感兴趣。那么,是什么引起了你们对这个话题的兴趣?

    吉娜:我一直热衷于寻找能让我们把网络社区建设得更好的方法。过去,我更关注定义潜在的有害行为(希望以一种幽默的方式让人们看见这些行为)。

    但有趣的是,我比较倾向于回避冲突。因此,和唐娜一起工作棒极了。她向我展示了如果处理得当,把冲突视作潜在想法和能量的丰富源泉可以说是好处多多。

    唐娜:几年前,Drupal创始人德瑞斯·布塔特(DriesBuytaert)让我加入Drupal社区的工作团队。这是为了让这个巨大且成功的开源社区在管理上更规范更有条理。Drupal社区有一些“未完成的事情”,也就是落实冲突解决的政策和程序。那次工作经历带领着我探索冲突自身的本质。

    唐娜住在澳大利亚,吉娜住在美国,你们两个人是怎么碰面并同意在OSCON开源大会上协作演讲的?

    吉娜:OSCON开源大会已经同意了唐娜做演讲。她看到我在2015年的ApacheCon上的演讲,并通过Twitter联系上我。最初我们在计划上有些争吵,之后,我们在一个谷歌文档上“一起工作(simul-worked)”,在谷歌环聊(Hangouts)上讨论我们的想法。多亏了先进的科技,如今实现这种跨越全球的合作十分简单。最困难的是我得尽早吃晚饭,好准备接晚上8点的电话!

    唐娜:哈哈!我和吉娜还没在现实世界“见过面”。我们期待碰面,然后亲自给我们的演讲稿写上最后一笔。我认为这是开源工作的重要事实。我们联系上来自世界各地的人们,科技促进了我们的交流,我们各自不同的经历也丰富了这些互动。驱动这些互动的正是分享这一目的,以及一同实现某件事物的愿望。我发现事实上对一个人了解的程度在共事时并不重要,这一点常常既令人惊讶却又是非常合理的。但我们在工作过程中会逐渐了解彼此,这真的是一件很有趣的事。你问还有什么?可别跟我提时区的事儿。

    参与开源社区是如何引发你们对冲突解决的兴趣的?

    吉娜:当人们满怀激情时冲突就几乎是不可避免的了,而在开源社区工作的人们从来都是满怀激情的。此外,开源社区没有任何外在实体可以帮我们“处理问题”,比如人力资源部、法律或政府。开源社区只有我们,而我们需要学着以创造性的方式解决冲突。要让这些方式为我们服务,而不是分裂我们。

    唐娜:吉娜总结地非常好。我想我也开始欣赏冲突的潜力了,而且我也热衷于探索让我们以一种更健康的方式认识发生在我们社区的冲突。但是,我打心底里觉得这已经超越开源的范畴。我们也可以学习一些其他的团体、社区和产业“解决”一些此类问题的方式。我想,正如开源瓦解了软件开发和传播的传统模式,我们的社区也可能发现新的方式来应对由于冲突产生的问题、挑战和机会。

    在不泄露你们演讲中所有建议的前提下给我讲讲好坏之争的冲突解决是什么样的吧。

    吉娜:我认为解决冲突的关键之一是带有同情之心。我知道,我们不是面对面时很难做到这样。如果你看见自己把我弄哭了,你没准就会停下来,想想你说了些什么。但是,由于时间和空间上的距离,人们很容易忘记对话的另一端是真实存在的人,而且这些人值得同情。

    唐娜:是的,毫无疑问。同情是创造性冲突解决成功的关键因素之一。另一个呢?是尊重。在武术中尊重你的对手非常重要。我认为我们应该借鉴这个思想,然后把它应用到我们社区的争论中。我的观点是,我们需要把冲突作为一种能源来利用。我们利用冲突深层的、潜在的激情或挫败感,然后转换成专注改变的力量。有很多原因会让人们变得人们暴躁。也许他们只是累了或饿了或者度过了糟糕的一天呢?那么,他们需要的只不过是理解、同情、睡眠或食物。或者也许我们需要解决一个实际问题,以改善每个人的处境。

    你们如何确保冲突解决的建议有足够的吸引力,从而与工程师团体产生共鸣?

    吉娜:猫的图片吧。另外动画片也很管用。

    严格来说,关于这件事的问题之一是,参加这些谈话的人通常是“团队”,就像“我们在跟合唱团谈话。”这些人对冲突解决很感兴趣,也想了解更多信息。因此对他们来说,重要的是如何提供解决问题的有效信息。我觉得挑战在于如何把这个信息传达给不想要或者觉得根本不需要的人。我不确定该怎么做,我也很想听听别人的意见。

    唐娜:这个问题问得好。我也不知道该怎么回答。不过我同意吉娜的说法,很多时候我们是在劝说那些已经转变的团队。那些专注工作的人很容易忽视这一问题,他们只想继续工作。有些人认为处理冲突最好的办法就是完全避免冲突。但不是每个人都擅长处理感觉和情感这些事。不过我最关心的是,这是一份实实在在的工作。我觉得我们群体里有太多人要么不重视,要么觉得这根本不是个实实在在的工作。

    吉娜我还对你和高校为了让更多人参与开源项目而进行的合作非常感兴趣。说说你最近在做的工作和它们的重要性吧

    吉娜:我们在RedHat(红帽)上的大学推广项目包括好几个部分,我参与最多的部分着眼于让更多大学层面的教师能将开源融入到课堂中。为此我们采用了很多方法,包括:

    “向讲师讲述教授开源的价值所在;

    “编写可以直接融入课堂的开源教材;

    “与美国国家科学基金会(NationalScienceFoundation)一起共同赞助叫”教授开源软件体验“(POSSE)的集中研讨会,让教师们了解开源的方方面面。

    这些工作之所以重要是因为,大多数计算机科学的大学生根本不知道开源是什么或者有什么用处。过去一年里我花了大量时间跟学生和老师交谈,让我惊讶的是,没几个学生”拥有“开源软件,也没有几个老师提到开源软件,更不用说向学生讲解了。这确实是个问题,因为Linux开发人员的年龄越来越大,而且我们的行业需要更多新的、更年轻的开发人员茁壮成长。

    我花了很长时间研究Sugarlabs(一款专注合作的教育工具),然后无意中看到了唐娜博客的引文。你是怎么参与到这个项目里的?

    唐娜:Sugarlabs出自”每个孩子一台笔记本计划“。这个项目刚刚公布时,我就在想:“这个项目将会改变世界。”对此我很激动,而且我觉得这个项目确实改变了世界。“百元笔记本电脑”计划之前,世界上大多数人根本买不起这种功能强大的便携设备。不久之后,我们有了上网本,而上网本重新定义了笔记本市场。

    从本世纪初开始,我就开始在我的家乡维多利亚州参与了技术教育社区,所以我非常想支持使用科技并用科技进行教育的教育工作者。Sugar是一个可以用来学习的全新界面,假如你喜欢的话,可以在里面看到许多平时见不到的绝佳想法。可惜的是,现在我也不知道Sugar计划进展如何,我很想再去看看现在那里怎么样了。

    参加OSCON开源大会的演讲之前,跟我说一件大家应该知道的关于你们的趣闻吧。

    吉娜:趣闻是吗?我学过空中飞人,还坐过齐柏林飞艇。
    唐娜:真的假的?太棒了。我们应该跑去参加马戏团。我会表演杂耍!明白我说的要在过程中互相了解的意思了吧?咱们波特兰再见吧!
     
    内容来源:UDN技术社区 收起阅读 »

    技巧分享:impress.js 使用总结

    在美团参加 hackathon 时,使用 impress.js 做了一个商家上线流程的复盘工具。觉得 impress.js 很适合用于做 presentation, 因此进行一个简单地总结。   what is impress.js ...
    继续阅读 »
    在美团参加 hackathon 时,使用 impress.js 做了一个商家上线流程的复盘工具。觉得 impress.js 很适合用于做 presentation, 因此进行一个简单地总结。
     
    what is impress.js


    1.png



    impress.js 是一个用于展示的前端框架,基于大量 css3 的动画等特性。最大的特点是其基于 transform 来构建,通过空间位置的移动以及旋转来体现变化,视觉冲击性很强。
    该库需要有很好的 css 功底以及空间想象能力,因为整个 app 完全是靠代码进行编辑,并不是像传统的 ppt 工具进行拖拽以及鼠标点击。

    可以在github上查看 example 和 demo。

    如何用 impress.js 设计 presentation

    其实作者制作的 demo 就几乎将所有可能用到的技巧都包含了,并在 github 中提供了源码,不过如果不仔细读源码的话很可能走很多弯路。

    基本思路

    其实作者的想法很巧妙也很简单,impress.js 会根据 html 中 step 的顺序来渲染整个页面,对于每个页面来说只有三种重要的属性——scale,position,rotate。

    scale 决定了该页的大小。对应属性 width,height
    position 决定了在三维空间中的坐标。对应属性 transform
    rotate 则是旋转方式。对应属性 rotate[XYZ]
     

    • 绕 X 轴

    • 绕 Y 轴

    • 绕 Z 轴



    impress.js 会根据这些属性将每一页进行渲染,最后从第一页开始一步步进行播放,因此这些属性也就决定了补间动画的形式。在开始写代码之前一定要想好空间结构。
     
    技巧总结
     
    虽然第一眼看过去很容易,可如果想做一个定制性很强的ppt还是会遇到很多问题,在这里总结一些经验与技巧。

    设置补间动画

    在 impress.js 文件中可以设置一些默认值,我会慢慢对这些参数进行说明。
    // some default config values.
    var defaults = {
    width: 1024,
    height: 768,
    maxScale: 1,
    minScale: 0,

    perspective: 1000,

    transitionDuration: 700
    };
    画布大小

    width与height是每页的基准长度和宽度,与 scale 相乘之后才是该页的大小,设置偏移量时需要对width与height进行参考。

    以下是计算 window 真实比例的代码。
    var computeWindowScale = function ( config ) {
    var hScale = window.innerHeight / config.height,
    wScale = window.innerWidth / config.width,
    scale = hScale > wScale ? wScale : hScale;

    if (config.maxScale && scale > config.maxScale) {
    scale = config.maxScale;
    }

    if (config.minScale && scale < config.minScale) {
    scale = config.minScale;
    }

    return scale;
    };
    可以知道画布会随着window的缩放而自动进行缩放,maxScale决定了画布最大值,而minScale决定了最小为多大。千万不要将这里的scale与data-scale搞混淆。

    透明度

    .impress-enabled .step 调整 opacity 来控制非当前页的透明度

    渐变动画时间

    需要注意的是在更改 default transitionDuration之后还需要在 css 中修改 transitionDuration。这样才能保证动画的同步。

    实用的 class

    body 状态

    • impress-disabled is added to body element by the impress.js script

    • impress-enabled after init() function is called



    page 状态

    additional past, present and future classes are added to step elements。通过这三个状态可以做出很酷的动画效果。

    • future class appears on steps that were not yet visited

    • present class appears on currently visible step - it's different from active class as present class is added when transition finishes (step is entered)

    • past class is added to already visited steps (when the step is left)



    插件推荐
     

    • impress-progress.js 显示 ppt 的进度条


     来源:segmentfault 收起阅读 »

    Android M Developer Preview 2 发布

    Google 更新 Android M 开发者预览版,相比 Lollipop 主要是安全和电池寿命方面的改进。此版本包括更多的授权设置改进,比如指纹验证,外部存储处理的授权方式改进。 Google 强烈建议大家升级到最新版本,最新版本包括最新的平台 APIs...
    继续阅读 »
    Google 更新 Android M 开发者预览版,相比 Lollipop 主要是安全和电池寿命方面的改进。此版本包括更多的授权设置改进,比如指纹验证,外部存储处理的授权方式改进。

    Google 强烈建议大家升级到最新版本,最新版本包括最新的平台 APIs:更新了 Bluetooth Stylus APIs,为 Media API 添加了新的调用,一些类的重构等等。

    此外还有一系列的 bug 修复。Google 温馨提示: Messenger 应用还不能很好的在 64 位模拟器上运行,还有些 YouTube 视频分享方面的问题,联系人同步方面的问题,Android M 在 Nexus Player 上的问题等等。
     
    原文地址:http://www.oschina.net/news/64112/android-m-developer-preview-update 收起阅读 »

    imgeek更新20150710

    imgeek更新: 1. “问题”列表页中帖子下面加入“话题”,方便大家知道问题分类  2.更新页脚 3.更新帖子里的显示时间为绝对时间  
    imgeek更新:
    1. “问题”列表页中帖子下面加入“话题”,方便大家知道问题分类 
    2.更新页脚
    3.更新帖子里的显示时间为绝对时间
     

    实战指导:Ceph存储性能优化总结

    最近一直在忙着搞Ceph存储的优化和测试,看了各种资料,但是好像没有一篇文章把其中的方法论交代清楚,所以下文只是一个经验总结,拿出来与大家分享下。   一:优化方法论 做任何事情还是要有个方法论的,“授人以鱼不如授人以渔”的道理吧,方法通了,所有的问题就有了...
    继续阅读 »
    最近一直在忙着搞Ceph存储的优化和测试,看了各种资料,但是好像没有一篇文章把其中的方法论交代清楚,所以下文只是一个经验总结,拿出来与大家分享下。
     
    一:优化方法论

    做任何事情还是要有个方法论的,“授人以鱼不如授人以渔”的道理吧,方法通了,所有的问题就有了解决的途径。通过对公开资料的分析进行总结,对分布式存储系统的优化离不开以下几点:

    1. 硬件层面
    • 硬件规划
    • SSD选择
    • BIOS设置
    2. 软件层面
    • Linux OS
    • Ceph Configurations
    • PG Number调整
    • CRUSH Map
    • 其他因素
    硬件优化1. 硬件规划
    • Processor
    ceph-osd进程在运行过程中会消耗CPU资源,所以一般会为每一个ceph-osd进程绑定一个CPU核上。当然如果你使用EC方式,可能需要更多的CPU资源。ceph-mon进程并不十分消耗CPU资源,所以不必为ceph-mon进程预留过多的CPU资源。ceph-msd也是非常消耗CPU资源的,所以需要提供更多的CPU资源。
    • 内存
    ceph-mon和ceph-mds需要2G内存,每个ceph-osd进程需要1G内存,当然2G更好。
    • 网络规划
    万兆网络现在基本上是跑Ceph必备的,网络规划上,也尽量考虑分离cilent和cluster网络。2. SSD选择硬件的选择也直接决定了Ceph集群的性能,从成本考虑,一般选择SATA SSD作为Journal,Intel® SSD DC S3500 Series基本是目前看到的方案中的首选。400G的规格4K随机写可以达到11000 IOPS。如果在预算足够的情况下,推荐使用PCIE SSD,性能会得到进一步提升,但是由于Journal在向数据盘写入数据时Block后续请求,所以Journal的加入并未呈现出想象中的性能提升,但是的确会对Latency有很大的改善。如何确定你的SSD是否适合作为SSD Journal,可以参考SÉBASTIEN HAN的Ceph: How to Test if Your SSD Is Suitable as a Journal Device?,这里面他也列出了常见的SSD的测试结果,从结果来看SATA SSD中,Intel S3500性能表现最好。3. BIOS设置
    • Hyper-Threading(HT)
    基本做云平台的,VT和HT打开都是必须的,超线程技术(HT)就是利用特殊的硬件指令,把两个逻辑内核模拟成两个物理芯片,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高的CPU的运行效率。
    • 关闭节能
    关闭节能后,对性能还是有所提升的,所以坚决调整成性能型(Performance)。当然也可以在操作系统级别进行调整,详细的调整过程请参考链接,但是不知道是不是由于BIOS已经调整的缘故,所以在CentOS 6.6上并没有发现相关的设置。
    for CPUFREQ in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do [ -f $CPUFREQ ] || continue; echo -n performance > $CPUFREQ; done 
    • NUMA
    简单来说,NUMA思路就是将内存和CPU分割为多个区域,每个区域叫做NODE,然后将NODE高速互联。 node内cpu与内存访问速度快于访问其他node的内存,NUMA可能会在某些情况下影响ceph-osd。解决的方案,一种是通过BIOS关闭NUMA,另外一种就是通过cgroup将ceph-osd进程与某一个CPU Core以及同一NODE下的内存进行绑定。但是第二种看起来更麻烦,所以一般部署的时候可以在系统层面关闭NUMA。CentOS系统下,通过修改 /etc/grub.conf文件,添加numa=off来关闭NUMA。
    kernel /vmlinuz-2.6.32-504.12.2.el6.x86_64 ro root=UUID=870d47f8-0357-4a32-909f-74173a9f0633 rd_NO_LUKS rd_NO_LVM LANG=en_US.UTF-8 rd_NO_MD SYSFONT=latarcyrheb-sun16 crashkernel=auto  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM   biosdevname=0 numa=off 
    二:软件优化 1. Linux OS
    • Kernel pid max
    echo 4194303 > /proc/sys/kernel/pid_max 
    Jumbo frames, 交换机端需要支持该功能,系统网卡设置才有效果
    ifconfig eth0 mtu 9000 
    永久设置
    echo "MTU=9000" | tee -a /etc/sysconfig/network-script/ifcfg-eth0 2./etc/init.d/networking restart 
    read_ahead, 通过数据预读并且记载到随机访问内存方式提高磁盘读操作,查看默认值
    cat /sys/block/sda/queue/read_ahead_kb 
    根据一些Ceph的公开分享,8192是比较理想的值
    echo "8192" > /sys/block/sda/queue/read_ahead_kb 
    swappiness, 主要控制系统对swap的使用,这个参数的调整最先见于UnitedStack公开的文档中,猜测调整的原因主要是使用swap会影响系统的性能。
    echo "vm.swappiness = 0" | tee -a /etc/sysctl.conf 
    I/O Scheduler,关于I/O Scheculder的调整网上已经有很多资料,这里不再赘述,简单说SSD要用noop,SATA/SAS使用deadline。
    echo "deadline" > /sys/block/sd[x]/queue/scheduler 2.echo "noop" > /sys/block/sd[x]/queue/scheduler 
    cgroup这方面的文章好像比较少,昨天在和Ceph社区交流过程中,Jan Schermer说准备把生产环境中的一些脚本贡献出来,但是暂时还没有,他同时也列举了一些使用cgroup进行隔离的原因。
    • 不在process和thread在不同的core上移动(更好的缓存利用)
    • 减少NUMA的影响
    • 网络和存储控制器影响 - 较小
    • 通过限制cpuset来限制Linux调度域(不确定是不是重要但是是最佳实践)
    • 如果开启了HT,可能会造成OSD在thread1上,KVM在thread2上,并且是同一个core。Core的延迟和性能取决于其他一个线程做什么。


    这一点具体实现待补充!!!

    2. Ceph Configurations

    [global]


    1.jpg



    查看系统最大文件打开数可以使用命令


    2.jpg



    调整omap的原因主要是EXT4文件系统默认仅有4K
    filestore queue相关的参数对于性能影响很小,参数调整不会对性能优化有本质上提升
     
    [osd] - journal


    3.jpg



    [osd] - journal
     
    Ceph OSD Daemon stops writes and synchronizes the journal with the filesystem, allowing Ceph OSD Daemons to trim operations from the journal and reuse the space.
    上面这段话的意思就是,Ceph OSD进程在往数据盘上刷数据的过程中,是停止写操作的。
     
    [osd] - osd config tuning


    4.jpg


     
    增加osd op threads和disk threads会带来额外的CPU开销

    [osd] - recovery tuning


    5.jpg


     
    [osd] - client tuning


    6.jpg


     
    关闭Debug
     
    三: PG Number
     
    PG和PGP数量一定要根据OSD的数量进行调整,计算公式如下,但是最后算出的结果一定要接近或者等于一个2的指数。
    Total PGs = (Total_number_of_OSD * 100) / max_replication_count 
    例如15个OSD,副本数为3的情况下,根据公式计算的结果应该为500,最接近512,所以需要设定该pool(volumes)的pg_num和pgp_num都为512.
    ceph osd pool set volumes pg_num 512 
    2.ceph osd pool set volumes pgp_num 512



    4. CRUSH Map

    CRUSH是一个非常灵活的方式,CRUSH MAP的调整取决于部署的具体环境,这个可能需要根据具体情况进行分析,这里面就不再赘述了。

    5. 其他因素的影响

    在今年的(2015年)的Ceph Day上,海云捷迅在调优过程中分享过一个由于在集群中存在一个性能不好的磁盘,导致整个集群性能下降的case。通过osd perf可以提供磁盘latency的状况,同时在运维过程中也可以作为监控的一个重要指标,很明显在下面的例子中,OSD 8的磁盘延时较长,所以需要考虑将该OSD剔除出集群:
    ceph osd perf 
    osd fs_commit_latency(ms) fs_apply_latency(ms)
    osd fs_commit_latency(ms) fs_apply_latency(ms) 
    2. 0 14 17
    3. 1 14 16
    4. 2 10 11
    5. 3 4 5
    6. 4 13 15
    7. 5 17 20
    8. 6 15 18
    9. 7 14 16
    10. 8 299 329



    ceph.conf
    1.[global] 
    2.fsid = 059f27e8-a23f-4587-9033-3e3679d03b31
    3.mon_host = 10.10.20.102, 10.10.20.101, 10.10.20.100
    4.auth cluster required = cephx
    5.auth service required = cephx
    6.auth client required = cephx
    7.osd pool default size = 3
    8.osd pool default min size = 1
    9.
    10.public network = 10.10.20.0/24
    11.cluster network = 10.10.20.0/24
    12.
    13.max open files = 131072
    14.
    15.[mon]
    16.mon data = /var/lib/ceph/mon/ceph-$id
    17.
    18.[osd]
    19.osd data = /var/lib/ceph/osd/ceph-$id
    20.osd journal size = 20000
    21.osd mkfs type = xfs
    22.osd mkfs options xfs = -f
    23.
    24.filestore xattr use omap = true
    25.filestore min sync interval = 10
    26.filestore max sync interval = 15
    27.filestore queue max ops = 25000
    28.filestore queue max bytes = 10485760
    29.filestore queue committing max ops = 5000
    30.filestore queue committing max bytes = 10485760000
    31.
    32.journal max write bytes = 1073714824
    33.journal max write entries = 10000
    34.journal queue max ops = 50000
    35.journal queue max bytes = 10485760000
    36.
    37.osd max write size = 512
    38.osd client message size cap = 2147483648
    39.osd deep scrub stride = 131072
    40.osd op threads = 8
    41.osd disk threads = 4
    42.osd map cache size = 1024
    43.osd map cache bl size = 128
    44.osd mount options xfs = "rw,noexec,nodev,noatime,nodiratime,nobarrier"
    45.osd recovery op priority = 4
    46.osd recovery max active = 10
    47.osd max backfills = 4
    48.
    49.[client]
    50.rbd cache = true
    51.rbd cache size = 268435456
    52.rbd cache max dirty = 134217728
    53.rbd cache max dirty age = 5




    总结

    优化是一个长期迭代的过程,所有的方法都是别人的,只有在实践过程中才能发现自己的,本篇文章仅仅是一个开始,欢迎各位积极补充,共同完成一篇具有指导性的文章。
    作者:xiaoquqi 收起阅读 »

    什么叫有专职工程师值守的话题

    什么叫有专职工程师值守的话题  如今的社区,热心的人越来越少,当你有问题需要发贴求助的时候,大多会遇到的情况会是这样的:  . 永远不知道问题何时能得到回复  终于有了回复的,还是:  . 无聊捣乱的  . 自动顶贴机的自动回复,后面还带有一串含有Url的...
    继续阅读 »
    什么叫有专职工程师值守的话题 

    如今的社区,热心的人越来越少,当你有问题需要发贴求助的时候,大多会遇到的情况会是这样的: 
    . 永远不知道问题何时能得到回复 

    终于有了回复的,还是: 
    . 无聊捣乱的 
    . 自动顶贴机的自动回复,后面还带有一串含有Url的签名 
    . 各种奇葩的、不搭调的回复 
    . ...... 

    最郁闷的是,你的贴子就像一颗石子扔到湖里,到最后,却没有任何涟漪:没.有.回.帖...... 

    imgeek努力想改变这种现状,循着极客们开放、分享、协作、创新的精神,我们努力构建一个具有服务质量保障(Service Level Assurance , SLA)的社区
    我们努力征集一些热心的技术专家,得到他们的承诺,可以值守一些话题,当有该话题提交的时候,他可以及时的收到消息提醒,这样可以保障他可以及时的看到你提的问题,以便及时回复。 

    当然,如果你在提交一个问题之前,可以先搜索一下,说不定你要提的问题已经有人提过并且得到解答。这样可以省却不少你的时间。 

    我们努力的寻找一些热心的专家,可以为社区兄弟们解决一些问题,imgeek社区也可以提供一些必要的补贴给你们,你可以填写表格申请成为我们的”社区专家“,我们将会及时联系你 收起阅读 »

    imgeek社区更新20150708, 增加”有专职工程师值守的话题"

    1.发起问题时新增“有专职工程师值守的话题”,选择“有专职工程师值守“里的话题,支持工程师将会即时收到邮件提醒 2.更改主菜单为"动态"”问答“”文章“ ”活动“, 去掉”发现“,"帮助",折叠”话题“, 3.问题发起者可以设置“最佳答案”,原来只有管理员可以...
    继续阅读 »
    1.发起问题时新增“有专职工程师值守的话题”,选择“有专职工程师值守“里的话题,支持工程师将会即时收到邮件提醒
    2.更改主菜单为"动态"”问答“”文章“ ”活动“, 去掉”发现“,"帮助",折叠”话题“,
    3.问题发起者可以设置“最佳答案”,原来只有管理员可以设置
    4.主菜单上“活动”前面加上图标
      收起阅读 »

    技术分享:Python 并行任务技巧

    Python的并发处理能力臭名昭著。先撇开线程以及GIL方面的问题不说,我觉得多线程问题的根源不在技术上而在于理念。大部分关于Pyhon线程和多进程的资料虽然都很不错,但却过于细节。这些资料讲的都是虎头蛇尾,到了真正实际使用的部分却草草结束了。   传统例...
    继续阅读 »
    Python的并发处理能力臭名昭著。先撇开线程以及GIL方面的问题不说,我觉得多线程问题的根源不在技术上而在于理念。大部分关于Pyhon线程和多进程的资料虽然都很不错,但却过于细节。这些资料讲的都是虎头蛇尾,到了真正实际使用的部分却草草结束了。
     
    传统例子
     
    在DDG https://duckduckgo.com/ 搜索“Python threading tutorial”关键字,结果基本上却都是相同的类+队列的示例。

    标准线程多进程,生产者/消费者示例:


    1.jpg



    这里是代码截图,如果用其他模式贴出大段代码会很不美观。文本模式点这里 here
    Mmm.. 感觉像是java代码
    在此我不想印证采用生产者/消费者模式来处理线程/多进程是错误的— 确实没问题。实际上这也是解决很多问题的最佳选择。但是,我却不认为这是日常工作中常用的方式。

    问题所在

    一开始,你需要一个执行下面操作的铺垫类。接着,你需要创建一个传递对象的队列,并在队列两端实时监听以完成任务。(很有可能需要两个队列互相通信或者存储数据)

    Worker越多,问题越大.

    下一步,你可能会考虑把这些worker放入一个线程池一边提高Python的处理速度。下面是
    IBM tutorial 上关于线程较好的示例代码。这是大家常用到的利用多线程处理web页面的场景
    Seriously, Medium. Fix your code support. Code is Here.

    感觉效果应该很好,但是看看这些代码!初始化方法、线程跟踪,最糟的是,如果你也和我一样是个容易犯死锁问题的人,这里的join语句就要出错了。这样就开始变得更加复杂了!

    到现在为止都做了些什么?基本上没什么。上面的代码都是些基础功能,而且很容易出错。(天啊,我忘了写上在队列对象上调用task_done()方法(我懒得修复这个问题在重新截图)),这真是性价比太低。所幸的是,我们有更好的办法.


    2.jpg



    引入:Map

    Map 是个很酷的小功能,也是简化Python并发代码的关键。对那些不太熟悉Map的来说,它有点类似Lisp.它就是序列化的功能映射功能. e.g.
    urls = [', ']
    results = map(urllib2.urlopen, urls)
    这里调用urlopen方法,并把之前的调用结果全都返回并按顺序存储到一个集合中。这有点类似
    results = []
    for url in urls:
    results.append(urllib2.urlopen(url))
    Map能够处理集合按顺序遍历,最终将调用产生的结果保存在一个简单的集合当中。
    为什么要提到它?因为在引入需要的包文件后,Map能大大简化并发的复杂度!


    3.jpg



    支持Map并发的包文件有两个:

    Multiprocessing,还有少为人知的但却功能强大的子文件 multiprocessing.dummy. .
    Digression这是啥东西?没听说过线程引用叫dummy的多进程包文件。我也是直到最近才知道。它在多进程的说明文档中也只被提到了一句。它的效果也只是让大家直到有这么个东西而已。这可真是营销的失误!
    Dummy是一个多进程包的完整拷贝。唯一不同的是,多进程包使用进程,而dummy使用线程(自然也有Python本身的一些限制)。所以一个有的另一个也有。这样在两种模式间切换就十分简单,并且在判断框架调用时使用的是IO还是CPU模式非常有帮助。

    准备开始
     
    准备使用带有并发的map功能首先要导入相关包文件:
    from multiprocessing import Pool
    from multiprocessing.dummy import Pool as ThreadPool
    然后初始化:
    pool = ThreadPool()
    就这么简单一句解决了example2.py中build_worker_pool的功能. 具体来讲,它首先创建一些有效的worker启动它并将其保存在一些变量中以便随时访问。
    pool对象需要一些参数,但现在最紧要的就是:进程。它可以限定线程池中worker的数量。如果不填,它将采用系统的内核数作为初值。

    一般情况下,如果你进行的是计算密集型多进程任务,内核越多意味着速度越快(当然这是有前提的)。但如果是涉及到网络计算方面,影响的因素就千差万别。所以最好还是能给出合适的线程池大小数。

    pool = ThreadPool(4) # Sets the pool size to 4
    如果运行的线程很多,频繁的切换线程会十分影响工作效率。所以最好还是能通过调试找出任务调度的时间平衡点。
    好的,既然已经建好了线程池对象还有那些简单的并发内容。咱们就来重写一些example2.py中的url opener吧!


    4.jpg



    看吧!只用4行代码就搞定了!其中三行还是固定写法。使用map方法简单的搞定了之前需要40行代码做的事!为了增加趣味性,我分别统计了不同线程池大小的运行时间。


    5.png


     
    结果:


    6.jpg


    效果惊人!看来调试一下确实很有用。当线程池大小超过9以后,在我本机上的运行效果已相差无几。

    示例 2:

    生成上千张图像的缩略图:

    现在咱们看一年计算密集型的任务!我最常遇到的这类问题之一就是大量图像文件夹的处理。

    其中一项任务就是创建缩略图。这也是并发中比较成熟的一项功能了。

    基础单线程创建过程


    7.jpg


     
    作为示例来说稍微有点复杂。但其实就是传一个文件夹目录进来,获取到里面所有的图片,分别创建好缩略图然后保存到各自的目录当中。

    在我的电脑上,处理大约6000张图片大约耗时27.9秒.

    如果使用并发map处理替代其中的for循环:


    8.jpg


     
    只用了5.6 秒!

    就改了几行代码速度却能得到如此巨大的提升。最终版本的处理速度还要更快。因为我们将计算密集型与IO密集型任务分派到各自独立的线程和进程当中,这也许会容易造成死锁,但相对于map强劲的功能,通过简单的调试我们最终总能设计出优美、高可靠性的程序。就现在而言,也别无它法。
    好了。来感受一下一行代码的并发程序吧。 收起阅读 »

    Go语言对Android原生应用开发的支持情况

    Google工程师和独立开发人员提出了几份不同的提案,旨在让Go语言支持开发原生的Android应用。这项工作无法让Go语言编写的应用使用Android NDK的全部接口,但有可能使用其中的一个子集。 David Crawshaw是Google的工程师,他写了...
    继续阅读 »
    Google工程师和独立开发人员提出了几份不同的提案,旨在让Go语言支持开发原生的Android应用。这项工作无法让Go语言编写的应用使用Android NDK的全部接口,但有可能使用其中的一个子集。
    David Crawshaw是Google的工程师,他写了一份提案,旨在让Go语言部分支持编写Android应用。根据他的说法,“用Go语言来实现整个Android平台非常困难。Android平台是用Java写的,并拥有庞大的API层。”
    但是,Crawshaw说,一部分Android应用——比如游戏——使用了精简得多的C语言API编写代码,这些API由Android NDK提供。这样,使用Go语言来开发和NDK一样的功能,提供对Android的支持是有可能实现的。
    Crawshaw建议在Go 1.4的开发周期中,Go语言的代码库引入一个叫GOOS=android的选项,这个选项可以提供以下功能:
    • 为Android NDK中导出的OpenGL、OpenSL和OpenMAX接口,提供Go语言的绑定(binding)。
    • 从Java语言到Go语言的绑定生成器(binding generator)。如果我们有一个Go语言编写的软件包,那么这个生成器可以帮助Java代码调用它,所以游戏菜单界面就可以直接使用标准的SDK来编写了。
    • 集成到Android Studio的编译系统中。

    不止Crawshaw一个人提出了把Go语言和Android结合起来的想法。Elias Naur建议扩展Go语言的工具链来支持创建动态库。这样我们就可以在Android应用中使用Go语言编写的库,它们被Android应用加载和运行,并打包在apk中发行。要把这个想法变为现实,有一个重要的前提条件:加入对交叉编译的支持,而Go 1.3已经实现了它。交叉编译是必需的,因为NDK本身并不能在Android上运行,只有使用NDK编译和(或)链接的可执行程序和动态库才能在Android设备上运行。
    上面这个提案基于已有的开源项目goandroid,作者就是Elias Naur。Goandroid修改了Go语言的工具链和运行时库,使之能编写动态库,在原生的Android应用中运行,而Google官方并不支持这个功能。
    最后,还有一个叫Mandala的项目,它是一个更全面的框架,它的目标是使Go语言能编写Android原生应用。Mandala利用了Goandroid的工具链,它的作者Andrea Fazzi说,感谢Goandroid,“你可以在桌面环境中开发、测试和运行你的应用,然后再把它部署到Android设备上。它鼓励大家以Go语言独特的方式来编写Android应用:使用通道(channels)来实现通讯,而不是回调函数(callbacks)”
    在功能方面,Mandala项目跟Crawshaw的提案很接近,它的目标也主要是为游戏提供解决方案:“我们不应该把Mandala框架看作是一个上层的游戏引擎,而是应该在它基础之上构建游戏引擎,或者把已有的游戏引擎移植到它上面。” Fazzi 提醒道,Google并不支持用Go语言来开发原生的Android应用,但他也表达了他的期望“当前这些工作可以起到某种激励作用,促使Go语言开发团队从官方层面支持Android。”
    来源:infoq
    参考原文链接:The State of Go Language for Android Native Development 收起阅读 »

    请问:webim使用时,本地数据库和环信数据库的关系?

    我公司想使用环信这个聊天工具,有几个问题请环信工程师帮助解答一下,非常感谢 1. 我自己的本地程序数据库如何与环信数据库对接,比如好友,聊天信息,群组信息 2. 我发信息时,WEB-IM 和 IOS 聊天,用DEMO已经实现及时通信,怎么及时同步到我的数据库中...
    继续阅读 »
    我公司想使用环信这个聊天工具,有几个问题请环信工程师帮助解答一下,非常感谢
    1. 我自己的本地程序数据库如何与环信数据库对接,比如好友,聊天信息,群组信息
    2. 我发信息时,WEB-IM 和 IOS 聊天,用DEMO已经实现及时通信,怎么及时同步到我的数据库中?
    3. 添加删除好友增量变化,如何及时同步到我的数据库中?

    我是不是可以理解成
    比如我添加一个好友,我第一步调用自己程序的方法,把这个操作添加到自己的数据库中,同时再调用环信的方法,就保存在了环信的数据库中?
    有没有咱们环信的成功案例,给与我一个最好的解决方案?

    如果我使用自己的数据
    整体的解决方案是不是这样:
    1. 我加载好友及群组数据,数据来源是自己的数据库
    2. 我发送消息给好友,需要调用环信的发信息接口
    3. 我添加好友删除好友等操作,也是保存到自己的数据库,不需要保存到环信的数据库中
    也就是说,我只有在发送和接收消息的时候,才用到环信?

    一些问题求解答,确实想使用环信,谢谢
      收起阅读 »

    干货:Nginx/LVS/HAProxy 负载均衡软件的优缺点详解

    Nginx/LVS/HAProxy是目前使用最广泛的三种负载均衡软件,一般对负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术,具体的应用需求还得具体分析。 如果是中小型的Web应用,比如日PV小于1000万,用Nginx就完全可以了;如果机器不...
    继续阅读 »
    Nginx/LVS/HAProxy是目前使用最广泛的三种负载均衡软件,一般对负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术,具体的应用需求还得具体分析。
    如果是中小型的Web应用,比如日PV小于1000万,用Nginx就完全可以了;如果机器不少,可以用DNS轮询,LVS所耗费的机器还是比较多的;大型网站或重要的服务,且服务器比较多时,可以考虑用LVS。
    目前关于网站架构一般比较合理流行的架构方案:Web前端采用Nginx/HAProxy+Keepalived作负载均衡器;后端采用MySQL数据库一主多从和读写分离,采用LVS+Keepalived的架构。当然要根据项目具体需求制定方案,下面说说各自的特点和适用场合。
    Nginx
    Nginx的优点:
    • 工作在网络的7层之上,可以针对http应用做一些分流的策略,比如针对域名、目录结构,它的正则规则比HAProxy更为强大和灵活,这也是它目前广泛流行的主要原因之一,Nginx单凭这点可利用的场合就远多于LVS了。
    • Nginx对网络稳定性的依赖非常小,理论上能ping通就就能进行负载功能,这个也是它的优势之一;相反LVS对网络稳定性依赖比较大,这点用过Nginx的人都深有体会;
    • Nginx安装和配置比较简单,测试起来比较方便,它基本能把错误用日志打印出来。LVS的配置、测试就要花比较长的时间了,LVS对网络依赖比较大。
    • 可以承担高负载压力且稳定,在硬件不差的情况下一般能支撑几万次的并发量,负载度比LVS相对小些。
    • Nginx可以通过端口检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会把返回错误的请求重新提交到另一个节点,不过其中缺点就是不支持url来检测。比如用户正在上传一个文件,而处理该上传的节点刚好在上传过程中出现故障,Nginx会把上传切到另一台服务器重新处理,而LVS就直接断掉了,如果是上传一个很大的文件或者很重要的文件的话,用户可能会因此而不满。
    • Nginx不仅仅是一款优秀的负载均衡器/反向代理软件,它同时也是功能强大的Web应用服务器。LNMP也是近几年非常流行的web架构,在高流量的环境中稳定性也很好。
    • Nginx现在作为Web反向加速缓存越来越成熟了,速度比传统的Squid服务器更快,可以考虑用其作为反向代理加速器。
    • Nginx可作为中层反向代理使用,这一层面Nginx基本上无对手,唯一可以对比Nginx的就只有lighttpd了,不过lighttpd目前还没有做到Nginx完全的功能,配置也不那么清晰易读,社区资料也远远没Nginx活跃。
    • Nginx也可作为静态网页和图片服务器,这方面的性能也无对手。还有Nginx社区非常活跃,第三方模块也很多。
    Nginx的缺点:
    • Nginx仅能支持http、https和Email协议,这样就在适用范围上面小些,这个是它的缺点。
    • 对后端服务器的健康检查,只支持通过端口来检测,不支持通过url来检测。不支持Session的直接保持,但能通过ip_hash来解决。
    LVSLVS是使用Linux内核集群实现的一个高性能、高可用的负载均衡服务器,它具有很好的可伸缩性(Scalability)、可靠性(Reliability)和可管理性(Manageability)。LVS的优点:
    • 抗负载能力强,工作在网络4层之上,仅作分发之用,没有流量的产生,这个特点也决定了它在负载均衡软件里的性能最强的,对内存和cpu资源消耗比较低。
    • 配置性比较低,这是一个缺点也是一个优点,因为没有可太多配置的东西,所以并不需要太多接触,大大减少了人为出错的几率。
    • 工作稳定,因为其本身抗负载能力很强,自身有完整的双机热备方案,如LVS+Keepalived,不过我们在项目实施中用得最多的还是LVS/DR+Keepalived。
    • 无流量,LVS只分发请求,而流量并不从它本身出去,这点保证了均衡器IO的性能不会收到大流量的影响。
    • 应用范围比较广,因为LVS工作在4层,所以它几乎可以对所有应用做负载均衡,包括http、数据库、在线聊天室等等。
    LVS的缺点:
    • 软件本身不支持正则表达式处理,不能做动静分离;而现在许多网站在这方面都有较强的需求,这个是Nginx/HAProxy+Keepalived的优势所在。
    • 如果是网站应用比较庞大的话,LVS/DR+Keepalived实施起来就比较复杂了,特别后面有Windows Server的机器的话,如果实施及配置还有维护过程就比较复杂了,相对而言,Nginx/HAProxy+Keepalived就简单多了。
    HAProxyHAProxy的特点:
    • HAProxy也是支持虚拟主机的。
    • HAProxy的优点能够补充Nginx的一些缺点,比如支持Session的保持,Cookie的引导;同时支持通过获取指定的url来检测后端服务器的状态。
    • HAProxy跟LVS类似,本身就只是一款负载均衡软件;单纯从效率上来讲HAProxy会比Nginx有更出色的负载均衡速度,在并发处理上也是优于Nginx的。
    • HAProxy支持TCP协议的负载均衡转发,可以对MySQL读进行负载均衡,对后端的MySQL节点进行检测和负载均衡,大家可以用LVS+Keepalived对MySQL主从做负载均衡。
    • 5、HAProxy负载均衡策略非常多,HAProxy的负载均衡算法现在具体有如下8种:
    roundrobin:表示简单的轮询,这个不多说,这个是负载均衡基本都具备的;static-rr:表示根据权重,建议关注;leastconn:表示最少连接者先处理,建议关注;source:表示根据请求源IP,这个跟Nginx的IP_hash机制类似,我们用其作为解决session问题的一种方法,建议关注;ri:表示根据请求的URI;rl_param:表示根据请求的URl参数’balance url_param’ requires an URL parameter name;hdr(name):表示根据HTTP请求头来锁定每一次HTTP请求;rdp-cookie(name):表示根据据cookie(name)来锁定并哈希每一次TCP请求。Nginx和LVS对比总结:Nginx工作在网络的7层,所以它可以针对http应用本身来做分流策略,比如针对域名、目录结构等,相比之下LVS并不具备这样的功能,所以Nginx单凭这点可利用的场合就远多于LVS了;但Nginx有用的这些功能使其可调整度要高于LVS,所以经常要去触碰触碰,触碰多了,人为出问题的几率也就会大。Nginx对网络稳定性的依赖较小,理论上只要ping得通,网页访问正常,Nginx就能连得通,这是Nginx的一大优势!Nginx同时还能区分内外网,如果是同时拥有内外网的节点,就相当于单机拥有了备份线路;LVS就比较依赖于网络环境,目前来看服务器在同一网段内并且LVS使用direct方式分流,效果较能得到保证。另外注意,LVS需要向托管商至少申请多一个ip来做Visual IP,貌似是不能用本身的IP来做VIP的。要做好LVS管理员,确实得跟进学习很多有关网络通信方面的知识,就不再是一个HTTP那么简单了。Nginx安装和配置比较简单,测试起来也很方便,因为它基本能把错误用日志打印出来。LVS的安装和配置、测试就要花比较长的时间了;LVS对网络依赖比较大,很多时候不能配置成功都是因为网络问题而不是配置问题,出了问题要解决也相应的会麻烦得多。Nginx也同样能承受很高负载且稳定,但负载度和稳定度差LVS还有几个等级:Nginx处理所有流量所以受限于机器IO和配置;本身的bug也还是难以避免的。Nginx可以检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会把返回错误的请求重新提交到另一个节点。目前LVS中 ldirectd也能支持针对服务器内部的情况来监控,但LVS的原理使其不能重发请求。比如用户正在上传一个文件,而处理该上传的节点刚好在上传过程中出现故障,Nginx会把上传切到另一台服务器重新处理,而LVS就直接断掉了,如果是上传一个很大的文件或者很重要的文件的话,用户可能会因此而恼火。Nginx对请求的异步处理可以帮助节点服务器减轻负载,假如使用apache直接对外服务,那么出现很多的窄带链接时apache服务器将会占用大 量内存而不能释放,使用多一个Nginx做apache代理的话,这些窄带链接会被Nginx挡住,apache上就不会堆积过多的请求,这样就减少了相当多的资源占用。这点使用squid也有相同的作用,即使squid本身配置为不缓存,对apache还是有很大帮助的。Nginx能支持http、https和email(email的功能比较少用),LVS所支持的应用在这点上会比Nginx更多。在使用上,一般最前端所采取的策略应是LVS,也就是DNS的指向应为LVS均衡器,LVS的优点令它非常适合做这个任务。重要的ip地址,最好交由LVS托管,比如数据库的 ip、webservice服务器的ip等等,这些ip地址随着时间推移,使用面会越来越大,如果更换ip则故障会接踵而至。所以将这些重要ip交给 LVS托管是最为稳妥的,这样做的唯一缺点是需要的VIP数量会比较多。Nginx可作为LVS节点机器使用,一是可以利用Nginx的功能,二是可以利用Nginx的性能。当然这一层面也可以直接使用squid,squid的功能方面就比Nginx弱不少了,性能上也有所逊色于Nginx。Nginx也可作为中层代理使用,这一层面Nginx基本上无对手,唯一可以撼动Nginx的就只有lighttpd了,不过lighttpd目前还没有能做到 Nginx完全的功能,配置也不那么清晰易读。另外,中层代理的IP也是重要的,所以中层代理也拥有一个VIP和LVS是最完美的方案了。具体的应用还得具体分析,如果是比较小的网站(日PV小于1000万),用Nginx就完全可以了,如果机器也不少,可以用DNS轮询,LVS所耗费的机器还是比较多的;大型网站或者重要的服务,机器不发愁的时候,要多多考虑利用LVS。现在对网络负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术:
    • 第一阶段:利用Nginx或HAProxy进行单点的负载均衡,这一阶段服务器规模刚脱离开单服务器、单数据库的模式,需要一定的负载均衡,但是仍然规模较小没有专业的维护团队来进行维护,也没有需要进行大规模的网站部署。这样利用Nginx或HAproxy就是第一选择,此时这些东西上手快, 配置容易,在七层之上利用HTTP协议就可以。这时是第一选择。
    • 第二阶段:随着网络服务进一步扩大,这时单点的Nginx已经不能满足,这时使用LVS或者商用Array就是首要选择,Nginx此时就作为LVS或者Array的节点来使用,具体LVS或Array的是选择是根据公司规模和预算来选择,Array的应用交付功能非常强大,本人在某项目中使用过,性价比也远高于F5,商用首选!但是一般来说这阶段相关人才跟不上业务的提升,所以购买商业负载均衡已经成为了必经之路。关人才的能力以及数量也随之提升,这时无论从开发适合自身产品的定制,以及降低成本来讲开源的LVS,已经成为首选,这时LVS会成为主流。
    • 第三阶段:这时网络服务已经成为主流产品,此时随着公司知名度也进一步扩展,相关人才的能力以及数量也随之提升,这时无论从开发适合自身产品的定制,以及降低成本来讲开源的LVS,已经成为首选,这时LVS会成为主流。最终形成比较理想的基本架构为:Array/LVS — Nginx/Haproxy — Squid/Varnish — AppServer。

     来源:linux中国 收起阅读 »

    移动5年 Android生态系统的演进

    由Google、HTC、Qualcomm联手打造的第一部Android手机G1,开启了移动时代的Andr​​oid纪元(如图1所示),直到现在Android也是唯一能在移动市场上与iOS相抗衡的平台。简单地说,Android与iOS占尽了移动时代的先机(这个故...
    继续阅读 »
    由Google、HTC、Qualcomm联手打造的第一部Android手机G1,开启了移动时代的Andr​​oid纪元(如图1所示),直到现在Android也是唯一能在移动市场上与iOS相抗衡的平台。简单地说,Android与iOS占尽了移动时代的先机(这个故事要从2007年的iPhone和2008年的Andr​​oid G1开始说起,甚至是更早之前的开发史及并购史),App开发者已在Android及iOS上扎下了深厚的根基,因此再也无力也没有必要去为第三个平台开发或移植自己的App,原因很简单,因为受众太少,支出与收入不成正比。


    1.jpg



    一开始Android生态系统很简单,就是拉拢App开发者,并且寻求与更多芯片制造商及手机厂商的合作,共同推广Android这个开放平台。但随着Android市场占有率逐步攀升,Google开始一步步地收紧及控制生态系的发展,以期自己能在Android平台上获得更多的利益(简单地说,就是收入,Google希望Android能够为自己带来更多的收益),而不再满足于只是打造及提供Android平台的角色。

    随着Android一路开疆辟土,出现在越来越多的移动设备上,再加上Google对Android策略的转变,现今的Andr​​oid生态系统已变得较以往复杂了许多,系统中的角色较以往多了电信运营商、汽车制造商、串流内容及媒体提供商等。Google在全球移动市场的策略也已从“移动优先”转变至“攻占所有屏幕(装置)”,而Android也企图往这个目标上不断迈进(如图2所示)。


    2.jpg



    时至今日,欧洲、美国、日本的手机制造商节节败退,摩托罗拉、诺基亚、索尼等老牌制造商相继裁员(甚至出售手机及移动业务),而中国手机制造商却不断崛起,中兴、华为、联想、酷派、OPPO、小米等相继杀入手机市场后,如今中国已成为全球最大的智能机生产及销售国,智能机用户数早已超越美国。而新兴的国外市场,如印度、南美、东南亚正刮起一阵低价智能机风暴,Android正是这波低价智能机的推手(如采用MTK芯片的Andr​​oid One)。低价智能机席卷着新兴国家的市场,而新兴国家的市场也俨然成为智能机的一个重要主战场,Android的野心当然不止于此,Android企图发力于高、中、低阶的智能机市场,而这些细微的变化正一点点地牵动着Android生态系统的转变。

    Android的开放与制约

    Android一开始由Andy Rubin领军,从一开始的全面开放,到像选妃似的,每次释放出新版Android软件的同时,选择与特定的手机制造商合作开发Android原生机,例如HTC、三星等。这一举动令各家手机制造商对Android仰望备至,都想抱上Android的大腿。因为手机制造商一旦获得与Android合作开发新版原生机的机会,就等于能提早得到新版Android软件代码,进而取得先机来开发其他自家的产品(如三星的Galaxy系列),早对手一步将自家产品推向市场销售,从而取得市场先机。这种方式无疑令部分手机制造商对Android产生反感。

    2013年3月,Android从Andy Rubin转由原领导Chrome的Sundar Pichai接手负责,而Android也由工程导向转为营利导向,例如减少手机制造商与Google分成Google Play和Google Search的收入。Android更强烈地主导其UI显示的一致性,并与各家手机制造商签订约束性的协议,强加Google的相关应用服务于Android系统上,甚至将应用摆放在Android桌面上的位置都强加限制,大幅度收紧了各家Android设备制造商分散且碎片化的UI风格,以期用户在汽车、电视、可穿戴设备、手机等所有Android设备上都能得到相同的用户体验,Google此举也造成了部分手机制造商及开发者的反感。

    归根结底,Google还是希望自己的角色能像苹果一样介于用户和运营商之间,由目前的“用户/手机品牌商→运营商”,转变为“用户/手机品牌商→Google→运营商”,以期收紧分散且碎片化的Andr​​oid生态圈,并获取更大的潜在利益。这一点,我们可以在最新发布的Android L版本的软件中看到,Google加入了更多特定运营商所需的功能。

    移动互联网时代,Android已然成为Google的一个标准平台,而非过去传闻的与Chrome整合成一个新的平台,或是将Android整合进Chrome中。这种情况下,Android设备制造商如何应对Google的策略调整及Android版本的快速演进,并调整自己迅速定位市场,从而避免自己成为只是帮Google打工的打工仔是至关重要的。Android设备制造商必须打造出自己独特的生态系统(例如跨手机、平板、电视等),而非只是一个设备制造商。这样一来,我们可以清晰地看到,目前纯手机制造商已越发难以生存。

    Android系统与架构演进

    从Android 1.0至今(更甚至是1.0之前的m*至今),Android系统每一版都有不少的改动(如图3所示),例如HAL有过两次的版本改进、Camera HAL已迭代至第三版、多媒体核心由一开始的OpenCore直到现在的Stagefright。而一开始的WebView至今则完全被Chrome取代,Android也从ARM一路拓展到x86及MIPS平台。版本演进的过程及内容实在太多,如果真的要巨细靡遗地写,可能三天三夜也写不完,所以我想仅对每一版本的改动给予一句代表性的描述​​,并在最后针对Android的最新版(代号L),做一些基本介绍。


    3.JPG



    Android 1.0:第一部Android手机以及Google Apps的诞生;

    Android 1.5 Cupcake:支持软件键盘;

    Android 1.6 Donut:支持CDMA;

    Android 2.0 Éclair:GPS大放异彩,GoogleNexus One手机诞生;

    Android 2.2 Froyo:加入语音识别功能;

    Android 2.3 Gingerbread:Nexus S;

    Android 3.0 Honeycomb:第一次专给平板设备设计UI;

    Android 4.0 Ice Cream Sandwich:一个新的UI界面Holo与内嵌字体Roboto,并支持人脸解锁;

    Android 4.1 Jelly Bean:Nexus 7:Google官方第一台Android平板设备;

    Android 4.3 Jelly Bean:引入对可穿​​戴设备的支持;

    Android 4.4 KitKat:支持更少内存的移动设备。

    接下来是Android L,它的预设虚拟机为ART,支持64位,所需要的系统空间较以往的Andr​​oid版本更大,因为ART有一个转换档案格式的动作,其缺点就是会占用更多的系统空间,但之后软件的执行速度可能有一定的提升。另外,Android L可能会加入部分地区及运营商的特殊需求,例如multi-SIM、NFC、Wi-Fi等;此外​​,Android L也更强调系统安全,它可以预设为强制性安全模式,即一般应用启动时需要多道的身分及权限确认才能执行。

    时至今日,Android身影几乎无处不在,回顾以往,Android系统的演进总是先求有、再求好,智能移动时代发展至今,全球正迈向下一个里程碑,希望Android此时能不忘初心,做一个平衡生态系统的维护者及领导者,而非仅是利益上的掮客,不要为了利益而让这个生态圈走向封闭。

    作为开发者,此时更应该思考自己拥有如此巨大的市场优势、健全的物流体系及现金流系统,以及众多的手机及移动设备制造商,如果能积极培养系统级的软件人才,有条件打造出一个来自中国的自主生态系统,以期将来与Android分庭抗礼。
     
    作者:钟文昌 收起阅读 »

    2015 年 6 月 RedMonk 编程语言排行榜

    RedMonk 发布了 2015 年 6 月的编程语言排行榜,JavaScript 居榜首,Go,Swift 继续上升,函数式编程语言 Scala,Haskell 和 Clojure 上升明显。 现在已经是 2015 的第三个季度了,RedMonk 发布...
    继续阅读 »
    RedMonk 发布了 2015 年 6 月的编程语言排行榜,JavaScript 居榜首,Go,Swift 继续上升,函数式编程语言 Scala,Haskell 和 Clojure 上升明显。

    现在已经是 2015 的第三个季度了,RedMonk 发布了一年两次的编程语言排行榜,一如既往,跟 Drew Conway 和 John Myles White 在 2010 分析的过程差不多,是根据编程语言在 GitHub 和 Stack Overflow 上讨论的多少和使用量来统计的,可以预测未来编程语言的发展趋势。

    排名的根据是编程语言在 Stack Overflow 和 GitHub 都观察过,同时结合很多其他社区的分析统计,GitHub 主要是根据代码行数统计。

    下图是 2015 年 6 月的排行榜图表


    1.jpg


    根据上面的图表,很难分析大体情况,所以提供了下面的数值排名。注意下面列出的是前 21 个编程语言:
    1   JavaScript
    2   Java
    3   PHP
    4   Python
    5   C#
    5   C++
    5   Ruby
    8   CSS
    9   C
    10  Objective-C
    11  Perl
    11  Shell
    13  R
    14  Scala
    15  Go
    15  Haskell
    17  Matlab
    18  Swift
    19  Clojure
    19  Groovy
    19  Visual Basic
    跟上一季度一样,JavaScript 比第二的 Java 只是稍稍领先了一点,这些数值差距是非常微小的。这些能体现编程语言的持久热度,但是也一定程度反映了语言的多样性和在企业和初创企业中的作用。

    除了这两个语言,前十的语言位置非常稳固的。除了一些小的改变,事实上这些年都差不多是这样的排名。同时发现这是一定周期支持一种特定的语言或者是一种风格的语言,简单的来说,最受欢迎的语言几乎没什么改变,对未来也没什么倾向性的变化,这是不是意味着语言的采用和语言的分化已经达到了顶峰?
     
    除了前十,值得关注的变化有:

    • Go:一年前,我们预言 Go 在 6 -12 个月的时间内会成为前 20 的语言。在 1 月份的排行中 Go 成为了第 17 位,预言成真。现在 Go 是第 15 位,超越了 Haskell 和 Matlab。

    • Erlang:这是并发方面开发者长期选择的一种语言。Erlang 之前从第 26 位升到第 25 位,这主要是两周前 Erlang 抛弃了之前的 MPL 协议,选择了 Apache 协议。

    • Julia/Rust::历史性原因,这两个语言的发展轨迹很相似。上一季度,Rust 向上跳了 8 位,Julia 向上跳了 3 位。此次排名 Julia 比之前上升了 2 位到第 52 位,Rust 向上跳了 2 位到第 48 位。继续保持观望!

    • CoffeeScript::2013 年 Q3 排名 17,之后排名 18,18,21,现在是 22。重回前 20 这不是不可能的,至少找到了立足点和稳定的地位,但是前景并不乐观,因其缺乏动力和竞争。

    • Dart / Visual Basic: 这是经常被问到的两个语言。Visual Basic 现在跟 Clojure,Groovy 排名第 19,未来是否还会在前 20 还不是很明朗。Dart,有着 Google 血统,还有 JavaScript 方面的野心,还在稳定增长中,比 Google 的另一个语言 Go 稍稍落后那么一点点,现在是从第 34 位升到第 33 位。

    • Swift:这个月的 排名因为某些原因有些小小的问题。在几方的要求下,苹果 WWDC 前,我们去看 Swift 是否从第 68 升至第 22 位。不幸的是,因为 Stack Overflow 页面结构的改变,数据抓取失败,所以只能手动查看,缩小了范围,Swift 直接在前 20 位以后,排名 21。



    在 我们官方排名中,当然会要求完整的 Stack Overflow 数据,所以 WWDC 之后又收集了一次数据,最新的结果 Swift 从第 21 名升至第 18 名。这就是 WWDC 效应,Swift 排名历史从 68,22 到 18,成为第一个在一年内挺近前 20 的语言。

    未来

    Go 和 Swift 是前十的种子选手,这也许只是时间问题,我们将会继续关注!Go 也许会取代 Objective C, Perl, Shell, R 和 Scala 的位置。Perl 和 Shell 无处不在,但是频率却不够高;R 和 Scala 非常流行,但是使用范围不够广泛。

    Go 成为一个非常受欢迎的现代化后端语言,Swift 也在 iOS 占有一席之地,下一次排名应该会更有趣~ 收起阅读 »

    技术干货:网络性能测试

    实时音视频这种实时业务一般用udp传输数据,其对网络性能是非常敏感的,在实战中,经常需要测试当前端到端或端到云的网络性能。在这里我们讨论一下网络性能测试中所涉及到指标,技术和相关工具,以及如何编写自己的网络性能测试工具。   性能指标 先给出几个比较重要的指...
    继续阅读 »
    实时音视频这种实时业务一般用udp传输数据,其对网络性能是非常敏感的,在实战中,经常需要测试当前端到端或端到云的网络性能。在这里我们讨论一下网络性能测试中所涉及到指标,技术和相关工具,以及如何编写自己的网络性能测试工具。
     
    性能指标

    先给出几个比较重要的指标的定义以及它们的意义。
     带宽(吞吐量)
    • 单位时间内传输的数据量,单位通常是每秒比特数,记作bps;
    • 带宽反映了网络的传输能力,越大越好;
    丢包
    • 数据包丢失个数,等于“发送数据包数” - “接受数据包数”;
    • 丢包反映了网络可靠性,越小越好;
    时延
    • 数据包从发送开始到接收到该数据所耗费的时间,单位通常是毫秒;
    • 时延反映了网络的速度,越小越好;
    抖动
    • 指时延的变化,即两个数据包时延的差值;
    • 抖动反映了网络的稳定性,越小越好;
    乱序
    • 指接收到的数据包顺序和发送顺序不一致的次数;
    • 乱序反映了网络的稳定性,越小越好;
    • 当乱序比较严重时,丢包也会比较严重,所以一般都以丢包指标为主,忽略乱序指标;

     
    测试工具

    网上有很多测试网络性能的工具,如果它们能满足需求的话,就不用自己再造轮子了。

    ping

    ping是最常见的,几乎在所有的OS上都有它的存在。 其工作原理如图


    1.png



    Local发送的数据包,Remote收到数据包后原样发回来;
    数据包里包含有序号和时间戳信息;
    序号用于判断是否丢包;
    时间戳用于计算来回时延(图中蓝色部分),它等于接收时间减去数据包时间戳;

    不同OS的ping命令选项可能会略有差别,以Mac OSX的ping为例
    $ping -s 1024 192.168.1.100

    PING www.microsoft.com (23.42.217.205): 1024 data bytes

    1032 bytes from 23.42.217.205: icmp_seq=0 ttl=49 time=83.883 ms

    1032 bytes from 23.42.217.205: icmp_seq=1 ttl=49 time=77.958 ms

    1032 bytes from 23.42.217.205: icmp_seq=2 ttl=49 time=80.053 ms

    1032 bytes from 23.42.217.205: icmp_seq=3 ttl=49 time=78.244 ms

    1032 bytes from 23.42.217.205: icmp_seq=4 ttl=49 time=77.937 ms

    ...

    ^C

    --- 192.168.1.100 ping statistics ---

    30 packets transmitted, 29 packets received, 3.3% packet loss

    round-trip min/avg/max/stddev = 77.843/95.375/141.314/19.167 ms

    其中 -s 1024 指示包的大小为1024字节;从ping结果可以看出,发送了30个包,收到29个包,3.3%的丢包率,最小时延77.843毫秒,最大时延141.314毫秒,平均时延95.375毫秒,时延的标准差19.167。另外,ping用的是ICMP协议,网络对ICMP协议处理性能,可能跟UDP或TCP是不一样的,所以测试结果只能做为参考。
    小结:ping的优点是简单便捷,可以测试时延和丢包,缺点是无法测试带宽。

    iperf

    iperf功能功能强一些,可以测带宽,丢包,抖动, 但是测不了时延。它的工作原理如图:


    2.png



    服务端:
    $iperf -u -s -p 12345 -i 1 -w 1000000
    ------------------------------------------------------------

    Server listening on UDP port 12345

    Receiving 1470 byte datagrams

    UDP buffer size: 977 KByte

    ------------------------------------------------------------

    客户端:
    $ iperf -u -c 127.0.0.1 -p 12345 -i 1 -t 5 -b 16K -l 62

    ------------------------------------------------------------

    Client connecting to 127.0.0.1, UDP port 12345

    Sending 62 byte datagrams

    UDP buffer size: 9.00 KByte (default)

    ------------------------------------------------------------

    [ 4] local 127.0.0.1 port 59805 connected with 127.0.0.1 port 12345

    [ ID] Interval Transfer Bandwidth

    [ 4] 0.0- 1.0 sec 2.00 KBytes 16.4 Kbits/sec

    [ 4] 1.0- 2.0 sec 1.94 KBytes 15.9 Kbits/sec

    [ 4] 2.0- 3.0 sec 1.94 KBytes 15.9 Kbits/sec

    [ 4] 3.0- 4.0 sec 1.94 KBytes 15.9 Kbits/sec

    [ 4] 4.0- 5.0 sec 2.00 KBytes 16.4 Kbits/sec

    [ 4] 0.0- 5.1 sec 9.87 KBytes 16.0 Kbits/sec

    [ 4] Sent 163 datagrams

    [ 4] Server Report:

    [ 4] 0.0- 5.1 sec 9.87 KBytes 16.0 Kbits/sec 0.046 ms 0/ 163 (0%)

    其中 -b 16K 指定了带宽参数。测试结果为丢包0个,平均抖动为0.046毫秒。
     
    自己开发
    or(unsigned second = 0; second < test_seconds; second++)

    {

    for(unsigned ui = 0; ui < 8; ui++)

    {

    sendto 1024 bytes;

    }

    msleep(1000);

    }

    从上面可以看出,ping和iperf各有优缺点,通常需要两者组合才能满足我们的需求。有时候现有工具不能满足实际应用的需求,比如说完全模拟实际业务环境或者在产品里集成测试功能,这时候就需要发挥“自己动手,丰衣足食”的精神,造出一个适合自己用的轮子来。我们这里只讨论关键点之一:如何匀速发送数据。
    我们以设定发送包长为1024字节,带宽为64kbps为例子,讨论发送数据的实现方案。
    发送数据最简单的方法就是,起一个线程,每秒直接发送完当前秒的数据,然后sleep一秒,再继续发送,如下:

    这种方法比较简单,但是因为发送数据是需要花费时间的,假如发送64Kbit花费了5毫秒,实际发送码率(带宽)为64/1005≈63.68Kbps,比设定值低一些。把发送时间考虑在内,第2个改进后的代码版本如下:
    for(unsigned second = 0; second < test_seconds; second++)

    {

    unsigned ts_start = gettimestamp();

    for(unsigned ui = 0; ui < 8; ui++)

    {

    sendto 1024 bytes;

    }

    unsigned elapsed = gettimestamp() - ts_start;

    msleep(1000-elapsed);

    }

    从大尺度上看,这个版本确实会按设定带宽发送数据,但从小的的时间片上看,其瞬时发送速率是非常高的。假如发送64Kbit花费了5毫秒,则瞬时速率为 64*1000/5=12800Kbps,是设定值的20倍。这种瞬时高发送速率可能会导致网络中某些路由器或交换机来不及处理而大量丢包。所以我们继续改进,在每发送一个包时check是否发送太快,如果发送太快的话就sleep一下缓一缓。改进后的第三个版本如下:
    uint64_t sent_bytes = 0;

    unsigned kick_time = gettimestamp();

    for(unsigned second = 0; second < test_seconds; second++) { sendto 1024 bytes; sent_bytes += 1024; unsigned elapsed = gettimestamp() - kick_time; unsigned normal = sent_bytes * 1000 * 8 / (64*1000); if(normal > elapsed)

    {

    msleep(normal-elapsed);

    }

    }

    这个版本基本能够按照设定值匀速发送数据了。当然,它还不是最完美的,当设定带宽很高而包长很小时,会导致太多的check,占用太多CPU。这里就不继续改进了,有兴趣的看官可以自己实现之。
     
    作者:符宁 收起阅读 »

    Airbnb开源的三个大数据神器

    7月5日 Airbnb召开了第一次开放技术大会OpenAir,重点是数据驱动在airbnb开发过程中的实践,在大会上,Airbnb开源了3个大数据神器 Airpal 第一个神器叫Airpal,是airbnb内部最炙手可热的数据分析工具,目前在g...
    继续阅读 »
    7月5日 Airbnb召开了第一次开放技术大会OpenAir,重点是数据驱动在airbnb开发过程中的实践,在大会上,Airbnb开源了3个大数据神器

    Airpal


    1.jpg



    第一个神器叫Airpal,是airbnb内部最炙手可热的数据分析工具,目前在github上面有900多个star。

    Airpal是建立在Facebook的Prestodb上的一个可视化分布式SQL查询引擎。Airbnb现在大概有1.5PB的数据。传统上是可以用hive查询,但Hive有以下几个缺点。

    第一是对于一些小规模的query,map reduce的overhead太大,比如我就想看一张表的前10行,select * from * limit 10 Hive会触发一个map reduce job,然后半分钟过去了还在map阶段。。。而Airpal背后采用的prestodb则没有这个问题,并且Airpal提供对一个表的数据预览。

    Hive的第二个缺点是对于非技术人士不大友好,而airpal是图形界面,只要会sql就可以使用,结果直接生成一个csv文件。很多非技术部门,比如finance的分析员需要做大数据分析的时候,Airpal会非常方便。据我观察,在airbnb,数据科学家还是喜欢用命令行的hive,而非技术人士,或者需要做一些简单查询的工程师和产品经理,则多用airpal

    Airpal还有个好处是可以直接和公司的LDAP相连,员工用LDAP登陆,可以直接设置相应的访问权限,使得全公司可以放心用一套数据分析系统

    Aerosolve

    Aerosolve是支撑Airbnb定价建议系统的机器学习引擎。

    传统的机器学习引擎更像一个黑箱,很难知道是哪一个feature对最后的结果产生 了最大的影响。比如Airbnb上的房东设定价格后,我们不仅是希望提示这个价格是过高或过低(模型判断结果),而是希望给房东具体的原因,比如位置太偏,或者评价数不够多(feature的权重)。

    比如下图就说明了评价数量以及三星评价数量对价格的影响。我们(惊奇)的发现,一个评价和15个评价的效果差不多,房东并不会因为有更多的评价而得到更多的订单,而3星评价甚至会起到副作用


    2.jpg



    Airflow

    大数据的基础还是data pipeline。Airflow则是Airbnb内部发起、排序、监控data pipeline的工具。


    3.gif


    来源:用友开发者 收起阅读 »

    2015 年度开源项目新秀榜

    黑鸭(Black Duck)软件公布了一份名叫“年度开源项目新秀”的报告,介绍了由全球开源协会发起的10个最有趣、最活跃的新项目。 年度开源项目新秀 每年都有上千新的开源项目问世,但只有少数能够真正的吸引我们的关注。一些项目因为利用了当前比较流...
    继续阅读 »
    黑鸭(Black Duck)软件公布了一份名叫“年度开源项目新秀”的报告,介绍了由全球开源协会发起的10个最有趣、最活跃的新项目。


    1.jpg



    年度开源项目新秀

    每年都有上千新的开源项目问世,但只有少数能够真正的吸引我们的关注。一些项目因为利用了当前比较流行的技术而发展壮大,有一些则真正地开启了一个新的领域。很多开源项目建立的初衷是为了解决一些生产上的问题,还有一些项目则是世界各地志同道合的开发者们共同发起的一个宏伟项目。

    从2009年起,开源软件管理公司黑鸭便发起了 年度开源项目新秀 这一活动,它的评选根据 Open Hub 网站(即以前的Ohloh)上的活跃度。今年,我们很荣幸能够报道2015年10大开源项目新秀的得主和2名荣誉奖得主,它们是从上千个开源项目中脱颖而出的。评选采用了加权评分系统,得分标准基于项目的活跃度,交付速度和几个其它因数。

    开源俨然成为了产业创新的引擎,就拿今年来说,和Docker容器相关的开源项目在全球各地兴起,这也不恰巧反映了企业最感兴趣的技术领域吗?最后,我们接下来介绍的项目,将会让你了解到全球开源项目的开发者们的在思考什么,这很快将会成为一个指引我们发展的领头羊。

    2015年度开源项目新秀: DebOps


    2.jpg



    DebOps 收集打包了一套 Ansible 方案和规则(Ansible是一种自动化运维工具),可以从1个容器扩展到一个完整的数据中心。它的创始人Maciej Delmanowski将DebOps开源来保证项目长久进行,从而更好的通过外部贡献者的帮助发展下去。

    DebOps始创于波兰的一个不起眼大学校园里,他们运营自己的数据中心,一切工作都采用手工配置。有时系统崩溃而导致几天的宕机,这时Delmanowski意识到一个配置管理系统是很有必要的。以Debian作为基础开始,DebOps是一组配置一整个数据基础设施的Ansible方案。此项目已经在许多不同的工作环境下实现,而创始者们则打算继续支持和改进这个项目。

    2015年度开源项目新秀: Code Combat


    3.jpg



    传统的纸笔学习方法已近不能满足技术学科了。然而游戏却有很多人都爱玩,这也就是为什么 CodeCombat 的创始人会去开发一款多人协同编程游戏来教人们如何编码。

    刚开始CodeCombat是一个创业想法,但其创始人决定取而代之创建一个开源项目。此想法在社区传播开来,很快不少贡献者加入到项目中来。项目发起仅仅两个月后,这款游戏就被接纳到Google’s Summer of Code活动中。这款游戏吸引了大量玩家,并被翻译成45种语言。CodeCombat希望成为那些想要一边学习代码同时获得乐趣的同学的风向标。

    2015年度开源项目新秀: Storj


    4.jpg



    Storj 是一个点对点的云存储网络,可实现端到端加密,保证用户不用依赖第三方即可传输和共享数据。基于比特币block chain技术和点对点协议,Storj提供安全、私密、加密的云存储。

    云数据存储的反对者担心成本开销和漏洞攻击。针对这两个担忧,Storj提供了一个私有云存储市场,用户可以通过Storjcoin X(SJCX) 购买交易存储空间。上传到Storj的文件会被粉碎、加密和存储到整个社区。只有文件所有者拥有密钥加密的信息。

    在2014年举办的Texas Bitcoin Conference Hackathon会议上,去中心化的云存储市场概念首次被提出并证明可行。在第一次赢得黑客马拉松活动后,项目创始人们和领导团队利用开放论坛、Reddit、比特币论坛和社交媒体增长成了一个活跃的社区,如今,它们已成为影响Storj发展方向的一个重要组成部分。

    2015年度开源项目新秀: Neovim


    5.jpg


     
    自1991年出现以来,Vim已经成为数以百万计软件开发人员所钟爱的文本编辑器。 而 Neovim 就是它的下一个版本。

    在过去的23年里,软件开发生态系统经历了无数增长和创新。Neovim创始人Thiago de Arruda认为Vim缺乏当代元素,跟不上时代的发展。在保留Vim的招牌功能的前提下,Neovim团队同样在寻求改进和发展这个最受欢迎的文本编辑器的技术。早期众筹让Thiago de Arruda可以连续6个月时间投入到此项目。他相信Neovim社区会支持这个项目,激励他继续开发Neovim。

    2015年度开源项目新秀: CockroachDB


    6.jpg



    前谷歌员工开发了一个开源的大型企业数据存储项目 CockroachDB ,它是一个可扩展的、跨地域复制且支持事务的数据存储的解决方案。

    为了保证在线的百万兆字节流量业务的质量,Google开发了Spanner系统,这是一个可扩展的,稳定的,支持事务的系统。许多参与开发CockroachDB的团队现在都服务于开源社区。就像真正的蟑螂(cockroach)一样,CockroachDB可以在没有数据头、任意节点失效的情况下正常运行。这个开源项目有很多富有经验的贡献者,创始人们通过社交媒体、Github、网络、会议和聚会结识他们并鼓励他们参与其中。
     
    2015年度开源项目新秀: Kubernetes


    7.jpg



    在将容器化软件到引入开源社区发展时, Docker 是一匹黑马,它创新了一套技术和工具。去年6月谷歌推出了 Kubernetes ,这是一款开源的容器管理工具,用来加快开发和简化操作。

    谷歌在它的内部运营上使用容器技术多年了。在2014年夏天的DockerCon上大会上,谷歌这个互联网巨头开源了Kubernetes,Kubernetes的开发是为了满足迅速增长的Docker生态系统的需要。通过和其它的组织、项目合作,比如Red Hat和CoreOS,Kubernetes项目的管理者们推动它登上了Docker Hub的工具下载榜榜首。Kubernetes的开发团队希望扩大这个项目,发展它的社区,这样的话软件开发者就能花更少的时间在管理基础设施上,而更多的去开发他们自己的APP。

    2015年度开源项目新秀: Open Bazaar


    8.jpg


    OpenBazaar是一个使用比特币与其他人交易的去中心化的市场。OpenBazaar这一概念最早在编程马拉松(hackathon)活动中被提出,它的创始人结合了BitTorent、比特币和传统的金融服务方式,创造了一个不受审查的交易平台。OpenBazaar的开发团队在寻求新的成员,而且不久以后他们将极度扩大Open Bazaar社区。Open Bazaar的核心是透明度,其创始人和贡献者的共同目标是在商务交易中掀起一场革命,让他们向着一个真实的、一个无控制的,去中心化的市场奋进。

    2015年度开源项目新秀: IPFS


    9.jpg


     
    IPFS 是一个面向全球的、点对点的分布式版本文件系统。它综合了Git,BitTorrent,HTTP的思想,开启了一个新的数据和数据结构传输协议。

    人们所知的开源,它的本意用简单的方法解决复杂的问题,这样产生许多新颖的想法,但是那些强大的项目仅仅是开源社区的冰山一角。IFPS有一个非常激进的团队,这个概念的提出是大胆的,令人惊讶的,有点甚至高不可攀。看起来,一个点对点的分布式文件系统是在寻求将所有的计算设备连在一起。这个可能的 HTTP 替换品通过多种渠道维护着一个社区,包括Git社区和超过100名贡献者的IRC。这个疯狂的想法将在2015年进行软件内部测试。

    2015年度开源项目新秀: cAdvisor


    10.jpg



    cAdvisor (Container Advisor) 是一个针对在运行中的容器进行收集,统计,处理和输出信息的工具,它可以给容器的使用者提供资源的使用情况和工作特性。对于每一个容器,cAdvisor记录着资源的隔离参数,资源使用历史,资源使用历史对比框图,网络状态。这些从容器输出的数据跨越主机传递。

    cAdvisor可以在绝大多数的Linux发行版上运行,并且支持包括Docker在内的多种容器类型。事实上它成为了一种容器的代理,并被集成在了很多系统中。cAdvisor在DockerHub下载量也是位居前茅。cAdvisor的开发团队希望把cAdvisor改进到能够更深入地理解应用性能,并且集成到集群系统。

    2015年度开源项目新秀: Terraform


    11.jpg


    Terraform 提供了一些常见设置来创建一个基础设施,从物理机到虚拟机,以及email服务器、DNS服务器等。这个想法包括从家庭个人机解决方案到公共云平台提供的服务。一旦建立好了以后,Terraform可以让运维人员安全又高效地改变你的基础设施,就如同配置一样。

    Terraform.io的创始者工作在一个Devops模式的公司,他找到了一个窍门把建立一个完整的数据中心所需的知识结合在一起,可以从添加服务器到支持网络服务的功能齐备的数据中心。基础设施的描述采用高级的配置语法,允许你把数据中心的蓝图按版本管理,并且转换成多种代码。著名开源公司HashiCorp赞助开发这个项目。

    荣誉奖: Docker Fig


    12.jpg



    Drone 是一个基于Docker的持续集成平台,而且它是用Go语言写的。Drone项目不满于现存的设置开发环境的技术和流程。

    Drone提供了一个简单的自动测试和持续交付的方法:简单选择一个Docker镜像来满足你的需求,连接并提交至GitHub即可。Drone使用Docker容器来提供隔离的测试环境,让每个项目完全自主控制它的环境,没有传统的服务器管理的负担。Drone背后的100位社区贡献者强烈希望把这个项目带到企业和移动应用程序开发中。

    开源新秀


    13.jpg


     
    作者: Black Duck Software
    Linux中国|译者: sevenot 收起阅读 »

    环信CEO:“即时通讯云+移动客服”为App打造用户体验闭环

      随着移动互联网的发展,即时通讯、移动客服已经成为了很多移动应用的必备功能,环信作为新晋移动即时通讯PaaS平台服务商,凭借着近期刚刚上线的跨平台移动端客服产品吸引了大量应用开发者的关注。为了进一步了解移动客服产品的发展现状,InfoQ专门对环信联合创始人及...
    继续阅读 »


    ban9.png


     
    随着移动互联网的发展,即时通讯、移动客服已经成为了很多移动应用的必备功能,环信作为新晋移动即时通讯PaaS平台服务商,凭借着近期刚刚上线的跨平台移动端客服产品吸引了大量应用开发者的关注。为了进一步了解移动客服产品的发展现状,InfoQ专门对环信联合创始人及CEO刘俊彦进行了专访。
     
    InfoQ:环信成立已经有两年的时间,能聊一下这两年环信的整体发展吗?
     
    刘俊彦:环信在2013年4月成立,2014年6月第一个产品”环信即时通讯云”正式上线。然后在今年4月份上线了移动客服产品。在过去的一年里面,环信做了三轮融资,分别是去年5月份的天使轮,去年8月份的A轮,和去年10月份的A+轮。截止到今年5月底,一共有23000款APP在使用环信即时通讯云SDK。环信的SDK覆盖了2.5亿的终端用户,环信即时通讯平台日活是千万级别,每天处理将近两亿条消息。这就是环信过去两年大概的情况。
    InfoQ:移动客服可以算是现在移动应用通讯领域的一个刚性需求了,您能否谈一谈移动客服的形式都有哪些?
    刘俊彦:虽然移动客服是一种移动互联网时代的新产品,但其实也只是形式上的一个创新。基本上每一个APP的设置页面都会有一个“意见反馈”的功能。其实这个功能就是一个客服功能,只是受限于技术、资源等因素,需要以表单的形式来呈现,有的时候还需要用户选择类别、提交联系方式,很难做到实时更新。

    最近这一年,随着移动电商、O2O,在线教育,在线旅游,互联网金融产品的发展,出现了很多形式的客服产品。比如:作为电商,做O2O一定要做售前、售中、售后。这个过程中就涉及到用户与商家的沟通。

    常见的沟通形式有4种,第一种是在App里面提供一个按钮,用户点击该按钮直接跳转到QQ,然后通过QQ去完成与商家的交流。

    第二种是通过链接跳转微信。第三种形式就是我们最开始提到的表单形式,这种形式是非实时的。

    最新的一种形式是用即时通讯的方法来跟商家沟通。用户打开一个窗口,在该窗口可以发语音、图片、文字,可以跟商家进行实时互动,这种形式就类似与微信、旺旺。第四种形式是最受大家欢迎,受O2O的商家、电商、医疗、互联网金融认可度最高的的一种客服形式,这种形式非常有利于用户通过手机与商家进行沟通。

    InfoQ:针对这种移动客服的形式,存在哪些技术难点?环信是怎样客服这些难点的?
    刘俊彦:用IM来做客服虽然很方便,但它的技术门槛比较高。第一需要你的服务器能够做到千万级、亿级的并发处理。移动客服是基于IM的长连接技术实现的。举个例子,如果某个App有100万日活用户,那么用户的手机和客服服务器之间就存在一百万条长连接。一些大型的App可能会有几百万、几千万的日活用户,那么提供服务的厂家就需要支持几百万、几千万的用户长连接。如果你的平台要支持上几百家厂商,那么平台就需要有支持几千万到几亿用户同时连接的能力。

    第二需要做到不丢消息,并且每一条消息能够做到最实时的到达。即时通讯服务是帮助商家来进行销售的,用户可能是在三线城市,也可能是在四线城市,网络环境可能是2G、3G或4G。要做到任何情况下,只要有网络,消息一定能够即时到达是非常困难的。但如果你的平台做不到,就会给商家带来损失。拿电商来举例:一个消费者想买一样东西,发了一条咨询消息,但这个消息丢失了,那么就意味着这个单子丢了。这个用户可能是商家花了很多钱,从其它平台导流过来的,但是因为一条咨询没有即时收到,结果流失了,这样就给商家带来了损失。

    环信对于移动客服的技术已经非常成熟,因为环信从去年6月1日正式上线,做的就是即时通讯云。我们在即时通讯云这一块已经做到了全国有2.3万家APP使用,有2.5亿用户,平台的日活用户是几千万。两年多的技术储备让我们敢保证绝对不丢消息,并且消息能够非常实时的到达。

    InfoQ:因为领域的不同导致用户流量分布特点也不同,所以说即时通讯服务里面会经常产生波峰波谷。能不能谈一谈环信在这个弹性方面的具体措施呢?
    刘俊彦:解决该问题要从技术与非技术两方面着手。非技术其实就只能靠烧钱来解决。我们系统上大概有50%的余量,超过50%的压力之后,就开始加服务器。这样能保证系统在不到50%冗余的情况下运行。当然这也意味着有50%的容量浪费,其实这个“浪费”是应付一些不可预料的波峰和波谷。单个APP的用户行为基本是固定的。社交类APP,大概在晚上十点半会产生波峰。而有一些企业办公类、教育类App是在白天产生波峰。我们为两万多家APP提供服务,综合起来整个波峰和波谷就会比较平均一些。

    我们也采取了一些技术手段来解决该问题。现在有一些云服务平台提供秒级计算API。当到半夜两点钟,所有APP都进入波谷后,我们就会调用API释放掉一批服务器。但这样带来的经济效益也不高,因为秒级API走的是另外一套收费体系。

    InfoQ:环信开放了UI源码,现在用户可以深度定制应用的UI。环信还建立了自己的开源社区,能不能谈一谈环信在开源方面有什么样的规划?是否考虑给开发者开放更灵活的API,或者是直接开源一些核心技术?
    刘俊彦:环信的四位创始人有三位都是长期在开源社区工作的。而我从03年以后,基本上没有做过商业软件。所以开源精神已经深深植入到每一位创始人。

    除了UI开源之外,我们还建了一个自己的开源社区。在过去的两年里,我们看到了一个很有意思的现象。很多人用环信来做社交,有单聊、群聊、匿名群聊等等各种玩法。但是归纳之后,大概可分为几十种。所以我们希望通过社区的力量,把这几十种常见的社交模块做出来,然后用开源的形式提供给大家。当用户想要做一款新的社交APP时,基于环信这样的底层云服务模块,有可能会节约几个月的时间。

    有了上面的基础,我们就想走的更远一点。当你想做一款APP的时候,你可能用到一个朋友圈的功能,用到一个匿名的功能,我们希望这些功能也变成一个现成的模块,甚至整个APP都能够以开源的形式完整呈现。这样大家在开发一款APP的时候,就像是搭积木,选不同的积木模块,然后把他们拼起来。特别是对于创业者,可以把更多的时间用于提升用户体验。

    最近这一个月我们开放了三个应用级别的模块:第一个是凡信,它是高仿微信的一款APP。凡信实现了单聊、群聊、朋友群等功能。这是我们社区里一个网友开发的,他完全无私的把服务器端和客服端代码开源出来。第二个是一个类似于陌陌的陌生人交友APP,功能主要是看附近的人,看到附近的人之后可以跟他聊天。第三个是我们面向企业的开源产品。上面提到的开源项目大家都可以到我们的社区下载,当然你也可以成为这些项目的贡献者,一起来推动这些产品往前走。

    InfoQ:最近IT行业内数据中心机房出的问题也很多,环信在异地多活这方面有什么样的规划吗?
    刘俊彦:到现在为止,环信的云服务都托管在国内最一流的云服务平台上。按照他们的星级来说,都是最顶尖的机房,可靠性、安全性都是有保证的。但为了给大家提供更可靠、更放心的云服务,“异地多活”已经列入了我们的开发计划,未来的几个月环信的“异地多活”就会正式上线。
    InfoQ:最近有消息说环信要开始新一轮的融资,您能不能谈一谈环信在短期内的发展计划呢?
    刘俊彦:刚刚我提到过环信在过去一年里进行了三轮融资。我们目前其实正在做B轮融资,因为还在进行中,如果有更多的细节我会尽快告诉大家。

    环信发展到现在已成规模,下一步我们会继续巩固环信在“即时通讯云”领域国内第一的位置,我们希望以最低的价格、最好的服务为做社交以及各种应用服务者提供即时通讯功能。其次我们在今年4月份上线了环信移动客服产品。移动客服是我们在即时通讯领域一个很大的扩展。在即时通讯领域有两种场景,一种是在APP里面用户和用户之间进行社交活动,另一种是用户与商家之间的聊天,也就是客服。我们一直认为任何一款APP,都需要即时通讯,那么要达到100%的覆盖,光做社交是无法实现的,所以我们要加上客服这一块。我们最终的愿景是用环信的力量和技术为每一款APP提供即时通讯功能。

      收起阅读 »

    盘点:被互联网女皇报告点名,美国14家潜在“独角兽”SaaS公司

     这个slide是从女皇报告中节选出来的,互联网对消费者群体带来了颠覆式的影响,这种影响也诞生了伟大的新时代的互联网公司,Airbnb,uber,instantcart就是其中在住行吃方面的典范。而这种颠覆的影响将迅速扩展到企业端,于是企业SaaS服务拥有巨大...
    继续阅读 »
     这个slide是从女皇报告中节选出来的,互联网对消费者群体带来了颠覆式的影响,这种影响也诞生了伟大的新时代的互联网公司,Airbnb,uber,instantcart就是其中在住行吃方面的典范。而这种颠覆的影响将迅速扩展到企业端,于是企业SaaS服务拥有巨大的机会。


    1.jpg



    而在美国,这些企业服务公司已获得极大的关注:

    1、Slack:成立1年多估值28亿美元当人们谈论Slack时,经常会说它特别“有趣”。使用Slack不像是在工作,而是在“放松”,但是在这个过程中却能通过Slack把该做工作切实有效地完成。有3个方面的因素是Slack脱颖而出的关键:用户界面与众不同;使用感受与众不同;交流方式与众不同。


    2[1].jpg



     2、Square:移动支付鼻祖,估值60亿美元Twitter之父Jack Dorsey的创办的Square,移动支付公司日交易额已达1亿美元,深入到中小企业、个体零售商和移动商贩里去,并不断拓展到各种O2O的订单支付环节。


    3.jpg


      3、Stripe:在线支付挑战者,估值50亿美元由20多岁的两兄弟Patrick Collison和John Collison创办的Stripe为公司提供网上支付的解决方案。在这两位分别从麻省理工和哈佛辍学创业的兄弟看来,PayPal的电子支付流程是在是太繁琐太复杂了,他们的目标是要把在线接收付款服务变成像添加 YouTube 视频一般简单。


    4.jpg



      4、Domo:低调的商业智能软件公司,估值20亿美DOMO是一个真正的业务管理平台,可以将许多不同来源的数据以真正实时、直观的方式呈现出来。而Domo的创始人乔希•詹姆斯(Josh James)早在1996年就创办了Omniture公司,SaaS这个商业模式的鼻祖,也是Analytics这个领域的开创者和领导者。2009年,Adobe以18亿美元的价格收购了Omniture,后者改名叫做Adobe Analytics。2010年乔希•詹姆斯(Josh James)离开Adobe,从Benchmark Capital拿到了3000万美元的第一轮融资并创建了DOMO。


    5.jpg



      5、DocuSign:小签名大市场,数字签名公司估值30亿美元DocuSign是一家提供电子签名服务的初创企业,成立于2003年,总部位于旧金山,在全球有10个分支机构,共有1300名员工。通过DocunSign的服务,用户只需通过智能手机或平板电脑即可完成手写签名,免除了用户要通过传真或邮件签名的麻烦;同时DocuSign还通过数字签名等方式验证用户的真伪,从而帮助企业用户安全地在网上获取具有法律效力的电子签名。目前全球188个国家有超过10万家公司以及5000万个人都在使用DocuSign的电子签名服务。


    6.jpg



      6、Intercom:应用内的客户沟通工具,估值4亿美元Intercom是一款面向企业用户的客户沟通上SaaS工具,可以让企业与用户的沟通轻易在应用和网站内实现,强大的后台管理系统能够实时监控客户反馈,指派任务并统计客户的订单转换率,而相对于传统的电子邮件营销产品,应用内消息能带来更好的用户参与度(基于回复率)。


    conew_7.jpg



      7、Gainsight:帮你留住客户,估值6亿美元Gainsight公司整合了第三方应用,他们评估销售数据,使用日志,支持投票,调研报告,以及其他客户智能资源。通过收集和分析这些数据,让企业减少客户流失率。当客户出现离开风险的时候,Gainsight的系统会发出早期预警,企业因此可以采取行动,挽留客户,另外还可以提供一些工具,识别出那些对企业产品评价较高的客户,这样可以为企业带来提升销售量的机会。


    conew_8.jpg



      8、Directly:众包客户服务中心,估值1亿美元Directly以帮助创业公司将客户服务外包给其他人。Directly 希望帮助客户通过提问与回答的方式向用户提供更好的体验,而注册这家网站的一干“专家”则希望能够因此获得报酬。Directly 用户在网站上发布问题后,系统将自动分析问题,并匹配可能解答这些问题的专家,然后通过短信提醒他们。然后专家们便可以通过 Directly 网页端或者 Android 应用进行解答。大多数问题会在数小时内得到妥善解决,一旦对答案感到满意,提问者便可以关注回答者。


    conew_9.jpg



      9、Zenefits:疯狂的HR管理工具,2年估值45亿美元Zenefits 为中小企业提供免费的一站式云 HR 管理工具,简单地说,就是让 HR 杂活更简单便捷,包括员工的入职和离职手续办理,工资和福利发放,保险和退休基金办理,缴税缴费,专利追踪等等。和 Uber 一样, Zenefits 在推广过程中也受到了部分地方政府的管制。在美国犹他州, Zenefits 一度被判定为非法,罪名是不正当返利和诱导用户。罪名的由头是, Zenefits 免费提供保险办理的入口,如果用户通过 Zenefits 办理保险,保险公司需要支付给 Zenefits 5%的佣金。这样一来,绕过了保险经销商。


    conew_10.jpg



     10、Anaplan:帮助企业业务建模,估值5亿美元Anaplan是一家基于云的企业销售、运营及财务建模与规划公司。其解决方案可以收集企业客户的销售、运营及财务数据,然后运用复杂的模型对企业的绩效、开支等情况进行深入分析,为决策提供支持。


    conew_11.jpg



      11、Greenhouse:企业一体化招聘流程,估值3亿美元Greenhouse 是一家招聘领域的SaaS软件服务商,为企业提供招聘管理、求职信管理、面试、人才招聘广告评估等服务,提供求职者比较、推荐等服务。Uber、Pinterest、airbnb Snapchat、和Buzzfeed都是Greenhouse的客户。


    conew_12.jpg



      12、Checkr:高效的自动化背景调查平台,估值0.5亿美元Checkr 使用相同的数据源,提供包括社会安全号码、历史地址、性犯罪检索、恐怖分子监控名单、国家犯罪记录、驾驶记录等方面的背景调查。但与传统调查报告不同的是,Checkr 提供了一个客户只需输入名字就能得到数据反馈的 API,简化了调查报告的手工操作过程,使得背景调查更加自动化,从而更容易链接到公司现行的入职流程之中。


    conew_13.jpg


     
     13、Guidespark:数字化员工培训资料,估值2亿美元创立于 2008 年,为人力资源管理人员提供沟通和培训解决方案,将纸质文件变成电子文件,并将这些资料移动化。这样能够减少人力资源支持的时间,提升人力资源的效率。


    conew_14.jpg


     
     14、Envoy:智能访客管理系统,估值2亿美元智能访客管理系统 Envoy 专门为科技范儿的互联网公司量身打造,简它将整个访客管理流程电子化,提供从登记信息到通知负责人再到访客管理的一条龙服务,大大提升了前台效率。


    conew_15.jpg


     
    来源:创业投资笔记
    作者:苏东 收起阅读 »

    群详情获取失败

    在确保登录成功的情况下,并且初始化完成,可以进行群创建和聊天的情况下   调用群详情获取 //根据群聊ID从服务器获取群聊信息 EMGroup group =EMGroupManager.getInstance().getGroupFromServer(gro...
    继续阅读 »
    在确保登录成功的情况下,并且初始化完成,可以进行群创建和聊天的情况下
     
    调用群详情获取
    //根据群聊ID从服务器获取群聊信息
    EMGroup group =EMGroupManager.getInstance().getGroupFromServer(grounId);
    这个代码走之后直接进去了异常,
    EaseMobException的描述是
    com.easemob.exceptions.EaseMobException: android.os.NetworkOnMainThreadException
    打印的堆栈消息:
    07-04 07:42:34.087: W/System.err(7386): com.easemob.exceptions.EaseMobException: android.os.NetworkOnMainThreadException
    07-04 07:42:34.097: W/System.err(7386):     at com.easemob.cloud.HttpClientManager.sendRequestWithCountDown(Unknown Source)
    07-04 07:42:34.107: W/System.err(7386):     at com.easemob.cloud.HttpClientManager.sendRequest(Unknown Source)
    07-04 07:42:34.107: W/System.err(7386):     at com.easemob.cloud.HttpClientManager.sendHttpRequestWithRetryToken(Unknown Source)
    07-04 07:42:34.117: W/System.err(7386):     at com.easemob.cloud.HttpClientManager.sendRequestWithToken(Unknown Source)
    07-04 07:42:34.127: W/System.err(7386):     at com.easemob.cloud.EMHttpClient.sendRequestWithToken(Unknown Source)
    07-04 07:42:34.137: W/System.err(7386):     at com.easemob.chat.EMGroupManager.getGroupFromRestServer(Unknown Source)
    07-04 07:42:34.137: W/System.err(7386):     at com.easemob.chat.EMGroupManager.getGroupFromServer(Unknown Source)
    07-04 07:42:34.137: W/System.err(7386):     at com.tomatotown.util.InitIM.getPublicGroupInfo(InitIM.java:324)
    07-04 07:42:34.137: W/System.err(7386):     at com.tomatotown.parent.activity.friends.GroupInfoActivity.getGroupInfo(GroupInfoActivity.java:246)
    07-04 07:42:34.137: W/System.err(7386):     at com.tomatotown.parent.activity.friends.GroupInfoActivity.onCreate(GroupInfoActivity.java:67)
    07-04 07:42:34.137: W/System.err(7386):     at android.app.Activity.performCreate(Activity.java:5231)
    07-04 07:42:34.137: W/System.err(7386):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
    07-04 07:42:34.137: W/System.err(7386):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2227)
    07-04 07:42:34.147: W/System.err(7386):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2313)
    07-04 07:42:34.147: W/System.err(7386):     at android.app.ActivityThread.access$800(ActivityThread.java:147)
    07-04 07:42:34.147: W/System.err(7386):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1226)
    07-04 07:42:34.147: W/System.err(7386):     at android.os.Handler.dispatchMessage(Handler.java:102)
    07-04 07:42:34.147: W/System.err(7386):     at android.os.Looper.loop(Looper.java:136)
    07-04 07:42:34.147: W/System.err(7386):     at android.app.ActivityThread.main(ActivityThread.java:5137)
    07-04 07:42:34.147: W/System.err(7386):     at java.lang.reflect.Method.invokeNative(Native Method)
    07-04 07:42:34.147: W/System.err(7386):     at java.lang.reflect.Method.invoke(Method.java:515)
    07-04 07:42:34.147: W/System.err(7386):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:801)
    07-04 07:42:34.147: W/System.err(7386):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:617)
    07-04 07:42:34.147: W/System.err(7386):     at dalvik.system.NativeStart.main(Native Method)
     
     
     
    求助!! 收起阅读 »

    分享:大型网站图片服务器架构的演进

    在主流的Web站点中,图片往往是不可或缺的页面元素,尤其在大型网站中,几乎都将面临“海量图片资源”的存储、访问等相关技术问题。在针对图片服务器的架构扩展中,也会历经很多曲折甚至是血泪教训(尤其是早期规划不足,造成后期架构上很难兼容和扩展)。 本文将以一个...
    继续阅读 »
    在主流的Web站点中,图片往往是不可或缺的页面元素,尤其在大型网站中,几乎都将面临“海量图片资源”的存储、访问等相关技术问题。在针对图片服务器的架构扩展中,也会历经很多曲折甚至是血泪教训(尤其是早期规划不足,造成后期架构上很难兼容和扩展)。

    本文将以一个真实垂直门户网站的发展历程,向大家娓娓道来。

    构建在Windows平台之上的网站,往往会被业内众多技术认为很“保守”,甚至会有点。很大部分原因,是由于微软技术体系的封闭和部分技术人员的短视造成的(当然,主要还是人的问题)。由于长期缺乏开源支持,所以很多人只能“闭门造车”,这样很容易形成思维局限性和短板。以图片服务器为例子,如果前期没有容量规划和可扩展的设计,那么随着图片文件的不断增多和访问量的上升,由于在性能、容错/容灾、扩展性等方面的设计不足,后续将会给开发、运维工作带来很多问题,严重时甚至会影响到网站业务正常运作和互联网公司的发展(这绝不是在危言耸听)。

    很多公司之所以选择Windows(.NET)平台来构建网站和图片服务器,很大部分由创始团队的技术背景决定的,早期的技术人员可能更熟悉.NET,或者团队的负责人认为Windows/.NET的易用性、“短平快”的开发模式、人才成本等方面都比较符合创业初期的团队,自然就选择了Windows。后期业务发展到一定规模,也很难轻易将整体架构迁移到其它开源平台上了。当然,对于构建大规模互联网,更建议首选开源架构,因为有很多成熟的案例和开源生态的支持(也会有很多坑,就看是你自己最先去踩坑,还是在别人踩了修复之后你再用),避免重复造轮子和支出高额授权费用。对于迁移难度较大的应用,个人比较推荐Linux、Mono、Jexus、Mysql、Memcahed、Redis……混搭的架构,同样能支撑具有高并发访问和大数据量等特点的互联网应用。
     
    单机时代的图片服务器架构(集中式)
     
    初创时期由于时间紧迫,开发人员水平也很有限等原因。所以通常就直接在website文件所在的目录下,建立1个upload子目录,用于保存用户上传的图片文件。如果按业务再细分,可以在upload目录下再建立不同的子目录来区分。例如:upload\QA,upload\Face等。

    在数据库表中保存的也是”upload/qa/test.jpg”这类相对路径。

    用户的访问方式如下:

    http://www.yourdomain.com/upload/qa/test.jpg

    程序上传和写入方式:

    程序员A通过在web.config中配置物理目录D:\Web\yourdomain\upload  然后通过stream的方式写入文件;

    程序员B通过Server.MapPath等方式,根据相对路径获取物理目录  然后也通过stream的方式写入文件。

    优点:实现起来最简单,无需任何复杂技术,就能成功将用户上传的文件写入指定目录。保存数据库记录和访问起来倒是也很方便。

    缺点:上传方式混乱,严重不利于网站的扩展。

    针对上述最原始的架构,主要面临着如下问题:

    • 随着upload目录中文件越来越多,所在分区(例如D盘)如果出现容量不足,则很难扩容。只能停机后更换更大容量的存储设备,再将旧数据导入。

    • 在部署新版本(部署新版本前通过需要备份)和日常备份website文件的时候,需要同时操作upload目录中的文件,如果考虑到访问量上升,后边部署由多台Web服务器组成的负载均衡集群,集群节点之间如果做好文件实时同步将是个难题。


     集群时代的图片服务器架构(实时同步)
     
    在website站点下面,新建一个名为upload的虚拟目录,由于虚拟目录的灵活性,能在一定程度上取代物理目录,并兼容原有的图片上传和访问方式。用户的访问方式依然是:

    http://www.yourdomain.com/upload/qa/test.jpg

    • 优点:配置更加灵活,也能兼容老版本的上传和访问方式。



    因为虚拟目录,可以指向本地任意盘符下的任意目录。这样一来,还可以通过接入外置存储,来进行单机的容量扩展。

    • 缺点:部署成由多台Web服务器组成的集群,各个Web服务器(集群节点)之间(虚拟目录下的)需要实时的去同步文件,由于同步效率和实时性的限制,很难保证某一时刻各节点上文件是完全一致的。



    基本架构如下图所示:


    1.jpg

    在早期的很多基于Linux开源架构的网站中,如果不想同步图片,可能会利用NFS来实现。事实证明,NFS在高并发读写和海量存储方面,效率上存在一定问题,并非最佳的选择,所以大部分互联网公司都不会使用NFS来实现此类应用。当然,也可以通过Windows自带的DFS来实现,缺点是“配置复杂,效率未知,而且缺乏资料大量的实际案例”。另外,也有一些公司采用FTP或Samba来实现。

    上面提到的几种架构,在上传/下载操作时,都经过了Web服务器(虽然共享存储的这种架构,也可以配置独立域名和站点来提供图片访问,但上传写入仍然得经过Web服务器上的应用程序来处理),这对Web服务器来讲无疑是造成巨大的压力。所以,更建议使用独立的图片服务器和独立的域名,来提供用户图片的上传和访问。
     
    独立图片服务器/独立域名的好处
     

    • 图片访问是很消耗服务器资源的(因为会涉及到操作系统的上下文切换和磁盘I/O操作)。分离出来后,Web/App服务器可以更专注发挥动态处理的能力。

    • 独立存储,更方便做扩容、容灾和数据迁移。

    • 浏览器(相同域名下的)并发策略限制,性能损失。

    • 访问图片时,请求信息中总带cookie信息,也会造成性能损失。

    • 方便做图片访问请求的负载均衡,方便应用各种缓存策略(HTTP Header、Proxy Cache等),也更加方便迁移到CDN。


    ......
     
    我们可以使用Lighttpd或者Nginx等轻量级的web服务器来架构独立图片服务器。
     
    当前的图片服务器架构(分布式文件系统+CDN)
     
    在构建当前的图片服务器架构之前,可以先彻底撇开web服务器,直接配置单独的图片服务器/域名。但面临如下的问题:

    • 旧图片数据怎么办?能否继续兼容旧图片路径访问规则?

    • 独立的图片服务器上需要提供单独的上传写入的接口(服务API对外发布),安全问题如何保证?

    • 同理,假如有多台独立图片服务器,是使用可扩展的共享存储方案,还是采用实时同步机制?


     
    直到应用级别的(非系统级) DFS(例如FastDFS HDFS MogileFs MooseFS、TFS)的流行,简化了这个问题:执行冗余备份、支持自动同步、支持线性扩展、支持主流语言的客户端api上传/下载/删除等操作,部分支持文件索引,部分支持提供Web的方式来访问。

    考虑到各DFS的特点,客户端API语言支持情况(需要支持C#),文档和案例,以及社区的支持度,我们最终选择了FastDFS来部署。

    唯一的问题是:可能会不兼容旧版本的访问规则。如果将旧图片一次性导入FastDFS,但由于旧图片访问路径分布存储在不同业务数据库的各个表中,整体更新起来也十分困难,所以必须得兼容旧版本的访问规则。架构升级往往比做全新架构更有难度,就是因为还要兼容之前版本的问题。(给飞机在空中换引擎可比造架飞机难得多)
     
    解决方案如下:
     
    首先,关闭旧版本上传入口(避免继续使用导致数据不一致)。将旧图片数据通过rsync工具一次性迁移到独立的图片服务器上(即下图中描述的Old Image Server)。在最前端(七层代理,如Haproxy、Nginx)用ACL(访问规则控制),将旧图片对应URL规则的请求(正则)匹配到,然后将请求直接转发指定的web 服务器列表,在该列表中的服务器上配置好提供图片(以Web方式)访问的站点,并加入缓存策略。这样实现旧图片服务器的分离和缓存,兼容了旧图片的访问规则并提升旧图片访问效率,也避免了实时同步所带来的问题。
     
    整体架构如图:


    2.jpg

    基于FastDFS的独立图片服务器集群架构,虽然已经非常的成熟,但是由于国内“南北互联”和IDC带宽成本等问题(图片是非常消耗流量的),我们最终还是选择了商用的CDN技术,实现起来也非常容易,原理其实也很简单,我这里只做个简单的介绍:

    将img域名cname到CDN厂商指定的域名上,用户请求访问图片时,则由CDN厂商提供智能DNS解析,将最近的(当然也可能有其它更复杂的策略,例如负载情况、健康状态等)服务节点地址返回给用户,用户请求到达指定的服务器节点上,该节点上提供了类似Squid/Vanish的代理缓存服务,如果是第一次请求该路径,则会从源站获取图片资源返回客户端浏览器,如果缓存中存在,则直接从缓存中获取并返回给客户端浏览器,完成请求/响应过程。

    由于采用了商用CDN服务,所以我们并没有考虑用Squid/Vanish来自行构建前置代理缓存。

    上面的整个集群架构,可以很方便的做横向扩展,能满足一般垂直领域中大型网站的图片服务需求(当然,像taobao这样超大规模的可能另当别论)。经测试,提供图片访问的单台Nginx服务器(至强E5四核CPU、16G内存、SSD),对小静态页面(压缩后大概只有10kb左右的)可以扛住几千个并发且毫无压力。当然,由于图片本身体积比纯文本的静态页面大很多,提供图片访问的服务器的抗并发能力,往往会受限于磁盘的I/O处理能力和IDC提供的带宽。Nginx的抗并发能力还是非常强的,而且对资源占用很低,尤其是处理静态资源,似乎都不需要有过多担心了。可以根据实际访问量的需求,通过调整Nginx的参数,对Linux内核做调优,加入分级缓存策略等手段能够做更大程度的优化,也可以通过增加服务器或者升级服务器配置来做扩展,最直接的是通过购买更高级的存储设备和更大的带宽,以满足更大访问量的需求。

    值得一提的是,在“云计算”流行的当下,也推荐高速发展期间的网站,使用“云存储”这样的方案,既能帮你解决各类存储、扩展、备灾的问题,又能做好CDN加速。最重要的是,价格也不贵。

    总结,有关图片服务器架构扩展,大致围绕这些问题展开:
     

    • 容量规划和扩展问题。

    • 数据的同步、冗余和容灾。

    • 硬件设备的成本和可靠性(是普通机械硬盘,还是SSD,或者更高端的存储设备和方案)。

    • 文件系统的选择。根据文件特性(例如文件大小、读写比例等)选择是用ext3/4或者NFS/GFS/TFS这些开源的(分布式)文件系统。

    • 图片的加速访问。采用商用CDN或者自建的代理缓存、web静态缓存架构。

    • 旧图片路径和访问规则的兼容性,应用程序层面的可扩展,上传和访问的性能和安全性等。


    收起阅读 »

    解析:带你从源码的角度彻底理解,Android事件分发机制(下)

    记得在前面的文章中,我带大家一起从源码的角度分析了Android中View的事件分发机制,相信阅读过的朋友对View的事件分发已经有比较深刻的理解了。 还未阅读过的朋友,请先参考上文:http://www.imgeek.org/article/51 ...
    继续阅读 »
    记得在前面的文章中,我带大家一起从源码的角度分析了Android中View的事件分发机制,相信阅读过的朋友对View的事件分发已经有比较深刻的理解了。

    还未阅读过的朋友,请先参考上文:http://www.imgeek.org/article/51

    那么今天我们将继续上次未完成的话题,从源码的角度分析ViewGruop的事件分发。


    首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别?

    顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子VewGroup,是Android中所有布局的父类或间接父类,像LinearLayout、RelativeLayout等都是继承自ViewGroup的。但ViewGroup实际上也是一个View,只不过比起View,它多了可以包含子View和定义布局参数的功能。ViewGroup继承结构示意图如下所示:


    7.png



    可以看到,我们平时项目里经常用到的各种布局,全都属于ViewGroup的子类。

    简单介绍完了ViewGroup,我们现在通过一个Demo来演示一下Android中VewGroup的事件分发流程吧。


    先我们来自定义一个布局,命名为MyLayout,继承自LinearLayout,如下所示:
    public class MyLayout extends LinearLayout {  
    02.
    03. public MyLayout(Context context, AttributeSet attrs) {
    04. super(context, attrs);
    05. }
    06.
    07.}
    然后,打开主布局文件activity_main.xml,在其中加入我们自定义的布局:

    [html] view plaincopy
    01. 02. xmlns:tools="http://schemas.android.com/tools"
    03. android:id="@+id/my_layout"
    04. android:layout_width="match_parent"
    05. android:layout_height="match_parent"
    06. android:orientation="vertical" >
    07.
    08.
    可以看到,我们在MyLayout中添加了两个按钮,接着在MainActivity中为这两个按钮和MyLayout都注册了监听事件:
    myLayout.setOnTouchListener(new OnTouchListener() {  
    02. @Override
    03. public boolean onTouch(View v, MotionEvent event) {
    04. Log.d("TAG", "myLayout on touch");
    05. return false;
    06. }
    07.});
    08.button1.setOnClickListener(new OnClickListener() {
    09. @Override
    10. public void onClick(View v) {
    11. Log.d("TAG", "You clicked button1");
    12. }
    13.});
    14.button2.setOnClickListener(new OnClickListener() {
    15. @Override
    16. public void onClick(View v) {
    17. Log.d("TAG", "You clicked button2");
    18. }
    19.});
     我们在MyLayout的onTouch方法,和Button1、Button2的onClick方法中都打印了一句话。现在运行一下项目,效果图如下所示:


    8.png



    分别点击一下Button1、Button2和空白区域,打印结果如下所示:


    9.png



    你会发现,当点击按钮的时候,MyLayout注册的onTouch方法并不会执行,只有点击空白区域的时候才会执行该方法。你可以先理解成Button的onClick方法将事件消费掉了,因此事件不会再继续向下传递。

    那就说明Android中的touch事件是先传递到View,再传递到ViewGroup的?现在下结论还未免过早了,让我们再来做一个实验。

    查阅文档可以看到,ViewGroup中有一个onInterceptTouchEvent方法,我们来看一下这个方法的源码:
    /** 
    02. * Implement this method to intercept all touch screen motion events. This
    03. * allows you to watch events as they are dispatched to your children, and
    04. * take ownership of the current gesture at any point.
    05. *
    06. *

    Using this function takes some care, as it has a fairly complicated
    07. * interaction with {@link View#onTouchEvent(MotionEvent)
    08. * View.onTouchEvent(MotionEvent)}, and using it requires implementing
    09. * that method as well as this one in the correct way. Events will be
    10. * received in the following order:
    11. *
    12. *


      13. *
    1. You will receive the down event here.
      14. *
    2. The down event will be handled either by a child of this view
      15. * group, or given to your own onTouchEvent() method to handle; this means
      16. * you should implement onTouchEvent() to return true, so you will
      17. * continue to see the rest of the gesture (instead of looking for
      18. * a parent view to handle it). Also, by returning true from
      19. * onTouchEvent(), you will not receive any following
      20. * events in onInterceptTouchEvent() and all touch processing must
      21. * happen in onTouchEvent() like normal.
      22. *
    3. For as long as you return false from this function, each following
      23. * event (up to and including the final up) will be delivered first here
      24. * and then to the target's onTouchEvent().
      25. *
    4. If you return true from here, you will not receive any
      26. * following events: the target view will receive the same event but
      27. * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
      28. * events will be delivered to your onTouchEvent() method and no longer
      29. * appear here.
      30. *

    31. *
    32. * @param ev The motion event being dispatched down the hierarchy.
    33. * @return Return true to steal motion events from the children and have
    34. * them dispatched to this ViewGroup through onTouchEvent().
    35. * The current target will receive an ACTION_CANCEL event, and no further
    36. * messages will be delivered here.
    37. */
    38.public boolean onInterceptTouchEvent(MotionEvent ev) {
    39. return false;
    40.}
    如果不看源码你还真可能被这注释吓到了,这么长的英文注释看得头都大了。可是源码竟然如此简单!只有一行代码,返回了一个false!好吧,既然是布尔型的返回,那么只有两种可能,我们在MyLayout中重写这个方法,然后返回一个true试试,代码如下所示:
    public class MyLayout extends LinearLayout {  
    02.
    03. public MyLayout(Context context, AttributeSet attrs) {
    04. super(context, attrs);
    05. }
    06.
    07. @Override
    08. public boolean onInterceptTouchEvent(MotionEvent ev) {
    09. return true;
    10. }
    11.
    12.}
    现在再次运行项目,然后分别Button1、Button2和空白区域,打印结果如下所示:


    10.png


    你会发现,不管你点击哪里,永远都只会触发MyLayout的touch事件了,按钮的点击事件完全被屏蔽掉了!这是为什么呢?如果Android中的touch事件是先传递到View,再传递到ViewGroup的,那么MyLayout又怎么可能屏蔽掉Button的点击事件呢?

    看来只有通过阅读源码,搞清楚Android中ViewGroup的事件分发机制,才能解决我们心中的疑惑了,不过这里我想先跟你透露一句,Android中touch事件的传递,绝对是先传递到ViewGroup,再传递到View的。记得在Android事件分发机制完全解析,带你从源码的角度彻底理解(上) 中我有说明过,只要你触摸了任何控件,就一定会调用该控件的dispatchTouchEvent方法。这个说法没错,只不过还不完整而已。实际情况是,当你点击了某个控件,首先会去调用该控件所在布局的dispatchTouchEvent方法,然后在布局的dispatchTouchEvent方法中找到被点击的相应控件,再去调用该控件的dispatchTouchEvent方法。如果我们点击了MyLayout中的按钮,会先去调用MyLayout的dispatchTouchEvent方法,可是你会发现MyLayout中并没有这个方法。那就再到它的父类LinearLayout中找一找,发现也没有这个方法。那只好继续再找LinearLayout的父类ViewGroup,你终于在ViewGroup中看到了这个方法,按钮的dispatchTouchEvent方法就是在这里调用的。修改后的示意图如下所示:


    11.png



    那还等什么?快去看一看ViewGroup中的dispatchTouchEvent方法的源码吧!代码如下所示:
    public boolean dispatchTouchEvent(MotionEvent ev) {  
    02. final int action = ev.getAction();
    03. final float xf = ev.getX();
    04. final float yf = ev.getY();
    05. final float scrolledXFloat = xf + mScrollX;
    06. final float scrolledYFloat = yf + mScrollY;
    07. final Rect frame = mTempRect;
    08. boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    09. if (action == MotionEvent.ACTION_DOWN) {
    10. if (mMotionTarget != null) {
    11. mMotionTarget = null;
    12. }
    13. if (disallowIntercept || !onInterceptTouchEvent(ev)) {
    14. ev.setAction(MotionEvent.ACTION_DOWN);
    15. final int scrolledXInt = (int) scrolledXFloat;
    16. final int scrolledYInt = (int) scrolledYFloat;
    17. final View[] children = mChildren;
    18. final int count = mChildrenCount;
    19. for (int i = count - 1; i >= 0; i--) {
    20. final View child = children[i];
    21. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
    22. || child.getAnimation() != null) {
    23. child.getHitRect(frame);
    24. if (frame.contains(scrolledXInt, scrolledYInt)) {
    25. final float xc = scrolledXFloat - child.mLeft;
    26. final float yc = scrolledYFloat - child.mTop;
    27. ev.setLocation(xc, yc);
    28. child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
    29. if (child.dispatchTouchEvent(ev)) {
    30. mMotionTarget = child;
    31. return true;
    32. }
    33. }
    34. }
    35. }
    36. }
    37. }
    38. boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
    39. (action == MotionEvent.ACTION_CANCEL);
    40. if (isUpOrCancel) {
    41. mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
    42. }
    43. final View target = mMotionTarget;
    44. if (target == null) {
    45. ev.setLocation(xf, yf);
    46. if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
    47. ev.setAction(MotionEvent.ACTION_CANCEL);
    48. mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
    49. }
    50. return super.dispatchTouchEvent(ev);
    51. }
    52. if (!disallowIntercept && onInterceptTouchEvent(ev)) {
    53. final float xc = scrolledXFloat - (float) target.mLeft;
    54. final float yc = scrolledYFloat - (float) target.mTop;
    55. mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
    56. ev.setAction(MotionEvent.ACTION_CANCEL);
    57. ev.setLocation(xc, yc);
    58. if (!target.dispatchTouchEvent(ev)) {
    59. }
    60. mMotionTarget = null;
    61. return true;
    62. }
    63. if (isUpOrCancel) {
    64. mMotionTarget = null;
    65. }
    66. final float xc = scrolledXFloat - (float) target.mLeft;
    67. final float yc = scrolledYFloat - (float) target.mTop;
    68. ev.setLocation(xc, yc);
    69. if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
    70. ev.setAction(MotionEvent.ACTION_CANCEL);
    71. target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
    72. mMotionTarget = null;
    73. }
    74. return target.dispatchTouchEvent(ev);
    75.}
    这个方法代码比较长,我们只挑重点看。首先在第13行可以看到一个条件判断,如果disallowIntercept和!onInterceptTouchEvent(ev)两者有一个为true,就会进入到这个条件判断中。disallowIntercept是指是否禁用掉事件拦截的功能,默认是false,也可以通过调用requestDisallowInterceptTouchEvent方法对这个值进行修改。那么当第一个值为false的时候就会完全依赖第二个值来决定是否可以进入到条件判断的内部,第二个值是什么呢?竟然就是对onInterceptTouchEvent方法的返回值取反!也就是说如果我们在onInterceptTouchEvent方法中返回false,就会让第二个值为true,从而进入到条件判断的内部,如果我们在onInterceptTouchEvent方法中返回true,就会让第二个值为false,从而跳出了这个条件判断。这个时候你就可以思考一下了,由于我们刚刚在MyLayout中重写了onInterceptTouchEvent方法,让这个方法返回true,导致所有按钮的点击事件都被屏蔽了,那我们就完全有理由相信,按钮点击事件的处理就是在第13行条件判断的内部进行的!

    那我们重点来看下条件判断的内部是怎么实现的。在第19行通过一个for循环,遍历了当前ViewGroup下的所有子View,然后在第24行判断当前遍历的View是不是正在点击的View,如果是的话就会进入到该条件判断的内部,然后在第29行调用了该View的dispatchTouchEvent,之后的流程就和Android事件分发机制(上)中讲解的是一样的了。我们也因此证实了,按钮点击事件的处理确实就是在这里进行的。

    然后需要注意一下,调用子View的dispatchTouchEvent后是有返回值的。我们已经知道,如果一个控件是可点击的,那么点击该控件时,dispatchTouchEvent的返回值必定是true。因此会导致第29行的条件判断成立,于是在第31行给ViewGroup的dispatchTouchEvent方法直接返回了true。这样就导致后面的代码无法执行到了,也是印证了我们前面的Demo打印的结果,如果按钮的点击事件得到执行,就会把MyLayout的touch事件拦截掉。

    那如果我们点击的不是按钮,而是空白区域呢?这种情况就一定不会在第31行返回true了,而是会继续执行后面的代码。那我们继续往后看,在第44行,如果target等于null,就会进入到该条件判断内部,这里一般情况下target都会是null,因此会在第50行调用super.dispatchTouchEvent(ev)。这句代码会调用到哪里呢?当然是View中的dispatchTouchEvent方法了,因为ViewGroup的父类就是View。之后的处理逻辑又和前面所说的是一样的了,也因此MyLayout中注册的onTouch方法会得到执行。之后的代码在一般情况下是走不到的了,我们也就不再继续往下分析。

    再看一下整个ViewGroup事件分发过程的流程图吧,相信可以帮助大家更好地去理解:


    12.png



    现在整个ViewGroup的事件分发流程的分析也就到此结束了,我们最后再来简单梳理一下吧。
     

    •  Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View的。

    •  在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。

    • 子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。

    • 好了,Android事件分发机制完全解析到此全部结束,结合上下两篇,相信大家对事件分发的理解已经非常深刻了。


      收起阅读 »