Proxy server and client for Local Area Network (LAN) using netty.
内网部署了多个服务,如图中蓝色所示。
Proxy Server 是代理服务,部署在公网服务器上。
监听两种端口。
- 一个端口。负责建立与 Proxy Client 之间的 TCP 连接通道。
- 多个端口。每个端口负责一个被代理的服务,内存存储
listenedPort
->realServerPort
的映射。
Proxy Client 是代理客户端,部署在内网服务器上,需要能够访问到需要被代理的服务。
可以有多个 Proxy Client。
每个 Proxy Client 向 Proxy Server 主动发起多次 TCP 连接请求。这样在 Proxy Server 与 Proxy Client 间建立了多个 TCP 连接,二者可以通过这些连接相互发送数据。
- Proxy Server 与用户建立 TCP 连接后,
- 为这个 TPC 连接分配一个编号
channelId
,存储channelId
到clientChannel
的映射。 - 解析出是哪个端口接收到的请求
listenedPort
,找到对应的realServerPort
。 - 从与 Proxy Client 的多个 TCP 通道中选择一个,之后这个用户的连接都是用这个 TCP 通道转发数据。
- 为这个 TPC 连接分配一个编号
- 收到用户的数据,
- 用自定义协议把数据包进行封装:消息长度(2 bytes) + 消息类型(1 byte) +
channelId
(4 bytes) +realServerPort
(2byte) + 数据包。
- Proxy Client 收到 Proxy Server 的数据包后,解析封装的数据包,存储
channelId
与proxyChannel
的映射关系。。 - 看
channelId
是否已经与realServerPort
的 Real Server 建立了 TCP 连接。- 如果没有建立,则发起 TCP 连接请求
serverChannel
,存储channelId
与serverChannel
的映射关系。 - 建立连接后,使用这个连接发送数据。
- 如果没有建立,则发起 TCP 连接请求
- Proxy Client 收到 Real Server 的请求后,根据
serverChannel
找到channelId
。 - 通过
channelId
找到proxyChannel
。 - 封装数据包。
- 用
proxyChannel
发送数据包给 Proxy Server。
- Proxy Server 解析数据包,得到
channelId
。 - 用
channelId
找到clientChannel
。 - 把数据包发送给
clientChannel
。
- 使用 VisualVM 发现
wait()
和select()
占 CPU 比率比较靠前。Proxy Server 与 Proxy Client 之间建立多个 TCP 连接,避免所有数据都通过一个连接发送、成为瓶颈。 - 使用 VisualVM 发现 CAS 占 CPU 比率比较靠前。
LengthFieldBasedFrameDecoder
+ProxyDecoder
改为ProxyDecoder
继承LengthFieldBasedFrameDecoder
,避免过长的piepline
、多次 retain/release(需要 CAS 更新引用计数)。 - 使用 VisualVM 发现 allocate 占 CPU 比率比较靠前。
ProxyEncoder
中使用CompositeByteBuf
组装数据。 - 心跳机制:
- 客户端每隔一段时间(5s)向服务端发起心跳请求。
- 服务端如果隔了 5s 没有收到心跳请求,就关闭这个socket连接。
- 客户端如果发现 TCP 连接断开了,就发起重启连接的尝试。每次失败后尝试时间增长。
- 使用抽象工厂模式,不同平台使用不同的
ServerSocketChannel
、SocketChannel
、EventLoopGroup
。
Maven
> 3.0Java
> 8.0
- Use
Junit
to test. - Use
travis
to build after push to GitHub.
# 生成server私钥和证书仓库
$ keytool -genkey -alias netty-lan-proxy-server -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass sNetty -storepass sNetty -keystore server.jks
# 生成server自签名证书
$ keytool -export -alias netty-lan-proxy-server -keystore server.jks -storepass sNetty -file server.cer
# 生成client密钥对和证书仓库
$ keytool -genkey -alias netty-lan-proxy-client -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass cNetty -storepass cNetty -keystore client.jks
# 生成client签名证书
$ keytool -export -alias netty-lan-proxy-client -keystore client.jks -storepass cNetty -file client.cer
# 将server的证书导入到client的证书仓库中
$keytool -import -alias netty-lan-proxy-server -trustcacerts -file server.cer -storepass cNetty -keystore client-trust.jks
# 将client的证书导入到server的证书仓库中
keytool -import -alias netty-lan-proxy-client -trustcacerts -file client.cer -storepass sNetty -keystore server-trust.jks
$ mvn clean test
$ mvn clean package