写点什么

架构师训练营 1 期第 11 周:安全稳定 - 作业

用户头像
piercebn
关注
发布于: 2020 年 12 月 06 日

一、导致系统不可用的原因有哪些?保障系统稳定高可用的方案有哪些?请分别列举并简述。

引起故障导致系统不可用的原因

  • 硬件故障:内存或硬盘坏了,服务器宕机,交换机故障,光纤网络故障

  • 软件bug:代码逻辑问题导致的系统异常

  • 系统发布:配置参数错误,数据库更新不匹配

  • 并发压力:高并发情况下,大量用户请求到达服务器,产生大量的用户线程,在争用资源,最后把资源耗尽,导致系统崩溃

  • 网络攻击:恶意攻击破坏系统,如SQL注入删除表,DDoS攻击消耗网络带宽和系统资源,导致系统瘫痪

  • 外部灾害:光纤网络故障,机房火灾

保障系统稳定高可用的方案

解耦:系统的耦合度越高,它们的关系越复杂,任何一个功能模块的问题,都可能牵连到其他功能模块,最后发生级联反应。降低代码模块的耦合度,有利于提升系统的可用性

  • 高内聚、低耦合的组件设计原则:6种设计原则

  • 组件内聚原则:复用发布等同原则,共同封闭原则,共同复用原则

  • 组件耦合原则:无循环依赖原则,稳定依赖原则,稳定抽象原则

  • 面向对象基本设计原则:5种设计原则

  • 开闭原则,依赖倒置原则,里氏替换原则,单一职责原则,接口隔离原则

  • 面向对象设计模式:常用的23种设计模式

  • 领域驱动设计建模:通过梳理业务流程分解活动的方法,确定活动的业务边界,从而识别子域和界限上下文,也就确定微服务边界

隔离:通过解耦以后的模块业务子系统,如果能在物理上进一步的进行隔离,保证某个子系统内部的问题,不会在物理上扩散到其他子系统里去,也可以提高系统的可用性

  • 业务与子系统隔离

  • 将电子商务系统分为买家子系统(浏览商品,创建订单)和卖家子系统(发布商品,处理订单),物理上进行隔离部署,将卖家和买家系统业务耦合关系分解清楚,任一方子系统故障不会影响其他子系统的正常运行

  • 依赖的基础是解耦,实现的手段是物理上的隔离

  • 微服务与中台架构

  • 更细粒度的实现隔离,通过微服务把子系统内部的服务进行拆分,部署到不同的服务器,任何一个服务器的故障,只会影响调用这个服务的相关业务,如果调用这个服务的相关业务能够做到对这个服务的容灾备份,某些微服务的故障不会影响到整个系统,整个系统还是保持高可用

  • 生产者消费者隔离

  • 生产者和消费者部署在不同的服务器上,通过消息队列隔离开,生产者发布消息,消费者从消息队列获取消息进行处理,当生产者故障时,消费者还可以处理其他消息,消费者故障时,生产者还可以继续发布消息,消费者重新恢复正常以后,继续消费消息队列中的消息就可以了,不会引起两方的同步故障,系统看起来在某种程度上是可用的

  • 虚拟机与容器隔离

  • 在虚拟机层面把不同的业务隔离开来,不同的微服务部署在不同的虚拟机进程中,其中一个虚拟机内部的服务出现bug,如内存泄漏导致虚拟机进程退出,不会影响其他虚拟机内部服务的正常运行,使服务的故障局限在一个虚拟机或容器的内部,通过隔离的手段,使故障隔离开,提升系统的可用性

异步:通过异步的方式提高可用性

  • 多线程编程:一个线程出现故障,其他的线程不会受到影响

  • 反应式编程:实现微服务的异步调用,被调用者的处理阻塞不会影响调用者的继续执行

  • 异步通信网络编程:不会因为网络的阻塞,降低系统的可用性

  • 事件驱动异步架构:利用消息队列,实现生产者和消费者的隔离,一个消费者故障,不会影响生产者和其他消费者

备份

  • 集群设计

  • 要保证应用的高可用,就要部署在多台服务器上,也就是负载均衡的集群方案,主要目标是提升性能,同时也可以提升可用性,当某台服务器宕机不可用,请求可以分发给其他的服务器,系统服务还是可用的,通过服务器的互为备份的方式提高可用性

  • 数据库复制

  • 数据库做主从复制,主主复制

失效转移(Failover)

  • 当系统有备份的时候,当某个服务器宕机的时候,需要把对它的访问请求,转移到其他的服务器上,就是所谓的失效转移,失效转移也是高可用的一个重要的架构策略

  • 如数据库主主复制中的失效转移,程序运行过程中不能同时访问两台主服务器,这样可能产生数据冲突(导致分布式系统脑裂问题),主主复制主要是提高数据写的高可用,当有一台主服务器宕机的时候,应用程序可以将写操作发送到另一台服务器,如何识别主服务器失效,何时进行主服务器切换,可以通过Zookeeper的方法去选主,所有的应用程序通过Zookeeper去访问主服务器

幂等

  • 应用调用服务失败后,会将调用请求重新发送到其他服务器,但是这个失败可能是虚假的失败。比如服务已经处理成功,但是因为网络故障应用没有收到响应,这时应用重新提交请求就导致服务重复调用,如果这个服务是一个转账操作,就会产生严重后果。

  • 服务重复调用有时候是无法避免的,必须保证服务重复调用和调用一次产生的结果相同, 即服务具有幂等性。有些服务天然具有幂等性,比如将用户性别设置为男性,不管设置多少次,结果都一样。但是对于交易等操作,问题就会比较复杂,需要通过交易编号等信息进行服务调用有效性校验,只有有效的操作才继续执行。

事务补偿

  • 传统事务的 ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性 (Durability)

  • 分布式事务的 BASE :基本可用(Basic Availability )、 软状态(Soft-state)、 最终一致性(Eventual consistency)

  • 事务补偿:通过执行业务逻辑逆操作,使事务回滚到事务前状态。通过事务补偿的方式,可以不使用较为复杂的分布式系统的技术方案,也可以实现事务的一致性

重试

  • 远程服务可能会由于线程阻塞、垃圾回收或者网络抖动,而无法及时返还响应,调用者 可以通过重试的方式修复单次调用的故障。

  • 上游调用者超时时间要大于下游调用者超时时间之和。

熔断:微服务中比较重要的架构策略

  • 当某个服务出现故障,响应延迟或者失败率增加,继续调用这个服务会导致调用者请求 阻塞,资源消耗增加,进而出现服务级联失效,这种情况下使用断路器阻断对故障服务的调用。

  • 断路器就是通过监控服务响应的失败率或延迟率,决定对服务调用是否打开

  • 断路器三种状态:关闭,打开,半开

限流

  • 在高并发场景下,如果系统的访问量超过了系统的承受能力,可以通过限流对系统进行保护。限流是指对进入系统的用户请求进行流量限制,如果访问量超过了系统的最大处理能力,就会丢弃一部分的用户请求,保证整个系统可用,保证大部分用户是可以访问系统的。这样虽然有一部分用户的请求被丢弃,产生了部分不可用,但还是好过整个系统崩溃,所有的用户都不可用要好。

  • 限流的几种算法(固定的限流算法)

  • 计数器算法(固定窗口,滑动窗口)

  • 固定窗口:使用计数器在周期内累加访问次数,当达到设定的限流值时,触发限流策略。下一个周期开始时,进行清零,重新计数。

  • 滑动窗口:将时间周期分为N个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期。

  • 令牌桶算法

  • 以固定的速度向令牌桶中增加令牌,直到令牌桶满,请求到达时向令牌桶请求令牌,如获取到令牌则通过请求,否则触发限流策略

  • 漏桶算法

  • 访问请求到达时直接放入漏桶,如当前容量已达到限流值,则进行丢弃。漏桶以固定的速率进行释放访问请求,直到漏桶为空。漏桶算法限流是最均匀的。

  • 自适应限流算法(使流量和系统的处理能力自动适应自动匹配)

  • 它根据实时的采集当前的系统性能指标,根据性能指标去控制限流器进入的流量,当流量多的时候系统变慢,采集到这些指标后,根据控制算法去控制限流器把流量降低,流量降低后,指标变好,然后可以让限流器再多放些流量进来,这些指标可能会变差,通过控制算法不断的调节震荡,达到某个平衡值,使请求性能达到一个最优值,这个最优值不需要提前去评估或设计,它是系统自动去适应出来的,保证系统的高可用

降级:保护系统除了限流策略还有降级策略,限流是控制流量进入,丢弃部分请求保护系统,降级是关闭部分非核心功能,以此来节约系统资源,降低系统的访问压力或负载压力

  • 有一些系统功能是非核心的,但是它也给系统产生了非常大的压力,比如说在电商系统中有确认收货这个功能,即便我们不去确认收货,系统也会超时自动确认收货。

  • 但实际上确认收货这个操作是一个非常重的操作,因为它会对数据库产生很大的压力:它要进行更改订单状态,完成支付确认,并进行评价等一系列操作。如果在系统高并发 的时候去完成这些操作,那么会对系统雪上加霜,使系统的处理能力更加恶化。

  • 解决办法就是在系统高并发的时候,比如说像淘宝双 11 的时候,当天可能整天系统都处于一种极限的高并发访问压力之下,这时候就可以将确认收货、评价这些非核心的功能 关闭,将宝贵的系统资源留下来,给正在购物的人,让他们去完成交易。

异地多活

  • 如果整个数据中心都不可用,比如说数据中心所在城市遭遇了地震,机房遭遇了火灾或者停电,这样的话,不管我们的设计和系统多么的高可用,系统依然是不可用的。

  • 为了解决这个问题,同时也为了提高系统的处理能力和改善用户体验,很多大型互联网应用都采用了异地多活的多机房架构策略,也就是说将数据中心分布在多个不同地点的机房里,这些机房都可以对外提供服务,用户可以连接任何一个机房进行访问,这样每个机房都可以提供完整的系统服务,即使某一个机房不可使用,系统也不会宕机,依然保持可用。

  • 异地多活是高可用架构的最高形式,也是投入成本最多,部署难度最大的一种高可用架构方案。

  • 异地多活的难点是每个数据中心如何保证数据的一致性。

二、请用你熟悉的编程语言写一个用户密码验证函数,Boolean checkPW(String 用户 ID,String 密码明文,String 密码密文),返回密码是否正确 boolean 值,密码加密算法使用你认为合适的加密算法。

package com.test;

import java.security.MessageDigest;

public class PasswordCheck {
/***
* 根据指定算法(MD5加密,SHA1加密 )生成单向数据加密
* @param 待加密字符串
* @return 返回加密后的编码
*/
public static String mdEncode(String inStr, String algorithm) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance(algorithm);
} catch (Exception e) {
System.out.println(e.toString());
e.printStackTrace();
return "";
}
StringBuffer hexValue = new StringBuffer();
try {
byte[] byteArray = inStr.getBytes("UTF-8");
byte[] mdBytes = messageDigest.digest(byteArray);
for (int i = 0; i < mdBytes.length; i++) {
int val = ((int) mdBytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
}catch (Exception e){
System.out.println(e.toString());
e.printStackTrace();
return "";
}
return hexValue.toString();
}

public static Boolean checkPW(String userId, String password, String cipherPassword, String algorithm) {
String mdString = mdEncode(password, algorithm);
System.out.println(algorithm + " : " + mdString);
if(mdString.equals(cipherPassword)){
return true;
}
return false;
}

public static void main(String[] args) {
String userId = "zhangsan";
String password = "123456";
String cipherPasswordMD5 = "e10adc3949ba59abbe56e057f20f883e";
String cipherPasswordSHA1 = "7c4a8d09ca3762af61e59520943dc26494f8941b";
Boolean ret = false;
ret = checkPW(userId, password, cipherPasswordMD5, "MD5");
ret = checkPW(userId, password, cipherPasswordSHA1, "SHA1");
}
}



发布于: 2020 年 12 月 06 日阅读数: 17
用户头像

piercebn

关注

还未添加个人签名 2019.07.24 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营 1 期第 11 周:安全稳定 - 作业