安全及高可用策略
一、安全
一、组件原则(指导性)
软件的复杂度和它的规模成指数关系
一个复杂度为100的软件系统,如果能拆分成两个互补相关,同等规模的子系统,那么每个子系统的复杂度应该是25,而不是50。软件开发这个行业很久之前就形成了一个共识,应该将复杂的软件系统进行拆分,拆成多个更低复杂度的子系统,子系统还可以继续拆分成更小粒度的组件。也就是说,软件需要进行模块化,组件化设计。
组件内聚原则
组件内聚原则主要讨论哪些类应该聚合在同一个组件中,以便组件既能提供相对完整的功能,又不至于太过庞大。
复用发布等同原则 ,共同封闭原则,共同复用原则
复用发布等同原则
复用发布等同原则是说软件复用的最小粒度应该等同于其发布的最小粒度。也就是说,如果你希望别人以怎样的粒度复用你的软件,你就应该以怎样的粒度发布你的软件。这其实就是组件的定义,组件的软件复用和发布的最小粒度软件单元。这个粒度既是复用的粒度,也是发布的粒度。
版本号约定建议:
版本号格式: 主版本号.次版本号.修订号。比如 1.3.12, 在这个版本号中,主版本号是1,次版本号是3,修订号是12。
1.主版本号升级,表示组件发生了不向前兼容的重大修订;
2.次版本号升级,表示组件进行了重要的功能修订或者bug修复,但是组件是向前兼容的;
3.修订号升级,表示组件进行了不重要的功能修订或者bug修复
共同封闭原则
共同封闭原则是说,我们应该将那些会同时修改,并且为了相同目的而修改的类放到同一个组件中。而将不会同时修改,并且不会为了相同目的而修改的类放到不同的组件中。
组件的目的虽然是为了复用,然而开发中常常引发问题,恰恰在于组件本身的可维护性。如果组件在自己的生命周期中必须经历各种变更,那么最好不要涉及其它组件,相关的变更都在同一个组件中。这样,当变更发生的时候,只需要重新发布这个组件就可以了,而不是将一大堆组件都收到牵连。
共同复用原则
共同复用原则是说,不要强迫一个组件的用户依赖他们不需要的东西。
这个原则一方面是说,我们应该将互相依赖,共同复用的类放在一个组件中。比如说,一个数据结构容器组件,提供数组、Hash表等各种数据结构容器,那么对数据结构遍历的类,排序的类也应该放在这个组件中,以使这个组件中的类公共对外提供服务。
另一个方面,这个原则也说明,如果不是被共同依赖的类,就不应该放在同一个组件中,如果不被依赖的类发生变更,就会引起组件变更,进而引起使用组件的程序发生变更。这样就会导致组件的使用者产生不必要的困扰,甚至讨厌使用这样的组件,也造成了组件复用的困难。
组件耦合原则
组件内聚原则讨论的是组件应该包含哪些功能和类,而组件耦合原则讨论组件之间的耦合关系应该如何设计。
无循环依赖原则 ,稳定依赖原则,稳定抽象原则
无循环依赖原则
组件依赖关系中不应该出现环,如果组件A依赖组件B,组件B依赖组件C,组件C又依赖组件A,就形成了循环依赖。
很多时候,循环依赖是在组件的变更过程中逐渐形成的,组件A版本1.0依赖组件 B 版本1.0,后来组件 B 升级到1.1,升级的某个功能依赖组件A的1.0版本,于是形成了循环依赖。如果钻设计的边界不清晰,组件开发设计缺乏评审,开发中之关注自己开发的组件,整个项目对组件依赖管理没有统一的规则,很有可能出现循环依赖。
稳定依赖原则
组件的依赖关系必须指向更稳定的方向。较少变更的组件是稳定的,也就是说,经常变更的组件是不稳定的。根据稳定依赖原则,不稳定的组件应该依赖稳定的组件,而不是反过来。
反过来说,如果一个组件被更多的组件依赖,那么它需要相对是稳定的,因为想要变更一个被很多组件依赖的组件,本身就是一件困难的事。相对应的,如果一个组件依赖了很多的组件,那么它相对也是不稳定的,因为它依赖的任何组件变更,都可能导致自己的变更。
稳定依赖原则通俗的说就是,组件不应该依赖一个比自己还不稳定的组件。
稳定抽象原则
一个组件的抽象化程度应该于其稳定性程序一致。也就是说,一个稳定的组件应该是抽象的,而不稳定的组件应该是具体的。
这个原则对具体开发的指导意义就是: 如果你设计的组件是具体的、不稳定的,那么可以为这个组件对外提供服务的类设计一组接口,并把这组接口封装在一个专门的组件中,那么这个组件相对就是比较抽象、稳定。
Java中的JDBC就是这样一个例子,我们开发应用程序的时候只需要使用JDBC的接口编程就可以了,而发布应用的时候,我们制定具体的实现组件,可以是MySQL实现的JDBC组件,也可以是Oracle实现的JDBC组件。
组件的边界与依赖关系,不仅仅是技术问题
组件的边界与依赖关系划分,不仅需要考虑技术稳定,也要考虑业务场景问题。易变与稳定,依赖于被依赖,都需要放在业务场景中去考察。有的时候,甚至不只是技术和业务的问题,还需要考虑人的问题,在一个负载的组织中,组件的依赖和设计。
二、安全架构
有一种架构师叫做安全架构师,它平时不用参与什么开发。但是要时时刻刻的去关注系统的安全。当被攻击的时候有没有信息泄漏。
在各种情况下每天都要去监控,每天要去提安全报告。发现代码中的问题,信息安全中的问题。被攻击的时候要想好策略。做一些
相关的准备。安全架构师要专职的去做。
管理性能的架构师每天注意性能,瓶颈点在哪里。定时的去出报告。写方案,评估方案,把这个方案融入到日常的开发中。把它执行了。
架构师可以入手的点,比如 系统架构师,他关注的是整个的系统如何拆分。还可以从性能和安全入手,从可用性入手,从分布式入手。
从代码重构,从DDD领域分析 入手,这些都是可以入手的点 ,只要把这些其中的一个点做好了也是一个方面的架构师。
当团队稍微有点儿规模的时候,每一个领域都需要一个专门的人去做。
XSS攻击
see https://zhuanlan.zhihu.com/p/26177815
XSS是跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。
恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
XSS的危害
XSS的攻击方式就是想办法“教唆”用户的浏览器去执行一些这个网页中原本不存在的前端代码。
可以干的坏事还有很多,比如
1.窃取网页浏览中的cookie值 document.cookie
2.劫持流量实现恶意跳转 在留言板等地方插入 <script>window.location.href="http://www.baidu.com";</script>
核心防御手段:
消毒: XSS攻击者一般都是通过在请求中嵌入恶意脚本达到攻击目的,这些脚本是一般用户输入中不使用的,如果进行过滤和消毒处理,即
对某些HTML危险字段转义,如”>”转义为 “>”、”<“转义为”<”等,就可以防止大部分攻击。为了避免对不必要的内容错误转义,如”3<5”
中的”<“,需要进行文本匹配后再转义,如”<img src=“这样的上下为”中 “<“才转义。事实上,消毒几乎是所有网站最必备的XSS防攻击手段。
Sql注入攻击
防御手段
消毒: 和防XSS攻击一样,请求参数消毒是一种比较简单粗暴又有效的手段。通过正则匹配,过滤请求数据中可能注入的SQL文。
如:”drop table”、”\b(?:update\b.*?\bsetIdelete\b\W*?\bfrom)\b” 等
SQL预编译参数绑定: 使用预编译手段,绑定参数是最好的防SQL注入方法。目前许多数据访问层框架,如myBatis,Hibernate等,都
实现SQL预编译和参数绑定,攻击者的恶意SQL会被当做SQL的参数,而不是SQL命令被执行。
DDos攻击
它其实就是流量攻击,如果能够识别出来DDos攻击的来源,比如攻击方使用了自己的服务器构造了一些请求,可以识别出来
来自哪里的攻击请求的话,那就可以在请求接入的时候拒绝掉这些请求。
它的原理就是通过大量的并发来压垮服务器,所以能识别出来这些攻击的话,也就可以做到不服务这些攻击就达到目的了。
识别这个动作在安全领域很多是很机器学习相关的了,通过机器学习去学习攻击模型。
DDos攻击的时候可能会压垮网络,这种情况下,就专门的防 DDos攻击的云服务,它可以帮助我们分摊流量。一些攻击请求
分摊到它的云服务上,云服务帮你抗下来流量。
CSRF攻击
CSRF(Cross-site request forgery), 中文名称:跨站请求伪造,也被称为:one click attack/session riding, 缩写为: CSRF/XSRF
通常可以考虑采用header中带token,或者是sign签名的形式来解决问题
验证码,验证码则更加简单有效,它的作用就是在验证是人的请求还是机器的请求
破解的思路是按照约定的格式或者是提交一些token易遍于确保请求的安全性
Web应用防火墙
它就像是网关作为一个管道,它是请求里面的一块儿。它的原理是在这个管道中对所有的字符串进行过滤看有没有这些
攻击的字符串。如果有的话,就调用对应的执行策略对它进行处理。
重要特性:
web中通用的防攻击的手段,是waf应用防火墙。
对于XSS工具和Sql注入攻击甚至是CSRF攻击(伪造请求)
开源的Web防火墙
ModSecurity是一个开源的Web应用防火墙,探测攻击并保护Web应用程序,既可以嵌入到 Web应用服务器中,也可以作为
一个独立的应用程序启动。ModSecurity最早只是Apache的一个模块儿,现在已经有Java、.NET多个版本,并支持Nginx
ModeSecurity采用处理逻辑与规则集合分离的架构模式。处理逻辑复杂请求和响应的拦截过滤,规则加载执行等功能。
而规则集合则负责对具体的攻击的规则定义、模式识别、防御策略等功能。处理逻辑比较稳定,规则集合需要不断针对
漏洞进行升级,这是一种可扩展的架构设计。
这个玩意儿可以玩玩儿看
网站安全漏洞扫描
和电脑安全漏洞扫描一样,网站也需要安全漏洞扫描。
网站安全漏洞扫描工具是根据内置规则,模拟黑客攻击行为,用以发现网站安全漏洞的工具。许多大型网站的安全团队都有自己
开发的漏洞扫描工具,不定期的对网站的服务器进行扫描,查漏补充缺。
目前市场上也有很多商用的网站安全漏洞扫描平台。
重要的安全策略
1. 使用mybatis这样的orm框架
核心的是使用PreparedStatement提前进行预编译,不是提前拼接字符串来提交数据库去执行,这个是sql要注意的点
2. 通用性的防攻击的手段 应用防火墙
核不管是sql注入攻击还是xss攻击,甚至是CSRF攻击(伪造请求)
在防火墙这一层,把请求里面的内容拿出来之后去看是不是有xss攻击脚本,是不是有sql注入脚本儿,然后对它进行拦截。
然后再响应里面也去添加一些数据,等提交的时候再提交回来,这些是都可以在这个应用防火墙里面来做的。
3. 应用防火墙通常是应用在互联网网关中的
互联网网关本身就应该拥有预防攻击,安全职责这个功能
信息加密技术及密钥安全管理
2011年12月被曝的CSDN密码泄露事故中,网站安全措施不力,导致用户数据库被黑客“拖库”并不稀奇,令人错愕的是数据库中的用户密码居然是明文保存,导致密码泄露,成为地下黑市交易的商品。
通常,为了保护网站的敏感数据,应用需要对这些信息进行加密处理,信息加密技术可分为三类: 单项散列加密,对称加密,非对称加密
单向散列加密
1.例如md5加密
对称加密
1.用的场景是比较多的,尤其是在系统内部
非对称加密
1.加密密钥(公钥)
2.解密密钥(私钥)
密钥安全管理与加解密服务系统架构
1.密钥分成了三段,分别是数据库,文件系统,存储在多处
2.对外提供服务的时候,整个的加解密算法本身是继承在客户端的sdk包儿里的,它在应用服务器的内存中存在
没有任何机会打印出密钥
垃圾邮件的识别过程
贝叶斯过滤算法
贝叶斯算法解决概率论中的一个典型问题:一号箱子放有红色球和白色球20个,二号箱子放有白色球10个,红色球30个,现在随机挑选一个箱子,取出来一个球的颜色是红色的,
请问这个球来自一号箱子的概率是多少?
利用贝叶斯算法进行垃圾邮件的识别基于同样原理,根据已分类的样本信息获得一组特征值的概率(如 “茶叶” 这个词出现在垃圾邮件中的概率和非垃圾邮件中的概率),就得到分类模型,
然后对待处理信息提取特征是,结合分类模型,判断其分类
布隆过滤器黑名单
1.黑名单的原理
2.存储空间固定为16G
3.根据输入值进行8种算法,然后将16G中的特定比特位置为1。
下次判断在不在黑名单中的时候,就根据特定的输入计算出特定的比特位去看是不是都为1
都是1就表示输入值在黑名单中
电子商务风险控制
电子商务网站在给人们代理购物交易的极大便利的同时,也将风险带给了对网络安全一无所知的人们。由于买卖双发的信息不对等,交易本来就存在风险,而当交易在
网上发生的时候,买卖双发彼此一无所知,交易风险也就更加难以控制。如果一个电商网站骗子横行,诚信的交易者屡屡被骗,那么网站就到了最危险的时候,
可以说,交易安全是电子商务网站的底线。
规则引擎
当交易的某些指标满足一定条件的时候,就会被认为具有高风险的欺诈可能性。
把所有可能引起欺诈的可能性都配置在规则引擎中
比如:
1.用户来自欺诈高发地区
2.交易金额超过某个数值
3.和上次登录的地址距离差距很大
4.用户登录地与收获地不符
5.用户第一次交易
大型网站在运营过程中,结合业界的最新发现,会总结出数以千计的此类高风险交易规则。一种方案是在业务逻辑中通过编程方式使用if else代码实现这些规则,可以想象,
这些代码会非常庞大,而且由于运营过程中不断发现新的交易风险类型,需要不断调整规则,代码也需要不断修改。
机器学习的手段
在安全领域使用的场景非常多
二、高可用策略
可用性指标
业界通常用多少个9来衡量网站的可用性,如QQ的可用性是4个9,即QQ服务9.99%可用,这意味着QQ服务要保证其在所有运行时间中,值哦与0.01%的时间不可用,
也就是一年中大约53分钟不可用
网站年度的可用性指标 = ( 1- 网站不可用时间/年度总时间 ) * 100%
网站不可用时间(故障时间) = 故障修复时间点 - 故障发现(报告)时间点
对可用性的定性描述,两个9是基本可用,年度停机时间小于88小时;3个9较高可用,年度停机时间小于9小时;4个9是具有自动回复能力的高可用,年度停机时间小于53
分钟;5个9是极高可用性,年度停机时间小于5分钟。由于可用性影响因素的很多,对于网站整体而言,达到4个9,乃至5个9的可用性,除了过硬的技术、大量的设备资金
投入和工程师的责任心,还要有个好运气。
故障分类管理
故障处理流程及考核
1.计算故障开始时间
2.故障提交给部门的接口人,接口人群里发或者是直接找相关的人
3.相关的人赶紧看问题出在哪里,或者是架构师做一些判断
4.让关键的负责人去解决问题
5.解决问题之后重新上线,确认故障结束时间,记录故障归档
客服或者是测试确认故障结束
6.确认故障归宿,计入绩效考核
引擎故障的原因
1.硬件故障
比如内存硬盘之类的不能用了
2.软件bug
比如内存硬盘之类的不能用
3.系统发布
比如配置之类的问题,它不一定是bug
4.并发压力
高并发的情况下,系统压力过大把系统压垮了
5.网络攻击
网络xss或sql注入之类的攻击
6.外部灾害
比如程序依赖的外部程序不可用,或者是异地多活的机房之间光线断了之类的问题
高可用的架构方案策略
解耦
1.高内聚、低耦合的组件设计原则
2.面向对象基本设计原则
3.面向对象设计模式
4.领域驱动设计建模
隔离
1.业务与子系统隔离
2.微服务与中台架构
3.生产者消费者隔离
4.虚拟机与容器隔离
异步
1.多线程编程
2.反应式编程
3.异步通信网络编程
4.事件驱动异步架构
备份
1.集群设计
2.数据库复制
CAP原理
失效转移(Failover)
1.数据库主主失效转移
2.负载均衡失效转移
如何确认失效,需要转移?
设计无状态服务
幂等
应用调用服务失败后,会将调用请求重新发送到其他服务器,但是这个和失败可能是虚假的失败。比如服务已经处理成功,但是因为网络故障应用没有收到响应,
这时应用重新跳请求就导致服务重复调研,如果这个服务是一个转账操作,就会产生严重后果。
服务重复调用有时候是无法避免的,必须保证服务重复调研和调用一次产生的结果相同,即服务具有幂等性。有些服务天然具有幂等性,比如将用户性别设置为
男性,不管设置多少次,结果都一样。但是对于交易等操作,问题就会比较复杂,需要通过交易编号等信息进行服务调用有效性校验,只有有效的操作才继续执行.
事务补偿
用于解决分布式系统部分失败的情况下系统的可用性
传统事务的ACID
1.原子性(Atomicity)、一致性(Consistency)、隔离性(lsolation, 又称独立性)、持久性(Durability)
分布式事务的BASE
1.基本可用(Basic Availability)、软状态(Soft-state)、最终一致性(Eventual consistency)
事务补偿:通过执行业务逻辑逆操作,使事务回滚到事务前的状态
重试
远程服务可能会由于线程阻塞、垃圾回收或者网路抖动,而无法及时返还响应,调用者可以通过重试的方式修复单次调用的故障。
1.上游调用者超时时间要大于下游调用者超时时间之和。
熔断
当某个服务出现故障,响应延迟或者失败率增加,继续调用这个服务导致调用者请求阻塞,资源消耗增加,进而出现服务极联失败,这种
情况下使用断路器阻断对故障服务的调用。
断路器的三种状态:关闭,打开,半开
限流
在高并发场景下,如果系统的访问量超过了系统的承受能力,可以通过限流对系统进行保护。限流是指对进入系统的用户请求进行流量限制,如果
访问量超过了系统的最大处理能力,就会丢弃一部分的用户请求,保证整个系统可用,保证大部分用户是可以访问系统的,这样虽然又一部分用户
的请求被丢弃,产生了部分不可用,但还是好过整个系统崩溃,所有的用户都不可用要好。
限流的几种算法
1.计数器算法(固定窗口,滑动窗口)
使用计数器在周期内累加访问次数,当达到设定的限流值时,触发限流策略。下一个周期开始时,进行清零,重新计数。
固定窗口算法的临界点问题:假设1min内服务器的负载均能力为100,因此一个周期的访问量限制在100,然而在第一个
周期的最后5秒和下一个周期的开始5秒时间段内,分别涌入100的访问量,虽然没有超过每个周期的限制量,但是整体上10秒
内已达到200的访问量,已远远超过服务器的负载能力
2.令牌桶算法
以固定的速度向令牌桶中增加令牌,直刀令牌桶满,请求到达时向令牌桶请求令牌,如获取到令牌则通过请求,否则
触发限流策略
3.漏桶算法
访问请求到达时直接放入漏桶,如当前容量已达到限流值,则进行丢弃。漏桶以固定的速率进行释放访问请求,直到漏桶为空。
自适应限流
没有提前的人工评估,便没有提前的评估过时与人的评估疏漏/错误
1.实时自动评估QPS
2.业务流量的不确定性与技术方案的自适应性天生一对
降级
有一些系统功能是非核心的,但是它也给系统产生了非常大的压力,比如说在电商系统中有确认收获这个功能,即便我们不去确认收货,系统也会超时自动确认收货。
但实际上确认收货这个操作是一个非常重的操作,因为它会对数据库产生很大的压力,它要进行更改订单状态,完成支付确认,并进行评价等一系列操作。如果在系统
高并发的时候去完成这些操作,那么会对系统雪上加霜,使系统的处理能力更加恶化。
解决办法就是在系统高并发的时候,比如说像淘宝双11的时候,当天可能整天系统都处于一种极限的高并发访问压力之下,这时候就可以将确认收货,评价这些非核心的
功能关闭,将宝贵的系统资源留下来,给正在购物的人,让它们去完成交易
异地多活
如果整个数据中心都不可用,比如说数据中心所在城市遭遇了地震,机房遭遇了火灾或者停电,这样的话,不管我们的设计和系统多么的高可用,系统依然是不可用的。
为了解决这个问题,同时也为了提高系统的处理能力和改善用户体验,很多大型互联网应用都采用了异地多活的多机房架构策略,也就是说数据中心分布在多个不同地点的
机房里,这些机房都可以对外提供服务,用户可以连接人和一个机房进行访问,这样每个机房都可以提供完整的系统服务,即使某一个机房不可使用,系统也不好宕机,
依然保持可用。
异地多活的难点是数据一致。
自动化测试
代码在发布到线上服务器之前需要进行严格的测试。即使每次发布的新功能都是在原有系统功能上的小幅增加,但是为了保证系统没有引入未预料的BUG,网站测试
还是需要对整个网站功能进行全面的回归测试。此外还需要测试各种浏览器的兼容性,在发布频繁的网站应用中,如果使用人工来进行测试,成本和时间都难以承受。
目前大部分网站都采用Web自动化测试技术,使用自动测试工具或脚本完成测试。比较流行的Web自动化测试工具是ThoughtWorks开发的Selenium。Selenium运行在
浏览器中,模拟用户操作进行测试,因此Selenium可以同时完成Web功能测试和浏览器兼容测试。
持续部署三步走
持续集成
允许工程师随时向公共分支提交代码,并立即进行自动化测试
持续支付
除了跑单元测试及软件打包,持续交付机制会将软件部署到各种测试环境中
持续部署
使用jenkins之类的工具持续部署
预发布验证
即使是经过严格的测试,软件部署到线上服务器之后还是经常会出现各种问题,甚至根本无法启动服务器。主要原因是测试环境和线上环境并不相同,特别是
应用需要依赖的其他服务,如数据库,缓存,公共业务服务等,以及一些第三方服务,如电信短信网关、银行网银接口等。也许是接口编号导致的通信失败;
也许是配置错误导致连接失败;也许依赖的服务在线上环境还没有准备好;这些问题都可能导致应用故障。
因此在网站发布的时候,并不是把测试通过的代码包直接发布到线上服务器,而是线发布到预发布机器上,开发工程师和测试工程师在预发布服务器上进行预
发布验证,执行一两个典型的业务流程,确认系统没有问题后才正式发布。
预发布服务器是一种特殊用途的服务器,它和线上的正式服务器唯一的不同就是没有配置在负载均衡服务器上,外部用户无法访问。
代码版本控制
对于大型互联网系统,核心应用系统和公用业务模块及涉及许多团队和工程师,需要对相同的代码库进行共同开发和维护,而这些团队对同一个应用的开发维护,其
开发周期和发布时间点各不相同。如何进行代码管理,既能保证代码发布版本的稳定正确,同时又能保证不同团队的开发互不影响。
主干开发、分支发布:
代码修改都在主干上进行,需要发布的时候,从主干上拉一个分支发布,该分支即成为一个发布版本,如果该版本发现Bug,继续在该分支上修改发布,并将修改
合并(merge)回主干,直到下次主干发布。
分支开发,主干发布:
任何修改都不得在主干上直接进行,需要开发一个新功能或者修复一个bug的时候,从主干拉一个分支进行开发,开发完成测试通过后,合并回主干,然后从主干
进行发布,主干上的代码永远是最新发布的版本
自动化发布
网站的版本发布频繁,整个发布过程需要许多团队合作,发布前,多个代码分支合并回主干可能会发生冲突(conflict),预发布验证也会带来风险,每次发布又相当于一次宕机
事故,因此网站发布过程荆棘丛生,一不小心就会踩到雷。
灰度发布
应用发布完成成功后,仍然可能会发现因为软件问题而引入的故障,这时候就需要做发布回滚,即写在刚刚发布的软件,将上一个版本的软件包重新发布,使系统复原,消除故障。
大型网站的主要业务服务器集群规模非常庞大,比如QQ的服务器数量超过一万台。一旦发现故障,即使想要发布回滚也需要很长时间才能完成,只能眼铮铮的看着故障时间在不断
增加干着急,为了应付这种局面,大型网站会使用灰度发布模式,将集群服务器分成若干部分,持续几天的时间把整个集群全部发布完毕,期间如果发现问题,就只需要回滚已发布
的一部分服务器即可。
网站运行监控
“不允许没有监控的系统上线”,这是许多网站架构师在做项目上线评审的时候常说的一句话。网站运行监控对于网站运维和架构设计优化至关重要,没有监控的网站,犹如盲人骑瞎马,夜
半临深渊而不知。生死未卜,提高可用性,减少故障率就更无从做起了。
监控数据采集
广义上的网站监控涵盖所有非直接业务行为的数据采集与管理,包括供数据分析师和产品设计师使用的网站用户行为日志,业务运行数据和系统性能数据等。
用户行为日志收集
用户行为日志指用户在浏览器上所做所有操作及其所在的操作环境,包括用户操作系统与浏览器版本信息, IP地址,页面访问路径,页面停留时间等,这些数据对统计网站PV/UV指标,
分析用户行为,优化网站设计,个性化营销与推荐等非常重要。
具体用户行为日志收集手段有两种
服务器端日志收集,这个方案比较简单, Apache等几乎所有Web服务器都具备日志记录功能,可以记录大部分用户行为日志,开启Web服务器的日志功能即可。其缺点是可能会
出现信息失真,如IP地址是代理服务器地址而不是用户真实IP; 多个链接指向同一个页面的情况下无法分辨访问路径等。
客户端浏览器日志收集,浏览器可以收集用户真实的操作行为,因此比服务器日志收集更加精确。其缺点是比较麻烦,需要在页面嵌入特定的JS脚本来完成。
服务器性能监控
收集服务器性能指标,如系统Load,内存占用,磁盘IO,网络IO等对尽早作出故障预警,及时判断应用状况,防患于未然,将故障扼杀在萌芽时期非常重要。此外根据性能监控数据,
运维工程师可以合理安排服务器集群规模,架构师及时改善系统性能及调整系统伸缩性策略
目前网站使用比较广泛的开源性能监控工具是Ganglia,支持大规模服务器集群,并支持以图形的方式在浏览器展示实时性能曲线。
业务运行数据报告
除了服务器系统性能监控,网站还需要监控一些具体业务场景相关的技术和业务指标,比如缓冲命中率、平均响应延迟时间,每分钟发送邮件数目、待处理的任务总数等。
不同于服务器性能监控,网站运维人员可以在初始化系统的时候统一部署,业务运行数据需要在具体程序中采集并报告,汇总后统一显示。
监控管理
监控数据采集后,除了用作系统性能评估,集群规模伸缩性预测等,还可以根据实时监控数据进行风险预警,并对服务器进行失效转移,自动负载调整,最大化利用集群所有机器的资源。
报警
服务器运行正常的情况下,其各项监控指标基本稳定在一个特定水平,如果这些指标超过某个阀值,就意味着系统可能将要出现故障,这时候就需要对相关人员报警,及时采取措施,在
故障还未真正发生就将其扼杀在萌芽状态。
监控管理系统可以配置报警阀值和值守人员的联系方式,报警方式除了邮件,即时通讯工具,还可以配置收集短信,语音报警,保证发生报警时,工程师即时在千里之外,夜里睡觉也能及时
同时,快速响应。
自动控制
自动失效转移:除了应用程序访问失败时进行失效转移,监控系统也可以在发生故障的情况下主动通知应用,进行失效转移。
自动扩容:如果因访问压力大而导致服务性能指标下降,监控系统自动出发服务集群扩容。
自动限流:根据监控指标,自动控制访问流量。
监控系统架构
高可用的价值观
保持简单,使问题易于发现,快速解决。
目标明确,解决特定环境下的具体问题。
价值回归,成本收益要合理。
三、故障案例分析
故障1
故障现象: 某应用服务器集群发布后不久就出现多台服务器相继报警,硬盘可用空间低于警戒值,并且很快有服务器宕机,登录到线上服务器,发现log文件夹里的文件迅速增加,
不断消耗磁盘空间。
原因分析:
这是一个普通的应用服务器集群,不需要存储数据,因此服务器里配置的是一快儿100GB的小硬盘,安装完操作系统,Web服务器,Java虚拟机,应用程序后,空闲空间只有几十GB了,
正常情况下这些磁盘空间足够了,但是该应用的开发人员将log输出的level全局配置为Debug,这样一次简单的Web请求就会产生大量的log文件输出,在高并发的用户请求下,很快就
消耗完了不多的磁盘空间。
经验教训:
检查log配置文件,日志输出级别至少为Warn,并且检查log输出代码调用,调用级别要符合其真实日志级别。
有些开源的第三方组件也会不恰当的输出太多的 Error日志,需要关系这些第三方库的日志输出,至于哪些第三方库有问题,只有在遇到问题时才知道。
应用程序自己的日志输出配置和第三方组件日志输出要分别配置
故障2
某应用发布后,数据库Load巨高不下,远超过正常水平,持续报警。
原因分析:
检查数据库,发现报警是因为某条SQL引起的,这条SQL是一条简单的有索引的数据查询,不应该引发报警。继续检查,发现这条SQL执行效率非常高,远远超过正常水平。
追查这条SQL,发现被网站首页应用调用,首页是被访问最频繁的网页,这条SQL被首页调用,也就被频繁执行了。
经验教训:
检首页不应该访问数据库,首页需要的数据可以从缓存服务器或者搜索引擎服务器获取
首页最好是静态的
故障3
某应用服务器不定时的因为超时而报警,但是很快又超时接触,恢复正常,如此反复,让运维人员非常苦恼。
原因分析:
程序中某个单里对象(singleton object)中多了方法使用了synchronized修饰符,由于this对象只有一个,即时执行不同方法,所有的并发请求也都哟啊排队获得这唯一的一把锁。
一般情况下,都是一些简单操作,获得所,迅速完成操作,释放锁,不会引起线程排队,但是每次执行都需要较长的时间才能完成,这段时间锁被占用,所有的线程都要等待,
响应超时,这个方法执行完后释放锁,其他线程迅速执行,超时解除。
经验教训:
检使用锁操作要谨慎,特别注意在有锁的方法中进行长时间的IO操作的时候。
针对使用锁的不同场景,使用不同锁对象,而不是简单的在所有方法上都加上synchronized
故障4
某没有新应用发布,用户并发请求也没有突然增加,但是数据库服务器突然Load飙升,并很快失去响应。DBA将数据库访问切换到备机,Load也很快飙升,并失去响应。最终
引发网站全部瘫痪。
原因分析:
程缓存服务器在网站服务器集群中的地位一致比较低,服务器配置和管理级别都比其他的服务器要低一些。人们都认为缓存是改善性能的手段,丢失一些缓存也没什么问题,有时候
关闭一两台缓存服务器也确实对应用没有明显一会给你行,所以长期疏于管理缓存服务器。结果这此一个缺乏经验的工程师关闭了缓存服务器集群中的全部的几十台Memcached服务器,
导致了网站全部瘫痪的重大事故。
经验教训:
当缓存已经不仅仅是改善性能,而是成为网站架构不可或缺的一部分时,对缓存的管理就需要提高到和其他服务器一样的级别。
故障5
某应用发布后,服务器立即崩溃
原因分析:
程应用程序Web环境使用 Apache+JBoss的模式,用户请求通过Apache转发到JBoss,在发布时,Apache和JBoss同时启动,由于JBoss启动时候需要加载很多应用并初始化,花费时间
较长,结果JBoss还没有完全启动,Apache就已经启动完毕开始接收用户请求,大量请求阻塞在JBoss进程中,最终导致JBoss崩溃,除了这种 Apache和JBoss启动不同步的情况,网站
还有很多类似的场景,都需要后台服务准备好,前台应用才能启动,否则就会导致故障。
这种情况被内部人戏称为”姑娘们还没穿好衣服,老鸨就开门迎客了。"
经验教训:
当老鸨开门前要检查下姑娘们是否穿好了衣服,就本例来说,在Java应用程序中加入一个特定的动态页面(比如只返还OK两个字母),启动脚本线启动JBoss,然后在脚本儿中不断用
curl命令访问这个特定页面,知道收到ok,再启动Apache。
故障6
某应用主要功能是管理用户图片,接到部分用户投诉,表示上传图片非常慢,原来只需要一两秒,现在需要十几秒,有时等半天结果浏览器显示服务器超时。
原因分析:
程图片需要使用存储,最有可能出错的地方就是存储服务器,检查存储服务器,发现大部分文件只有几百K,而有几个文件非常大,有数百兆,读写这些大文件一次需要十几秒,这段时间,
磁盘基本别这个文件操作独占导致其他用户的文件操作缓慢。
经验教训:
当存储的使用需要根据不同文件类型和用途进行管理,图片都是小文件,应该使用专用的存储服务器,不能和大文件公共存储,批处理用的大文件可以使用其他类型的分布式文件系统。
故障7
某监控发现某个时段内,某些应用突然变慢,内部网络访问延迟非常厉害。
原因分析:
程检查发现,该时段内网卡流量也下降,但是没有找到原因。过了一阵子才知道,原来有工程师在线上生产环境进行性能压力测试,占用了大部分交换机带宽。
经验教训:
访问线上生产环境要规范,不小心就会导致大事故。
滥用生产环境的一个例子: 网站数据库有专门的DBA维护,如果发现数据库存在错误记录,需要进行数据订正,必须走数据订正流程,申请DBA协助,于是就有工程师
未来避免麻烦,直接写一段数据库更新操作的代码,悄悄放到生产环境应用服务器上执行,神不知鬼不觉订正了数据,但是如果不小心写错了SQL,后果不堪设想。
故障8
某应用发布后,数据库Load迅速飙升,超过报警值,回滚发布会报警消除
原因分析:
程发现该应用发布后出现大量数据库读操作,而这些数据本来应该从分布式缓存读取,检查缓存,发现数据已经缓存了,检查嗲吗,发现访问缓存的那行代码被注释掉了,原来工程师
在开发的时候,为了测试方便,特意注释掉了读取缓存的代码,结果开发完成后忘记把代码注释去掉,直接提交到代码库被发布到线上环境。
经验教训:
代码提交前使用diff命令进行代码比较,确认没有提及不该提交的代码。
加强code review,代码在正式提交前必须被至少一个其他工程师做过code review, 并且共同承担因代码引起的故障责任。
故障9
某应用更新某功能后,有少量用户投诉无法正常访问该功能,一点击就显示出错信息
原因分析:
程分析这些用户,都是第一次使用该功能,检查代码,发现程序根据历史使用记录构造一个对象,如果该对象为Null,就会导致NullPointException
经验教训:
程序在处理一个输出的对象时,如果不能明确该对象是否为空,必须做空指针判断。
程序在调用其他方法时,输入的对象尽量保证不是null,必要时构造空对象(使用空对象模式)
评论