第一部分昨天一个新同事向我问起了Push Mail的解决方案的整体架构和思路,滔滔不绝的讲完以后,勾起了我不少的回忆。那是在3年前发生的事情,当时这个客户(美国人)的需求就是一句话:“我要做一个Push Mail全套解决方案。” 就是这句话我们写了厚厚的一份英文需求文档。从需求分析到最后交付我们将尽用了2年左右的时间,从 系统架构、程序发开、再到系统重构、这样的经历依然 历历在目。 项目当时就我们5个人在做,可以说这5个人都是百里挑一的人选,但我们需要面对以下挑战: 但是再我们所有成员的不断努力下,这些困难都被我们一一解决。 项目里面牵涉打了J2EE 领域多项技术,也算是一个难点吧,因为下列技术跨度都比较大,例如: 客户全部选择了将服务器部署在Amazon云环境上,大约总共有40几台。其中数据库采用了MySQL服务器,我们运用分库+同步技术来解决大规模存储和查询,应用服务器我们采用了Jboss。 MQ服务器采用的是Jboss的MQ。前端采用Apache和CDN做请求压力分载。操作系统采用64位的 Ubuntu 8。
第二部分这个 Push Mail 项目留给我最深的影响就是给我们团队来了丰厚的利润,客户他是一个美籍印度佬,就是我们常说的那种有钱人,做事爽快、干脆,也是全球移动领域具有影响的专家,因此他要求我们提供的服务必须是具有一个国际化专业水准的团队(International Corporate)。所谓专业化的主要需要体现在 1执行规范、2执行效率、3执行细节 这3个重要的环节上。 在项目实施过程中除了搞J2EE 的开发者, TA、QA 、SA、DBA 一个也不能少。 2、QA 测试团队在我们需求和概要设计 就开始对jboss和mysql其他几项技术学习,其中测试人员与我们一同参与需求分析,这样将来他们才知道该如何配合我们做各种测试,进行深入的测试,而不是我们在引导他们在测试,他们100%能知晓业务 并且制定出不同的测试计划。并不是依靠我们自己测试或者我们在指导他们测试,那样运动员和裁判员都是同一人,那样肯定出问题。 3、SA 的压力最大 需要对系统整体的架构进行设计这个都是分内的事情,更重要的 在真实生产的环境遇到致命性错误,可以回退或者拿出现成的备份方案,把问题在短时间内化解。 4、DBA 需要从高往低的系统架构层次进行对数据库设计与整体规划,必须根据客户需求设计出具有远景的 方案,不是一成不变,更不是所谓“一步到位”的规划设计,是根据预计的数据增长 进行实施规划的不同方案。 5、开发者们需要对每个业务模块都要详细了解。因为我们需要降低风险 开发者们如果出现有人家里有事或者生病 任何一个成员都可以替代,不会出现我今天不来上班,导致项目进度拖慢一天的现象。 在产品在设计开发到发布的过程中,我们分为四个阶段 prototype、demo、stage、producting。 prototype 是一个原型,主要完成核心的部分,完成核心的功能和业务逻辑,开发团队内部评估使用。 demo 阶段是将产品的主要部分演示给客户看,让客户确认主体的方向。 stage 根据项目计划分为多个不同的stage版本,每个stage阶段的版本都会在stage服务器上由我们 测试人员先测试5-7天,再发布到真实的生产环境中。客户对这样的流程要求的非常的严格。因此 stage 服务器的配置和数量与 producting 环境是100%相同的,没有他们的邮件确认我们是不能擅自发布到 producting 环境中的,如果 producting 环境出现问题,必须在短时间内能回归到上一个稳定版本的状态 。 后期会在 stage 和 producting 服务器上轮回 ,fix –> testing —> release。 我们当时使用SVN和Trac,值得一提的是Trac这个东西,客户有任何需求变更 通过 Trac 系统 TA,QA,DBA,SA 统统都会知道,并且知道我们会在下个版本 什么时候 会 发布在stage 服务器上,我们也能在第一时间知道他们对 当前版本的 性能情况,客户也能看见但似乎他们并不在意这个结果,因为他们需要知道我们当前的执行状态是否符合计划,其实TA的成员比他们更加关注我们的项目进度,呵呵。因此,所有人的开发进度执行情况都在trac上进行展现。客户也可以看见,也可以回复,所有信息所有人同步。 呵呵,另外那三十几台需要发布应用程序的机器,分别发布不同的应用程序或者不同的版本不可能完全依靠人工完成,我们需要一个半自动化的工具帮助我们完成,所以我们选择了hudson和自己编写的liunx脚本。这样可以提供效率,并且大大减少了发布时出错的机率。 先写这么多,有什么忘记的地方我回头补上,下一篇将开始 讲述 纯 技术方面的那点事儿了。 第三部分
自从1998年 Google 利用廉价的互联网计算机,以最快的速度提供最精确的搜索结果, 这一创新技术成功地缩短了响应时间,提高了可扩展性,并降低了成本。是当今众多公司一直在效仿的技术。在这一项目中完全采用低廉的硬件投入,期望能通过软件的手段、利用分布式计算、集群技术 达到高效的计算,并且能加强系统的可扩展性,提高系统软硬件资源的使用率, 降低系统运行中瞬间的瓶颈。
通过这个项目让我们充分的认识到一个用户的系统 例如:单用户的 OutLook 和 一个百万级的系统的是截然不同的,系统中仅有100条数据和100w条数据的系统更是截然不同的,从100w条数据上升到上亿条数据那样的系统更会让人感受到无论从代码结构、数据库设计还是从系统架构都是完全不同的设计。 客户端环境 服务器端环境 压力测试工具 当前运行状态 整体架构 概览 后端发送邮件实现过程 1.手机客户端通过http网络协议发送XML数据到服务器端, 2.服务器端接收到xml数据进行解析,服务器端传输层的web请求模块将xml 协议解析,并且重新包装成pojo 对象, 3.中间有一个业务模块分发器将 获得pojo对象交给不同的业务模块进行处理,如:DAL层、消息中间件, 4.将处理完成的业务通过业务模块分发器包装成xml报文回送给客户端。 整个业务处理组成部分如图所示:
内部业务逻辑 1.定时器从数据库装载帐户,采用多线程实现一个线程池,我们称这个玩意儿叫做任务工厂, 2.定时向JMS服务器中不同的消息队列发送消息 ,消息接收端收到消息后驱动业务模块去 SP Mail Server 收取邮件, 3.收邮件模块 上SP Mail Server 获取邮件列表头信息,并且和数据库中已存在的邮件列表头信息进行新邮件比对, 4.收取新邮件并且将邮件信息保存到数据库或者缓存,当用户需要读取的时候将从系统的数据库或者缓存中读取, 5.保存完毕后,将通过IP Push 或者 SMS Push 技术 通知用户上服务器获得新邮件。 后端内部架构 Web层 任务调度 BTW:任务工厂模块最开始在demo的时候采用Quartz 放入Spring微容器中进行集群,因为不能从缓存中直接读取,将来系统账户会越来越多,导致数据库那边的压力越来越大,所以后期我们用 线程池+oscache集群技术 自己开发了一套类似 Quartz+spring集群的方案。另外,这个项目发生在没有Memcached的年代,如果当时使用Memcached也许设计上会更加完美些。 分布式应用 EJB消息驱动Bean /JMS 消息系统 数据库 分片(sharding) 分布式文件系统(DFS) JavaMail
经验与教训 1.HAProxy的扩展使用,不仅仅可以使用在Web方面,还可以使用到其他应用层,进行压力分钟, 例如,可以应用到 数据MySQL。可以参考这篇文章,http://www./blog/2009/08/10/using-haproxy-for-mysql-failover-and-redundancy 2.JVM的优化是任何大型J2EE项目中必不可少的话题,当然并不是你把JVM的内存使用空间设置的越大越好,SUN的官方网站已经指出,不同的操作系统,不同的应用,建议你的JVM内存分配大小是不同的。另外,对与不同的JVM也有不同的参数配置,通常的JVM配置参数可以参见这里,http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html 3.Linux系统上的swap 空间,4G和4G内存 以内的机器还是非常有必要使用swap空间的,有一次有一个应用服务器系统down机,花了很久的时间才发现,居然是忘记建立Swap空间,多么简单的问题啊,可是把我们折磨了3个星期,3个星期里被老板和客户骂的惨不忍睹,血的教训,让我们再也不会忘记小内存机器建立swap空间了。 4.Jboss MQ的抱怨,首先声明我不是在给Apache 的JMS做广告,因为JbossMQ实在是不好用,而且性能也不咋地,我看见有一篇文章说到JbossMQ的性能在同类产品中是最好的,不知道结果是怎么来的,还是建议大家还是使用Apache的JMS,文档全面啊,不懂就可以找到很全面的资料,jboss MQ 资料的支持不多。 5.MySQL的优化,MySQL的一些参数调优就不说了,但使用MySQL 插件以后给我们带来了不少性能上的提升,特别是在数据查询上的瓶颈可以得到不少缓解。 6.MySQL集群,这玩意儿是个不错的解决方案,可是没有足够的内存是玩不起的,因为MySQL的集群因为完全依赖内存,另外对表的字段也有特别的限制,用起来有点不太自然,不支持所有大字段的类型。如果是老系统迁移过来,估计要折腾一段时间才能合用。 7.JMS 消息的使用,不需要放入数据库,将消息内存当中,并且对JVM运行参数进行优化 ,对与客户端的代码也需要进行优化,关闭消息id (setDisableMessageID),减少消息的大小并且 省去了创建唯一ID的时间。关闭消息的时间戳。如果不需要时间戳,用MessageProducer的setDisableMessageTimeStamp()方法将其关闭。避免使用AUTO_ACKNOWLEDGE。 AUTO_ACKNOWLEDGE 使得每收到一个消息就要向服务器发送一个通知--这样增加的网络传输的负担。如果可能,尽量使用 DUPS_OK_ACKNOWLEDGE或者CLIENT_ACKNOWLEDGE。或者使用事务性会话,将通知在提交时批量完成。 |
|
来自: goldbomb > 《Architecture》