我的2012-分享我的四个项目经验

来源:互联网 发布:微软的安全软件 编辑:程序博客网 时间:2024/05/21 08:00

前言

2010-11-01入职到现在有一年多了,这一年来,也做了比较多的事。也学到很多东西,基本来说这期间一直在忙碌着,没有停过。有些时候也会挺有压力的。11月底的时候就开始计划准备写个总结,既能回顾下这一年自己在哪些地方成长了,也能加深下自己的所做过项目的印象。之后的内容会按所做的项目进行大致的描述,小项目之类的都不会在此介绍,项目的设计,框架,细节方面到时会分别单独写文章放置到我的博客中,希望通过这系列的总结也能提高自己对事物的准备描述能力。

 

第一个项目:Nginx资源服务器

       刚入职的时候开始看nginx源码大概花了1个半月,这期间也有被带我的师傅鄙视过。Nginx大致10W行左右的代码,主要看了下程序的流程,http通信过程。然后尝试写了下nginx的模块。我们的基于nginx的资源服务器的主要功能是通过http求解进行解析出来后读取视频文件块并数据。需要知道是高并发请求server端接收到后如果是直接读取磁盘会由于磁盘读写速度的限制的瓶颈,从而会导致用户观看视频变卡不流畅,造成用户体验低等问题。因此我们基于nginx开发了http的缓存模块,所有请求会组织成相应结构存放到共享内存中等待后台处理程序处理,每个结构中会通过标识标出是否处理,以及处理结束后通过指针指向共享内存中的数据块数据块都会存放到内存中数据,内存中的数据块都会比请求数据的长度大,这样第一次除第一次数据块不存于内存中时,才会去读磁盘。Nginx通过共享内存进行与后台处理程序进行通信,共享内存中的数据块通过最后最少使用算法进行替换。使用率最高的内存块会较长时间存在于内存中,通过这种方式来提供用户请求的响应速度。另外在后期测试优化的过程中读磁盘的读写采用了asio去异步的读取,这样内核层能合并部分在磁道中距离近的读写请求。这部分的内容倒是会分成两部分或者三部分去讲,大致会较为细致的介绍该项目的架构与实现,以及后期的优化处理过程,如果时间充裕,也会将nginx的源码阅读的资料进行整理发布出来,可能包含HTTP协议的理解。

第二个项目:广告系统

       在资源服务器项目完成之后,在公司做了论文回学校答辩了之后回来,开始做广告系统。广告系统的功能大致是这样的:分成客户端和服务端。广告客户端可以提前启动,在系统中驻留;监听客户端(播放器)的请求解析处理,并通过构造本地HTTP服务器响应广告;与服务器交互,更新推送广告的版本,当版本不同时,存放广告的server上下载广告文件至本地。服务器对广告的推送可以通过配置文件去指定,可以完成预先推送,按地域,按频道,按影片,按合作商,广告轮播,影片黑名单等各式的广告推送以及版本更新控制。为了加快通信过程采用udp作为客户端和服务端的通信协议,为防止udp丢包会模拟tcp应答机制,去确认包的正确接收。考虑到广告客户端是部署在用户机子上的,因此内存,cpu的占用需要考虑,并且要尽量少的占用资源。因此广告客户端首先只会启动一个主线程,当存在HTTP请求才会启动HTTP处理进程。当存在推送内容是会启动下载线程去下载广告到用户机器中,并在推送版本变更时,会将上一个版本的广告内容从用户磁盘中删除,减少磁盘的占用。通过这一协议的手段,广告客户端能控制在2M左右的内存占用,CPU使用非常少。

第三个项目:采编系统

     大约在8月底9月初的时候时候,采编那边需要做一个自动化处理系统:包括视频下载,转码,合并字母,并将处理结束后的文件转存。设计基本是我去搞的,同样是客户端和服务端,大致设计是这样子的:采编会提交处理请求,一条处理请求就是一个任务。
这个任务可能只是去下载片子,也可能是转码,当然也可能是下片+转码,下片可以处理HTTP,FTP,ED2K,THUNDER,BT这几种协议。这条处理任务会写到数据库中,服务端要做的就是从服务端读取一个请求,并根据所有连接上服务端的机器,根据调度方式,决定这个任务是发完哪台客户机上去处理,并接受客户端的定时状态反馈,然后将状态写入数据库中,使得采编知道哪些任务处理完成。客户端要做的就是根据接受到的任务,进行排队处理,这是由于系统资源有些,且在转码的时候会占用大量的系统资源,包括CPU,内存等等,不过不同机器的配置不同,因此根据优先级之类的,以及配置文件的控制来决定同时处理下载任务数和转码任务数。客户端定期会发送一个心跳包给服务端,告知其存活,当存在任务处理的时候,也会定时返回当前的处理状态。总来的说就是根据采编提交的请求,服务端去读取,分发任务到客户端(也就是实际处理任务的机器),并更新处理状态到数据库中。

第四个项目:数据挖掘-推荐系统

       在10月下旬的时候公司决定要做一个推荐系统,前期是做一个影片推荐,后期可能会应用到广告推荐上面。推荐系统里面包含的东西很多,到目前为止,一直在进行当中,预计过年前可能能拿出第一版。
        这个推荐系统就是根据用户观看视频的日志,进行分析用户行为,并推荐其可能喜欢的影片给用户。
        要分析用户的数据,就需要做数据的收集,由于之前的数据都是存放在oracle当中的,而要进行大数据的分析处理,需要用hadoop,hbase,mahout那一套东西。因此需要将之前的数据从数据库中导出,一部分数据存放到hdfs上面,一部分数据存放hbase上面,不同的数据,各种导入导出的处理上也不同,并且需要考虑哪些数据是放置在hdfs上合适,哪些数据时放置在hbase上合适。然后是每天产生的数据,自动化的处理,不需要人为去干预。当然自动化处理过程需要监控,hadoop,hbase这套东西也需要监控,因此初期通过自己写脚本去实现监控,并在出现问题的时候发送短信和邮件。目前数据入库的工作基本完成,期间由于hbase,hadoop是java写的,虽然是提供了c++的接口,但是由于各种原因c++没用上,后来讨论了下,决定用java去实现各种处理,因此花了2天时间去看了下java。然后开始写处理程序,并查看API文档,基本是摸石头过河。。。
        在hadoop,hbase的优化上还有很多工作要做,不过一直没时间。。之后大致做结果数据,写mapreduce去讲需要的数据给分析出来,然后用mahout去跑。做完这些的话,就可以写推荐反馈的服务了。哈哈,希望时间够,争取过来,我来做。

收获

        这一年来,跟着师傅做各种服务,学到了很多,从大局上知道了一个项目如何去思考其架构,学会了,自己去讲一个项目从无到有的实现,也让自己在能在不同的情景下,选择合适的语言去解决这个问题。我喜欢去做各种为接触过的项目,在项目的进展中,解决各种问题,锻炼自己的思考能力,逻辑思维,在不断的遇到,解决,这个过程总是很让我兴奋。

        本来是在11月份的时候就想法将这个年终总结给写出来,也算是我对自己在2011年的回顾吧,一直由于各种事情的繁忙,没有时间去理顺思路来写,现在终于在2011年的最后一的晚上来写完这篇总结,也很高兴。另外可能过年回去的时候会整理更多的内容出来,和大家分享。期待我的2012能有更大的进步,也希望家人身体健康,高高兴兴。最后也祝大家元旦快乐,玩的开心。


PS:做C++服务端的朋友,搞hadoop,hbase的朋友,搞nginx的朋友可以加我,互相交流学习。


                                                                                                                                        写在2011-12-31晚 于

前言

    一看标题就俗套,一时也想不到各种华丽的名字了,直接copy去年的标题,只是将2011改成了2012。还好今年也没有末日,活着就是幸福。在12年最后一天的工作日,刚好可以花点时间回顾下今年的工作情况以及稍微规划下明年吧,希望慢慢的按着自己的规划往前走。
    如果有看过去年的朋友,可能会发现与去年的巧合就是都是在最后一天来写。。尴尬。。
    本文大致会分两部分,一个是今年回顾,一个是明年的设想,与规划相比我更喜欢设想这词。
    另外就是描述方便就直白叙述,不会太书面语,希望本文有些知识可以给大家一点帮助,下面是正文!

回顾

    既然是回顾,那么这部分就会从年初说到年尾。主要叙说工作上的事情,以及今年学到了哪些知识之类,还是和去年一样会按照项目来叙述。

    推荐系统:

    这个是和去年的最后一个项目是同一个,去年没有做完,在那边有描述是关于hadoop这一套的。整套推荐做到了4月份其实完成了demo,在7,8月份上线的,后面还做了很多更改。这里接去年的继续往下说。
    关于从hadoop出数据方面:目前也就是生成影片id-用户id,应该来说大部分的视频网站有很多用户可能没有注册的,那么要出推荐,并定位到用户,那么就需要取用户的mac地址来标识出用户的身份,这点客户端是可以做到的(我们就是客户端),而宽屏网站想youku,tudou怎么取的这我不太清楚,前端东西不了解。当根据历史观看记录来生成了movie_id->user_id的,用mahout就可以跑出来个性化推荐(根据用户推)和相似度推荐(根据影片推)这两部分数据。做出来的分别是user_id-》movie_id和movie_id-》movie_id。
    根据hadoop出来的数据,那么就要在hadoop,hbase中查询各种记录,并根据业务需求生成相关的数据,如影片名,描述,图片,导演,评分,还有一些其他方面的数据(不好透漏)。既然详细的数据出来了,就要考虑如果提供服务给用户,为了快速访问我们先期也考虑过很多nosql,最后选型为redis,不过单台的redis根本容不能将我们全部的数据load进去,又使用了一致性hash做成伪分布式redis,将底层redis访问做成异步库,这样抛弃master-slave模式,数据分别存在多台redis中,并且有些由于个性化推荐数据太多,还有一些其他要求,因此,我们不能贪图简单而所有的数据全部都通过简单的set key value存储进去,这样会浪费相当多的内存。查过文章,将符合hash存储的数据进行处理,一是减少key长度但保证key唯一,二是保证sub key 列表不要太大,一般都在几十到一两百,这样处理后,总共使用了48G的内存,比原先节省了一倍多,还是很ok的。结合封装hiredis做成异步库及做个入库程序,对于1.2亿条数据,可以在1个小时内全部倒入到redis。因此底层数据存储就是这样了。
    再说服务提供,毋庸置疑,我们选择了nginx,apache没有选是不需要用到这种多进程多线程的work模式,当然了如果高并发下nginx会资源占用少很多,由于底层访问redis是已经封装成异步的,因此nginx每次操作也不会阻塞,只要写个nginx模块,将http接口做好,该过滤的过滤,并且做份热点数据,作为如果针对该用户没有的推荐则从热点数据中取。这样保证任何人都有数据,这对于个性化推荐时这样的,对于相似度推荐则还要保证如果没有推荐数据,则要在热点数据中找到同类型的影片数据作为默认相似度推荐结果来返回。现在说到了nginx对于前端http请过过来,来返回推荐数据,还要再做一步,就是前端请求,肯定是需要你返回有某种格式的数据,那么nginx这边做成可配置,方便变更,我们采用了google ctemplate做几个模板,来针对不同的接口的需要。
    到这,就算基本差不多了,然后就是上压力测试,做配置进行自动部署,以及之后的需求变更,稍微小改改。

    搜索系统:

    这就是个基于clucene的全文检索,不过在我们这是做成了搜索影片的一套系统,关于clucene,可以参看apache的lucene。clucene是c++写的,算apache lucence的其他语言的一个实现。不好的就是,作者挺久没有更新了,我觉得,没接触过的,还是直接玩apache的比较好。或者其他的活跃的开源实现。
    搜索这套我们之前是有一套的,不过架构不好,代码混乱,维护起来不好维护,且无法动态更新,每次索引更新都要重启apache,也就对用户是用影响(虽然很短暂)。旧的架构模式就不说了,就说下新的架构,其实也简单,后端clucene的检索做成库实现所有搜索处理(后面简称库,也可以理解成含有搜索实例的一个pool),apache做个模块(后面简称模块)。模块会在apache进程初始化的初始化库。当前端有请求到来时,初步解析,并调用库的接口,传递请求,获取搜索实例,等待搜索结果的响应。这样就是写个模块,编译成so扔到apache/modules就可以了。库内部都有几个搜索实例,搜索实例是有针对不同虚拟主机的且每类实例个数都比较少(多的话,clucene占cpu太高),搜索实例用来完成搜索,当一个线程接受到请求,并去尝试获取符合这个请求类别的搜索实例,如果没有请求到则按照FIFO的原则push到请求队列中,避免请求时间不均。搜索到后会填充模版(还是ctemplate)返回给模块,模块会告知apache将结果响应给前端。为了支持多域名也就是虚拟主机使用不同的索引,还在模块中做了处理,使得能正确告知每个虚拟主机的索引位置,模版列表等参数给库,这样在库在初始化的时候可以生产对应类别的搜索实例。另外就是索引部分了,因为原始数据时每天变化的,有增加也有减少的,那么就不能使用增量索引,而是使用重建索引,还在量不是很大。建索引的程序每天会执行几次重建索引,那么我们会安装日期变化将索引生成到指定目录下的一个有着规则名称的目录名下,如所有索引在index中,动态索引目录就是2012.12.29.14.1356762061。当重建索引成功后,会将这个新的索引目录的绝对路径填充到共享内存中,而库则会由间隔一段时间读取共享内存,判断是否有新的索引需要使用。有的话,则搜索实例内部重新生成下。这里共享内存的话,使用boost::interprocess就可以了。这样就完成了动态索引更新,而无需重启apache也不会影响用户搜索体验。
    索引既然是每天都会重建,那么就需要定期清理旧索引,否则磁盘占用会越来越多。而这只需要用一个脚本每天定时跑一下就可以,只要将前一天的索引都删掉掉在这之前先查看共享内存中记录的当前使用的索引是哪个,防止误删。
    至于到代码层面,就不细说了。

    分布式flash系统:

    好吧,名字挺牛叉的,是什么东西么,举个例子;我们在优酷上看视频,从技术角度看,首先是走HTTP请求,然后定向到后端调度服务器集群,告知该片实际所在的存储服务器,然后重定向到实际影片所在的存储服务器去请求数据。我们也是做的这一套东西,有点遗憾的是,没有全面上。
    下面说说这东西我们是怎么实现的。从功能划分,有调度服务器和存储服务器,以及一个全局的注册服务器,用来监控其他两种服务的状态。
    在说每部分大致实现的之前,先说下zeromq,因为在这几个服务里面用了很多。zeromq封装了底层通信,提供了几种概念,如req-rep,pub-sub等。req-rep就是请求-响应,是一应一答这种模式,也客户端发起请求,然后服务端做响应,这种模式都是按照这个顺序来,不会没有接受到req而直接rep。pub-sub就是客户端是接受者,服务端是发送者,如服务端有1000个连接,需要发送消息广播给所有连接,就用pub-sub模式。至于详细的可以去google zeromq guide 。另外关于zeromq使用之前也写过一篇文章,也算是对细节方面的一个总结。
    存储服务器,分后台扫描计算服务和前端apache两个服务。后台在启动的时候向注册服务器注册。启动时,会启动tcp pub和ipc pub,tcp pub广播到所有调度服务器,ipc pub是广播到本机apache的每个进程,同时后台还启动tcp rep和ipc rep,tcp rep是用来监听所有调度服务器启动后的第一次请求获取当前存储服务器上的所有影片列表,ipc rep是用来监听本机apache启动后第一次请求本机所有影片文件列表信息。在之后定时扫面磁盘更新影片列表信息后会向所有调度服务器以及向apache进程广播列表对照信息。apache启动时,每个apache进程都会向后台发起一个req,后台如果已经有数据则rep列表回来。并且apache模块和后台还有一个sub-pub连接,也就是每个apache进程都可以接收后台的publish影片列表变更信息,这些都是使用zeromq。那么真正的提供数据服务就是前面提到的重定向到存储服务器上,也就是apache模块来接受这个请求,并从当前进程中所保存的列表信息中查找该请求所请求的文件是否在本机中,有则返回数据,否则返回NOT FOUND。另外还有一个就是状态监控,后台服务会定时向apache模块发起请求,并判断apache是否正常服务,并将状态通过req定时向注册服务器汇报,这样,注册服务器就可以监控到存储服务器上的apache运行状态,以及如果超时还没有接受到存储服务器的状态汇报还可以判断存储服务器已经失效。
    调度服务器,分后台tracker和前端apache两个服务,就如前面说的,存储服务器启动会向注册服务器注册,同时注册服务器会返回当前所有已经注册的存储服务器节点。然后存储服务会分别向所有存储服务器发起一次req请求每台存储服务器上的所有文件列表,之后还会启动sub模式向所有存储服务器发起连接,用来接受存储服务器的列表更新。也就是说,任何一台调度服务器都维护了文件映射到实际存储服务器的列表。并且tracker会启动ipc的pub和rep模式用来向调度服务器上的apache的每个进程广播或响应当前trackre所维护的文件到存储节点的映射。而apache用户在启动的时候也会做存储服务器的apache模块类似的启动req,sub,用来获取本机的后台所发送的列表(这两者apache模块仅仅就这部分功能是相同)。这样在apache的每个进程中就都维护了一份文件-》存储节点的列表
在调度服务器上的apache用来接收前端请求(前端总是先向这请求),并确认所请求的文件位置,然后返回一个302重定向,设置重定向的地址前端就会自动跳转到存储服务器,如果没有找到,则返回NOT FOUND。另外监控方面也是和前面差不多。同时,tracker还有一部分是做负载计算的,这里就不想说了。最后一点就是,当存储服务器失效的时候(如当机,掉线,服务崩了),那么要保证把将失效节点对应的所有影片列表从当前保存的列表中踢除,并广播的apache模块,因此,tracker还需要定时向注册服务器查询所有的存储服务器列表,如果对比之前保存的存储服务器列表少了,并且超时,则执行所说的踢除操作。
    关于失效,如果用优酷用的多的,就会发现,优酷有些时候也会存在文件找不到的情况,只要刷新下,就有可能可以正常播放,这就是由于某些存储节点没有及时踢除,当请求过去的时候定到了一个失效的节点,就会请求不到数据,刷新下,那么调度服务器一般会从一个文件所在的所有存储节点列表中按某种策略选择一个返回,这时就有可能返回的那个节点是好的。
    最后注册服务器,相当简单,一个是rep,分别接受前两者服务器的注册消息,状态汇报,以及调度服务器的查询消息。注册服务器可以分别配置全局配置,在响应消息的时候,可将对应类别的全局配置广播出去。这样就可以一个个修改所有的配置文件,当然,这些在前两者服务器也会做响应的处理,使得能正确重新应用全局配置。

    夭折的IM系统:

    由于前端没人做,搞了一个月应该有的,然后被宣判推迟了,这个在我看来是已经夭折了,不过还是有些东西可以拿来分享的。
    这个关键还是选型,可以在xmpp.org上看到有很多的lib,client,server都现成的,当然,为了匹配业务需要,要选一个容易的,还是比较麻烦的。我们选的是tigase,这个以前csdn上也有报道国外哪个社交网站用这用的挺不错,然后作者也是很活跃,有问题都会及时帮助解答。我这里所说的tigase是指tigase-server以及muc component。因为我们业务要搞群,以及点对点聊天,以及其他的一些功能,那么有些需要在开源代码上面修改。tigase是java写的,看代码不太容易,各种多继承,有点绕来绕去的,初次阅读,建议从eclipse倒入,因为目录下会有一个pox.xml,都懂得。然后debug跟踪下,会对流程熟悉点,否则真的不好看懂。
    点对点的话,基本的用用是可以的,不过还有各种权限状态之类的,如果要搞,也要改,不过这个可以写plugin。
    群,xmpp协议的muc(聊天室)是不支持持久化的,这个需要在muc component中改代码,这部分已经实现可以支持用户离线,且权限及相关数据正确保存
不过这只是最基本的一部分功能,还有些协议要扩展,没人搞前端,现在也没法测。
    关于tigase资料的话,可以在tigase.org找到。最后只能对这个项目说遗憾,不然搞起来还是有点意思的,虽然是java做的~
    其他:
    在年底的时候,自己也稍微总结了下,稍微写写写,挂在github上。如果想学apache/nginx的模块编写的话,或者了解下libevent demo怎么用,再或者像看看apache apr的使用的话,可以在这个地址github.com/iamwljiang里找到。
    另外的,年底前,各种事情缠身,年中的时候函数式编程clojure看了一个头,希望以后能好好学学,以后可能会有大发展。关于逻辑方面,看云风开源的skynet,向学习学习别人如果设计框架的,其核心虽然是c写的,但逻辑全部是lua做的,也就使得我对lua有兴趣了,看差不多了,还挺好玩的,希望能自己琢磨到如果合适的嵌入。今年大致就这样了,做了几个东西,自己独立完成能力有提高,接口设计还要学习~

设想

    过两天就是跨年了,乘着总结,也设想下在工作方面的话,如果有可能的话,希望接触游戏行业或者还是继续在当前行业,继续做一些有挑战的项目,继续提升自己能力,另外要开始专注于大并发,高性能网络服务器方面的开发,然后跟随技术脚步多了解开源事物,别掉队了。工作之外的话,自己希望做几个正规点的开源项目,目前已经有大致方向了,有空就一步步来。关于读书,手头还有几本书,可要看看完。能全部做完,我觉得在明年回过头来总结的时候,会没有遗憾

杂项

    吐槽下,一是这么长的篇幅,一年真没几次,早上写到晚上才写完。。
    二是希望身体真的健康,饮食注意,不然真就坑进去了,也不知道几时能康复,认识基督教的朋友,也许要请教下,是否信教有用,有用的话,我也去入门下。
    最后祝大家节日快乐
    
                                                                                                                                                                                        2012-12-29 于 杭州
0 0
原创粉丝点击