1. 导致系统不可用的原因有哪些?保障系统稳定高可用的方案有哪些?请分别列举并简述。
从软件到硬件的层面来看,导致系统不可用的原因有如下几点:
软件本身的 bug
软件的更新发布
高并发请求导致软件无响应或者崩溃
硬件故障
网络故障或者攻击
外部自然灾害
针对上述的问题,我们可以分别制定不同的保障方案:
软件本身的 bug:
分两方面考虑。第一从开发本身入手,通过制定合理的开发流程和规范,尽可能减少 bug 出现的概率。如常见的 code review,单元测试,和 SRE 规范等等,都可以有效减低 bug 出现的频率。第二是从 bug 的影响范围入手,构建快速的问题发现,定位和响应的运维平台,可以有效降低 bug 对业务产生的不良影响。如快速回滚,全链路追踪,预警平台等,都是比较有效的手段。
软件的更新发布
如果是服务器只有一个节点,软件的更新发布肯定是导致系统的不可用的。所以为了规避软件更新发布导致的系统不可用情况,常用的手段就是基于集群进行部署,然后通过滚动升级的方式来达到用户端的操作无感知。如果软件的规模足够大,还可以采用灰度发布这样的手段,通过流量切换的方式验证更新的功能,降低业务的影响。
高并发请求导致软件无响应或者崩溃:
在高并发的情况下面,一旦超过了软件正常的承受范围,软件就往往会表现出无响应甚至崩溃的情况。由于单个节点的处理能力总有上限,所以如果都要一个节点抗住所以的并发请求是不切实际的。通常是采用分流,截断和异步三种手段来解决这个问题。首先是分流,通过负载均衡的技术,将请求分发到集群的不同服务器,降低每个单点服务器的请求压力。其次在服务器节点有限的情况下面,通过熔断和断路器的机制,拒绝部分请求,保证系统总是可用的,防止被压垮。最后就是异步话,通常就是引入异步处理机制,比如消息队列,异步处理线程等,对瞬间高的并发流量进行削峰填谷,保证系统工作问题。
硬件故障:
硬件故障是常见的故障。硬件的损坏,比如内存错误,磁盘错误,网卡错误等等,在系统的运行过程中都是有可能会出现的。解决这个问题的基本思路就是备份。比如存储基于 RAID 进行,分布式存储 HDFS,服务节点集群化等等,都是通过备份的思路解决该问题。
网络故障或者攻击
网络故障或者攻击,属于我们只能被动防御的部分,因为我们无法从源头去解决这些问题。针对网络故障,比较常用的手段是,多运营商网络接入,如果其中有一个运营商的线路出现问题,可以通过 DNS 切换的方式,保证网络接入的高可用。针对网络攻击,一般的手段就是防火墙,包括硬件防火墙和软件防火墙,过滤掉非法的伪造的情况,保证正常客户可以将请求发送到服务器。
外部自然灾害
外部自然灾害通常指地震,洪水,战争等不可抗力,通常影响的访问就是某一个数据中心。对于大型的互联网应用来说,为了保证严苛的高可用要求,通常会选择两地三中心的方案对应用进行部署,然后通过其他的技术手段对数据进行跨机房的同步。
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));
}
}
复制代码
评论