更新todolist,服务端适应udp配置调整

This commit is contained in:
aoshiguchen
2023-09-16 17:47:27 +08:00
parent 0301f15628
commit 5b2fe447b2
9 changed files with 143 additions and 64 deletions
@@ -42,14 +42,8 @@ public class ProxyConfig {
@Data
public static class Server {
private Integer bossThreadCount;
private Integer workThreadCount;
private String domainName;
private Integer httpProxyPort;
private Integer httpsProxyPort;
private String keyStorePassword;
private String jksPath;
private Boolean transferLogEnable;
private Tcp tcp;
private Udp udp;
}
@Data
@@ -65,4 +59,22 @@ public class ProxyConfig {
private Boolean heartbeatLogEnable;
}
@Data
public static class Tcp {
private Integer bossThreadCount;
private Integer workThreadCount;
private String domainName;
private Integer httpProxyPort;
private Integer httpsProxyPort;
private String keyStorePassword;
private String jksPath;
private Boolean transferLogEnable;
}
@Data
public static class Udp {
private Integer bossThreadCount;
private Integer workThreadCount;
private Boolean transferLogEnable;
}
}
@@ -1,8 +1,12 @@
package org.dromara.neutrinoproxy.server.base.proxy;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum;
@@ -13,8 +17,8 @@ import org.dromara.neutrinoproxy.core.dispatcher.Dispatcher;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.nio.NioEventLoopGroup;
import org.dromara.neutrinoproxy.server.proxy.core.BytesMetricsHandler;
import org.dromara.neutrinoproxy.server.proxy.core.ProxyTunnelServer;
import org.dromara.neutrinoproxy.server.proxy.core.TcpVisitorChannelHandler;
import org.dromara.neutrinoproxy.server.proxy.core.UdpVisitorChannelHandler;
import org.noear.solon.Solon;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Configuration;
@@ -35,43 +39,80 @@ public class ProxyConfiguration implements LifecycleBean {
public void start() throws Throwable {
List<ProxyMessageHandler> list = Solon.context().getBeansOfType(ProxyMessageHandler.class);
Dispatcher<ChannelHandlerContext, ProxyMessage> dispatcher = new DefaultDispatcher<>("消息调度器", list,
proxyMessage -> ProxyDataTypeEnum.of((int)proxyMessage.getType()) == null ?
null : ProxyDataTypeEnum.of((int)proxyMessage.getType()).getName());
proxyMessage -> ProxyDataTypeEnum.of((int)proxyMessage.getType()) == null ?
null : ProxyDataTypeEnum.of((int)proxyMessage.getType()).getName());
Solon.context().wrapAndPut(Dispatcher.class, dispatcher);
}
@Bean("serverBossGroup")
public NioEventLoopGroup serverBossGroup(@Inject ProxyConfig proxyConfig) {
return new NioEventLoopGroup(proxyConfig.getServer().getBossThreadCount());
@Bean("tcpServerBossGroup")
public NioEventLoopGroup tcpServerBossGroup(@Inject ProxyConfig proxyConfig) {
return new NioEventLoopGroup(proxyConfig.getServer().getTcp().getBossThreadCount());
}
@Bean("serverWorkerGroup")
public NioEventLoopGroup serverWorkerGroup(@Inject ProxyConfig proxyConfig) {
return new NioEventLoopGroup(proxyConfig.getServer().getWorkThreadCount());
@Bean("tcpServerWorkerGroup")
public NioEventLoopGroup tcpServerWorkerGroup(@Inject ProxyConfig proxyConfig) {
return new NioEventLoopGroup(proxyConfig.getServer().getTcp().getWorkThreadCount());
}
@Bean("tcpServerBootstrap")
public ServerBootstrap tcpServerBootstrap(@Inject("serverBossGroup") NioEventLoopGroup serverBossGroup,
@Inject("serverWorkerGroup") NioEventLoopGroup serverWorkerGroup,
public ServerBootstrap tcpServerBootstrap(@Inject("tcpServerBossGroup") NioEventLoopGroup tcpServerBossGroup,
@Inject("tcpServerWorkerGroup") NioEventLoopGroup tcpServerWorkerGroup,
@Inject ProxyConfig proxyConfig
) {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(serverBossGroup, serverWorkerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
if (null != proxyConfig.getServer().getTransferLogEnable() && proxyConfig.getServer().getTransferLogEnable()) {
ch.pipeline().addFirst(new LoggingHandler(TcpVisitorChannelHandler.class));
}
ch.pipeline().addFirst(new BytesMetricsHandler());
ch.pipeline().addLast(new TcpVisitorChannelHandler());
bootstrap.group(tcpServerBossGroup, tcpServerWorkerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
if (null != proxyConfig.getServer().getTcp().getTransferLogEnable() && proxyConfig.getServer().getTcp().getTransferLogEnable()) {
ch.pipeline().addFirst(new LoggingHandler(TcpVisitorChannelHandler.class));
}
ch.pipeline().addFirst(new BytesMetricsHandler());
ch.pipeline().addLast(new TcpVisitorChannelHandler());
}
});
return bootstrap;
}
@Bean("udpServerBossGroup")
private NioEventLoopGroup udpBossGroup(@Inject ProxyConfig proxyConfig) {
return new NioEventLoopGroup(proxyConfig.getServer().getUdp().getBossThreadCount());
}
@Bean("udpServerWorkerGroup")
private NioEventLoopGroup udpWorkerGroup(@Inject ProxyConfig proxyConfig) {
return new NioEventLoopGroup(proxyConfig.getServer().getUdp().getWorkThreadCount());
}
@Bean("udpServerBootstrap")
public Bootstrap udpBootstrap(@Inject("udpServerBossGroup") NioEventLoopGroup udpServerBossGroup,
@Inject("udpServerWorkerGroup") NioEventLoopGroup udpServerWorkerGroup,
@Inject ProxyConfig proxyConfig) {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(udpServerBossGroup)
// 主线程处理
.channel(NioDatagramChannel.class)
// 广播
.option(ChannelOption.SO_BROADCAST, true)
// 设置读缓冲区为2M
.option(ChannelOption.SO_RCVBUF, 2048 * 1024)
// 设置写缓冲区为1M
.option(ChannelOption.SO_SNDBUF, 1024 * 1024)
.handler(new ChannelInitializer<NioDatagramChannel>() {
@Override
protected void initChannel(NioDatagramChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
if (null != proxyConfig.getServer().getUdp().getTransferLogEnable() && proxyConfig.getServer().getUdp().getTransferLogEnable()) {
ch.pipeline().addFirst(new LoggingHandler(UdpVisitorChannelHandler.class));
}
pipeline.addLast(udpServerWorkerGroup, new UdpVisitorChannelHandler());
}
});
return bootstrap;
}
@Bean("tunnelBossGroup")
public NioEventLoopGroup tunnelBossGroup(@Inject ProxyConfig proxyConfig) {
return new NioEventLoopGroup(proxyConfig.getTunnel().getBossThreadCount());
@@ -21,7 +21,7 @@ public class DomainNameController {
@Get
@Mapping("/bind-info")
public String bindInfo () {
return proxyConfig.getServer().getDomainName();
return proxyConfig.getServer().getTcp().getDomainName();
}
}
@@ -0,0 +1,18 @@
package org.dromara.neutrinoproxy.server.proxy.core;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
/**
* @author: aoshiguchen
* @date: 2023/9/16
*/
public class UdpVisitorChannelHandler extends SimpleChannelInboundHandler<DatagramPacket> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket) throws Exception {
}
}
@@ -28,7 +28,7 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
private ProxyConfig proxyConfig;
@Override
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName()) || null == proxyConfig.getServer().getHttpProxyPort()) {
if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName()) || null == proxyConfig.getServer().getTcp().getHttpProxyPort()) {
log.info("no config domain name,nonsupport http proxy.");
return;
}
@@ -42,15 +42,15 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
if (null != proxyConfig.getServer().getTransferLogEnable() && proxyConfig.getServer().getTransferLogEnable()) {
if (null != proxyConfig.getServer().getTcp().getTransferLogEnable() && proxyConfig.getServer().getTcp().getTransferLogEnable()) {
ch.pipeline().addFirst(new LoggingHandler(HttpProxy.class));
}
ch.pipeline().addFirst(new BytesMetricsHandler());
ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getDomainName()));
ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getTcp().getDomainName()));
}
});
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpProxyPort()).sync();
log.info("Http代理服务启动成功!port:{}", proxyConfig.getServer().getHttpProxyPort());
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getTcp().getHttpProxyPort()).sync();
log.info("Http代理服务启动成功!port:{}", proxyConfig.getServer().getTcp().getHttpProxyPort());
} catch (Exception e) {
log.error("http proxy start err!", e);
}
@@ -35,8 +35,8 @@ public class HttpsProxy implements EventListener<AppLoadEndEvent> {
private ProxyConfig proxyConfig;
@Override
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName()) || null == proxyConfig.getServer().getHttpsProxyPort() ||
StringUtils.isEmpty(proxyConfig.getServer().getJksPath()) || StringUtils.isEmpty(proxyConfig.getServer().getKeyStorePassword())) {
if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName()) || null == proxyConfig.getServer().getTcp().getHttpsProxyPort() ||
StringUtils.isEmpty(proxyConfig.getServer().getTcp().getJksPath()) || StringUtils.isEmpty(proxyConfig.getServer().getTcp().getKeyStorePassword())) {
log.info("no config domain name,nonsupport https proxy.");
return;
}
@@ -50,16 +50,16 @@ public class HttpsProxy implements EventListener<AppLoadEndEvent> {
.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
if (null != proxyConfig.getServer().getTransferLogEnable() && proxyConfig.getServer().getTransferLogEnable()) {
if (null != proxyConfig.getServer().getTcp().getTransferLogEnable() && proxyConfig.getServer().getTcp().getTransferLogEnable()) {
ch.pipeline().addFirst(new LoggingHandler(HttpsProxy.class));
}
ch.pipeline().addLast(createSslHandler());
ch.pipeline().addFirst(new BytesMetricsHandler());
ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getDomainName()));
ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getTcp().getDomainName()));
}
});
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpsProxyPort()).sync();
log.info("Https代理服务启动成功!port:{}", proxyConfig.getServer().getHttpsProxyPort());
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getTcp().getHttpsProxyPort()).sync();
log.info("Https代理服务启动成功!port:{}", proxyConfig.getServer().getTcp().getHttpsProxyPort());
} catch (Exception e) {
log.error("https proxy start err!", e);
}
@@ -67,13 +67,13 @@ public class HttpsProxy implements EventListener<AppLoadEndEvent> {
private ChannelHandler createSslHandler() {
try {
InputStream jksInputStream = FileUtil.getInputStream(proxyConfig.getServer().getJksPath());
InputStream jksInputStream = FileUtil.getInputStream(proxyConfig.getServer().getTcp().getJksPath());
SSLContext serverContext = SSLContext.getInstance("TLS");
final KeyStore ks = KeyStore.getInstance("JKS");
ks.load(jksInputStream, proxyConfig.getServer().getKeyStorePassword().toCharArray());
ks.load(jksInputStream, proxyConfig.getServer().getTcp().getKeyStorePassword().toCharArray());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, proxyConfig.getServer().getKeyStorePassword().toCharArray());
kmf.init(ks, proxyConfig.getServer().getTcp().getKeyStorePassword().toCharArray());
TrustManager[] trustManagers = null;
serverContext.init(kmf.getKeyManagers(), trustManagers, null);
@@ -110,8 +110,8 @@ public class PortMappingService implements LifecycleBean {
return;
}
item.setUserName(user.getName());
if (StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) && StrUtil.isNotBlank(item.getSubdomain())) {
item.setDomain(item.getSubdomain() + "." + proxyConfig.getServer().getDomainName());
if (StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(item.getSubdomain())) {
item.setDomain(item.getSubdomain() + "." + proxyConfig.getServer().getTcp().getDomainName());
}
if (NetworkProtocolEnum.HTTP.getDesc().equals(item.getProtocal())) {
item.setProtocal("HTTP(S)");
@@ -151,7 +151,7 @@ public class PortMappingService implements LifecycleBean {
// 更新VisitorChannel
visitorChannelService.addVisitorChannelByPortMapping(portMappingDO);
// 更新域名映射
if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort());
}
return new PortMappingCreateRes();
@@ -192,7 +192,7 @@ public class PortMappingService implements LifecycleBean {
ProxyUtil.removeSubdomainToServerPort(oldPortMappingDO.getSubdomain());
}
// 更新域名映射
if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort());
}
return new PortMappingUpdateRes();
@@ -279,7 +279,7 @@ public class PortMappingService implements LifecycleBean {
portMappingMapper.updateOnlineStatus(OnlineStatusEnum.OFFLINE.getStatus(), new Date());
// 未配置域名,则不需要处理域名映射逻辑
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName())) {
if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName())) {
return;
}
List<PortMappingDO> portMappingDOList = portMappingMapper.selectList(new LambdaQueryWrapper<PortMappingDO>().eq(PortMappingDO::getProtocal, NetworkProtocolEnum.HTTP.getDesc()).isNotNull(PortMappingDO::getSubdomain));
@@ -35,20 +35,27 @@ neutrino:
# 是否开启心跳日志
heartbeat-log-enable: ${HEARTBEAT_LOG:false}
server:
# 线程池相关配置,用于技术调优,可忽略
boss-thread-count: 5
work-thread-count: 20
# http代理端口,默认80
http-proxy-port: ${HTTP_PROXY_PORT:80}
# https代理端口,默认443 (需要配置域名、证书)
https-proxy-port: ${HTTPS_PROXY_PORT:443}
# 如果不配置,则不支持域名映射
domain-name: ${DOMAIN_NAME:}
# https证书配置
key-store-password: ${HTTPS_STORE_PASS:}
jks-path: ${HTTPS_JKS_PATH:}
# 是否开启代理服务报文日志(日志级别为debug时开启才有效)
transfer-log-enable: ${SERVER_LOG:false}
tcp:
# 线程池相关配置,用于技术调优,可忽略
boss-thread-count: 5
work-thread-count: 20
# http代理端口,默认80
http-proxy-port: ${HTTP_PROXY_PORT:80}
# https代理端口,默认443 (需要配置域名、证书)
https-proxy-port: ${HTTPS_PROXY_PORT:443}
# 如果不配置,则不支持域名映射
domain-name: ${DOMAIN_NAME:}
# https证书配置
key-store-password: ${HTTPS_STORE_PASS:}
jks-path: ${HTTPS_JKS_PATH:}
# 是否开启代理服务报文日志(日志级别为debug时开启才有效)
transfer-log-enable: ${SERVER_LOG:false}
udp:
# 线程池相关配置,用于技术调优,可忽略
boss-thread-count: 5
work-thread-count: 20
# 是否开启代理服务报文日志(日志级别为debug时开启才有效)
transfer-log-enable: ${SERVER_LOG:false}
data:
db:
# 数据库类型,目前支持sqlite、mysql、mariadb
+2 -1
View File
@@ -20,11 +20,12 @@
- [x] 客户端断开连接时,记录日志空指针异常问题修复
# 1.8.7
# 1.9.0
- [ ] 增加服务端/客户端jar式一键部署脚本
- [ ] 排查解决问题:https://gitee.com/dromara/neutrino-proxy/issues/I7LGLB
- [ ] 访问白名单
- [ ] 端口映射分组
- [ ] 支持UDP
# Bug
- 指令通达被close的问题,org.dromara.neutrinoproxy.server.proxy.core.ProxyTunnelChannelHandler.channelInactive