注册
环信即时通讯云

环信即时通讯云

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

环信开发文档

Demo体验

Demo体验

场景Demo,开箱即用
RTE开发者社区

RTE开发者社区

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

技术讨论区

技术交流、答疑
资源下载

资源下载

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

iOS Library

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

Android Library

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

环信CTO:如何打造高效的自组织技术团队

技术团队的管理目标是打造高效的自组织团队,也就是老子提出的“无为而治”。做为一个从业20年的老程序员,从自身出发,我深切知道程序员喜欢什么样的管理方式,喜欢怎样的团队氛围。在环信,我们希望顺其自然,发挥工程师的热情和创造力,每个成员都能做到自我实现,最终整个团...
继续阅读 »
技术团队的管理目标是打造高效的自组织团队,也就是老子提出的“无为而治”。做为一个从业20年的老程序员,从自身出发,我深切知道程序员喜欢什么样的管理方式,喜欢怎样的团队氛围。在环信,我们希望顺其自然,发挥工程师的热情和创造力,每个成员都能做到自我实现,最终整个团队被业界认同。

1:扁平的组织结构

我们研发团队有50多人,没有一个专门的管理岗位。作为CTO,我的大多数时间在技术上面,团队负责人会承担一部分管理工作,但更多是靠工程师的自我驱动,自我管理。

2:结果导向的日常管理

Google只招聘最聪明的人,这些人能很好的管理个人时间。做为创业公司,我们招聘最努力的人,因为这样的工程师很珍惜时间和成长的机会。团队里有些人每天下午才出现,有些同事在备战马拉松比赛,有酷爱高山滑雪的,也有业余足球运动员。努力工作,精彩生活,结果导向是我们日常管理的原则。

3:严格的个人成长

『work for fun, constantlyimprove, and big rewards ahead』。在团队的宽松氛围中,其实我们对工程师的个人成长要求很高:对年轻工程师我们破格使用,希望尽快成长;对高级工程师, 要求技术深度,成为某个方面的专家;对技术负责人,要求更多业界交流,才能打造技术领先产品,同时建立自己的社区影响力。

4:美好的远程办公

年轻人希望到世界各地体验生活;有小孩的技术中坚天天忧虑北京的雾霾想着移民;40多岁的同事希望每年回老家工作一段时间陪陪父母。面对这些美好的期待,我们在不断尝试协同工具和管理方法来提高团队内部配合、团队间协作,及团队和客户的沟通。希望一两年内能成为一支高效远程开发团队。到那时,团队成员每年回来参加InfoQ大会学习最新技术,也做为每年的团队聚会。

无为而无所不为,希望大家一起探索,把越来越多的团队,打造成工程师的“乐土”。

——环信CTO马晓宇 收起阅读 »

环信iGeek Camp第三期:全方位架构攻略与监控揭秘

近日,环信与众多小伙伴们一起在望京Wilddog野狗总部举办了iGeek Camp活动第三期(前身为环信SM Meetup),这次的活动以产品、架构及运维的大牛与大家分享全方位架构攻略与监控的经验心得为主。 (环信高级工程师赵亮正在与大家分享) ...
继续阅读 »
近日,环信与众多小伙伴们一起在望京Wilddog野狗总部举办了iGeek Camp活动第三期(前身为环信SM Meetup),这次的活动以产品、架构及运维的大牛与大家分享全方位架构攻略与监控的经验心得为主。


1.jpg



(环信高级工程师赵亮正在与大家分享) 
 
讲师简介:环信SDK高级研发工程师,曾就职于大唐电信,索尼爱立信,Nokia等公司,10年移动软件开发,专注于移动软件开发设计,擅长架构优化,软件重构,及软件性能优化。


1S222B48-1.jpg


 
环信ONE SDK总体设计  环信赵亮的演讲主题为《环信客户端的架构设计》,向大家介绍了移动端架构设计及关键技术,现场与大家一起探讨了如何应对性能问题,如何保证软件兼容性问题,如何提升软件可扩展及重用性,以及如何保证高质量的软件。而环信的ONE SDK也首次公开亮相:ONE SDK的设计总体分为三层,平台层——Platform Layer,适配层——Porting Layer,核心层——Common Layer.  平台层会根据各平台的不同,实现不同的接口,开放符合平台层的API给第三方开发者。 适配层主要要适配到ONE SDK 核心层所需要的一些接口,起到承上启下的作用。 核心层主要提供IM 功能和业务逻辑,保证最大化的代码重用。  
PPT下载:http://vdisk.weibo.com/s/sTtZFJCY4KKjw


3.jpg


 
(开发者现场提问)


4.jpg


 
(阿里巴巴茹云峰)  讲师简介:超级课程表前CTO,江湖人称“风云”,曾历任新浪,网易系统架构师,后创业公司智能360,明星衣橱技术顾问,主攻系统架构,存储,搜索引擎,数据挖掘领域技术,热爱创业并分享技术心得。


5.jpg




6.jpg



来自阿里巴巴的茹云峰,身为超级课程表前CTO的他,与大家分享了《超级课程表技术架构的变迁》,从分布式爬虫,实时搜索,推荐引擎,缓存存储,一直到高并发,高流量,高可扩展系统架构的变迁之路。 
 
PPT下载:http://vdisk.weibo.com/s/sTtZFJCY4KKiW


7.jpg


 
(野狗实时云 CTO 李贝)  
讲师简介:负责野狗实时技术团队建设和云端架构设计,曾在360,人人网,猫扑网等互联网公司担任高级研发工程师,架构师等职位。  野狗实时云 CTO李贝进行了主题为《 实时BaaS云服务架构实践》的演讲。与开发者们分享了野狗实时的云端架构设计和优化的实践经验,包括基于长连接的服务优化,数据实时同步架构,以及如何保障系统的可用性和可扩展性等。


8.jpg


 
(云智慧 徐新兵)  讲师简介:云智慧移动端高级架构师  云智慧的移动端高级架构师 徐新兵分享的内容是《实战移动应用性能监控实战》。云智慧总结了一下移动应用的性能瓶颈,归结为三个方面:APP反应慢、交互性能差、APP Crash。网络引起的性能问题,是最直接、最常见的。在开发或者是测试中,如何接触app crash的隐患。除此之外,徐老师还给大家介绍了hook机制。 收起阅读 »

可以把聊天服务器部署在自己的服务器上吗?指私有部署

环信不主动做私有云部署。除非是标杆项目并且单笔合同金额50万以上的单子。
环信不主动做私有云部署。除非是标杆项目并且单笔合同金额50万以上的单子。

关联中的密码是哪个密码?在哪里设置?

关联中的密码是要关联的im账号密码。
关联中的密码是要关联的im账号密码。

在环信系统里用户名的大小写转换是怎么样的?

这个大小写是这个样子的:REST系统忽略大小写,认为AA,Aa,aa,aA都是一样的。如果系统已经存在了username为AA的用户,再来一个aa,就是用户名重复了。但是数据的表现形式还是用户最初注册的形式,发来的是大写就保存了大写,发来的是小写就保存小写。但...
继续阅读 »
这个大小写是这个样子的:REST系统忽略大小写,认为AA,Aa,aa,aA都是一样的。如果系统已经存在了username为AA的用户,再来一个aa,就是用户名重复了。但是数据的表现形式还是用户最初注册的形式,发来的是大写就保存了大写,发来的是小写就保存小写。但是聊天系统(IM Server)那边的持有的用户名都是小写,没有大写。 收起阅读 »

环信的双肩包收到了

书包质量挺好的,可是谁能告诉我为什么明显是男款是男款是男款?!  
书包质量挺好的,可是谁能告诉我为什么明显是男款是男款是男款?!


QQ图片20150825073344.jpg


 

怎么判断这条消息是别人发的还是用户自己发的 ?

从EMMessage对象获取direct来判断。EMMessage.Direct.RECEIVE || EMMessage.Direct.SEND  也可以通过message的from和to来进行判断,from就是发送方的username,可以用这个和自己的u...
继续阅读 »
从EMMessage对象获取direct来判断。EMMessage.Direct.RECEIVE || EMMessage.Direct.SEND 
也可以通过message的from和to来进行判断,from就是发送方的username,可以用这个和自己的username比对一下 收起阅读 »

安卓和ios 录音的格式分别是什么?

iOS
录制部分是ui控制的,不过我们建议您录制wav,之后通过三方库转成amr,这样可以减小发送语音产生的流量。
录制部分是ui控制的,不过我们建议您录制wav,之后通过三方库转成amr,这样可以减小发送语音产生的流量。

IE7、8、9都登录不了移动客服,请问是什么原因?

移动客服的客服端支持IE10以上版本浏览器,推荐使用谷歌Chrome、火狐FireFox、ie11以及 苹果笔记本的safari 等主流浏览器。
移动客服的客服端支持IE10以上版本浏览器,推荐使用谷歌Chrome、火狐FireFox、ie11以及 苹果笔记本的safari 等主流浏览器。

环信对语音流做了什么格式的编码?二次加密会不会使其体积变大太多?

加密不会导致数据增大,加密后的数据长度和原来一样。
加密不会导致数据增大,加密后的数据长度和原来一样。

倡议: 如果你的问题得到了解决,请标注“最佳答案”

倡议: 如果你的问题得到了解决,请标注“最佳答案”   这样,可以方便管理员统计多少问题得到了解决,是否还需要继续跟踪。
倡议: 如果你的问题得到了解决,请标注“最佳答案”
 
这样,可以方便管理员统计多少问题得到了解决,是否还需要继续跟踪。

imgeek.org release notes 2015.8.24

imgeek.org release notes 2015.8.24   1.更新问题状态显示,分为三类:回复,最佳,0回复 2.问题列表显示改为两行,将标签显示加到问题后面 3.超出标签数量时及时提示  

imgeek.org release notes 2015.8.24
 
1.更新问题状态显示,分为三类:回复,最佳,0回复
2.问题列表显示改为两行,将标签显示加到问题后面
3.超出标签数量时及时提示
 

iOS实时位置共享,抛砖引玉。

iOS实时位置共享,抛砖引玉。 http://pan.baidu.com/s/1o6oY2Zw
iOS实时位置共享,抛砖引玉。
http://pan.baidu.com/s/1o6oY2Zw

【抽奖结果】环信即时通讯云平台,你了解多少

以下是本次活动中奖名单,其中用户"一等到天幻"中奖两次,予以去重,并补充一个抽奖名额。 名单见下方补抽名单。抽奖过程见附件中的抽奖视频。 请中奖用户迅猛通私信将奖品寄送地址发给我。 包括收件人姓名、手机号、收件地址。 上传稍晚还望大家见谅。 抽奖名单 ...
继续阅读 »
以下是本次活动中奖名单,其中用户"一等到天幻"中奖两次,予以去重,并补充一个抽奖名额。
名单见下方补抽名单。抽奖过程见附件中的抽奖视频。

请中奖用户迅猛通私信将奖品寄送地址发给我。
包括收件人姓名、手机号、收件地址。

上传稍晚还望大家见谅。


抽奖名单
------------------
诺风
快看_灰机在灰
farawei
卟、分扌の練。愛
wzpforwork
老农民
cena
简.缪
回音
IMCom
乌龟也有怒气
Ruby
一等到天幻
MirrorC.M
ying
南京可以依靠
一等到天幻【重复】
FatCat
mifan2009IM
成续缘


补抽名单
------------------
yuqi


  收起阅读 »

环信获B轮融资1250万美元开启万亿企业服务市场

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


1438145968909.jpg



环信CEO现场宣布获得B轮融资1250万美元


1438145981882.jpg



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

发布会当日,投资环信的VC代表:经纬中国副总裁熊飞、红杉资本相关负责人;环信的大客户代表: 58到家客服总监王辉、楚楚街联合创始人张翀;及行业专家代表:中国电子商务协会专委会常务副主任王汝林、动点科技创始人卢刚等均到场见证了这一盛况,并与环信CEO刘俊彦一起探讨和分享了对环信成长背后的万亿级企业服务市场趋势、电商在社交经济背景下的全渠道营销挑战等观点。


1438146005934.jpg



经纬中国副总裁熊飞主题分享

据了解,环信发布会当天吸引了来自投资界、媒体、研究机构的代表,电商、O2O、教育、医疗、旅游等行业的IT企业代表,以及移动互联网和云服务产业链上下游的服务商代表,还有众多创业者和开发者等到会聆听与交流,原本规划200人的会场挤进了300多人,现场火爆正反应出了中国企业级云服务市场的火爆。

企业云服务市场爆发,中国SaaS服务将迎来井喷

为什么会有这么多投资人、业界大佬、龙头企业、专家机构、著名媒体、创客看好环信?背后还是因为有足够大的市场支持。

环信B轮融资由红杉资本领投,经纬中国和SIG跟投,此轮环信共获得1250万美元融资。红杉投资人表示投资环信主要看中三点:1,客服市场足够大,属于刚性需求,在欧美市场已得到证明,同时中国SaaS服务公司正在快速崛起。2,环信团队技术功底扎实,有非常好的产品和技术基因,有电信运营商级别的技术储备。3,环信业务健康,增长势头迅猛。客户量、日消息量和DAU等硬性指标一年内达到了几十倍上百倍的增长。

环信引起了投资界追逐,而最终B轮被前三轮投资环信的老股东内部包揽,诸多投资机构欲进无门,望而兴叹。至此,环信在上线1年的时间里已经完成了4轮融资。即2014年5月经纬中国天使伦融资500万人民币,2014年8月SIG A轮融资500万美金,2014年10红杉300万美金A+轮融资,2015年4月红杉领投的1250万美金B轮(既本轮融资)。

三家老股东信心满满的排他性续投,以及投资界的追捧,让环信和其背后蕴含的商机显得更加炙手可热。而支撑环信估值暴涨的商机正是近来被业界关注的万亿级企业云服务市场前景,而且环信的“即时通讯云+移动客服”业务切中的恰是企业级服务市场王冠上的宝钻。

事实上,2015年将成为企业级服务市场的爆发元年,这一端倪在企业级服务市场率先爆发的美国已经十分清晰。据有关专家分析,在过去的1年时间里美国的企业服务市场,已经有超过15家企业IPO,他们的募集资金达到70亿美元,总市值超过400亿美元,其中有Zendesk、Hubspot、New Relic、Hortonworks、Box等不少明星企业,巧合的是其中不少“独角兽”级别企业的投资方也正是环信的投资方。同时在私募市场上,企业服务类公司融资总额超过100亿美金,这些公司的估值总和达到千亿美金量级。

而且,美国目前这样规模的企业级市场还正处在从青年走向壮年的阶段。据分析,从市值排名前十的云计算企业服务公司的市值总和看,2008年市值总和是250亿美元,到2015年这一数字已经是1800亿美元了,7年里增长了超过7倍。而预计到2016年,美国才会出现第一个云服务市场占有率超过传统软件的垂直领域,就是CRM。

而“客服”是CRM四大细分市场之一。而且,客服是CRM细分领域中最大的一个,占市场总额的37%。在北美,2015年客服软件市场采购总额高达96亿美元。这个市场中已经出现了两家“独角兽”公司,就是近来受到各界关注和追捧的Zendesk和Freshdesk。Zendesk已经上市,目前市值18.9亿美元,销售额1.87亿美元;Freshdesk在2015年4月份刚刚拿到E轮融资金额达到5000万美元,估值过10亿美金。这还不包括在这个领域中的传统的巨头,如微软,Salesforce, Oracle等。

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

“互联网+”时代,企业需要环信源自契合移动化和社交变革

相较中国企业云服务市场的空白,企业的需求却很旺盛。由于2C市场和社会的变革中国的人力成本在逐年增加,5年翻了不止一番,这成本倒逼是企业服务火爆的核心驱动力。长期来看每年10%-15%的人力成本增长已成为趋势,中国的人口红利期已经过去,在中国愿意做低端工作的人越来越少。也就是说,中国企业的人力成本发生了普遍的提高。这就要求传统企业的运营效率必须要大幅提升才具有可持续性。相对应的,IT相对成本的快速下降,就成为了企业服务火爆的正向核心驱动力。

而在中国分析企业服务的核心市场之一—客户服务软件市场,还需要考虑移动互联网的因素和社交经济的因素。

在“互联网+”大背景下,中国移动互联网发展之迅猛已经领先世界,互联网金融和O2O等行业发展已经远超美国,而随着人们对移动互联网的使用习惯增加,客服软件的未来逐渐转向移动端。不止中国,全球看也将是这样,Gartner预测,到2017年年底,超过70%的客户服务请求将来自于移动端。2014年阿里双十一的现场监控显示,总成交额571亿其中移动端贡献了243亿,移动端交易量比例将有超过PC端的趋势。同时,京东2014年第四季度财报显示移动端的订单量占比接近40%。在用户体验为王的时代,用户行为发生不断的变化。用户行为细微的变化在偷偷革命。这导致电商、O2O、互联网金融、教育、旅游等行业移动化趋势明显。APP开发者们和企业主们正致力打造APP内交易闭环,即从商品/服务的展示,售前咨询,支付,物流,售中服务退换货,售后等全流程在APP内形成闭环。客户服务是其中重要一环。

同时,由于近年来微信和陌陌等软件的流行,教育了大众,社交已经成为用户的强需求,易观数据显示80%的用户移动应用行为与社交有关。而围绕社交已经形成经济圈,而在多社交媒体提供统一的优质客户服务很重要。传统的客服大多是通过电话完成的,现在崛起的社交网络和移动App、IM应用让用户可以更方便地反馈意见。但是如何保证这些意见及时集中地到达客服人员那里,对企业而言是个头疼的问题。

移动端客服软件市场基本空白。传统客服软件巨头转型缓慢,并且移动客服所需要的高并发高可靠IM技术获取门槛高。专注移动端客服技术的公司有可能弯道超车。

连接“人与商业”,环信将成为下一个“独角兽”

互联网时代商业模式的核心之一是“连接”,如QQ和微信是 “人与人的连接”,百度搜索是“人与信息的连接”,淘宝则是“人与商品的连接”。环信移动客服是“人与商业的连接”。核心之二是“找到”,依靠互联网的技术,实现人更快、更智能地“找到”信息、商品。

环信移动客服是将移动社交全渠道整合的智能云端客服平台。环信是中国乃至全球领先的即时通讯云服务提供商,上线1年来,已成为唯一一家经历了亿级长连接真实稳定运行的即时通讯云平台,中国主流的采用第三方即时通讯云服务的App几乎都是环信的客户,每日环信平台的消息量达到数亿条,有着即时通讯云领域亿级长连接的绝对垄断优势,在此基础上发展起来的环信移动客服,对移动网络下通讯的优化和社交质量的保障是任何一家传统SaaS服务企业所无法比拟的。

同时,环信移动客服继承了环信即时通讯云积累的开放能力,对几乎所有主流系统、硬件、社交模型,甚至传统客服软件都留有接口,具有完美的兼容性。环信移动客服不仅可以整合多渠道的服务平台,通过与包括传统Call Centre的合作和微博、微信、App、邮件等渠道的整合,可以最终把用户反馈汇集到同一个界面内,方便客服人员操作。

而环信移动客服特有的基于大数据的智能聊天机器人和知识库可以为人工客服挡住80%的常见问题,是应对移动互联网时代,社交化经济可能带来的海量用户请求的必备法宝。


1438146055575.jpg



正是由于环信的移动客服产品切中了万亿级企业云服务市场王冠上的宝钻,才使得环信受到了投资界的青睐。而环信移动客服产品也确实不负VC重托,获得了成功。据介绍,环信的移动客服产品已经签约了4万个付费客服席位,典型用户包括国美在线、58到家、楚楚街9块9等上百家互联网巨头企业客户。同时,艾媒咨询现场发布的《2015年中国移动客服市场发展研究报告》显示环信在新兴的移动客服市场占有率第一,APP客户规模最大。不难预料,有着连接“人与商业”愿景的环信凭借惊人的成长速度将最有希望成为下一个“独角兽”。 收起阅读 »

环信移动客服助力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 收起阅读 »