老板让你抗住千万级流量,如何做架构设计?

简介: 随着互联网的发展,各项软件的客户量日益增多,当客户量达到一定峰值时,当数以万计的流量来临时,程序的顺利运行以及即时响应则显得尤为重要,就像双11那天的淘宝一样。那么,如何设计架构才能够抗住这千万级的流量。

随着互联网的发展,各项软件的客户量日益增多,当客户量达到一定峰值时,当数以万计的流量来临时,程序的顺利运行以及即时响应则显得尤为重要,就像双11那天的淘宝一样。那么,如何设计架构才能够抗住这千万级的流量。


老板让你抗住千万级流量,如何做架构设计?


首先,要在我们架构设计的时候建立一些原则。


1. 实现高并发


服务拆分:将整个项目拆分成多个子项目或者模块,分而治之,将项目进行水平扩展。


服务化:解决服务调用复杂之后的服务的注册发现问题。


消息队列:解耦,异步处理


缓存:各种缓存带来的并发


2. 实现高可用


集群、限流、降级


3. 业务设计


幂等:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用,就像数学里的数字1,多少次幂的结果都是1。举个最简单的例子,那就是支付,用户购买商品后支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额发现多扣钱了,流水记录也变成了两条。


防重:防止同样的数据同时提交


除了在业务方向判断和按钮点击之后不能继续点击的限制以外,在服务器端也可以做到防重:


在服务器端生成一个唯一的随机标识号(Token<令牌>)同事在当前用户的Session域中保存这个令牌,然后将令牌发送到客户端的form表单中,在form表单中使用隐藏域来存储这个Token,表单提交的时候联通这个Token一起提交到服务器,然后在服务器端判断客户提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就重复提交了,此时服务器端就可以不处理重复提交的表单,如果相同则处理表单,处理完后清楚当前用户的Session域中存储的标识号。高可用高并发架构参考:高可用高并发的 9 种技术架构。


在下列情况中,服务器程序将拒绝处理用户提交的表单请求:


1)存储Session域中的Token与表单提交的Token不一致 2)当前用户的Session中不存在Token 3)用户提交的表单数据中没有Token。


状态机


软件设计中的状态机概念,一般是指有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。


这里着重讲一下限流的概念和例子


限流的目的


限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的请求进行限速来保护系统的可用性,一旦达到限制速率就可以拒绝服务。就像手机预售一样,假如要卖出3万台,只需要接收3万用户的请求就可以,其他的用户请求可以选择过滤,可以提示"当前服务器过忙,请稍后再试"的提示。推荐大家看这篇文章:接口限流算法:漏桶算法&令牌桶算法。


限流方式:


1. 限制瞬时并发数 : 比如在入口层(nginx添加nginx_http_limit_conn_module)来限制同一个ip来源的连接数,防止恶意攻击访问的情况。


2. 限制总并发数:通过配置数据库连接池、线程池大小来约束总并发数


3. 限制时间窗口内的平均速率:在接口层面,通过限制访问速率来控制接口的并发请求。


4. 其他方式:限制远程接口的调用速率、限制MQ的消费速率。


常用限流算法


1. 滑动窗口协议:一种常见的流量控制技术,用来改善吞吐量的技术。


滑动窗口协议的由来:


滑动窗口(sliding window)是一种流量控制技术。早期的网络通讯中,通信双方不会考虑网络的拥挤情况直接发送数据。由于大家不知道网络拥塞状况,同时发送数据,导致中间节点阻塞掉包,谁也发送不了数据,所以就有了滑动窗口机制来解决此问题。   发送和接收方都会维护一个数据帧的序列,这个序列被称为窗口。


定义:滑动窗口协议(Sliding Window Protocol),属于TCP协议的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生。该协议允许发送方在停止并等待确认前发送多个数据分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输,提高网络吞吐量。

image.png

发送窗口:就是发送端允许连续发送的帧的序号表。发送端可以不等待应答而连续发送数据(可以通过设置窗口的尺寸来控制)


接收窗口:接收方允许接收的帧的序列表,凡是落在接收窗口内的帧,接收方都必须处理,落在接收窗口外的帧将被丢弃。接收方每次允许接收的帧数称为接收窗口的尺寸


演示地址:


https://media.pearsoncmg.com/aw/ecs\_kurose\_compnetwork_7/cw/content/interactiveanimations/selective-repeat-protocol/index.html


2. 漏桶:漏桶算法能强行限制数据的传输速率。


漏桶算法思路很简单,请求先进入到漏桶里,漏桶以一定的速度出水。当水请求过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。进入端无需考虑出水端的速率,就像mq消息队列一样,provider只需要将消息传入队列中,而不需要关心Consumer是否接收到了消息。


对于溢出的水,就是被过滤的数据,可以直接被丢弃,也可以通过某种方式暂时保存,如加入队列之中,像线程池里对溢出数据的4种处理机制一样


image.png


3. 令牌桶:属于控制速率类型的限流算法。


对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。


设置 Rate = 2 :每秒放入令牌的个数


桶的大小:100


image.png


这里用一个小demo来实现一下令牌桶

public class TokenDemo {
  //qps:每秒钟处理完请求的次数;tps:每秒钟处理完的事务次数
  //代表qps是10;
  RateLimiter rateLimiter = RateLimiter.create(10);
  public void doSomething(){
      if (rateLimiter.tryAcquire()){
          //尝试获得令牌.为true则获取令牌成功
          System.out.println("正常处理");
      }else{
          System.out.println("处理失败");
      }
  }
  public static void main(String args[]) throws IOException{
      /*
      * CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量,此值是线程将要等待的操作数(线程的数量)。
      * 当某个线程为了想要执行这些操作而等待时, 它要使用 await()方法。
      * 此方法让线程进入休眠直到操作完成。
      * 当某个操作结束,它使用countDown() 方法来减少CountDownLatch类的内部计数器,计数器的值就会减1。
      * 当计数器到达0时,它表示所有的线程已经完成了任务,这个类会唤醒全部使用await() 方法休眠的线程们恢复执行任务。
      *
      * */
      CountDownLatch latch = new CountDownLatch(1);
      Random random = new Random(10);
      TokenDemo tokenDemo = new TokenDemo();
      for (int i=0;i<20;i++){
          new Thread(()->{
              try {
                  latch.await();
                  Thread.sleep(random.nextInt(1000));
                  tokenDemo.doSomething();
              }catch (InterruptedException e){
                  e.printStackTrace();
              }
          }).start();
      }
      latch.countDown();
      System.in.read();
  }
}

执行结果:

正常处理
正常处理
正常处理
正常处理
正常处理
处理失败
正常处理
处理失败
处理失败
处理失败
正常处理
处理失败
正常处理
处理失败
正常处理
正常处理
正常处理
正常处理
处理失败
处理失败

由此可见,当令牌不足时,会获取令牌失败,达到限流的效果。

4. 计数器:最简单的一种。通过控制时间段内的请求次数。

相关文章
|
5天前
|
存储 监控 API
构建高效微服务架构:后端开发的现代实践
【5月更文挑战第9天】 在本文中,我们将深入探讨如何在后端开发中构建一个高效的微服务架构。通过分析不同的设计模式和最佳实践,我们将展示如何提升系统的可扩展性、弹性和维护性。我们还将讨论微服务架构在处理复杂业务逻辑和高并发场景下的优势。最后,我们将分享一些实用的工具和技术,以帮助开发者实现这一目标。
|
1天前
|
负载均衡 JavaScript Java
构建高效微服务架构:后端开发的新视角
【5月更文挑战第13天】在现代软件开发中,微服务架构已经成为一种流行趋势。它通过将应用程序拆分为一组小型、独立的服务来提高可扩展性、弹性和可维护性。本文将探讨如何构建一个高效的微服务架构,包括选择合适的技术栈、设计良好的服务接口、确保数据一致性以及实现有效的服务发现和负载均衡。
|
1天前
|
监控 Java 开发者
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第13天】随着现代应用的复杂性日益增加,传统的单体应用架构已不足以满足快速迭代和可扩展性的需求。本文将探讨如何通过微服务架构来提升后端开发的效率和系统的可靠性,涵盖微服务设计原则、技术栈选择、部署策略以及维护实践。我们将分析微服务的优势与挑战,并提供一系列实施建议,帮助开发者在构建和维护分布式系统时做出明智决策。
|
1天前
|
存储 监控 API
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第13天】在现代软件开发中,随着业务需求的多样化和开发流程的复杂化,传统的单体应用架构逐渐显得笨重且难以适应快速变化。微服务架构作为一种新兴的分布式系统设计方式,以其灵活性、可扩展性和技术多样性受到广泛关注。本文旨在探讨微服务架构的核心概念、设计原则以及实施策略,为后端开发人员提供一种提升系统性能和开发效率的有效途径。
11 2
|
1天前
|
监控 持续交付 数据库
构建高效可靠的微服务架构:后端开发的新范式
【5月更文挑战第13天】 在当今软件开发的世界中,微服务架构已经成为了一种流行且有效的设计模式。它通过将大型复杂系统分解为一组独立的、可部署的服务来提高系统的可维护性、可扩展性和敏捷性。本文将探讨如何构建一个高效且可靠的微服务架构,包括关键的设计原则、技术选型以及可能面临的挑战。我们的目标是为后端开发者提供一套实用的指南,以便在构建现代化应用程序时做出明智的决策。
|
2天前
|
监控 API 开发者
构建高效微服务架构:后端开发的新范式
【5月更文挑战第12天】 在现代软件开发的浪潮中,微服务架构已经成为了设计复杂系统的首选模式。它通过将大型应用程序拆分成一组小而专注的服务来增强系统的可维护性和可扩展性。本文将探讨微服务架构的关键概念、优势以及如何在后端开发中实现一个高效的微服务系统。我们还将讨论一些常见的挑战和最佳实践,以帮助开发者避免陷入常见的陷阱。
15 6
|
3天前
|
存储 NoSQL MongoDB
【MongoDB 专栏】MongoDB 与微服务架构的结合
【5月更文挑战第11天】微服务架构流行趋势下,选择合适的数据库至关重要。MongoDB作为非关系型数据库,与微服务有天然契合度。其灵活的文档模型、水平扩展性、高性能及局部事务支持,满足微服务对数据模型多样性、高可用性、快速读写的需求。实践中,需注意数据划分、索引优化、监控调优和版本控制。未来,MongoDB在微服务中的应用将更广泛,新技术将提升其在微服务架构中的价值。
【MongoDB 专栏】MongoDB 与微服务架构的结合
|
3天前
|
监控 数据库 开发者
构建高效可靠的微服务架构:策略与实践
【5月更文挑战第11天】在当今软件开发的世界中,微服务架构已经成为构建可扩展、灵活且容错的系统的首选方法。本文深入探讨了设计、部署和维护微服务系统时面临的挑战,并提出了一系列实用的策略和最佳实践。我们将从服务的划分原则出发,讨论如何确保每个微服务的自治性,以及如何通过容器化和编排技术实现服务的高效运行。文章还将涉及监控、日志记录和故障恢复的策略,旨在帮助开发人员构建一个既高效又可靠的微服务环境。
|
3天前
|
Kubernetes API 开发者
构建高效微服务架构:后端开发的新范式
【5月更文挑战第11天】 在现代软件开发的快速演变中,微服务架构已成为企业追求敏捷性、可扩展性和技术多样性的关键解决方案。本文旨在探讨如何构建高效的微服务架构,并分析其对后端开发的影响。我们将通过一系列最佳实践和策略,展示如何优化服务的独立性、弹性和性能,同时确保系统的整体稳定性和安全性。文章还将介绍容器化、API网关、服务发现和分布式追踪等关键技术的应用,为后端开发者提供一份全面的微服务实施指南。