1 高性能限流器GuavaRateLimiter
令牌桶算法:记录一个下一令牌产生的时间,并动态更新它,就能够轻松完成限流功能
与信号量区别:信号量是“一次性可以有多少个线程一起执行”,限流器是“每秒最多允许几个请求通过”“1个请求/xxx秒”
申请令牌时调用acquire方法
1.如果请求时间now在下⼀令牌产⽣时间next之后:
resync:通过(now-next)/interval来新增令牌,并且因为next到now这段时间的令牌发放的逻辑处理完毕了,所以将下一次发放令牌的时间next重置为当前时间now
reserve:然后根据令牌数来判断next:如果令牌桶中还有令牌,则next不用变,减去一个令牌当作被预支了;如果没有则需要变:因为当前next发放的这个令牌会给当前请求使用(sleep掉next-now的时间后再返回,等价于获取此令牌),next需要变为下一个令牌的发放时间,等待下一个请求;
2.如果请求时间now在下⼀令牌产⽣时间next之前:
reserve:同上
class SimpleLimiter {
//当前令牌桶中的令牌数量
long storedPermits = 0;
//令牌桶的容量
long maxPermits = 3;
//下⼀令牌产⽣时间
long next = System.nanoTime();
//发放令牌间隔:纳秒
long interval = 1000_000_000;
//请求时间在下⼀令牌产⽣时间之后,则
// 1.重新计算令牌桶中的令牌数
// 2.将下⼀个令牌发放时间重置为当前时间
void resync(long now) {
if (now > next) {
//新产⽣的令牌数
long newPermits = (now-next)/interval;
//新令牌增加到令牌桶
storedPermits = min(maxPermits,storedPermits + newPermits);
//将下⼀个令牌发放时间重置为当前时间
next = now;
}
}
//预占令牌,返回能够获取令牌的时间
//如果令牌桶中还有令牌,则next不用变
//如果没有令牌,则next需要变
synchronized long reserve(long now){
resync(now);
//能够获取令牌的时间
long at = next;
//令牌桶中能提供的令牌
long fb=min(1, storedPermits);
//令牌净需求:⾸先减掉令牌桶中的令牌
long nr = 1 - fb;
//重新计算下⼀令牌产⽣时间
next = next + nr*interval;
//重新计算令牌桶中的令牌
this.storedPermits -= fb;
return at;
}
//申请令牌
void acquire() {
//申请令牌时的时间
long now = System.nanoTime();
//预占令牌
long at = reserve(now);
long waitTime=max(at - now, 0);
//按照条件等待
if(waitTime > 0) {
try {
TimeUnit.NANOSECONDS.sleep(waitTime);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}漏桶算法:
请求就像水一样注入漏桶,漏桶会按照一定的速率自动将水漏掉,只有漏桶里还能注入水的时候,请求才能通
过限流器。
2 高性能网络应用框架Netty
线程模型直接影响着网络程序的性能,所以这里分析一下
BIO线程模型:
为每个socket分配一个独立的线程,这样就不会因为线程阻塞在一个socket上而影响对其他socket的读写
每个连接上的请求并不频繁,所以线程大部分时间都在等待I/O就绪。也就是说线程大部分时间都阻塞在那里,这完全是浪费

理想的线程模型:
用一个线程来处理多个连接

使用Reactor模式实现理想线程模型:

核心逻辑:
首先通过同步事件多路选择器提供的select()方法监听网络事件
当有网络事件就绪后,就遍历事件处理器来处理该网络事件
由于网络事件是源源不断的,所以在主程序中启动Reactor模式,需要以while(true){} 的方式调用handle_events()方法。
void Reactor::handle_events(){
//通过同步事件多路选择器提供的
//select()⽅法监听⽹络事件
select(handlers);
//处理⽹络事件
for(h in handlers){
h.handle_event();
}
}
// 在主程序中启动事件循环
while (true) {
handle_events();Netty线程模型:

Netty中最核心的概念是事件循环(EventLoop),其实也就是Reactor模式中的Reactor,负责监听网络事件并调用事件处理器进行处理;
网络连接和EventLoop是稳定的多对1关系,而EventLoop和Java线程是1对1关系;
稳定指的是关系一旦确定就不再发生变化。也就是说一个网络连接只会对应唯一的一个EventLoop,而一个EventLoop也只会对应到一个Java线程,所以一个网络连接只会对应到一个Java线程;
一个网络连接对应到一个Java线程上,最大的好处就是对于一个网络连接的事件处理是单线程的,这样就避免了各种并发问题。
一个EventLoopGroup由一组EventLoop组成;
实际使用中,一般都会创建两个EventLoopGroup,一个称为bossGroup,一个称为workerGroup;
bossGroup就用来处理连接请求的,workerGroup是用来处理读写请求的;
bossGroup处理完连接请求后,会将这个连接提交给workerGroup来处理
使用轮询算法来决定新的连接会交给workerGroup的哪个EventLoop来处理
3 高性能队列Disruptor
在高并发场景下,用来代替ArrayBlockingQueue 和 LinkedBlockingQueue的有界队列
高性能的原因:
1.内存分配更加合理,使用RingBuffer数据结构,数组元素在初始化时一次性全部创建,提升缓存命中率;
对象循环利用,避免频繁GC:
消费者线程在消费的时候,是遵循空间局部性原理的;
消费完第1个元素,很快就会消费第2个元素;
当消费第1个元素E1的时候,CPU会把内存中E1后面的数据也加载进Cache;
如果E1和E2在内存中的地址是连续的,那么E2也就会被加载进Cache中,然后当消费第2个元素的时候,由于E2已经在Cache中了,所以就不需要从内存中加载了,这样就能大大提升性能。

2.能够避免伪共享,提升缓存利用率。(缓存行填充)
3.采用无锁算法,避免频繁加锁、解锁的性能消耗。
4.支持批量消费,消费者可以无锁方式消费多个消息。
4 高性能数据库连接池HiKariCP
性能好的原因:
微观上HiKariCP程序编译出的字节码执行效率更高,站在字节码的角度去优化Java代码
宏观上主要是和两个数据结构有关,一个是FastList,另一个是ConcurrentBag。
FastList适用于逆序删除场景;而ConcurrentBag通过ThreadLocal做一次预分配,避免直接竞争共享资源,非常适合池化资源的分配。
















