第十一周学习心得
Web 攻击与防护:
常见攻击方式及防护手段:
1. XSS 攻击:主要攻击手段是嵌入脚本攻击
1. 用户在自己的浏览器中执行包含攻击者发送的攻击脚本 url,并通过用户来散播恶意脚本 url
2. 攻击者直接攻击目标服务器
3. 主要防御手段:消毒:XSS 攻击者一般是通过在请求中嵌入恶意脚本达到攻击目的,这些脚本是一般用户输入中不使用的,如果进行消毒处理,即对某些 HTML 危险字符转译,如“>”转译为“>”、“<”转译为“<”等,就可以防止大部分攻击。为了避免对不必要的内容错误转译,如“3<5”中的<,需要经行文本匹配后再转译,如“<img src=”这样的上下文中“<”才转译。事实上,消毒几乎是所有网站最必备的 XSS 防攻击手段
2. SQL 注入攻击:
1. 提交的字符串中包含非法 sql
2. 实施 sql 注入的前提条件:
1. 开源:如果网站采用开源软件搭建,如用 Discuz!搭建论坛网站,那么网站数据库结构就是公开的,攻击者可以直接获得
2. 错误回显:如果网站开启错误回显,攻击者故意构造非法参数,服务端异常信息会输出到浏览器端,为攻击者猜测数据库表结构提供了便利
3. 盲注:网站关闭错误回显,攻击者根据页面变化情况判断 sql 语句的执行情况,据此猜测数据库表结构,此种方式攻击难度较大
3. 主要防御手段:
1. 消毒:和防御 XSS 攻击一样,请求参数消毒是一种比较简单粗暴又有效的手段。通过正则匹配,过滤请求数据中可能注入的 sql 文本
2. sql 预编译参数绑定:使用预编译手段,绑定参数是最好的防御 sql 注入方法。目前许多数据访问层框架,如:myBatis,Hibernate 等,都实现 sql 预编译和参数绑定,攻击者的恶意 sql 会被当做 sql 的参数,而不是 sql 命令被执行。
3. CSRF 跨站请求伪造:
1. 还是需要先正常登录服务器,但是每次访问前都会先经过攻击者的服务器
2. 与 XSS 攻击的区别:xss 攻击是脚本注入的,但是 CSRF 是诱导用户登录钓鱼网站,通过钓鱼网站让用户访问本不该,用户本身不想访问的网站
3. 主要防御手段:
1. 表单 token:CSRF 是一个伪造用户请求的操作,所以需要构造用户请求的所有参数才可以。表单 token 就是阻止攻击者获得所有请求参数的可能性,再页面表单中增加一个随机数 token,每次请求的 token 都不相同,请求提交后检查 token 的值是否正确以确定请求提交者是否合法
2. 验证码:相对来说,验证码则更加简单有效,即请求提交时,需要用户输入验证码,以避免在用户不知情的情况下被攻击者伪造请求。但是输入验证码是一个糟糕的用户体验,所以必要的时候才使用,如支付交易等关键页面
3. Referer check:HTTP 请求头的 referer 域中记录着请求来源,可通过检查请求来源,验证其是否合法。但是该方法有一定的局限性,referer 也不到一定总能得到。
4. 其他的攻击和漏洞:
1. ErrorCode:也称作错误回显,许多 Web 服务器默认是打开异常信息输出的,即服务器端未处理的异常堆栈信息会直接输出到客户端浏览器,这种方式虽然对程序调试和错误报告有好处,但是同事也给黑客造成可乘之机。通过故意制造非法输入,使系统运行时出错,获得异常信息,从而寻找系统漏洞进行攻击。防御手段也很简单,通过配置 web 服务器参数,跳转 500 页面到专门的错误页面即可,这个功能 Web 应用常用的 MVC 框架也可以做到。
2. HTML 注释:为了程序调试方便或者其他不恰当的因素,有时候程序开发人员毁在 PHP、JSP 等服务器页面程序中使用 HTML 注释语法进行程序注释,这些 HTML 注释就会显示在客户端浏览器,给黑客造成攻击便利。程序最终发布前需要进行代码 review 或者自动扫描,避免 HTML 注释漏洞。
3. 文件上传:一般网站都会有文件上传功能,设置头像、分享视频、上传附件等。如果上传的是可执行的程序,并通过该程序获得服务器端命令执行能力,那么攻击者几乎可以在服务器上为所欲为,并以此为跳板攻击集群环境的其他机器。最有效的防御手段是设置上传文件白名单,只允许上传可靠的文件类型。此外还可以修改文件名、使用专门的存储手段,保护服务器免受上传文件攻击。
4. 路径遍历:攻击者在请求的 URL 中使用相对路径,遍历系统未开放的目录和文件。防御方法主要是将 JavaScript、CSS 等资源封建独立服务器、独立域名,其他文件不使用静态 url 访问,动态参数不包含文件路径信息。
5. Web 应用防火墙:
1. 自研 web 安全模块:
2. 开源 Web 应用防火墙 ModSecurity
1. ModSecurity 是一个开源的 Web 应用防火墙,探测攻击并保护 Web 应用程序,既可以嵌入到 Web 应用服务器中,也可以作为一个独立的应用程序启动。ModSecurity 最早只是 Apache 的一个模块,现在已经有 Java、.NET 多个版本,并支持 Nginx。
2. ModSecurity 采用处理逻辑与规则集合分离的架构模式。处理逻辑负责请求和响应的拦截过滤,规则加载执行等功能。而规则集合则负责对具体的攻击的规则定义、模式识别防御策略等功能。处理逻辑比较稳定,规则集合需要不断针对漏洞进行升级,这是一种可扩展的架构设计。
安全架构:加密与解密
在上面的防护手段都无法拦截黑客攻击的情况下,服务器在被黑客突破后,为了保证重要数据不被泄漏,就需要用到加密技术,来给重要信息加密,即使黑客访问成功,也只能看到一堆加密过的数据,无法破译就没有价值,从而保护数据的安全
信息加密技术及密钥安全管理:
2011 年 12 月被曝的 CSDN 密码泄露事故中,网站安全措施不力,导致用户数据库被黑客“拖库”并不稀奇,令人错愕的是数据库中的用户密码居然是明文保存,导致密码泄露,成为地下黑市交易的商品。通常,为了保护网站的敏感数据,应用需要对这些信息进行加密处理。
信息加密技术可分为三类:
1. 单项散列加密:整个加密过程是单向的,加密之后不能从密文反推,比如用户密码信息
1. 在不加盐的情况下,通过足够多的密码尝试,可以建立映射表,破译这种单向散列算法,从而达到解密的目的
2. 多种加盐方式,可以很好的保证数据的安全性对称加密:可以通过密钥解密出密文的加密方式,加密密钥和解密密钥相同,
2. 对称加密:可以通过密钥解密出密文的加密方式,加密密钥和解密密钥相同,比如信用卡卡号等使用时需要还原回明文的数据
3. 非对称加密:可以通过密钥解密出密文的加密方式,加密密钥和解密密钥不同,比如 HTTPS 加密传输用的 tls 加密算法
1. 还可以用私钥加密,用公钥解密的方式,这是一种数据签名,保证数据出处正确;如数据货币,区块链
4. 如何保护密钥:
1. 密钥安全管理与加解密服务系统架构:
安全架构:反垃圾与风控
反垃圾邮件:
贝叶斯分类算法:
贝叶斯算法解决概率论中的一个典型问题:一号箱子放有红色球和白色球各 20 个,二号箱子放有白色球 10 个,红色球 30 个,现在随机挑选一个箱子,取出来一个球的颜色是红色的,请问这个球来自一号箱子的概率是多少利用贝叶斯算法进行垃圾邮件的识别基于同样原理,根据已分类的样本信息获得一组特征值的概率(如“茶叶”这个词出现在垃圾邮件中的概率和非垃圾邯任界的慨率),就得到分类模型,然后对待处理信息提取特征值,结合分类模型,判断其分类。
具体实践过程中的流程:
布隆过滤器黑名单:
用较小的空间,记录大量的黑名单数据的方式,用于根据邮箱来判断垃圾邮件的场景
布隆过滤器存储机制:
1. 使用在黑名单中的数据,通过 8 种算法,得到 8 个值,映射到 16G 位的 2 进制数中
2. 在新邮件进入时,还是用 8 中算法计算邮箱的 8 个特征值,将这 8 个特征值,对比已经有的 16G 位数字中对应的值,是否相同,只要有一个相同就不是垃圾邮件,反之则当做垃圾邮件处理
3. 特点:可能出现误判,但不会出现漏判
风控系统:
电子商务网站在给人们代理购物交易的极大便利的同时,也将风险带给了对网络安全一无所知的人们。由于买卖双方的信息不对等,交易本来就存在风险,而当交易在网上发生的时候,买卖双方彼此一无所知,交易风险也就更加难以控制。如果一个电商网站骗子横行,诚信的交易者屡屡被骗,那么网站就到了最危险的时候,可以说,交易安全是电子商务网站的底线。
电子商务具有多种形式,B2B,B2C,C2C 每种交易的场景都不相同,风险也各有特点,大致可分为以下几种:
1. 账户风险:包括账户被黑客盗用,恶意注册账号等几种情形
2. 买家风险:买家恶意下单占用库存进行不正当竞争;黄牛利用促销抢购低价商品;此外还有良品拒收;欺诈退款以及常见于 B2B 交易的虚假询盘等。
3. 卖家风险:不良卖家进行恶意欺诈行为,例如货不对板,虚假发货,炒作信用等,此外还有发布违禁商品、侵权产品等。
4. 交易风险:信用卡盗刷,支付欺诈,洗钱套现等。
对应方法:
1. 规则引擎:当交易的某些指标满足一定条件的时候,就会被认为具有高风险的欺诈可能性。
1. 用户来自欺诈高发地区
2. 交易金额超过某个数值
3. 和上次登录的地址距离差距很大
4. 用户登录机制与收获地不符
5. 用户第一次交易
6. 其他等等情况
2. 大型网站在运营过程中,结合业界的最新发现,会总结出数以千计的此类高风险交易规则。一种方案是在业务逻辑中通过编程方式使用 if...else..代码实现这些规则,可以想见,这些代码会非常庞大,而且由于运营过程中不断发现新的交易风险类型,需要不断调整规则,代码也需要不断修改。
3. 规则引擎使用:
4. 机器学习:规则引擎虽然技术简单,但是随着规则的逐渐增加,出现规则冲突,难以维护等情况,而且规则越多,性能也越差。大型互联网应用更倾向于使用机器学习模型进行风控
5. 机器学习使用:
高可用:可用性度量
可用性指标:
业界通常用多少个 9 来衡量网站的可用性,如 QQ 的可用性是 4 个 9,即 QQ 服务 99.99%可用,这意味着 QQ 服务要保证其在所有运行时间中,只有 0.01%的时间不可用,也就是一年中大约 53 分钟不可用。
可用性指标计算方法:
1. 网站年度可用性指标=(1-网站不可用时间/年度总时间) ×100%
2. 网站不可用时间(故障时间)=故障修复时间点-故障发现(报告)时间点
对可用性的定性描述:
1. 两个 9 是基本可用,年度停机时间小于 88 小时
2. 3 个 9 较高可用,年度停机时间小于 9 小时
3. 4 个 9 是具有自动恢复能力的高可用,年度停机时间小于 53 分钟
4. 5 个 9 是极高可用性,年度停机时间小于 5 分钟
5. 由于可用性影响因素很多,对于网站整体而言,达到 4 个 9,乃至 5 个 9 的可用性,除了过硬的技术、大量的设备资金投入和工程师的责任心,还要有个好运气
故障分管理:
故障处理流程及考核:
引起故障的原因:
1. 硬件故障
2. 软件 bug
3. 系统发布
4. 并发压力
5. 网络攻击
6. 外部灾害
高可用:提升系统可用性的架构方案
解耦:
1. 高内聚、低耦合的组件设计原则
2. 面向对象基本设计原则
3. 面向对象设计模式
4. 领域驱动设计建模
隔离:
1. 业务与子系统隔离
2. 微服务与中台架构
3. 生产者消费者隔离
4. 虚拟机与容器隔离
异步:
1. 多线程编程
2. 反应式编程
3. 异步通信网络编程
4. 事件驱动异步架构
备份:
1. 集群设计
2. 数据库复制
1. CAP 原理
3. Failover(失效转移)
1. 数据库主主失效转移
2. 负载均衡失效转移
3. 如何确认失效,需要转移?
4. 设计无状态的服务
幂等:
应用调用服务失败后,会将调用请求重新发送到其他服务器,但是这个失败可能是虚假的失败。比如服务已经处理成功,但是因为网络故障应用没有收到响应,这时应用重新提交请求就导致服务重复调用,如果这个服务是一个转账操作,就会产生严重后果。服务重复调用有时候是无法避免的,必须保证服务重复调用和调用一次产生的结果相同,即服务具有幂等性。有些服务天然具有幂等性,比如将用户性别设置为男性,不管设置多少次,结果都一样。但是对于交易等操作,问题就会比较复杂,需要通过交易编号等信息进行服务调用有效性校验,只有有效的操作才继续执行。
事务补偿:
1. 传统事务的 ACID
1. 原子性(Atomicity)、一致性(Consistency)、隔离性(lIsolation,又称独立性)、持久性(Durability)
2. 分布式事务的 BASE
1. 基本可用(Basic Availability)、软状态(Soft-state)、最终一致性(Eventual consistency)
事务补偿:通过执行业务逻辑逆操作,使事务回滚到事务前状态
重试:
远程服务可能会由于线程阻塞、垃圾回收或者网络抖动,而无法及时返还响应,调用者可以通过重试的方式修复单次调用的故障。
• 上游调用者超时时间要大于下游调用者超时时间之和。
熔断:
当某个服务出现故障,响应延迟或者失败率增加,继续调用这个服务会导致调用者请求阻塞,资源消耗增加,进而出现服务级联失效,这种情况下使用断路器阻断对故障服务的调用。
• 断路器三种状态:关闭,打开,半开
限流:
在高并发场景下,如果系统的访问量超过了系统的承受能力,可以通过限流对系统进行保护。限流是指对进入系统的用户请求进行流量限制,如果访问量超过了系统的最大处理能力,就会丢弃一部分的用户请求,保证整个系统可用,保证大部分用户是可以访问系统的。这样虽然有一部分用户的请求被丢弃,产生了部分不可用,但还是好过整个系统崩溃,所有的用户都不可用要好。
限流的几种算法:
1. 计数器算法(固定窗口,滑动窗口)
2. 令牌桶算法
3. 漏桶算法
• 计数器(固定窗口)算法:
– 使用认数器在周期内累加访问次数,当达到设定的限流值时,触发限流策略。下一个周期开始时,进行清零,重新计数
– 固定窗口算法的临界点问题:假设 1min 内服务器的负载能力为 100,因此一个周期的访问量限制在 100,然而在第一个周期的最后 5 秒和下一个周期的并始 5 秒时间段内,分别涌入 100 的访问量,虽然没看超付每个周期的限制毫,但是整休上 10 秒内已达到 200 的访问量,已远远超过服务器的负载能力
• 计数器(滑动窗口)算法:
– 将时间周期分为 N 个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期。
– 假设时间周期为 1min,将 1min 再分为 2 个小周期,统计每个小周期的访问数量,则可以看到,第一个时间周期内,访问数量为 75,第二个时间周期内,访问数量为 100,超过 100 的访问则被限流掉了
• 令牌桶算法:
– 以固定的速度向令牌桶中增加令牌,直到令牌桶满,请求到达时向令牌桶请求令牌,如获取到令牌则通过请求,否则触发限流策略
• 漏桶算法:
– 访问请求到达时直接放入漏桶,如当前容量已达到限流值,则进行丢弃。漏桶以固定的速率进行释放访问请求,直到漏桶为空。
• 自适应限流:
– 没有提前的人工评估,便没有提前的评估过时与人的评估疏漏/错误!
1. 实时自动评估 QPS
2. 业务流量的不确定性与技术方案的自适应性天生一对!
降级:
有一些系统功能是非核心的,但是它也给系统产生了非常大的压力,比如说在电商系统中有确认收货这个功能,即便我们不去确认收货,系统也会超时自动确认收货。
但实际上确认收货这个操作是一个非常重的操作,因为它会对数据库产生很大的压力:它要进行更改订单状态,完成支付确认,并进行评价等一系列操作。如果在系统高并发的时候去完成这些操作,那么会对系统雪上加霜,使系统的处理能力更加恶化。
解决办法就是在系统高并发的时候,比如说像淘宝双 11 的时候,当天可能整天系统都处于一种极限的高并发访问压力之下,这时候就可以将确认收货、评价这些非核心的功能关闭,将宝贵的系统资源留下来,给正在购物的人,让他们去完成交易。
异地多活:
如果整个数据中心都不可用,比如说数据中心所在城市遭遇了地震,机房遭遇了火灾或者停电,这样的话,不管我们的设计和系统多么的高可用,系统依然是不可用的。
为了解决这个问题,同时也为了提高系统的处理能力和改善用户体验,很多大型互联网应用都采用了异地多活的多机房架构策略,也就是说将数据中心分布在多个不同地点的机房里,这些机房都可以对外提供服务,用户可以连接任何一个机房进行访问,这样每个机房都可以提供完整的系统服务,即使某一个机房不可使用,系统也不会宕机,依然保持可用。
异地多活的难点是数据一致。
高可用:架构运维方案
发布:
网站需要保证 7×24 高可用运行,同时网站又需要不断的发布新功能吸引用户以保证在激烈的市场竞争中获得成功。许多大型网站每周都需要发布一到两次,而中小型网站则更加频繁,一些处于快速发展期的网站甚至每天发布十几次。
不管发布的新功能是修改了一个按钮的布局还是增加一个核心交易功能,都需要在服务器上关闭原有的应用,然后重新部署启动新的应用,整个过程还要求不影响用户的使用。这相当于是要求给飞行中的飞机换个引擎,既不能让飞机有剧烈的晃动,也不能让飞机降落,更不能让飞机坠毁。
既然网站的发布过程事实上和服务器宕机效果相当,那么就可以用服务器宕机的高可用方案来应对网站的发布。所以设计一个网站的高可用架构的时候,需要考虑的服务器宕机概率不是物理上的每年一两次,而是事实上的每周一两次。也许你认为这个应用不重要,重启也非常快,用户可以忍受每年一到两次的宕机故障,因而不需要复杂的高可用设计。事实上,由于应用的不断发布,用户需要面对的是每周一到两次的宕机故障。用户哭了。
自动化测试:
代码在发布到线上服务器之前需要进行严格的测试。即使每次发布的新功能都是在原有系统功能上的小幅增加,但是为了保证系统没有引入未预料的 BUG,网站测试还是需要对整个网站功能进行全面的回归测试。此外还需要测试各种浏览器的兼容性,在发布频繁的网站应用中,如果使用人工来进行,成本和时间都难以承受。
目前大部分网站都采用 Web 自动化测试技术,使用自动测试工具或脚本完成测试。比较流行的 Web 自动化测试工具是 ThoughtWorks 开发的 Selenium。Selenium 运行在浏览器中,模拟用户操作进行测试,因此 Selenium 可以同时完成 Web 功能测试和浏览器兼容测试。
自动化测试和手工测试的总体成本关系:
自动化部署:
持续部署三步走:
持续集成
• 允许工程师随时向公共分支提交代码,并立即进行自动化测试。
持续交付
• 除了跑单元测试及软件打包,持续交付机制会将软件部署到各种测试环境中
持续部署
• 代码在没有人工干预的情况下被测试、构建、部署并推送到生产环境。
预发布验证:
即使是经过严格的测试,软件部署到线上服务器之后还是经常会出现各种问题,甚至根本无法启动服务器。主要原因是测试环境和线上环境并不相同,特别是应用需要依赖的其他服务,如数据库,缓存、公用业务服务等,以及一些第三方服务,如电信短信网关、银行网银接口等。也许是接口变化导致的通信失败;也许是配置错误导致连接失败;也许依赖的服务在线上环境还没有准备好;这些问题都有可能导致应用故障。
因此在网站发布的时候,并不是把测试通过的代码包直接发布到线上服务器,而是先发布到预发布机器上,开发工程师和测试工程师在预发布服务器上进行预发布验证,执行一两个典型的业务流程,确认系统没有问题后才正式发布。
预发布服务器是一种特殊用途的服务器,它和线上的正式服务器唯一的不同就是没有配置在负载均衡服务器上,外部用户无法访问。
代码版本控制:
对于大型互联网系统,核心应用系统和公用业务模块涉及许多团队和工程师,需要对相同的代码库进行共同开发和维护,而这些团队对同一个应用的开发维护,其开发周期和发布时间点各不相同。如何进行代码管理,既能保证代码发布版本的稳定正确,同时又能保证不同团队的开发互不影响。
主干开发、分支发布:
代码修改都在主干上进行,需要发布的时候,从主干上拉一个分支发布,该分支即成为一个发布版本,如果该版本发现 Bug,继续在该分支上修改发布,并将修改合并(merge )回主干,直到下次主干发布。
分支开发,主干发布∶
任何修改都不得在主干上直接进行,需要开发一个新功能或者修复一个 bug 的时候,从主干拉一个分支进行开发,开发完成测试通过后,合并回主干,然后从主干进行发布,主干上的代码永远是最新发布的版本。
自动化发布:
网站的版本发布频繁,整个发布过程需要许多团队合作,发布前,多个代码分支合并回主干可能会发生冲突(conflict),预发布验证也会带来风险,每次发布又相当于一次宕机事故。因此网站发布过程荆棘丛生,一不小心就会踩到雷。
灰度发布:
应用发布完成功后,仍然可能会发现因为软件问题而引入的故障,这时候就需要做发布回滚,即卸载刚刚发布的软件,将上一个版本的软件包重新发布,使系统复原,消除故障。
大型网站的主要业务服务器集群规模非常庞大,比如 QQ 的服务器数量超过一万台。一旦发现故障,即使想要发布回滚也需要很长时间才能完成,只能眼睁睁看着故障时间在不断增加干着急。为了应付这种局面,大型网站会使用灰度发布模式,将集群服务器分成若干部分,每天只发布一部分服务器,观察运行稳定没有故障,第二天继续发布一部分服务器,持续几天的时间才把整个集群全部发布完毕,期间如果发现问题,就只需要回滚已发布的一部分服务器即可。
网站运行监控:
“不允许没有监控的系统上线”,这是许多网站架构师在做项目上线评审的时候常说的一句话。网站运行监控对于网站运维和架构设计优化至关重要,没有监控的网站,犹如盲人骑瞎马,夜半临深渊而不知。生死未卜,提高可用性、减少故障率就更无从做起了。
用户行为日志收集:
用户行为日志指用户在浏览器上所做所有操作及其所在的操作环境,包括用户操作系统与浏览器版本信息,IP 地址,页面访问路径,页面停留时间等,这些数据对统计网站 PV/UV 指标,分析用户行为,优化网站设计,个性化营销与推荐等非常重要。
具体用户行为日志收集手段有两种:
1. 服务器端日志收集,这个方案比较简单,Apache 等几乎所有 Web 服务器都具备日志记录功能,可以记录大部分用户行为日志,开启 Web 服务器的日志记录功能即可。其缺点是可能会出现信息失真,如 IP 地址是代理服务器地址而不是用户真实 IP;多个链接指向同一个页面的情况下无法分辨访问路径等。
2. 客户端浏览器日志收集,浏览器可以收集用户真实的操作行为,因此比服务器日志收集更加精准。其缺点是比较麻烦,需要在页面嵌入特定的 JS 脚本来完成。
服务器性能监控:
收集服务器性能指标,如系统 Load,内存占用,磁盘 IO,网络 IO 等对尽早做出故障预警,及时判断应用状况,防患于未然,将故障扼杀在萌芽时期非常重要。此外根据性能监控数据,运维工程师可以合理安排服务器集群规模,架构师及时改善系统性能及调整系统伸缩性策略
目前网站使用比较广泛的开源性能监控工具是 Ganglia,支持大规模服务器集群,并支持以图形的方式在浏览器展示实时性能曲线。
监控管理:
监控数据采集后,除了用作系统性能评估、集群规模伸缩性预测等,还可以根据实时监控数据进行风险预警,并对服务器进行失效转移,自动负载调整,最大化利用集群所有机器的资源。
报警:
服务器运行正常的情况下,其各项监控指标基本稳定在一个特定水平,如果这些指标超过某个阀值,就意味着系统可能将要出现故障,这时候就需要对相关人员报警,及时采取措施,在故障还未真正发生就将其扼杀在萌芽状态。
监控管理系统可以配置报警阀值和值守人员的联系方式,报警方式除了邮件,即时通讯工具,还可以配置手机短信,语音报警,保证发生报警时,工程师即使在千里之外、夜里睡觉也能及时通知,迅速响应。
自动控制:
1. 自动失效转移∶除了应用程序访问失败时进行失效转移,监控系统也可以在发现故障的情况下主动通知应用,进行失效转移。
2. 自动扩容∶如果因访问压力大而导致服务性能指标下降,监控系统自动触发服务集群扩容。
3. 自动限流:根据监控指标,自动控制访问流量。
监控系统架构:
高可用的价值观:
1. 保持简单,使问题易于发现,快速解决。
2. 目标明确,解决特定环境下的具体问题。
3. 价值回归,成本收益要合理。
高可用故障案例分析:
故障 1:
故障现象:
某应用服务器集群发布后不久就出现多台服务器相继报警,硬盘可用空间低于警戒值,并且很快有服务器宕机。登录到线上服务器,发现 log 文件夹里的文件迅速增加,不断消耗磁盘空间。
原因分析:
这是一个普通的应用服务器集群,不需要存储数据,因此服务器里配置的是一块 100GB 的小硬盘,安装完操作系统、Web 服务器、Java 虚拟机、应用程序后,空闲空间只有几十 GB 了,正常情况下这些磁盘空间足够了,但是该应用的开发人员将 log 输出的 level 全局配置为 Debug。这样一次简单的 Web 请求就会产生大量的 log 文件输出,在高并发的用户请求下,很快就消耗完不多的磁盘空间。
经验教训:
1. 检查 log 配置文件,日志输出级别至少为 Warn,并且检查 log 输出代码调用,调用级别要符合其真实日志级别。
2. 有些开源的第三方组件也会不恰当地输出太多的 Error 日志,需要关闭这些第三方库的日志输出,至于哪些第三方库有问题,只有在遇到问题时才知道。
3. 应用程序自己的日志输出配置和第三方组件日志输出要分别配置。
故障 2:
故障现象:
某应用发布后,数据库 Load 居高不下,远超过正常水平,持续报警。
原因分析:
检查数据库,发现报警是因为某条 SQL 引起的,这条 SQL 是一条简单的有索引的数据查询,不应该引发报警。继续检查,发现这条 SQL 执行频率非常高,远远超过正常水平。追查这条 SQL,发现被网站首页应用调用,首页是被访问最频繁的网页,这条 SQL 被首页调用,也就被频繁执行了。
经验教训:
1. 首页不应该访问数据库,首页需要的数据可以从缓存服务器或者搜索引擎服务器获取。
2. 首页最好是静态的。
故障 3:
故障现象:
某应用服务器不定时地因为响应超时而报警,但是很快又超时解除,恢复正常,如此反复,让运维人员非常苦恼。
原因分析:
程序中某个单例对象(singleton object)中多个方法使用了 synchronized 修饰符,由于 this 对象只有一个,即使执行不同方法,所有的并发请求也都要排队获得这唯一的一把锁。一般情况下,都是一些简单操作,获得锁,迅速完成操作,释放锁,不会引起线程排队。但是某个需要执行远程调用的方法也被加了 synchronized,这个方法只是偶尔会被执行,但是每次执行都需要较长的时间才能完成,这段时间锁被占用,所有的用户线程都要等待,响应超时,这个方法执行完后释放锁,其他线程迅速执行,超时解除。
经验教训:
1. 使用锁操作要谨慎,特别注意在有锁的方法中进行长时间的 IO 操作的时候
2. 针对使用锁的不同场景,使用不同锁对象,而不是简单的在所有方法上都加上 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:
故障现象:
监控发现某个时段内,某些应用突然变慢,内部网络访问延迟非常厉害。
原因分析:
检查发现,该时段内网卡流量也下降,但是没有找到原因。过了一阵子才知道,原来有工程师在线上生产环境进行性能压力测试,占用了大部分交换机带宽。
经验教训:
1. 访问线上生产环境要规范,不小心就会导致大事故
2. 滥用生产环境的一个例子︰网站数据库有专门的 DBA 维护,如果发现数据库存在错误记录,需要进行数据订正,必须走数据订正流程,申请 DBA 协助。于是就有工程师为避免麻烦,直接写一段数据库更新操作的代码,悄悄放到生产环境应用服务器上执行,神不知鬼不觉地订正了数据。但是如果不小心写错了 SQL,后果可想而知。
故障 8:
故障现象:
某应用发布后,数据库 Load 迅速飙升,超过报警值,回滚发布后报警消除。
原因分析:
发现该应用发布后出现大量数据库读操作,而这些数据本来应该从分布式缓存读取。检查缓存,发现数据已经被缓存了。检查代码,发现访问缓存的那行代码被注释掉了。原来工程师在开发的时候,为了测试方便,特意注释掉读取缓存的代码,结果开发完成后忘记把注释去掉,直接提交到代码库被发布到线上环境。
经验教训:
1. 代码提交前使用 diff 命令进行代码比较,确认没有提交不该提交的代码。
2. 加强 code review,代码在正式提交前必须被至少一个其他工程师做过 code review ,并且共同承担因代码引起的故障责任。
故障 9:
故障现象:
某应用更新某功能后,有少量用户投诉无法正常访问该功能,一点击就显示出错信息。
原因分析:
分析这些用户,都是第一次使用该功能,检查代码,发现程序根据历史使用记录构造一个对象,如果该对象为 null,就会导致 NullPointException.
经验教训:
1. 程序在处理一个输入的对象时,如果不能明确该对象是否为空,必须做空指针判断。
2. 程序在调用其他方法时,输入的对象尽量保证不是 null,必要时构造空对象(使用空对象模式)。
评论