写点什么

架构师训练营第十一周作业

用户头像
李日盛
关注
发布于: 2021 年 01 月 02 日

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


从软件到硬件的层面来看,导致系统不可用的原因有如下几点:

  • 软件本身的 bug

  • 软件的更新发布

  • 高并发请求导致软件无响应或者崩溃

  • 硬件故障

  • 网络故障或者攻击

  • 外部自然灾害


针对上述的问题,我们可以分别制定不同的保障方案:

  1. 软件本身的 bug:

分两方面考虑。第一从开发本身入手,通过制定合理的开发流程和规范,尽可能减少 bug 出现的概率。如常见的 code review,单元测试,和 SRE 规范等等,都可以有效减低 bug 出现的频率。第二是从 bug 的影响范围入手,构建快速的问题发现,定位和响应的运维平台,可以有效降低 bug 对业务产生的不良影响。如快速回滚,全链路追踪,预警平台等,都是比较有效的手段。

  1. 软件的更新发布

如果是服务器只有一个节点,软件的更新发布肯定是导致系统的不可用的。所以为了规避软件更新发布导致的系统不可用情况,常用的手段就是基于集群进行部署,然后通过滚动升级的方式来达到用户端的操作无感知。如果软件的规模足够大,还可以采用灰度发布这样的手段,通过流量切换的方式验证更新的功能,降低业务的影响。

  1. 高并发请求导致软件无响应或者崩溃:

在高并发的情况下面,一旦超过了软件正常的承受范围,软件就往往会表现出无响应甚至崩溃的情况。由于单个节点的处理能力总有上限,所以如果都要一个节点抗住所以的并发请求是不切实际的。通常是采用分流,截断和异步三种手段来解决这个问题。首先是分流,通过负载均衡的技术,将请求分发到集群的不同服务器,降低每个单点服务器的请求压力。其次在服务器节点有限的情况下面,通过熔断和断路器的机制,拒绝部分请求,保证系统总是可用的,防止被压垮。最后就是异步话,通常就是引入异步处理机制,比如消息队列,异步处理线程等,对瞬间高的并发流量进行削峰填谷,保证系统工作问题。

  1. 硬件故障:

硬件故障是常见的故障。硬件的损坏,比如内存错误,磁盘错误,网卡错误等等,在系统的运行过程中都是有可能会出现的。解决这个问题的基本思路就是备份。比如存储基于 RAID 进行,分布式存储 HDFS,服务节点集群化等等,都是通过备份的思路解决该问题。

  1. 网络故障或者攻击

网络故障或者攻击,属于我们只能被动防御的部分,因为我们无法从源头去解决这些问题。针对网络故障,比较常用的手段是,多运营商网络接入,如果其中有一个运营商的线路出现问题,可以通过 DNS 切换的方式,保证网络接入的高可用。针对网络攻击,一般的手段就是防火墙,包括硬件防火墙和软件防火墙,过滤掉非法的伪造的情况,保证正常客户可以将请求发送到服务器。

  1. 外部自然灾害

外部自然灾害通常指地震,洪水,战争等不可抗力,通常影响的访问就是某一个数据中心。对于大型的互联网应用来说,为了保证严苛的高可用要求,通常会选择两地三中心的方案对应用进行部署,然后通过其他的技术手段对数据进行跨机房的同步。


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


参考的实现仓库链接见这里:https://gitee.com/easonleeone/art-home-work/tree/master/week7/src/main/java/week11


核心的思路就是通过 salt+密码进行 sha256 散列加密,生成加密后的字符串存储到数据,在登录验证的时候,通过同样的加密逻辑,对用户输入的密码进行加密,然后比对两者是否一致,完成登录验证。


核心代码如下:

package week11.impl;
import org.apache.commons.codec.binary.StringUtils;import week11.EncryptUtil;import week11.LoginService;
/** * @Description: TODO * @Author: easonlee * @CreateDate 2021/1/2 */public class LoginServiceImpl implements LoginService {
/*** * 公共的随机加密盐 */ private static final String COMMON_SALT = "5Ilz81VJvjb1Lg8u";
@Override public boolean isClientExist(String clientId) { // TODO 这里应该是从数据库里面获取对应的用户信息,演示代码不做实现 return "rslee".equals(clientId); }
@Override public boolean checkPW(String clientId, String password, String secretPassword) { if(isClientExist(clientId) && password != null){ // 基于salt+密码,后算sha256的值进行比对 String encodePwd = EncryptUtil.SHA256(COMMON_SALT+password);// System.out.println("encodePwd:"+encodePwd); return encodePwd.equals(secretPassword); } return false; }}
复制代码


加密用到 util 类如下:

package week11;
import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;
/** * @Description: TODO * @Author: easonlee * @CreateDate 2019-07-05 */public class EncryptUtil {
/** * 传入文本内容,返回 SHA-256 串 * * @param strText * @return */ public static String SHA256(final String strText) { return SHA(strText, "SHA-256"); }
/** * 传入文本内容,返回 SHA-512 串 * * @param strText * @return */ public static String SHA512(final String strText) { return SHA(strText, "SHA-512"); }
/** * 字符串 SHA 加密 * * @param strText * @return */ private static String SHA(final String strText, final String strType) { // 返回值 String strResult = null;
// 是否是有效字符串 if (strText != null && strText.length() > 0) { try { // SHA 加密开始 // 创建加密对象 并傳入加密類型 MessageDigest messageDigest = MessageDigest.getInstance(strType); // 传入要加密的字符串 messageDigest.update(strText.getBytes()); // 得到 byte 類型结果 byte byteBuffer[] = messageDigest.digest();
// 將 byte 轉換爲 string StringBuffer strHexString = new StringBuffer(); // 遍歷 byte buffer for (int i = 0; i < byteBuffer.length; i++) { String hex = Integer.toHexString(0xff & byteBuffer[i]); if (hex.length() == 1) { strHexString.append('0'); } strHexString.append(hex); } // 得到返回結果 strResult = strHexString.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } }
return strResult; }}
复制代码


测试代码如下:

package week11;
import org.junit.Test;import week11.impl.LoginServiceImpl;
import static org.junit.Assert.assertTrue;
/** * @Description: 登录服务单元测试 * @Author: easonlee * @CreateDate 2021/1/2 */public class LoginServiceTest {
@Test public void testCheckPW(){ String clientId = "rslee"; String password = "test123456"; String secretPassword = "83309a61a62712878c1b4ca0862cd055cc7f8d121cb4672e3f529307325a4b16"; assertTrue("登录用户密码测试",new LoginServiceImpl().checkPW(clientId,password,secretPassword)); }}
复制代码


发布于: 2021 年 01 月 02 日阅读数: 25
用户头像

李日盛

关注

好架构=低成本+可实现 2018.01.22 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营第十一周作业