Spring Boot 启动流程

spring boot 启动流程必须懂。

启动类示例:

1
2
3
4
5
6
7
8
@SpringBootApplication
public class HiApplication {

    public static void main(String[] args) {
        // 先执行 SpringApplication 的构造方法,然后执行 run 方法
        SpringApplication.run(HiApplication.class, args);
    }
}

SpringApplication#run

源码位置: org.springframework.boot.SpringApplication#SpringApplication

常见的限流算法

固定窗口算法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
type FixedWindowRateLimiter struct {
	// 固定窗口大小, 单位ms
	windowInterval time.Duration
	// 限制
	limit int
	// 窗口开始时间
	prevTime time.Time
	// 当前限制
	curLimit int
}

func (s *FixedWindowRateLimiter) acquire() (bool, error) {
	// 不在一个时间窗口,重置
	if time.Until(s.prevTime) > s.windowInterval {
		s.curLimit = 0
	}
	s.curLimit++
	s.prevTime = time.Now()
	return s.curLimit < s.limit, nil
}

令牌桶算法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
type TokenBucketRateLimiter struct {
	// 桶大小
	bucketSize int
	// 速率,单位ms
	rate int
	// 剩余令牌数
	remainTokens int
	// 时间
	prevTime time.Time
}

func (t *TokenBucketRateLimiter) acquire() (bool, error) {
	// 计算新的令牌数
	newTokens := int(time.Until(t.prevTime).Milliseconds()) * t.rate
	t.remainTokens += newTokens
	if t.remainTokens >= t.bucketSize {
		t.remainTokens = t.bucketSize
	}
	t.remainTokens--
	t.prevTime = time.Now()
	return t.remainTokens > 0, nil
}

漏桶算法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
type LeakyBucketRateLimiter struct {
	// 速率,单位ms
	rate int
	// 剩余令牌数
	remainTokens int
	// 时间
	prevTime time.Time
}

func (l *LeakyBucketRateLimiter) acquire() (bool, error) {
	// 不是同一毫秒,重置令牌数
	if time.Now().UnixMilli() != l.prevTime.UnixMilli() {
		l.remainTokens = l.rate
	}
	l.remainTokens--
	l.prevTime = time.Now()
	return l.remainTokens > 0, nil
}

示例代码

demo-ratelimiter

简单实现 Gossip 协议

gossip 协议是为实现最终一致性提出的。

实现思路

每个节点都有基本属性,如 id, addr, port
每个节点都有成员列表 members,存储一部分数据 data
通过定时随机节点发送请求,同步成员列表给随机节点,这样就能达到成员列表最终一致性
客户端访问数据,先根据 key 来计算 hash 值,对成员列表取余,确定是哪个节点上,要么返回数据要么转发请求