【个人技术经验及开发技巧分享】 【个人技术经验及开发技巧分享】
首页
  • 操作系统初识
  • JAVA基础
  • JVM
  • 开发框架
  • Redis
  • Zookeeper
  • 消息中间件
  • 持久化
  • 算法
  • 网络
  • 系统架构
  • 并发编程
  • 框架
  • 开发杂货
  • 线上排查
  • 技巧备忘
  • 部署指南
  • 版本管理
  • 工作流程
  • 发版流程
  • 友情链接
  • 网站备忘
  • 在线工具
  • 学习
  • 各种云
  • 应用下载

Louis

首页
  • 操作系统初识
  • JAVA基础
  • JVM
  • 开发框架
  • Redis
  • Zookeeper
  • 消息中间件
  • 持久化
  • 算法
  • 网络
  • 系统架构
  • 并发编程
  • 框架
  • 开发杂货
  • 线上排查
  • 技巧备忘
  • 部署指南
  • 版本管理
  • 工作流程
  • 发版流程
  • 友情链接
  • 网站备忘
  • 在线工具
  • 学习
  • 各种云
  • 应用下载
  • 操作系统初识

  • JAVA基础

  • JVM

  • 开发框架

  • Redis

  • Zookeeper

  • 消息中间件

  • 持久化

  • 算法

  • 网络

  • 系统架构

    • 架构设计
      • 前言
      • 1 高性能
        • 1.1 服务拆分
        • 1.2 缓存
        • 1.3 消息队列解耦削峰
        • 1.4 负载均衡
        • 1.5 读写分离&分库分表
        • 1.5.1 读写分离会带来的问题
        • 1.5.2 主从复制原理
        • 1.5.3 分库分表带来的问题
        • 1.5.4 分库分表后,数据怎么迁移?
      • 2 高可用
        • 2.1 衡量高可用
        • 2.2 硬件层面保证高可用
        • 2.2.1 灾备设计
        • 2.2.2 异地多活
        • 2.2.3 灾备到异地多活的演变过程
        • 2.2.3.1 同城灾备
        • 2.2.3.2 同城双活
        • 2.2.3.3 两地三中心
        • 2.2.3.4 异地双活
        • 2.2.3.5 异地多活
        • 2.3 系统及代码层面保证高可用
        • 2.3.1 集群
        • 2.3.2 版本可回滚
        • 2.3.3 超时重试
        • 2.3.4 降级
        • 2.3.5 熔断
        • 2.3.6 限流
        • 2.3.6.1 常见限流方案
        • 1.计数器法
        • 2.漏桶算法
        • 3.令牌桶算法(推荐)
        • 2.3.6.2 Guava限流工具类
        • 1.说明
        • 2.代码示例
    • 分布式
    • 机器评估
    • TP50、TP90、TP99
  • 学习笔记
  • 系统架构
luoxiaofeng
2022-04-28
目录

架构设计

# 系统架构设计

# 前言

一个好的软件架构,应该遵循高性能、高可用、易扩展 3 大原则,其中 高可用 在系统规模变得越来越大时,变得尤为重要。

# 1 高性能

# 1.1 服务拆分

按功能纬度、读写维度(如提供一个专门的查询服务给外部调用,数据库配置读库)

# 1.2 缓存

浏览器缓存、cdn缓存、应用层缓存(redis、内存缓存等)

# 1.3 消息队列解耦削峰

运单收发到派签消息通过RabbitMQ发送

# 1.4 负载均衡

常见的负载均衡系统包括3种:

DNS负载均衡:一般用来实现地理级别的均衡。

硬件负载均衡:通过独立的硬件设备,比如F5,实现负载均衡功能(硬件价格一般较贵)。

软件负载均衡:通过软件的方式,比如Nginx,实现负载均衡功能。

# 1.5 读写分离&分库分表

# 1.5.1 读写分离会带来的问题

主库和从库数据同步存在延迟,写完数据马上读取时可能读不到最新数据。

解决方法:

1.强制将读请求路由到主库处理

比如 Sharding-JDBC,可以通过 HintManager 分片键值管理器强制使用主库。

HintManager hitManager = HintManager.getInstance();
hintManager.setMasterRouteOnly();
// 继续JDBC操作
1
2
3

2.延迟读取

这种方式不太合适 ...

# 1.5.2 主从复制原理

1.主库将数据变化写入binlog。

2.从库创建一个I/O线程向主库请求更新的binlog。

3.主库创建一个线程发送binlog,从库I/O线程负责接收。

4.从库I/O线程接收的binlog写入到relay log中。

5.从库的SQL线程读取relay log同步数据到本地(也就是再执行一遍SQL)。

# 1.5.3 分库分表带来的问题

1.join操作:表分布到不同数据库,导致无法进行连表操作。

2.事务问题:操作不同数据库的表,自带的事务无法支持。

3.分布式id:自增主键等方式无法使用,需引入分布式ID。

# 1.5.4 分库分表后,数据怎么迁移?

使用数据库同步工具 Canal 做增量数据迁移(还是依赖 binlog,开发和维护成本较低)。

# 2 高可用

# 2.1 衡量高可用

高可用通常通常用两个指标来衡量。

平均故障时间间隔:表示两次故障的时间间隔,也就是系统正常运行平均时间。这个时间越长越稳定。

故障恢复时间:系统发生故障后的恢复时间。这个时间越短,对用户影响越小。

可用性跟这两个指标之间的关系为:

可用性 = 平均故障时间间隔 / (平均故障时间间隔 + 故障恢复时间) * 100%

这个公式得出的结果是一个 比例 ,通常我们会用 N 个 9 来描述一个系统的可用性。

系统可用性 年故障时间 日故障时间
90%(1个9) 36.5天 2.4小时
99%(2个9) 3.65天 14分钟
99.9%(3个9) 8小时 86秒
99.99%(4个9) 52分钟 8.6秒
99.999%(5个9) 5分钟 0.86秒
99.9999%(6个9) 32秒 86毫秒

# 2.2 硬件层面保证高可用

# 2.2.1 灾备设计

灾备设计 = 容灾 + 备份

容灾:建立两个相同的系统。当其中一个系统出问题时,可以直接切换另一个系统使用。

备份:将系统产生的重要数据进行备份。

# 2.2.2 异地多活

将服务部署到异地,并且多地服务能同时对外提供服务。

异地多活主要应对突发情况,如火灾、地震、人为灾害等。

# 2.2.3 灾备到异地多活的演变过程

# 2.2.3.1 同城灾备

同城灾备 分为 冷备 和 热备 , 冷备 只备份数据,不提供服务。 热备 实时同步数据,做好随时切换的准备。

# 2.2.3.2 同城双活

同城双活 比同城灾备的优势在于,两个机房都可以接入 读写 流量。提高可用性的同时也提高系统性能。(由于机房部署同一城市,可不考虑网络延迟问题。光纤传输的速度大概为 300km/ms )

# 2.2.3.3 两地三中心

两地三中心 是在同城双活基础上再部署一个异地机房做 灾备 ,用来抵御 城市 级别的灾害。但启用灾备机房需要耗费一定时间。(两地是指两个城市,三中心是指三个机房)

# 2.2.3.4 异地双活

异地双活 才是抵御 城市 级别灾害的更好方案。异地两个机房同时提供服务,有故障随时切换,可用性高。但是实现也很复杂。异地双活要两个机房都可以读写(不同城市的两个机房如果只有一个机房数据库做主库,会导致另一个只读的机房查数据延迟很高)。MySQL本身提供了双主架构,支持双向复制数据,但是像redis、mq等都不支持双向同步数据,需要另外开发。

此外,还需要在业务上将数据区分开,保证指定数据操作指定机房,避免各种脏数据的产生。这样,需要在接入层之上再部署一个 路由层 (通常部署在云服务器上),自己可以配置不同路由规则,将用户分流到不同的机房内。

# 2.2.3.5 异地多活

异地多活 是在异地双活的基础上扩展多个机房,这样不仅保证了高可用,还保证了高性能,可以应对更大规模的流量压力。是实现高可用的最终方案。

这种星状的方案必须要设立一个 中心机房 ,任意机房写入数据后要先同步到中心机房,再由中心机房同步到其他机房。中心机房的稳定性要求比较高,不过中心机房如果发生故障的话,可以把任意一个机房提升为中心机房,继续按照之前的架构提供服务。

# 2.3 系统及代码层面保证高可用

# 2.3.1 集群

使用集群,减少单点故障。

# 2.3.2 版本可回滚

应用部署支持版本回滚。

数据库脚本也需要有回滚脚本。

# 2.3.3 超时重试

重试次数一般为3次。

# 2.3.4 降级

同步改异步(如同步导出通过配置调整成异步导出)。

直接读缓存(关键功能本来查库的调整成临时查缓存)。

# 2.3.5 熔断

熔断和降级是两个容易混淆的概念,这两者的含义并不一样。

降级针对的是自身系统的故障,而熔断是要应对其他系统的故障。

# 2.3.6 限流

# 2.3.6.1 常见限流方案
# 1.计数器法

原理:在单位时间段内,对请求数进行计数,如果数量超过了单位时间的限制,则执行限流策略,当单位时间结束后,计数器清零,这个过程周而复始,就是计数器法。

缺点:不能均衡限流,在一个单位时间的末尾和下一个单位时间的开始,很可能会有两个访问的峰值,导致系统崩溃。

改进方式:可以通过减小单位时间来提高精度。

# 2.漏桶算法

原理:假设有一个水桶,水桶有一定的容量,所有请求不论速度都会注入到水桶中,然后水桶以一个恒定的速度向外将请求放出,当水桶满了的时候,新的请求被丢弃。

优点:可以平滑请求,削减峰值。

缺点:瓶颈会在漏出的速度,可能会拖慢整个系统,且不能有效地利用系统的资源。

# 3.令牌桶算法(推荐)

原理:有一个令牌桶,单位时间内令牌会以恒定的数量(即令牌的加入速度)加入到令牌桶中,所有请求都需要获取令牌才可正常访问。当令牌桶中没有令牌可取的时候,则拒绝请求。

优点:相比漏桶算法,令牌桶算法允许一定的突发流量,但是又不会让突发流量超过我们给定的限制(单位时间窗口内的令牌数)。即限制了我们所说的 QPS(每秒查询率)。

# 2.3.6.2 Guava限流工具类
# 1.说明

Google开源工具包Guava提供了限流工具类RateLimiter,基于令牌桶算法实现。

常用方法:

create(Double permitsPerSecond) 方法根据参数(令牌:单位时间(1s))比例为令牌生成速率。
tryAcquire() 方法尝试获取一个令牌,立即返回true/false,不阻塞,重载方法具备设置获取令牌个数、获取最大等待时间等参数。
acquire() 方法与tryAcquire类似,但是会阻塞,尝试获取一个令牌,没有时则阻塞直到获取成功。

可能有人在想既然是令牌桶算法,应该有个类似定时器的东东来持续往桶放令牌才对啊,我刚开始也是这么想的,看了代码觉得自己还是太嫩了,如果开启一个定时器无可厚非,但如果系统需要N个不同速率的桶来针对不同的场景或用户,就会极大的消耗系统资源。

RateLimiter用了一种类似于延迟计算的方法,把桶里令牌数量的计算放在下一个请求中计算,即桶里的令牌数 storedPermits 不是实时更新的,而是等到下一个请求过来时才更新。

# 2.代码示例
@Component
@Aspect
public class RateLimitAspect {

  private static final double permitsPerSecond = 10.0;

  private static RateLimiter rateLimiter = RateLimiter.create(permitsPerSecond);

  @Pointcut("@annotation(com.xxx.common.base.config.RateLimitAnno)")
  public void aspectService() {
  }

  @Around("aspectService()")
  public Object aroundMsg(ProceedingJoinPoint joinPoint) throws Throwable {
    Object obj = null;
    boolean flag = rateLimiter.tryAcquire();
    if (flag) {
      obj = joinPoint.proceed();
    } else {
      throw new RateLimitException(ResultCodeEnum.RATELIMIT_ERROR);
    }
    return obj;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public @interface RateLimitAnno {
}
1
2
@Override
@RateLimitAnno
public Bill getPostal(String billNo, List<String> fields) {
    if (StringUtils.isEmpty(billNo)) {
        return null;
    }
    Bill bill = this.detail(billNo, fields);
    return bill;
}
1
2
3
4
5
6
7
8
9

需关注好服务监控指标,如qps,响应时间,tomcat线程信息等(acceptcount,maxConnections)

#系统架构
Session的生命周期
分布式

← Session的生命周期 分布式→

最近更新
01
SpringBoot
10-21
02
Spring
10-20
03
Sentinel
10-14
更多文章>
Copyright © 2022-2023 Louis | 粤ICP备2022060093号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式