Blog of Woo

  • 首页

  • 标签

  • 归档

  • 公益 404

  • 搜索

分布式系统中的重试机制

发表于 2020-02-16 | 评论数: | 阅读次数:
本文字数: 663 | 阅读时长 ≈ 1 分钟

分布式系统中的重试机制

在分布式系统中,多个组件之间的网络通信随时可能发生故障。

客户端应用程序通过实现重试来处理这些失败。

常用的重试机制是在服务端程序抛出异常或超时时,客户端进行重试。重试次数可以由客户端或服务端指定,一般而言未指定重试间隔,或指定了固定时间间隔。

上述简单的重试机制在大多数场景下都可以正常工作,比如分布式系统中,服务端节点挂掉一个或少数几个,这时重试到其他节点,得到正常相应。客户端程序的服务质量可以不受影响。

而弊端在于:如果下游服务质量特别差,每次都有大量服务节点失效,此时的重试无疑加重了下游服务的压力,造成一定程度的雪崩效应。

指数回退 是处理失败网络调用重试的常用策略。简单地说,客户端在连续重试之间等待的时间间隔越来越长:

1
wait_interval = base * multiplier^n

指数回退在分布式系统中的一个主要缺点是,在同一时间开始回退的请求,也会在同一时间进行重试。这导致了请求簇的出现。那么,我们并没有减少每一轮进行竞争的客户端数量,而是引入了没有客户端竞争的时期。固定的指数退避并不能减少很多竞争,并会生成负载峰值。

抖动 就是为了减少请求簇而出现的,在重试的间隔中添加随机性,从而分摊了负载,避免了出现网络请求簇。

重试中的几个点

  1. 重试用来解决瞬时性故障,例如网络拥塞,数据库过载,是一种在有足够的冷却周期之后也许能自己恢复的故障。
  2. 重试前添加延迟,以解决初始化连接或网络包阻塞造成的故障
  3. 如果采用指数回退,记得添加最多重试次数
  4. 一定一定考虑可重试调用是否幂等

百万QPS下的缓存架构与设计

发表于 2020-02-10 | 更新于 2020-02-16 | 评论数: | 阅读次数:
本文字数: 1.8k | 阅读时长 ≈ 2 分钟

百万QPS下的缓存架构与设计

多级缓存

本地缓存

ConcurrentHashMap

存最简单的键值对,coder自己维持过期策略

guava的LocalCache

仅支持LRU,需要重写load 和 loadAll方法,支持指定过期时间和重刷时间

caffeine

Caffeine 因使用了 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率。

三种填充策略:手动, 同步填充指定一个从key到value的function, 异步填充buildAsync.

三种回收策略:

  1. 基于大小回收:指定个数或者权重。可以为每个kv指定一个权重
  2. 基于时间回收:访问之后多久, 创建之后多久, 自定义
  3. 基于引用回收:将key和value指定为WeakReference,当没有强引用时被gc掉。配置soft-reference来启动lru的gc.存储大对象的时候,即使外部程序没有在用,但大对象还是被缓存里的map强引用着,不能被回收掉。所以使用弱引用就是当外部没有在用这个k-v时,将它们清理掉。这两种是可以跟基于时间的回收策略结合使用的。

分布式缓存 - memcached

Easy to use, distributed, in-memory key-value store for use as a high performance cache or session store.

数据结构简单,只支持单纯的k-v结构
多线程
没有持久化
分布式:因为不同server间无感知,所以高可用由客户端实现
高可用:因为不同server间无感知,所以高可用由客户端实现

分布式缓存 - redis

风险及应对

缓存穿透

请求一个缓存中不存在的值,这样每次请求都会落到db里去。于是就对DB造成了压力,一般来说这种要分两种情况。正常的请求和黑客的请求。

缓存控制

对于正常的请求,请求内容也就是key一般是不变的,这个key在db中确实也没有数据。这种情况我们可以把空值缓存下来。

BloomFilter

对于黑客的请求,比如爬虫或者网络攻击,如果我们大量的缓存不存在的值很可能会把我们的缓存容量打满,导致正常值被逐出。这不是我们想要看到的。所以最好是在前端就能将非法请求过滤掉。假设我们有一个服务是根据用户id请求用户信息,我们首先要保证黑客拿到的用户id是加密过的也就是无法进行累加遍历,即使可累加遍历也可以在前面就被我们过滤掉。这部分再深入些就是安全方面的知识了,暂且不谈。

在前面提到的反爬的基础上,我们再请求缓存或db之前加一层布隆过滤器,如果这个key存在于缓存或db中就进行查询,否则就不进行查询。

缓存雪崩

缓存承担了大部分的请求,当缓存层由于某些原因失效时,大量请求就会直接到db层,db扛不住就挂掉了。

预防缓存层挂掉:

  1. 缓存的高可用,不让他挂掉
  2. 多级缓存做备份:一个缓存层挂了,另一个缓存层顶上
  3. 缓存失效时间随机,避免同一时刻大量缓存失效。

缓存层挂掉之后产生的问题:大量线程进入缓存层重建的代码,进行海量的请求db.那么在缓存重建时我们最重要的目的就是减少重建缓存的次数,为此可以牺牲部分可用性。

即使用互斥锁将重建缓存的代码保护起来,也即是保证同一时刻只有一个key的请求访问db.

缓存热点

大量请求访问同一个条目,即缓存集群中的某一台机器承担了大量的流量。想一下导致的后果:大量请求把这台缓存实例打挂了,如果是memcached,一致性hash之后到达了另一台机器,另一台机器大量读db,因为我们做了缓存雪崩的预防我们抗住了,但是流量还是扛不住于是又打挂了,以此往复,整个缓存集群都挂了。。。

上面举的例子当然是一个比较极端的case.实际上memcached单机支持百万QPS问题是不太大的,就算是redis支持十万QPS也是足够的。而在真实场景中,除了像什么明星离婚结婚,大型活动开始,应该是比较难遇到。

如果真的遇到了那么我们该怎么办?

  1. 通过流式计算技术(storm)计算出热点key并存放的zookeeper中
  2. 代码中监听zookeeper的对应节点变化来加载到本地缓存,设置一个较长的过期时间,直接用本地缓存扛。

如上极端的热点场景实际并没有遇到过,纸上得来终觉浅,以上仅供参考

大key

分散

Spring AOP 漫游指南

发表于 2020-02-02 | 更新于 2020-02-16 | 评论数: | 阅读次数:
本文字数: 722 | 阅读时长 ≈ 1 分钟

Spring AOP 漫游指南

对于切面编程,有一个目标对象(targetObject),也有一个代理对象(proxyObject),目标对象是我们声明的业务逻辑对象,而代理对象是使用切面逻辑对业务逻辑进行包裹之后生成的对象。

如果使用的是 Jdk 动态代理,那么业务对象和代理对象将是两个对象,在调用代理对象逻辑时,其切面逻辑中会调用目标对象的逻辑;

如果使用的是 Cglib 代理,由于是使用的子类进行切面逻辑织入的,那么只有一个对象,即织入了代理逻辑的业务类的子类对象,此时是不会生成业务类的对象的。

简单理解:jdk 动态代理根据接口生成两个对象,这两个对象都实现了同一个接口。

Cglib 代理生成了织入了代理逻辑的业务类的子类对象,只有一个对象。可以认为目标对象和代理对象是同一个。

this() target()

this 语义:如果当前对象生成的代理对象符合 this 指定的类型,那么就为其织入切面逻辑。

target 语义:如果当前对象符合 target 指定的类型,那么就为其织入切面逻辑。

this(SomeClass) 或 target(SomeClass),这里 SomeClass 实现了某个接口 SomeInterface.jdk 代理生成的代理对象的类型只是实现了 SomeInterface,并不是SomeClass,语义不符合;而target目标对象本身就是 SomeClass 类型,所以语义符合。

但是,如果改变 Spring 默认采用的 JDK 代理模式,改为 CGlib 代理模式。那么 CGlib 生成的代理对象是继承了 SomeClass,根据“is-a”,this 语义也符合。

AOP 基础概念

一花一世界,一树一菩提:编码压缩探索与实践

发表于 2019-07-12 | 更新于 2019-07-14 | 评论数: | 阅读次数:
本文字数: 5k | 阅读时长 ≈ 5 分钟

一花一世界,一树一菩提:编码压缩探索与实践

前言

你可曾面临这种问题,高并发条件下数据必须99%cache命中才能满足性能需求?

你可曾面对这种场景,对cache集群不断扩容是否让你感到厌烦?

那么是否有一种办法在提高缓存命中率的同时,又能提高缓存集群键个数,甚至于提高整个集群的吞吐?

本文将从实践出发,对编码方式及压缩算法进行介绍,并分享了在真实项目中使用缓存压缩对性能的影响。

阅读全文 »

java_GC是如何做的(二)

发表于 2019-03-21 | 更新于 2019-04-25 | 评论数: | 阅读次数:
本文字数: 102 | 阅读时长 ≈ 1 分钟

通过上文我们知道了Java GC的相关理论,那么在实际中,jvm是通过哪些垃圾收集器来完成GC的? Stop the World又是怎么一回事呢?

这是本文我们将解决的两个问题.

阅读全文 »

jvm调优总结

发表于 2019-02-18 | 更新于 2019-03-24 | 评论数: | 阅读次数:
本文字数: 101 | 阅读时长 ≈ 1 分钟

大多数调优参数都是调整堆内存的大小,以及根据实际情况选择最合适的垃圾收集器.

GC相关

  • 通过NewRatio控制新生代老年代比例
  • 通过MaxTenuringThreshold控制进入老年前生存次数等

java_GC是如何做的

发表于 2019-02-17 | 更新于 2019-07-14 | 评论数: | 阅读次数:
本文字数: 4.9k | 阅读时长 ≈ 4 分钟

在使用Java之前,我在大学使用过两年多的C++.刚接触Java时,肯定会下意识的与cpp去比较.譬如说:指针,单继承,值传递引用传递和垃圾回收.

众所周知,在写cpp时最重要的工作之一就是delete ptr,稍有不慎就会造成内存泄漏,出现大的问题.也正因为此,对java的GC机制感到十分神奇.Java是怎么做到垃圾回收的,它的垃圾回收机制是如何实现的,Java有了GC机制之后是否就不可能发生内存泄漏了呢?

在去回答这几个问题之前,我们先想一下以下三个问题:

  • Java GC 一般说不依靠程序员手动调用,那它是什么时候发生的?
  • 对于cpp来说,可以通过delete或free的方式释放指定区域内存,那Java GC怎么知道哪些内存是可以被释放掉的呢,换言之,Java GC是如何发现’垃圾’的?
  • 在发现’垃圾’之后,Java GC又是通过哪些操作/算法去’清除’掉垃圾的呢?
阅读全文 »

扒一扒ReentrantLock利用AQS实现原理

发表于 2019-02-16 | 更新于 2019-02-19 | 评论数: | 阅读次数:
本文字数: 11k | 阅读时长 ≈ 10 分钟

前言

ReetrantLock? AQS?

为什么会有 AQS 呢?(其中 AQS 是 AbstractQueuedSynchronizer 的缩写)

因为有人的地方就有江湖,所以有并发的地方就有资源共享,有资源共享的地方就有线程安全,有线程安全的地方就有 “线程同步”,有线程同步的地方就有 “锁”(有点长,但是理是这个理)

AQS 就是 JDK 中为 “线程同步” 提供的一套基础工具类(网上其他帖子叫做 “框架” 我觉得这个就说的太大了,其实就是一个类而已(不算它衍生出来的子类)国内存在一些翻译会导致很多初学者很难理解,比如 classloader 的 parents 被翻译为 “双亲”,简直败笔,此处默哀 3 秒钟),因此 AQS 就成了非常重要的一个知识点,因为基于它可以写出 JAVA 中的很多“锁” 类。比如此文要分析的 ReetrantLock,它就是基于 AQS 而形成了一个“可重入锁”

阅读全文 »

Http重定向301,302区别

发表于 2019-02-13 | 更新于 2019-02-18 | 评论数: | 阅读次数:
本文字数: 1.1k | 阅读时长 ≈ 1 分钟
  1. 什么是301转向?什么是301重定向?

  301转向(或叫301重定向,301跳转)是当用户或搜索引擎向网站服务器发出浏览请求时,服务器返回的HTTP数据流中头信息(header)中的状态码的一种,表示本网页永久性转移到另一个地址。

  1. 什么是302重定向?

  302重定向又称之为302代表暂时性转移(Temporarily Moved ),英文名称:302 redirect。 也被认为是暂时重定向(temporary redirect),一条对网站浏览器的指令来显示浏览器被要求显示的不同的URL,当一个网页经历过短期的URL的变化时使用。一个暂时重定向是一种服务器端的重定向,能够被搜索引擎蜘蛛正确地处理。

阅读全文 »

mysql 四种事务隔离级别

发表于 2019-02-12 | 更新于 2019-02-15 | 评论数: | 阅读次数:
本文字数: 2.7k | 阅读时长 ≈ 2 分钟

我们可以为数据库或分别的会话设置四种事务隔离级别.

  • READ UNCOMMITTED(读取未提交数据)
  • READ COMMITTED(可以读取其他事务提交的数据)
  • REPEATABLE READ(可重读)—MySQL默认的隔离级别
  • SERIALIZABLE(串行化)
阅读全文 »
123

woo

愿你我能摆脱冷气,只是向上走,不必听自暴自弃者流的话。

28 日志
18 标签
GitHub E-Mail
Links
  • Google
  • Github
0%
© 2020 woo | 78k | 1:11
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Gemini v7.0.0