背景:
使用 nginx 代理 tcp 服务,假如后端有一个 tcp 的业务服务,中间用 nginx 做代理,前面有 1000 个客户端连接,那么每当一个客户端连接 nginx 的时候,nginx 就会发起一个 tcp 连接至后端的业务服务。
问题:
假设我的那个 tcp 后端服务是负责提供下载地图数据的,对所有的 1000 个客户端来说,从地图服务器下载的数据都是一样的。那么当一个客户端连接上 nginx 的时候,nginx 发起了一个 tcp 连接至地图服务器,地图服务器把地图数据推给了 nginx ,nginx 把数据转给了客户端,这样相当于把一份地图数据在网络中走了两遍(地图服务器到 nginx ,nginx 到客户端),当地图数据比较大的时候,带来的开销就很大了。
想要达到的效果:
能否对 nginx 做改造,在 nginx 中缓存一下地图数据,当第一个客户端连接 nginx 的时候,nginx 从后端的地图服务器获取了地图数据,一直缓存在内存里,后面 999 个客户端连接的时候,nginx 就直接把地图数据推给客户端,而不再朝后端的地图服务器下载地图数据。
如果想实现上述的效果,需要对 stream 模块做改造吗?还是有其他更好的解决办法?麻烦各位老哥赐教。
1
killva4624 2022-12-01 11:10:51 +08:00
一个问题,nginx 如何识别这 999 个客户端里,哪些需要缓存,哪些是需要请求新的数据呢?
|
2
liuguangxuan OP @killva4624 老哥精准的理解了我的困惑,我需要解决的就是这部分。
如果想要精准的识别哪些需要缓存,哪些是需要请求新的数据,我需要怎么做?对 nginx 的 stream 模块改造,加自定义协议吗?是否还有更好的办法? |
3
cnoder 2022-12-01 11:14:54 +08:00
有反向代理缓存,proxy_cache
|
4
ng1nx 2022-12-01 11:16:51 +08:00
能否把地图上数据推到 Redis ,设置数据有效期(time to live); nginx 从 Redis 取数据,如果取不到就从后端再取一次。
|
5
cnoder 2022-12-01 11:18:13 +08:00
想复杂一点可以用 openresty 这样的,自己实现一下
|
6
victorc 2022-12-01 11:19:19 +08:00
nginx+lua 就是干这个的,不建议直接修改 nginx 的 c 代码,可维护性太差了
去翻 openrestry 的文档吧,能解决 |
7
jiangzm 2022-12-01 11:21:44 +08:00
地图服务应该下发一个更新包链接(文件服务或者 OSS 存储),给客户端下载下来。
|
8
wzy44944 2022-12-01 11:23:16 +08:00
文件小的话 nginx 自己就有个内存缓存,一般十几 KB 的数据可以用,再多会有些性能和功能上的问题,文件比较多或者比较大就需要 nginx+缓存软件比如 ATS ,squid 。是否需要 cache 这个在 http 交互里有标准的,比如请求头里的 no-cache,响应头里的过期时间
|
9
seers 2022-12-01 11:24:25 +08:00 via Android
起一个 mem 数据库,ng 从里面拿数据
|
10
killva4624 2022-12-01 11:25:52 +08:00
@liuguangxuan 如果是 HTTP 这种高层协议是有缓存的,自己包 TCP 方法的话可能就像楼上老哥推荐的,走 openrestry 自己实现缓存协议,或者把重的下载数据逻辑放到另外的存储层去下载。
|
11
leafre 2022-12-01 11:25:59 +08:00
在 nginx 中缓存不如在服务端缓存,nginx 和服务端同个内网,哪来的大开销
|
12
lambdaq 2022-12-01 11:31:05 +08:00 7
为什么人们不基于 tcp 直接做浏览器,而是要搞个 http 。。就是为了解决 LZ 这问题的。。http 特点之一就是为了方便中间盒子来缓存。。。。
|
13
iphoneXr 2022-12-01 11:31:07 +08:00
我的做法一般都是 负载均衡做 4 层代理转发 Nginx 做 7 层代理转发和静态文件
其他下载类的尽量用 OSS 便宜且稳定 |
14
iphoneXr 2022-12-01 11:32:41 +08:00
还没有写完 提前提交了
缓存类走 CDN 主要是流量便宜的多 |
16
0ZXYDDu796nVCFxq 2022-12-01 11:36:10 +08:00
nginx 的 stream 模块不识别 TCP 协议的上一层协议
所以无法对数据进行缓存 如果要实现缓存,你得在 nginx 里写个模块,识别你的协议,然后才能解包出原数据并对数据进行缓存 搞这么麻烦不如换 HTTP 算了 |
17
rrfeng 2022-12-01 11:36:54 +08:00 8
1. TCP 是数据流,不存在『缓存』这种东西,你没法缓存一个『数据流』
2. 所以要做的是把『数据流』变为『数据块』,有边界的数据就能够缓存了 3. 那么在 TCP 里自己切分一下数据,做一个『自定义协议』来实现『数据块』 4. 所以为什么不用 HTTP ? |
18
pjntt 2022-12-01 11:37:08 +08:00
这样做让 nginx 完成的事情就很多了,比如用户鉴权。或者你可以想想在应用服务端上做这个缓存处理。代理就只做代理工作就好。
|
20
xyjincan 2022-12-01 12:05:49 +08:00
这是服务器下载地图,发送地图都使用公网带宽是吗,开发一个地图客户端代理服务,地图数据缓存在硬盘上吧
|
21
villivateur 2022-12-01 13:01:07 +08:00
TCP 本身不能缓存,你得要一个更高层次的应用层服务。
然后,既然用到应用层服务了,为什么不直接用 HTTP ? |
23
DefoliationM 2022-12-01 13:50:19 +08:00
自己写个 tcp 转发吧,不复杂
|
24
xuanbg 2022-12-01 14:17:35 +08:00
TCP 要什么代理?哦哦,你是想在代理服务器上缓存数据来加速访问。可 TCP 是流,这可怎么缓存?再说,没有应用层协议分包,你这边怎么知道数据读完了?这不就那个啥,“粘包”了吗?
你这必须要自己造个协议来传数据才行。既然是自己造的协议,那就要在 nginx 上加载一个识别这个协议的模块。既然模块都加载了,你想干点什么还不是你自己说了算? |
25
liuguangxuan OP @cnoder
@victorc @killva4624 看到了 3 位老哥都推荐了 openrestry ,刚查了一下资料,openrestry 是 nginx+lua ,我用 lua 控制我想要实现的逻辑,是这样的吗? |
26
liuguangxuan OP @xuanbg 老哥,原有的地图服务是自定义协议和客户端进行传输的,传输 tcp 数据流,所以造协议这块儿已经有了,缺的是您说的这个模块,如何加在 nginx 上?
|
27
liuguangxuan OP @DefoliationM tcp 转发之前写过一套,现在想把它和 nginx 统一到一起,这样就能统一 http 和 tcp 的入口。所以才有了我上面的提问。
|
28
seakingii 2022-12-01 14:41:25 +08:00
NGINX+LUA,NGINX 是 C 代码,不好改,LUA 是脚本,容易改一点,
不过, TCP 缓存不好做啊,像上面说的,TCP 是流,不好定边界 另外你们自己实现的协议 ,那你至少要在 LUA 这边解析你们的请求流,根据请求流返回原始数据或者返回缓存数据? 最简单的就是 NGINX+LUA 来缓存 HTTP,很好解析请求也很好缓存 |
29
liuguangxuan OP @seakingii 是的,我目前了解到的也是这种,其中 lua 控制业务逻辑,来实现是从内存中取缓存数据推送给客户端,还是重新建立 socket 连接至地图服务器,进行数据的拉取。
|
30
seakingii 2022-12-01 15:54:16 +08:00
@liuguangxuan 如果只是这样的转发,是否自己用代码做个端口转发,用 C++或者 GO 这样的编译语言来实现,性能更高?而不是利用 NGINX+LUA 这样的组合
|
31
liuguangxuan OP @seakingii 可能是我没有表达清楚哈,老哥。我们之前写了一个转发 tcp 的服务( C++的),中间缓存了地图的数据,所以对下面 1000 个客户端的话,它只向地图服务器请求一次数据即可,然后对下转发给 1000 个客户端。
现在我们又新增了一些 web 的服务,所以会有 http 请求。 我想把 web 的 http 请求和地图的 tcp 请求统一入口,都走 nginx ,或者都走我们之前写的 tcp 转发服务。所以就有了我今天问的问题,我想在 nginx 里面集成我们之前的 tcp 转发服务的功能(并且能对地图服务只保持 1 个 socket 连接,请求 1 次地图数据,节省带宽),但是不知道该怎么集成进去。😂 |
32
liuguangxuan OP @seakingii 看看各位老哥,有没有什么更好的方案。
|
33
Twan 2022-12-01 17:02:18 +08:00
怎么感觉是个开发游戏的
|
34
ysc3839 2022-12-01 17:05:19 +08:00
游戏资源下载用 http 没什么问题吧?
|
35
xhinliang 2022-12-01 17:09:17 +08:00
TCP 是传输层协议,是一个流协议,没听说过在传输层协议做缓存的....
再者,这个「把一份地图数据在网络中走了两遍」,如果是内网的话,是不是开销可以忽略不计? |
36
JingKeWu 2022-12-01 17:21:55 +08:00
tcp 是流,没办法缓存。建议还是采用 http ,用 p2p 分发
|
37
JingKeWu 2022-12-01 17:22:30 +08:00
或者自定义协议 加入缓存
|
38
seakingii 2022-12-01 17:36:42 +08:00
|
39
rrfeng 2022-12-01 17:51:06 +08:00
不用 HTTP ,那就在 Nginx 里把你 C++ 实现的『私有协议』搞一遍就行了。
Nginx Stream 就是纯 TCP 代理,做不到你想要的。 |
40
wangritian 2022-12-01 18:16:51 +08:00
也可以考虑用 go 自己写一个网关代替 nginx ,想搞啥都方便
|
41
liuguangxuan OP |
42
liuguangxuan OP @seakingii #38 多谢老哥耐心解答。
|
43
liuguangxuan OP @rrfeng #39 老哥,你提到的“那就在 Nginx 里把你 C++ 实现的『私有协议』搞一遍就行了”,具体怎么实现呢?
我目前了解到的信息有: 方式 1:直接改造 nginx 代码,但是感觉这种难度比较大,最起码需要看懂 stream 模块的代码及变量。 方式 2:nginx 好像支持扩展模块,用 C 语言,新增自定义的 nginx 模块,但是目前还不清楚自己增加的 nginx 模块如何截获 stream 模块的流量数据,然后加上自己的私有协议。 方式 3:使用 OpenResty ,用 lua 模块重写,但是问题和方式 2 一样。 烦请老哥有空的时候帮忙解答一下疑惑。 |
44
liuguangxuan OP @wangritian #40 go 语言不熟啊老哥。
@xhinliang #35 不能忽略不计,老哥。业务场景类似于游戏,而且地图数据特别大,如果按 1G 来计算的话,1000 个客户端同时开一局,那么这种局域网的流量也就是 1000G 。 |
45
ysc3839 2022-12-01 19:53:29 +08:00 via Android
@liuguangxuan 实时性要求高和使用 http 传输资源冲突吗? CS 都支持 http 下载地图呀。
|
46
rrfeng 2022-12-01 20:03:57 +08:00
@liuguangxuan
这得靠你自己学了 |
47
garyox64 2022-12-01 20:10:32 +08:00
我理解用啥改,你都得学 nginx 的模块和架构设计了
|
48
liuguangxuan OP |
49
wangritian 2022-12-01 21:29:15 +08:00
@liuguangxuan go 语法非常精简,容易学习,其中的精髓 goroutine 协程开发容易,并发性能也强,非常适合做网络中间件,推荐你趁机实践一下
|
50
liuguangxuan OP @wangritian #49 多谢老哥。
|
51
maichaide 2022-12-01 21:37:09 +08:00
不能用 squid 缓存么?
|
52
ouyangjun 2022-12-01 21:49:22 +08:00
Openresty 本身的 stream 模块支持上下游连接,然后你可以在 lua 里面保存数据,对于你的需求来说应该是可以完成的,有兴趣的话我们可以聊聊细节的。
|
53
des 2022-12-01 22:10:00 +08:00 via iPhone
想想办法能不能改造客户端?
地图用 http 也能瓦片加载啊,你这种自定义协议就不建议在 nginx 上改了 |
54
0ZXYDDu796nVCFxq 2022-12-01 22:27:42 +08:00 via Android
HTTP 协议把地图通过 CDN 分发不更快?
HTTP2 就是个二进制协议,有啥效率问题啊 |
55
liuguangxuan OP @maichaide #51 老哥,这个 squid 技术是针对 tcp 的吗?
|
56
liuguangxuan OP @ouyangjun #52 老哥,怎么联系?
|
57
liuguangxuan OP @des #53 感谢老哥,改造客户端是个办法。但是目前所有的后台服务还是以 tcp 为主,不仅仅是这一个地图服务器,目前后台服务加进来几个 web 服务,所以想统一一下所有后台服务的入口。
|
58
liuguangxuan OP @gstqc #54 感谢老哥,目前因为历史原因,后台已有的服务还是以 tcp 为主,不仅仅是这一个地图服务器,如果改造成 http 或者 http2 的话,代价可能比改造 nginx 还大。
|
59
des 2022-12-01 22:50:49 +08:00 via iPhone
@liuguangxuan 如果说地图不是经常变动的话,长期来讲还是建议改客户端走 cdn
|
60
des 2022-12-01 22:51:59 +08:00 via iPhone
客户端一旦多了之后,你这系统就没法部署
|
61
Weixiao0725 2022-12-02 00:42:32 +08:00
我觉得,这问题真的是匪夷所思。你应该缓存的是所有参数相同的请求,跟客户没有关系。如果有第 1001 个客户请求用相同的参数请求数据,就不走缓存了? 第二点是,专业的人干专业的事,nginx 被设计出来就不是干缓存的,要不然要 memcached, redis 是干啥的?
|
62
PolarBears 2022-12-02 07:47:11 +08:00
没必要执着在 nginx 里做 tcp 的协议识别和缓存吧,不如直接用别的高级语言实现一个 tcp 转发+缓存功能来的更快,
反正都是在一台服务器上跑的。 |
63
liuguangxuan OP @des 老哥,非公网,内部网络,没有 cdn 。😂
@Weixiao0725 已经有了一个类似 redis 的缓存服务了,我的疑问点是 nginx 到缓存服务( redis ),这一段,地图数据的流量太大了,如果每连上一个客户端,就要 nginx 向 redis 请求一次的话,网络压力很大。又因为地图数据对每个客户端来说都是一样的,怎么样能把 nginx 到 redis 这一段给只请求一次,然后 nginx 就可以转发给下面所有的客户端。 @PolarBears 老哥,您提到的这个“不如直接用别的高级语言实现一个 tcp 转发+缓存功能来的更快”,我们已经自己写了这样的一个后台服务,类似于网关,现在呢,又多了一些 B/S 的后台服务,所以有了 http 的流量,我们想把 http 的流量和 tcp 的流量统一入口。所以才有了我的提问。 |
64
julyclyde 2022-12-02 09:16:41 +08:00
把控制流放在自定义协议里
资源用 http 另一个通道来传输就行了呗 要善用现有的基础设施 不要重复发明问题 |
65
0ZXYDDu796nVCFxq 2022-12-02 09:25:31 +08:00 via Android
散了吧,我总结出 OP 的问题其实是
我的历史包袱很重,我的架构不能动,你们看有什么东西能帮我实现,明天上线! |
66
ysjdx 2022-12-02 09:33:45 +08:00
@liuguangxuan nginx+lua 用共享内存做一次缓存不就行了?
|
67
Ansen 2022-12-02 09:42:54 +08:00
统一入口后,是否可以区分一下流量?比如:tcp==>缓存服务,http ==>缓存服务转发==>后台服务
|
68
fengfisher3 2022-12-02 09:46:38 +08:00
还没这方面的经验,希望楼主有合理的方案后,帮不上忙。整理分享一下,谢谢。
|
69
lostsquirrelX 2022-12-02 09:57:19 +08:00
有没有可能这个 X-Y Problem
|
70
duckyrain 2022-12-02 10:16:30 +08:00
你们是不是该考虑使用 UDP 协议了
|
71
angryfish 2022-12-02 10:38:35 +08:00
你纠结的 nginx 到地图服务器之间的流量。
你原来就有一个 tcp 缓存代理。 但你又想用 nginx 统一 http 和这个 tcp 。 那你不如把原来的 tcp 缓存代理和 nginx 部署在同一个服务器。nginx 在代理去旧的 tcp 缓存代理。可解决内部网络流量大,http 和 tcp 统一整合。 |
72
kiddingU 2022-12-02 15:26:51 +08:00
丢共享内存可以,丢外部 cache 也行,比如 redis, lua redis 连接池,开销也不大
|
73
PolarBears 2022-12-02 16:55:59 +08:00
@liuguangxuan #63 统一一个入口的意思是想要端口复用吗?如果是想端口复用的话 haproxy 能做到。
|
74
ouyangjun 2022-12-04 01:31:43 +08:00
@liuguangxuan 给我你的微信或者 tg 帐号?
|
75
liuguangxuan OP @ouyangjun #74 绿色:Z3Vhbmd4dWFubGl1
|