入门了解

【10分钟学会DDD领域模型】

bilibili

设计

找名词,就是思考网关整个链路有哪些名词

Config,Request,Response,Filter,FilterChain,Context,Processor(处理逻辑),Rule

找属性

比如Request,Context。Request里要有请求唯一ID,请求url,方式….

项目模块:

BreezeGateway-Core

BreezeGateway-Client

BreezeGateway-Nebula-Bootstrap

BreezeGateway-Register-Center

BreezeGateway-Config-Center

BreezeGateway-Common

BreezeGateway-backend-Dubbo

BreezeGateway-backend-Httpserver

groupId:

io.github.soyorin.gw

1
2
3
<groupId>io.github.soyorin.gw</groupId>
<artifactId>Breeze-Gateway</artifactId>
<version>0.1.0-SNAPSHOT</version>

使用缓存

map,queue。将一些策略比如负载均衡进行缓存

提高吞吐量

除了使用mq,我们还可以使用Disruptor,mcmp队列

适当串行化

串行化适合于耗时短的任务

而并行化适合于耗时长,任务之间无依赖关系,比如远程RPC调用

工作线程

【拒绝八股,线程池线程数设置不再迷茫】

bilibili

readBuffer,writeBuffer优化

CPU瓶颈

特别是Java 21 以前,没有虚拟线程,即使你开启零拷贝,上下文切换这种东西还是有的,但现代CPU其实上下文切换可以忽略不计的。主要是线程调度

网络带宽

相比于cpu,网络带宽更是瓶颈,10w在同一时刻发送,如果你的消息是1kb,那么它就需要100mb的带宽 => 1 gib带宽

去优化你们的 Linux参数,主要是tcp连接有关,文件描述符

消息收发优化

收:

这个感觉没啥要优化的

发:

1.压缩2.使用紧凑型协议,比如说用 protobuf => json

这两种东西其实是用 cpu 换 网络带宽

3.聚合发送/单个改批量

你怎么单个改批量发

1.基于时间or数量

2.kafka stream 滑动窗口来实现批量发

使用Buffer pool 减少内存使用

jvm调优

优化心跳

心跳之间是占带宽的

两种心跳

原来每隔30s就会发心跳,运用了request as ping 后发消息后下一次心跳时间就会往后延迟

1.消息发送-接受就被看做是一次心跳

2.只有超过心跳间隔,也没有收发过消息,才会针对发起心跳

3.websocket频繁收发消息的情况下,几乎不会产生额外的心跳消息(在message里 增加一个 headbeat:true)

总结

插件过滤使用单异步模式

请求响应使用双异步模式

单异步和双异步模式

单异步模式其实就是指你发送和接受请求的线程是同一个,那么你是没法同时发送和接受的

怎么实现双异步模式呢?

用不同的线程去发送接收请求

completableFuture演示

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
37
38
39
40
41
42
43
44
45
46
47
48
package async;

import java.util.concurrent.*;
/**
* @author: lkl
* @date: 2025/7/12 17:20
* 说明单异步和双异步模式的区别
*/
public class CompletableFutureDemo {
public static void main(String[] args) {
//ExecutorService pool = Executors.newFixedThreadPool(1);
////单异步
//CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
// System.out.println("异步任务线程:" + Thread.currentThread().getName());
// return 10;
//},pool).thenApply(result -> {
// System.out.println("回调线程:" + Thread.currentThread().getName()); // 可能是主线程
// return result * 2;
//});
////双异步
//CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
// System.out.println("异步任务线程:" + Thread.currentThread().getName());
// return 10;
//},pool).thenApplyAsync(result -> {
// System.out.println("回调线程:" + Thread.currentThread().getName()); // 一定是异步线程
// return result * 2;
//},pool);


//单异步
CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
System.out.println("异步任务线程:" + Thread.currentThread().getName());
return 10;
}).thenApply(result -> {
System.out.println("回调线程:" + Thread.currentThread().getName()); // 可能是主线程
return result * 2;
});
//双异步
CompletableFuture<Integer> future4 = CompletableFuture.supplyAsync(() -> {
System.out.println("异步任务线程:" + Thread.currentThread().getName());
return 10;
}).thenApplyAsync(result -> {
System.out.println("回调线程:" + Thread.currentThread().getName()); // 一定是异步线程
return result * 2;
});

}
}

但其实也不是绝对,如果使用的线程池,线程有限,发送和接收也有可能用的同一个线程

使用线程池的时候记得关闭,不然main线程退出后,会由于还存在用户线程,导致程序卡住

thenApplyAsync和thenApply区别

https://segmentfault.com/q/1010000042935593

单异步和双异步的适用场景

如果我们的下游服务处理比较快,那么双异步模式就会比较吃亏,因为会频繁地进行上下文切换,这样耗时就

会比单异步的大

Future局限性和CompletableFuture使用

Future的局限性

CompletableFuture原理与实践-外卖商家端API的异步化

CompletableFuture使用:

创建异步线程

1
2
runAsync
applyAsync

获取结果

1
2
3
4
//
future.get()
//
future.jion()

  • 高性能组件:Netty、Disruptor
  • 异步交互:asynchttpclient
  • 缓存:caffeine、guava
  • 序列化:protobuf、json
  • 注册中心:Etcd、Nacos、Zookeeper
  • 配置中心:Nacos

网关大体分为两种:流量和业务网关

SpringCloud Gateway:依赖Spring生态、启动时间较长

Zuul:早期版本是同步阻塞的,而新版本代码复杂,学习成本高,且停止维护了

Nginx:更适合静态资源的请求,比如html、css、图片、音频、视频等