写点什么

架构师训练营作业 -20200823

用户头像
caibird1984
关注
发布于: 2020 年 08 月 22 日

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



通常认为,系统不可用还可细分为系统部分不可用及系统整体不可用两种情况。引起系统不可用的原因可能有以下几个方面:



1、硬件设备故障,包括单台服务器的硬件故障,或整体机房的故障等;单台服务器故障通常会使该服务器及服务器所承载的虚拟机无法提供服务,如果出现设备损坏服务器是数据库服务器或者负载均衡服务器等比较重要的节点,可能会使应用整体无法访问;

2、访问量过大,导致系统负载过高,响应时间延长,最终导致后续响应堵塞或超时,使系统处于不可用状态;秒杀、购物节等短时间大并发量的场景,或者遭受DDos攻击等,都会使系统的访问压力激增,进而导致系统无法承受访问压力,最终导致系统不可用;

3、对软件进行升级,由于部分语言或中间件的特性,在进行软件版本更新的时候需要停止服务,替换应用程序包为新版本,之后重新启动服务;在程序的发布过程中,进行软件更新操作的服务器将会变为不可用状态;

4、由于软件设计层面的错误导致系统不可用,例如无法完成整个执行流程的软件,也会使服务不可用。



保障系统的高可用方案包括:

1、硬件层面,在设计系统的部署方案的时候应避免出现单点,即某个应用、中间件等避免出现只部署在一台主机上的情况;在部署的时候同样的功能节点部署至少2份,相同功能的节点尽量不部署在同一台物理机上,避免服务器出现物理故障时影响此角色的所有节点;

2、应对访问量的层面,对于中小型应用可直接在接入层选择硬件防火墙产品,或采购云服务商提供的防火墙产品,可以过滤掉绝大多数攻击性的访问,保证系统安全;对于预计访问量较高的应用,在进行架构设计时应独立进行部署(如采用独立的服务器硬件、独立域名等),避免目标应用由于无法承受访问压力时影响其他系统,导致应用雪崩;此外,可采用具备限流功能的中间件,如Nginx等,在流量高峰时限制进入系统的并发量,对系统进行保护;最后,在软件设计层面增加访问限制,如采用计数器或令牌桶等算法,只允许一部分访问通过,对于剩下的访问直接返回失败结果,保障系统整体的平稳运行;

3、在应对软件升级操作层面,首先在设计系统时尽量将系统拆分为不同模块进行独立开发、部署,例如电商网站可以将商品、订单、进销存等模块分别部署在不同的服务器中,这样在某个模块更新时不会对其它模块造成影响;此外,进行更新操作的时候,可以考虑配合反向代理中间件或采用网关模式,实现灰度发布,即一次只更新一部分硬件为新版本,并导入少量流量,通过日志等方式验证新版本的功能是否符合预期,服务器硬件指标是否符合预期等,验证正常再部署下一批服务器,直到所有服务器的应用程序都更新完毕,认为整个发布流程完成;这样做首先可以避免由于应用程序更新需要重启导致的应用不可用问题,也可以首先在一少部分服务器中首先对应用的功能进行验证,避免由于软件开发层面的错误影响到整个系统的稳定性;此外,由于每次只有一部分服务器进行重启,配合反向代理等方式,可以使用户近似感知不到重新发布时的重启操作带来的影响,达到系统平滑上线的效果,避免应用由于发布导致不可用问题。



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



使用JAVA开发此密码验证函数,代码如下:



首先定义进行密码加密的工具类:

/**

* 处理加解密的工具类

* @author xiaofy

*/

public class EncryptUtil {



/**

* 对字符串进行MD5加密

* @param orgString 原始字符串

* @return 加密后的字符串

* @throws Exception 加密失败时抛出异常

*/

public static String md5(String orgString) throws Exception {

MessageDigest m = MessageDigest.getInstance("MD5");

m.update(orgString.getBytes("UTF8"));

byte s[] = m.digest();

String result = "";

for (int i = 0; i < s.length; i++) {

result += Integer.toHexString((0x000000FF & s[i]) | 0xFFFFFF00).substring(6);

}

return result;

}

}



主函数如下:

/**

* 密码验证

* @author xiaofy

*/

public class PwdSampleMain {

/**

* 左侧撒盐的内容

*/

private static final String PWD_SALT_LEFT = "GEEK2020";

/*

* 右侧撒盐的内容

*/

private static final String PWD_SALT_RIGHT = "0823BANG";

/**

* 撒盐时的分割字符

*/

private static final String PWD_SPLIT = "-";



public static void main(String[] args) {

//用户名

String userId = "abc";

//明文密码

String pwd = "123";

String errorEnctrypted = "asdfgeafsd";

boolean passed = checkPW(userId, pwd, errorEnctrypted);

System.out.println("预期错误的密文,验证结果:" + passed);

String correctEnctrypted = encryptPwd(userId, pwd);

passed = checkPW(userId, pwd, correctEnctrypted);

System.out.println("预期正确的密文,验证结果:" + passed);

}



/**

* 验证密码正确性

* @param userId 用户ID

* @param orgPwd 明文密码

* @param encyptedPwd 加密后的密码

* @return 密码验证是否正确

*/

private static boolean checkPW(String userId, String orgPwd, String encyptedPwd) {

String encrtypted = encryptPwd(userId, orgPwd);

if(!encrtypted.equals(encyptedPwd)) {

return false;

}

return true;

}

/**

* 密码加密,使用MD5算法+撒盐并再次MD5的方式处理

* 实际应用还应考虑增加时间戳等其他安全校验

* @param userId 用户ID

* @param pwd 用户密码

* @return 加密后的密码

*/

private static String encryptPwd(String userId, String pwd) {

try {

String encryptedOrgPwd = EncryptUtil.md5(pwd);

String tempString = PWDSALTLEFT + PWDSPLIT + userId + PWDSPLIT + encryptedOrgPwd + PWDSPLIT + PWDSALT_RIGHT;

String result = EncryptUtil.md5(tempString);

return result;

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

}



执行结果如下图所示:



用户头像

caibird1984

关注

还未添加个人签名 2018.04.28 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营作业-20200823