0%

Class 类文件的结构

Java技术能够一直保持着非常良好的向后兼容性,Class文件结构的稳定功不可没,任何一门程序语言能够获得商业上的成功,都不可能去做升级版本后,旧版本编译的产品就不再能够运行这种事情。

Java 虚拟机关心的只是Class类文件结构,与是否使用Java语言并没有太大的关系。如下图所示:

Java虚拟机提供的语言无关性

同时,从动态代理中的 Cglib 这个包的存在,Class 类文件都不一定都得定义在磁盘文件里。

Class文件是一组以8个字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在文 件之中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎全部是程序运行的必要数 据,没有空隙存在。当遇到需要占用8个字节以上空间的数据项时,则会按照高位在前[2]的方式分割 成若干个8个字节进行存储。

Class文件格式采用一种类似于C语言结构体的伪结构来存储数 据,这种伪结构中只有两种数据类型:“无符号数”和“表”。

  • 无符号数

    无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个 字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串 值。

  • 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,为了便于区分,所有表的命名 都习惯性地以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上也可以视作是一张表.
    例如:
    class文件格式

无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的 容量计数器加若干个连续的数据项的形式,这时候称这一系列连续的某一类型的数据为某一类型的“集 合”。

Class的结构不像XML等描述语言,由于它没有任何分隔符号,所以在上图中的数据项,无论是顺序还是数量,甚至于数据存储的字节序(Byte Ordering,Class 文件中字节序为Big-Endian)这样的细节,都是被严格限定的,哪个字节代表什么含义,长度是多少, 先后顺序如何,全部都不允许改变。

类文件的16进制、反编译和解读

常量池

访问标识

类索引、父类索引与接口索引集合

负载均衡介绍

将请求或者说流量,以期望的规则分摊到多个操作单元上进行执行。

通过它可以实现横向扩展(scale out),将冗余的作用发挥为高可用。另外,还可以物尽其用,提升资源使用率。

阅读全文 »

注意:
首先打开两个或以上的shell连接,因为在升级过程中如果升级失败会导致不发新建shell连接;升级后使用xshell6,7连接,openssh版本对应修改,下载地址:https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/

  1. 解压:tar zxvf openssh-8.2p1.tar.gz

  2. 查看当前安装的ssh rpm -qa|grep ssh

  3. 卸载当前的

    ssh rpm -e –nodeps openssh-askpass-5.3p1-94.el6.x86_64 openssh-clients-5.3p1-94.el6.x86_64 openssh-5.3p1-94.el6.x86_64 openssh-server-5.3p1-94.el6.x86_64

  4. 安装依赖包 yum install -y openssl-devel

  5. 删除/etc/ssh/下的密钥对,编译新的ssh

    rm -f /etc/ssh/ssh_host_*
    cd openssh-8.2p1
    ./configure –prefix=/usr –sysconfdir=/etc/ssh
    make && make install

  6. 安装成功之后,查看版本 ssh-V

  7. 修改配置文件 sshd_config 把permitrootlogin xxx去掉注释 改成 permitrootlogin yes
    把 #UseDNS no 修改 UseDNS no

  8. 把sshd拷贝到 /etc/init.d下面

    cp -a contrib/redhat/sshd.init /etc/init.d/sshd
    cp -a contrib/redhat/sshd.pam /etc/pam.d/sshd.pam
    chmod +x /etc/init.d/sshd
    chkconfig –add sshd
    systemctl enable sshd
    chkconfig sshd on
    mv /usr/lib/systemd/system/sshd.service /opt/ (执行后如提示没有文件,忽略即可)
    mv /usr/lib/systemd/system/sshd.socket /opt/ (执行后如提示没有文件,忽略即可)

9.启动 /etc/init.d/sshd 后面无需加start等参数。

DDD 的目标

DDD 的目标是帮助我们创造一个可测试的、可伸缩的、组织良好的高质量软件模型。

架构

分层架构

image-20220401113015069

  • 重要原则:每层只能与位于其下方的层发生耦合

依赖注入原则 – spring

六边形架构

[TOC]

下载

下载地址:http://zookeeper.apache.org/

下载过程就不说了,我们下载了最新的zookeeper-3.4.14

安装

1、上传安装包

把下载的最新的包(如:zookeeper-3.4.14.tar.gz)上传到服务器,上传的方式也不多说了。

2、解压

1
$ tar zxvf zookeeper-3.4.14.tar.gz

3、移动到/usr/local目录下

1
$ ln -s zookeeper-3.4.14 /usr/local/zookeeper

服务器分配

主机名 IP
centos-node1 192.168.99.101
centos-node2 192.168.99.102
centos-node3 192.168.99.103

集群配置

Zookeeper集群原则上需要2n+1个实例才能保证集群有效性,所以集群规模至少是3台。

下面演示如何创建3台的Zookeeper集群,N台也是如此。

1、创建数据文件存储目录

1
2
$ cd /usr/local/zookeeper
$ mkdir data

2、添加主配置文件

1
2
$ cd conf
$ cp zoo_sample.cfg zoo.cfg

3、修改配置

1
$ vi zoo.cfg

先把dataDir=/tmp/zookeeper注释掉,然后添加以下核心配置。

1
2
3
4
dataDir=/usr/local/zookeeper/data
server.1=centos-node1:2888:3888
server.2=centos-node2:2888:3888
server.3=centos-node3:2888:3888

4、创建myid文件

1
2
3
$ cd ../data
$ touch myid
$ echo "1">>myid

每台机器的myid里面的值对应server.后面的数字x。

5、开放3个端口

使用 iptables

1
2
3
4
5
6
7
8
9
10
11
12
13
$ sudo /sbin/iptables -I INPUT -p tcp --dport 2181 -j ACCEPT
$ sudo /sbin/iptables -I INPUT -p tcp --dport 2888 -j ACCEPT
$ sudo /sbin/iptables -I INPUT -p tcp --dport 3888 -j ACCEPT

$ sudo /etc/rc.d/init.d/iptables save
$ sudo /etc/init.d/iptables restart

$ sudo /sbin/iptables -L -n
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3888
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:2888
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:2181

或者 firewalld

1
2
3
4
5
6
7
8
9
10
11
12
# 查看当前区域
$ firewall-cmd --get-active-zones
# 新建一个自定义服务
$ firewall-cmd --new-service=zookeeper --permanent
$ firewall-cmd --service=zookeeper --add-port 2181/tcp --permanent
$ firewall-cmd --service=zookeeper --add-port 2888/tcp --permanent
$ firewall-cmd --service=zookeeper --add-port 3888/tcp --permanent
# 不中断服务的重新加载
$ firewall-cmd --reload
$ firewall-cmd --add-service=zookeeper
# 将当前防火墙的规则永久保存;
$ firewall-cmd --runtime-to-permanent

6、配置集群其他机器

把配置好的Zookeeper目录复制到其他两台机器上,重复上面4-5步。

1
$ scp -r /usr/local/zookeeper centos-node2:/usr/local/
1
$ vi /etc/profile 
1
2
3
4
5
6
7
8
9
JAVA_HOME=/usr/local/java/
JRE_HOME=$JAVA_HOME/jre
HADOOP_HOME=/usr/local/hadoop
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
ZOOKEEPER_HOME=/usr/local/zookeeper

PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin:$HADOOP_HOME/bin:$ZOOKEEPER_HOME/bin

export JAVA_HOME JRE_HOME PATH CLASSPATH HADOOP_HOME HBASE_HOME

7、重启集群

1
$ /usr/local/zookeeper/bin/zkServer.sh start

3个Zookeeper都要启动。

8、查看集群状态

1
2
3
4
$ /usr/local/zookeeper/bin/zkServer.sh status 
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Mode: follower

客户端连接

1
./zkCli.sh -server 192.168.99.101:2181

连接本机的不用带-server。

注意

如果是在单机创建的多个Zookeeper伪集群,需要对应修改配置中的端口、日志文件、数据文件位置等配置信息。

[TOC]

1 名词与缩略语

LTE: 准4G标准

IMT-2020:5G推进组

SDN:软件定义网络

NFV:网络功能虚拟化

eMBB:增强移动宽带

mMTC:海量机器类通信

URLLC:超可靠低时延通信

2 如何定义5G?

综合5G关键能力与核心技术,5G概念可由“标志性能力指 标”和“一组关键技术”来共同定义。其中,标志性能力指标为 “Gbps用户体验速率”,一组关键技术包括大规模天线阵列、超密集组网、新型多址、全频谱接入和新型网络架构。

3 5G 架构

3.1 需求与挑战

  1. 关键性能挑战
  2. 网络运营需求

3.2 设计思路

从 1G 到 4G,移动网络的演进趋势是控制与转发分离,对于 5G 更是如此,将业务面功能下沉至用户侧。

为了解决网络开放能力和设备兼容性等问题,考虑引入软件和硬件功能解耦技术,以支持网元功能的虚拟化和模块化,便于按需组网。

总结起来,5G 网络需实现转发分离化、部署分布化、网络虚拟化和功能模块化的 4D 逻辑结构。

设计原则:

  • 灵活:根据不同的业务需求,构建以用户为中心的组网,支持多种接入技术融合;
  • 高效:简化状态、信令,同时使网络具有更低的传输成本,且易于拓展;
  • 智能:网络能够实现资源的自分配和自调整、组网的自配置和自由化;
  • 开放:网元能够突破软硬件紧耦合的限制,网络能力可向第三方开放,以支持新业务的打造,创新盈利点。

特点:一个逻辑架构,多种组网方式

3.3 技术基础

先决条件:SDN 和 NFV 的结合,有效地满足 5G 网络架构的主要技术特征,使 5G 网络具备网络能力开放性、可编程性、灵活性和可扩展性。

3.3.1 SDN——控制与转发分离

SDN:软件定义网络

主要特征:控制面与数据面分离和控制面集中化

SDN 的应用场景:

  1. 场景 1:SDN 在数据中心网络的应用
  2. 场景 2:SDN 在数据中心互联的应用
  3. 场景 3:SDN 在政企网络中的应用
  4. 场景 4:SDN 在电信运营商网络的应用
  5. 场景 5:SDN 在互联网公司业务部署中的应用

3.3.2 NFV——软件与硬件解耦

NFV:网络功能虚拟化

NFV 的应用场景:

  1. 场景 1:虚拟化 BRAS
  2. 场景 2:虚拟化 CPE
  3. 场景 3:虚拟化 EPC
  4. 场景 4:虚拟化 IMS
  5. 场景 5:虚拟化路由器 vSR

3.3.3 SDN 与 NFV 的关系

SDN 和 NFV 的共同点是网络从封闭走向开放、从独享的硬件发展到共享的软件。两者具有很强的互补性,但是它们彼此之间是相互独立的,没有必然的依赖性。

SDN 侧重于控制与转发的分离、网络集中控制(逻辑上)和网络虚拟化,主要影响的是网络的结构;

而 NFV 侧重的是软件与硬件的分离、硬件通用化和网络功能虚拟化,主要影响的是网元的形态。

3.4 关键特征

  1. 控制转发分离
  2. SBA 架构
  3. 网络切片
  4. 固移融合

4 5G 网络关键技术

4.1 网络切片

4.1.1 网络切片架构

网络切片(NS,Network Slicing)技术通过虚拟化将一个物理网络分成多个虚拟的逻辑网络,每一个虚拟网络对应不同的应用场景,从而提供高能效、易部署的网络解决方案。

网络切片架构包含接入侧切片(含无线接入和固定接入)、核心网切片以及将这些切片组成完整切片的选择功能单元。选择功能单元按照实际通信业务需求选择能够提供特定服务的核心网切片。
网络切片架构

每个网络切片都是一组网络功能(Network Function)及其资源的集合,由这些网络功能形成一个完整的逻辑网络,每一个逻辑网络都能以特定的网络特征来满足对应业务的需求。通过网络功能和协议定制,网络切片为不同业务场景提供所匹配的网络功能。其中每个切片都可独立按照业务场景的需要和话务模型进行网络功能的定制剪裁和相应网络资源的编排控制,是对 5G 网络架构的实例化。

4.1.2 网络切片实现方案

NFV 软硬件解耦及动态伸缩特性是 5G 切片的实现基础,而 SDN 的控制与转发分离特性是 5G 切片的实现引擎。

4.2 移动边缘计算——业务本地化

科普 | 到底什么是移动边缘计算?_朱小厮的博客-CSDN博客

低时延业务要求核心网功能部署到网络边缘。

参考

硬核图解!30张图带你搞懂!路由器,集线器,交换机,网桥,光猫有啥区别? (sdnlab.com)

分布式限流介绍

限流的目的是通过对并发访问/请求进行限速或者在一个时间窗口内的请求进行限速来保护系统,一旦请求到达限制速率则可以拒绝服务、排队或等待、降级。

主要是通过压测时找出每个系统的处理峰值,然后通过处理设定峰值阈值来防止系统过载时,通过拒绝处理过载的请求来保障系统可用性。

分布式限流的几种维度

  • 时间: 限流基于某段时间范围或者某个时间点,也就是我们常说的“时间窗口”,比如对每分钟、每秒钟的时间窗口做限定
  • 资源: 基于可用资源的限制,比如设定最大访问次数,或最高可用连接数

上面两个维度结合起来看,限流就是在某个时间窗口对资源访问做限制,比如设定每秒最多100个访问请求。但在真正的场景里,我们不止设置一种限流规则,而是会设置多个限流规则共同作用,主要的几种限流规则如下:

img

一般开发高并发系统常见的限流有:

  • 限制总并发数:如数据库连接池、线程池
  • 限制瞬时并发数:如Nginx的 limit_conn 模块
  • 限制时间窗口内的平均速率:如Guava的RateLimiter、Nginx的 limit_req 模块
  • 限制远程接口调用速率
  • 限制MQ的消费速率
  • 等等…
  • 还可以根据网络连接数、网络流量、CPU或内存负载

限流算法

令牌桶算法

image-20211207163639135

image-20211207163552357

img

有两个关键的角色:

  • 令牌:获取到令牌的Request才会被处理,其他Requests要么排队要么被直接丢弃
  • 桶:用来装令牌的地方,所有Request都从这个桶里面获取令牌

两个关键流程:

  • 生成令牌

    这个流程涉及到令牌生成器和令牌桶,令牌桶是一个装令牌的地方,既然是个桶那么必然有一个容量,也就是说令牌桶所能容纳的令牌数量是一个固定的数值。

    对于令牌生成器来说,它会根据一个预定的速率向桶中添加令牌,比如我们可以配置让它以每秒100个请求的速率发放令牌,或者每分钟50个。注意这里的发放速度是匀速,也就是说这50个令牌并非是在每个时间窗口刚开始的时候一次性发放,而是会在这个时间窗口内匀速发放。

    在令牌发放器就是一个水龙头,假如在下面接水的桶子满了,那么自然这个水(令牌)就流到了外面。在令牌发放过程中也一样,令牌桶的容量是有限的,如果当前已经放满了额定容量的令牌,那么新来的令牌就会被丢弃掉。

  • 获取令牌

    每个访问请求到来后,必须获取到一个令牌才能执行后面的逻辑。假如令牌的数量少,而访问请求较多的情况下,一部分请求自然无法获取到令牌,那么这个时候我们可以设置一个“缓冲队列”来暂存这些多余的令牌。

    缓冲队列其实是一个可选的选项,并不是所有应用了令牌桶算法的程序都会实现队列。当有缓存队列存在的情况下,那些暂时没有获取到令牌的请求将被放到这个队列中排队,直到新的令牌产生后,再从队列头部拿出一个请求来匹配令牌。

    当队列已满的情况下,这部分访问请求将被丢弃。在实际应用中我们还可以给这个队列加一系列的特效,比如设置队列中请求的存活时间,或者将队列改造为PriorityQueue,根据某种优先级排序,而不是先进先出。算法是死的,人是活的,先进的生产力来自于不断的创造,在技术领域尤其如此。

漏桶算法

img

漏桶算法的前半段和令牌桶类似,但是操作的对象不同,令牌桶是将令牌放入桶里,而漏桶是将访问请求的数据包放到桶里。同样的是,如果桶满了,那么后面新来的数据包将被丢弃。

漏桶算法的后半程是有鲜明特色的,它永远只会以一个恒定的速率将数据包从桶内流出。打个比方,如果我设置了漏桶可以存放100个数据包,然后流出速度是1s一个,那么不管数据包以什么速率流入桶里,也不管桶里有多少数据包,漏桶能保证这些数据包永远以1s一个的恒定速度被处理。

令漏洞算法 VS 漏洞算法

漏桶

漏桶的出水速度是恒定的,那么意味着如果瞬时大流量的话,将有大部分请求被丢弃掉(也就是所谓的溢出)。

令牌桶

生成令牌的速度是恒定的,而请求去拿令牌是没有速度限制的。这意味,面对瞬时大流量,该算法可以在短时间内请求拿到大量令牌,而且拿令牌的过程并不是消耗很大的事情。

最后,不论是对于令牌桶拿不到令牌被拒绝,还是漏桶的水满了溢出,都是为了保证大部分流量的正常使用,而牺牲掉了少部分流量,这是合理的,如果因为极少部分流量需要保证的话,那么就可能导致系统达到极限而挂掉,得不偿失。

滑动窗口

img

上图中黑色的大框就是时间窗口,我们设定窗口时间为5秒,它会随着时间推移向后滑动。我们将窗口内的时间划分为五个小格子,每个格子代表1秒钟,同时这个格子还包含一个计数器,用来计算在当前时间内访问的请求数量。那么这个时间窗口内的总访问量就是所有格子计数器累加后的数值。

比如说,我们在每一秒内有5个用户访问,第5秒内有10个用户访问,那么在0到5秒这个时间窗口内访问量就是15。如果我们的接口设置了时间窗口内访问上限是20,那么当时间到第六秒的时候,这个时间窗口内的计数总和就变成了10,因为1秒的格子已经退出了时间窗口,因此在第六秒内可以接收的访问量就是20-10=10个。

滑动窗口其实也是一种计算器算法,它有一个显著特点,当时间窗口的跨度越长时,限流效果就越平滑。打个比方,如果当前时间窗口只有两秒,而访问请求全部集中在第一秒的时候,当时间向后滑动一秒后,当前窗口的计数量将发生较大的变化,拉长时间窗口可以降低这种情况的发生概率

限流组件

Guava RateLimiter 客户端限流

引入依赖项

1
2
3
4
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

非阻塞式的限流方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Slf4j
@RestController
public class RateLimiterController {

private RateLimiter rateLimiter = RateLimiter.create(2.0);

/**
* 非阻塞限流
*
* @param count 请求令牌数
* @param timeout 请求令牌超时时间, 如果等于0, 表示不限定时间
* @return
*/
@GetMapping("/tryAcquire")
public String tryAcquire(Integer count, @RequestParam(defaultValue = "0") Integer timeout) {
if (rateLimiter.tryAcquire(count, timeout, TimeUnit.SECONDS)) {
log.info("success, rate is {}", rateLimiter.getRate());
return "success";
} else {
log.info("fail, rate is {}", rateLimiter.getRate());
return "fail";
}
}
}

同步阻塞式的限流方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Slf4j
@RestController
public class RateLimiterController {

private RateLimiter rateLimiter = RateLimiter.create(2.0);

/**
* 同步阻塞式限流
*
* @param count 请求令牌数
* @return
*/
@GetMapping("/acquire")
public String acquire(@RequestParam(defaultValue = "1") Integer count) {
rateLimiter.acquire(count);
log.info("success, rate is {}", rateLimiter.getRate());
return "success";
}
}

基于Nginx 的IP限流、服务器级别限流

nginx 的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 根据 IP 限制速度 
# 1) $binary_remote_addr : binary_ 目的是缩写内存占用,remote_addr 表示通过 IP 地址来限流
# 2) zone=iplimit:20m : iplimit 是一个内存区域(记录访问频率信息),20m 是指这块内存区域大小
# 3) rate=1r/s : r 代表 request,s 代表 seconds,比如 100r/m,标识访问的限流频率
limit_req_zone $binary_remote_addr zone=iplimit:20m rate=10r/s;

# 根据服务器级别做限流,一般的,服务器级别的限流速率是很大的,但为了测试,才改的比上面的小
limit_req_zone $server_name zone=serverlimit:10m rate=1r/s;

server {
listen 80
server_name www.rate-limiter.com
location /access-limit/ {
proxy_pass http://127.0.0.1:10000/;
# 1) zone=iplimit : 引用 limit_req_zone 中的 zone 变量
# 2) burst=2 : 设置一个大小为 2 的缓冲区域,当大量请求到来,请求数量超过限流频率时,将其放入缓冲区域
# 3) nodelay : 缓冲区满了以后,直接返回 503 异常
limit_req zone=iplimit burst=2 nodelay;

limit_req zone=serverlimit burst=1 nodelay;
}
}

基于Nginx 的连接数限制和单机限制

nginx 的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 基于连接数、IP的限流配置
limit_conn_zone $binary_remote_addr zone=perip:20m;
# 基于连接数、服务器的限流配置
limit_conn_zone $server_name zone=perserver:20m;

server {
listen 80;
server_name www.rate-limiter.com;
location /access-limit/ {
proxy_pass http://127.0.0.1:10000/;

# 每个 IP 地址最多保持 1 个连接
limit_conn perip 1;
# 每个 server 做多保持 100 个连接
limit_conn perserver 100;

# 异常情况返回的 HTTP 状态码, 默认 503;
limit_req_status 504;
limit_conn_status 504;
}

}

TODO 基于Nginx + Lua + Redis 实现动态封禁 IP

基于Redis+Lua的分布式限流

Lua 限流脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 用作限流的 Key, (一秒一个)
local key = KEYS[1]
redis.log(redis.LOG_DEBUG, 'key is ', key)

-- 限流的最大阈值
local limit = tonumber(ARGV[1])

-- 当前流量大小
local current = tonumber(redis.call("get", key) or "0")

-- 如果超出限流大小
if current + 1 > limit then
return false
else
-- 请求数 +1,并设置 2 秒过期
redis.call("INCRBY", key, "1")
redis.call("expire", key, "2")
return true
end

使用 spring-boot-starter-data-redis 结合 lua 限流脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Slf4j
@Aspect
@Component
public class AccessLimiterAspect {

@Autowired
private RedisLuaAccessLimiter redisLuaAccessLimiter;

@Pointcut("@annotation(AccessLimiter)")
public void cut() {

}

@Before(value = "cut()")
public void before(JoinPoint joinPoint) {
// 获取方法签名作为 KEY
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();

AccessLimiter annotation = method.getAnnotation(AccessLimiter.class);
String key = annotation.key();
if (StringUtils.isBlank(key)) {
// 如果没有设置 key,就自动生成
Class<?>[] parameterTypes = method.getParameterTypes();
key = method.getName();
String paramTypes = Arrays.stream(parameterTypes)
.map(Class::getName)
.collect(Collectors.joining(","));
key += ("#" + paramTypes);
}
Integer limit = annotation.limit();

// 调用redis
redisLuaAccessLimiter.limitAccess(key, limit);
}
}

[TOC]

分布式限流介绍

限流的目的是通过对并发访问/请求进行限速或者在一个时间窗口内的请求进行限速来保护系统,一旦请求到达限制速率则可以拒绝服务、排队或等待、降级。

主要是通过压测时找出每个系统的处理峰值,然后通过处理设定峰值阈值来防止系统过载时,通过拒绝处理过载的请求来保障系统可用性。

阅读全文 »

什么是幂等性?

  • 幂等性:f(f(x)) = f(x)
  • 幂等元素运行多次,还等于它原来的运算结果
  • 在系统中,一个接口运行多次,与运行一次的效果是一致的
阅读全文 »