应用架构种类 - 微服务架构

一、What

微服务架构是指,组成一个整体系统是由许多不同的子系统构成,这些子系统独立存在,而又会互相调用。一个典型的微服务系统有以下几个组成部分:

  1. 注册中心
  2. 配置中心
  3. 网关
  4. 各个独立的子模块

二、使用场景

数据量千万级别,访问量千万级别

三、优势

  1. 复用性,消除代码拷贝
  2. 专注性,防止复杂性扩散
  3. 解耦和,消除公共库耦合
  4. 高质量,SQL稳定有保障
  5. 易扩展,消除数据库耦合
  6. 高效率,调用方研发效率提升

四、粒度

  1. 统一服务层
  2. 一个子业务一个服务
  3. 一个数据库一个服务
  4. 一个接口一个服务

最佳实践是:一个子业务一个服务

五、高可用

  1. 怎么知道是否高可用:关闭线上任何一台机器,线上服务都不会down掉
  2. 方法论:集群化(冗余) + 故障自动转移
  3. 具体步骤:
    a. 反向代理层:反向代理冗余,VIP + Keepalived
    b. 站点应用层:站点应用层冗余,nginx自动感知
    c. 服务层:服务层冗余,服务连接池自动感知服务层的存活
    d. 缓存层:缓存层冗余,缓存客户端 + 缓存层哨兵发现机制
    e. 数据库读:数据库Slave节点冗余,数据库连接池自动发现数据库是否可用
    f. 数据库写:数据库Master节点冗余,VIP + Keepalived

六、高并发

  1. What:通过设计保证系统能够同时并行处理很多请求。概念有:响应时间,QPS, TPS,并发用户数等
  2. How:垂直扩展(scale up)、水平扩展(scale out)
  3. 具体步骤:
    a. 反向代理层:DNS轮询
    b. 站点应用层:nginx反向代理
    c. 服务层:服务层连接池
    d. 数据层:数据的水平切分(按照数据范围,或者数据哈希的方式来进行水平扩展)

七、负载均衡

  1. What:它通常是指,将请求/数据【均匀】分摊到多个操作单元上执行

  2. How:
    同构环境下,重点在于 “均匀”
    异构环境下,重点在于 “负载与能力匹配”

  3. 同构环境下,负载均衡的具体步骤:
    在同构环境下,负载均衡的实现基本上不需要额外的支持,在实现高并发、高可用的基础设施中:
    a. 客户端到反向代理由dns轮询完成;
    b. 反向代理到站点由nginx来完成;
    c. 站点到服务由连接池完成;
    d. 服务到数据层也是由数据层框架的客户端提供的连接池来完成。
    因为是同构的,所以均衡策略是简单,轮询,随机的方式都可以实现。

  4. 异构环境下,负载均衡的具体步骤:
    静态权重:
    What:静态权重和同构负载均衡策略几乎一样,假设三台机器,如果配置负载为1:1:1,那么就是负载均衡,所以可以把负载均衡看成是静态权重的一个特例;
    优点:快速,简单
    劣势:是静态的,无法实时变化,过载保护也实施不了

    动态权重:
    What:根据服务的处理能力动态的变化其权重,权重的大小体现了负载路由到这台机器的概率;
    How:
    a. 识别服务处理能力:成功返回说明能力可以,超时说明不能承受当前流量;
    b. 设计动态权重:成功加小分,失败减大分;
    优点:可以动态的体现异构环境下服务的处理能力,并分配与之能力想匹配的负载
    劣势:负载,需要额外开发

  5. 过载保护:
    What:当服务过载的时候很有可能造成我们说的“雪崩”,服务的流量到达处理能力的峰值,随之再增加流量,处理成功的请求直线下降,服务进入不可用的状态。
    How:
    a. 静态方式:
    静态的过载就是设定一个流量阈值,超过这个阈值就不会再有多的请求了。

    b. 动态方式:
    动态的过载保护和动态的负载均衡策略是相似的。

    1. 连接代表服务,分值代表处理能力
    2. 处理成功加小分,失败减大分
    3. 临界边界(比如连续失败1次)喘口小气(减小流量,持续时间短)
    4. 死亡状态(比如连续失败2次或以上)喘口大气(没有流量,持续时间长)
      如果过载保护实施的是某一节点,则本来由该节点处理的请求转发到其它节点;如果所有节点(即整个集群)都是过载状态,则丢弃请求;所以说集群的过载保护策略和某一节点的策略是不同的,一个是丢,一个是转。

连接池

  1. What:相对于短连接(使用后即关闭),连接池是一个维护如果若干个连接的一个池子,需要用的时候从池子里去,用完之后放入池子,池子里的连接是可以重复使用的。
  2. Why:如果每次都是建立连接,使用连接收发请求,关闭连接,当遇到高并发的场景,建立连接和关闭连接会成为瓶颈。
  3. How:
    a. 核心接口:初始化;拿连接;放回连接
/*数据结构很简单,总共是两个数组,一个是表示所有真正连接的数组,还有一个是第三行出现的一个lock数组,
lock数组的作用即是表征下标对应的连接的状态,当前是否被占用。*/
init() {
    
    
	for i to N {
    
    
		Array DBClientConnection[i] = new()
		Array DBClientConnection[i] -> connect()
		Array lock[i] = 0
	}
}

/*拿连接的过程也非常简单,首先就是遍历锁数组,如果为0,那么设置锁为1,返回连接即可*/
GetConnection() {
    
    
	for i to N {
    
    
		if(Array lock[i] == 0) {
    
    
			Array lock[i] = 1
			return Array DBClientConnection[i]
		}
	}
}

/*放回连接只需要把lock设置为0就可以了*/
FreeConnection(c) {
    
    
	for i to N {
    
    
		if(Array DBClientConnection[i] == c) {
    
    
			Array lock[i] = 0
		}
	}
}

b. 其他考虑因素:
1) 复杂度还可以优化为O(1)
2) 连接的可用性检测,如果连接失效了,需要重新连接,并替换原来的连接
3) 如果下游服务器故障,失效连接要剔除掉,以实现故障的自动转移,从而实现高可用
4) 如果下游有新增的节点,需要动态扩充连接池,以实现服务自动发现,从而实现扩展性。可以通过监控配置文件的方式(比如MD5,),如果改变则重新载入;或者通过配置中心回调
5) 要保证连接选取的概率,实现负载均衡。可以通过轮询,随机,静态权重,动态权重等方式实现基于连接池的负载均衡

参考

  1. 高可用:https://www.jianshu.com/p/dcb73d907342
  2. 高并发:https://www.jianshu.com/p/be66a52d2b9b
  3. 负载均衡1:https://www.jianshu.com/p/41f437542ffc
  4. 负载均衡2:https://blog.csdn.net/Sunsscode/article/details/107693303

猜你喜欢

转载自blog.csdn.net/hudmhacker/article/details/108199056