🌈Yu-Gateway:基于 Netty 与原生 Java 实现,使用 Nacos 作为注册与配置中心。项目实现多种过滤器,包含路由、负载均衡、鉴权、灰度发布等过滤器。
🌈项目代码地址:https://github.com/YYYUUU42/YuGateway-master
如果该项目对你有帮助,可以在 github 上点个 ⭐ 喔 🥰🥰
🌈自研网关系列:可以点开专栏,参看完整的文档
目录
实现 NettyServerConnectManagerHandler
1. Netty 概述
在基于Netty进行设计之前,我们先按照老方法,介绍一下Netty,以及它的作用和使用场景。
- Netty 是什么: Netty 是一个高性能的、异步事件驱动的网络应用程序框架,支持快速开发可维护的高性能协议服务器和客户端。它是一个在 Java NIO 的基础上构建的网络编程框架,提供了易于使用的API。
- Netty 的具体功能:
- 异步和事件驱动:Netty 提供了一个多线程的事件循环,用于处理所有网络事件,例如连接、数据发送和接收。
- 支持多协议:它可以支持多种传输协议,包括 TCP、UDP,以及更高级的协议如 HTTP、HTTPS、WebSocket。
- 高度可定制:可以通过ChannelHandler来定制处理网络事件的逻辑,支持编解码器、拦截器等。
- 性能优化:利用池化和复用技术来减少资源消耗,减少GC压力,优化内存使用。
- 安全性:内置了对 SSL/TLS 协议的支持,确保数据传输安全。
- Netty 中的核心概念:
- Boss 和 Worker 线程:在 Netty 的服务器端,"Boss" 线程负责处理连接的建立,而 "Worker" 线程负责处理已连接的通道的IO操作。这种模型允许Boss线程迅速处理新的连接,并将数据传输的处理任务委托给Worker线程。
- Channel:代表一个到远程节点的开放连接,可以进行读写操作。
- EventLoop:用于处理连接的生命周期中的所有事件,每个Channel都分配给了一个EventLoop。
- ChannelHandler:核心处理器,可以响应入站和/或出站事件和数据。
- Netty 的使用场景:
- Web服务器和客户端:使用Netty作为底层通信组件来构建自己的Web服务器和HTTP客户端。
- 实时通信系统:如在线游戏的服务器、聊天服务器,因为Netty支持WebSocket和TCP协议,适合需要低延迟和大量并发连接的应用。
2. Netty 核心概念
在基于 Netty设计服务端之前我们首先需要了解一下几个Netty中的核心概念:
EventLoopGroup
: 这是Netty中的一个核心组件,负责处理所有的I/O操作。EventLoopGroup
是一个包含多个EventLoop
的组,每个EventLoop
都是一个单线程循环,负责处理连接的生命周期内的所有事件。其分为boss和worker两种类型的线程组。boss线程组通常负责接受新的客户端连接,而worker线程组负责处理boss线程组接受的连接的后续I/O操作。ServerBootstrap
: 这个类是一个帮助类,用于设置服务器。它允许我们设置服务器所需的所有参数,如端口、使用的EventLoopGroup
等。ServerBootstrap
还允许为新接受的连接以及连接后的通道设置属性和处理程序。Channel
:Channel
接口代表一个到远程节点的开放连接,可以进行读写操作。在Netty中,Channel
是网络通信的基础组件,每个连接都会创建一个新的Channel
。ChannelInitializer
: 这是一个特殊的处理程序,用于配置新注册的Channel
的ChannelPipeline
,它提供了一个容易扩展的方式来初始化Channel
,一旦Channel
注册到EventLoop
上,就会调用ChannelInitializer
。ChannelPipeline
: 这个接口表示一个ChannelHandler
的链表,用于处理或拦截入站和出站操作。它使得可以容易地添加或删除处理程序。ChannelHandler
: 接口定义了很多事件处理方法,你可以通过实现这些方法来进行自定义的事件处理。事件可以是入站也可以是出站的,例如数据读取、写入、连接开启和关闭。ChannelHandlerContext
: 提供了一个接口,用于在ChannelHandler
中进行交互操作。通过这个上下文对象,处理程序可以传递事件、修改管道、存储处理信息等。ChannelOption
和ChannelConfig
: 这些类和接口用于配置Channel
的参数,如连接超时、缓冲区大小等。NioEventLoopGroup
和EpollEventLoopGroup
: 这些类是EventLoopGroup
的实现,分别对应于使用Java NIO和Epoll(只在Linux上可用)作为传输类型。Netty自动选择使用哪个实现,通常基于操作系统的能力和应用程序的需求。NioServerSocketChannel
和EpollServerSocketChannel
: 这些是Channel
实现,表示服务器端的套接字通道。选择哪个实现通常取决于你选择的EventLoopGroup
。- 编解码器(
Codec
): Netty提供了一系列的编解码器用于数据的编码和解码,例如HttpServerCodec
用于HTTP协议的编码和解码。
3. 项目中的结构
这六个类在项目中扮演了不同的角色,它们之间的关系如下:
- NettyHttpServer:这个类是服务器类,负责初始化和启动服务器。它使用 NettyServerHandler 和 NettyServerConnectManagerHandler 来处理网络事件。
- NettyHttpServerHandler:这个类是一个网络事件处理器,继承自 ChannelInboundHandlerAdapter,用于处理入站的网络事件,如接收到的数据。使用 NettyProcessor 来处理具体的业务逻辑。
- NettyServerConnectManagerHandler:这个类也是一个网络事件处理器,继承自 ChannelDuplexHandler,可以处理入站和出站的网络事件,用于管理网络连接的整个生命周期。
- NettyProcessor:这是一个接口,定义了处理业务逻辑的方法。NettyServerHandler 使用实现了这个接口的类来处理具体的业务逻辑。
- NettyCoreProcessor:这个类实现了 NettyProcessor 接口,提供了处理业务逻辑的具体实现。
- NettyHttpClient:这个类是客户端类,负责创建连接并发送请求到服务器。
总的来说,NettyHttpServer 是服务器类,它使用 NettyHttpServerHandler 和 NettyServerConnectManagerHandler 来处理网络事件。这两个处理器类使用 NettyProcessor 来处理具体的业务逻辑,而 NettyCoreProcessor 提供了这个接口的具体实现。NettyHttpClient 是客户端类,它可能会使用 NettyProcessor 来处理接收到的响应。
生命周期接口
生命周期这一块定义一个接口,提供初始化,启动,以及关闭等方法即可。
/**
* @author yu
* @description 组件生命周期接口
*/
public interface LifeCycle {
/**
* 初始化
*/
void init();
/**
* 启动
*/
void start();
/**
* 关闭
*/
void shutdown();
}
实现 NettyHttpService
实现 LifeCycle 接口,用于管理服务器的生命周期,包括初始化、启动和关闭等操作。这个类的主要作用是创建一个基于 Netty 的 HTTP 服务器,可以根据系统环境选择使用 Epoll 或 NIO 事件模型,处理客户端的连接和请求。
/**
* @author yu
* @description Netty 自定义服务端
*/
@Slf4j
public class NettyHttpServer implements LifeCycle {
/**
* 服务器配置对象,用于获取如端口号等配置信息
*/
private final Config config;
/**
* 自定义的Netty处理器接口,用于定义如何处理接收到的请求
*/
private final NettyProcessor processor;
/**
* 服务器引导类,用于配置和启动Netty服务
*/
private ServerBootstrap serverBootstrap;
/**
* boss线程组,用于处理新的客户端连接
*/
private EventLoopGroup eventLoopGroupBoss;
/**
* worker线程组,用于处理已经建立的连接的后续操作
*/
private EventLoopGroup eventLoopGroupWorker;
public NettyHttpServer(Config config, NettyProcessor processor) {
this.config = config;
this.processor = processor;
init();
}
public EventLoopGroup getEventLoopGroupWorker() {
return eventLoopGroupWorker;
}
/**
* 初始化服务器,设置线程组和选择线程模型
* 如果系统支持 Epoll,将创建 Epoll 事件循环组,否则创建 NIO 事件循环组。这些事件循环组用于处理服务器的网络事件。
*/
@Override
public void init() {
this.serverBootstrap = new ServerBootstrap();
if (useEpoll()) {
this.eventLoopGroupBoss = new EpollEventLoopGroup(config.getEventLoopGroupBossNum(),
new DefaultThreadFactory("netty-boss-nio"));
this.eventLoopGroupWorker = new EpollEventLoopGroup(config.getEventLoopGroupWorkerNum(),
new DefaultThreadFactory("netty-worker-nio"));
} else {
this.eventLoopGroupBoss = new NioEventLoopGroup(config.getEventLoopGroupBossNum(),
new DefaultThreadFactory("netty-boss-nio"));
this.eventLoopGroupWorker = new NioEventLoopGroup(config.getEventLoopGroupWorkerNum(),
new DefaultThreadFactory("netty-worker-nio"));
}
}
/**
* 是否选用 epoll 优化IO
* 检查当前系统是否可以使用Epoll事件模型。它返回一个布尔值,表示是否可以使用Epoll。Epoll是一种在Linux系统上提供更高性能的事件模型。
*/
public boolean useEpoll() {
return RemotingUtil.isIsLinuxPlatform() && Epoll.isAvailable();
}
/**
* 启动服务器,监听端口并开始接收请求
*/
@Override
public void start() {
// 配置服务器参数,如端口、TCP参数等
this.serverBootstrap
.group(eventLoopGroupBoss, eventLoopGroupWorker)
.channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
//TCP连接的最大队列长度
.option(ChannelOption.SO_BACKLOG, 1024)
// 允许端口重用
.option(ChannelOption.SO_REUSEADDR, true)
// 保持连接检测
.option(ChannelOption.SO_KEEPALIVE, true)
// 禁用Nagle算法,适用于小数据即时传输
.childOption(ChannelOption.TCP_NODELAY, true)
// 设置发送缓冲区大小
.childOption(ChannelOption.SO_SNDBUF, 65535)
// 设置接收缓冲区大小
.childOption(ChannelOption.SO_RCVBUF, 65535)
// 绑定监听端口
.localAddress(new InetSocketAddress(config.getPort()))
// 定义处理新连接的管道初始化逻辑
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
// 配置管道中的处理器,如编解码器和自定义处理器
ch.pipeline().addLast(
new HttpServerCodec(), // 处理HTTP请求的编解码器
new HttpObjectAggregator(config.getMaxContentLength()), // 聚合HTTP请求
new HttpServerExpectContinueHandler(), // 处理HTTP 100 Continue请求
new NettyHttpServerHandler(processor), // 自定义的处理器
new NettyServerConnectManagerHandler() // 连接管理处理器
);
}
});
try {
this.serverBootstrap.bind().sync();
log.info("server startup on port {}", config.getPort());
} catch (InterruptedException e) {
log.error("NettyHttpServer start failed", e);
throw new RuntimeException();
}
}
/**
* 关闭Netty服务器,释放资源
* 关闭 eventLoopGroupBoss 和 eventLoopGroupWorker,释放资源,确保服务器可以安全地关闭。
*/
@Override
public void shutdown() {
if (eventLoopGroupBoss != null) {
eventLoopGroupBoss.shutdownGracefully();
}
if (eventLoopGroupWorker != null) {
eventLoopGroupWorker.shutdownGracefully();
}
}
}
里面有可以选择 epoll 事件模型的方法,具体工具类代码在 com.yu.gateway.common.utils
在这段代码中,epoll 被用作优化 IO 的一种方式。epoll 是 Linux 系统上的一种 I/O 多路复用机制,相比于传统的 select 和 poll , epoll 在处理大量并发连接时可以提供更高的性能。
这是因为 select 和 poll 每次调用都会线性扫描所有的文件描述符,而 epoll 则使用了一种基于事件驱动的方式,只有活跃的文件描述符才会触发事件,因此在处理大量并发连接时, epoll 的效率会更高。
在这段代码中, useEpoll() 方法会检查当前系统是否支持 epoll ,如果支持并且当前系统是Linux平台,那么就会使用 epoll ,否则会使用 NIO 的事件循环组。这样做的目的是为了在支持 epoll 的系统上获取更高的性能,而在不支持 epoll 的系统上则使用 NIO 作为兼容方案。
public boolean useEpoll() {
return RemotingUtil.isIsLinuxPlatform() && Epoll.isAvailable();
}
实现 NettyHttpServerHandler
继承 ChannelInboundHandlerAdapter,这个类的主要作用是处理通过 Netty 传入的 HTTP 请求,包括接收请求、包装请求并传递给业务逻辑处理器处理,以及处理在处理请求过程中发生的异常。
- NettyHttpServerHandler(NettyProcessor processor):这是类的构造函数,接收一个 NettyProcessor 类型的参数,用于处理请求的业务逻辑处理器。
- channelRead(ChannelHandlerContext ctx, Object msg):当从客户端接收到数据时,该方法会被调用。这里将入站的数据(HTTP请求)包装后,传递给业务逻辑处理器。首先,将接收到的消息转换为 FullHttpRequest 对象,然后创建 HttpRequestWrapper 对象,并设置上下文和请求,最后调用业务逻辑处理器的 process 方法处理请求。
- exceptionCaught(ChannelHandlerContext ctx, Throwable cause):处理在处理入站事件时发生的异常。首先,调用父类的 exceptionCaught 方法,它将按照 ChannelPipeline 中的下一个处理器继续处理异常,然后记录异常信息。
/**
* @author yu
* NettyHttpServerHandler 用于处理通过 Netty 传入的 HTTP 请求。
* 继承 ChannelInboundHandlerAdapter,这样可以覆盖回调方法来处理入站事件。
*/
@Slf4j
public class NettyHttpServerHandler extends ChannelInboundHandlerAdapter {
/**
* 成员变量 processor,用于处理具体的业务逻辑
*/
private NettyProcessor processor;
/**
* 构造函数,接收一个 NettyProcessor 类型的参数。
*
* @param processor 用于处理请求的业务逻辑处理器。
*/
public NettyHttpServerHandler(NettyProcessor processor) {
this.processor = processor;
}
/**
* 当从客户端接收到数据时,该方法会被调用。
* 这里将入站的数据(HTTP请求)包装后,传递给业务逻辑处理器。
*
* @param ctx ChannelHandlerContext,提供了操作网络通道的方法。
* @param msg 接收到的消息,预期是一个 FullHttpRequest 对象。
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 将接收到的消息转换为 FullHttpRequest 对象
FullHttpRequest request = (FullHttpRequest) msg;
// 创建 HttpRequestWrapper 对象,并设置上下文和请求
HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper();
httpRequestWrapper.setCtx(ctx);
httpRequestWrapper.setRequest(request);
// 调用业务逻辑处理器的 process 方法处理请求
processor.process(httpRequestWrapper);
}
/**
* 处理在处理入站事件时发生的异常。
*
* @param ctx ChannelHandlerContext,提供了操作网络通道的方法。
* @param cause 异常对象。
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 调用父类的 exceptionCaught 方法,它将按照 ChannelPipeline 中的下一个处理器继续处理异常
super.exceptionCaught(ctx, cause);
log.error("Netty occur exception", cause);
}
}
代码中的 HttpRequestWrapper 对象:
public class HttpRequestWrapper {
private FullHttpRequest request;
private ChannelHandlerContext ctx;
}
实现 NettyServerConnectManagerHandler
继承 ChannelDuplexHandler,这个类的主要作用是管理网络连接的整个生命周期,包括注册、注销、活跃、不活跃等状态,以及处理用户自定义事件和异常。
- channelRegistered(ChannelHandlerContext ctx):当通道被注册到它的 EventLoop 时调用,即它可以开始处理I/O事件。在这个方法中,它获取远程客户端的地址,并记录调试信息。
- channelUnregistered(ChannelHandlerContext ctx):当通道从它的 EventLoop 注销时调用,不再处理任何I/O事件。在这个方法中,它获取远程客户端的地址,并记录调试信息。
- channelActive(ChannelHandlerContext ctx):当通道变为活跃状态,即连接到远程节点时被调用。在这个方法中,它获取远程客户端的地址,并记录调试信息。
- channelInactive(ChannelHandlerContext ctx):当通道变为不活跃状态,即不再连接远程节点时被调用。在这个方法中,它获取远程客户端的地址,并记录调试信息。
- userEventTriggered(ChannelHandlerContext ctx, Object evt):当用户自定义事件被触发时调用,例如,可以用来处理空闲状态检测事件。在这个方法中,它检查事件是否为IdleStateEvent(空闲状态事件),如果是所有类型的空闲事件,则关闭通道。
- exceptionCaught(ChannelHandlerContext ctx, Throwable cause):当处理过程中发生异常时调用,通常是网络层面的异常。在这个方法中,它获取远程客户端的地址,记录警告信息和异常堆栈,发生异常时关闭通道。
/**
* @author yu
* 连接管理器,管理连接对生命周期
* 当前类提供出站和入站事件的处理能力,能够管理网络链接的整个生命周期
* 服务器连接管理器,用于监控和管理网络连接的生命周期事件。
*/
@Slf4j
public class NettyServerConnectManagerHandler extends ChannelDuplexHandler {
/**
* 当通道被注册到它的 EventLoop 时调用,即它可以开始处理I/O事件。
*
* @param ctx 提供了操作网络通道的方法的上下文对象。
*/
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
// 获取远程客户端的地址
final String remoteAddr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
// 记录调试信息
log.debug("NETTY SERVER PIPLINE: channelRegistered {}", remoteAddr);
// 调用父类方法继续处理注册事件
super.channelRegistered(ctx);
}
/**
* 当通道从它的EventLoop注销时调用,不再处理任何I/O事件。
*
* @param ctx 提供了操作网络通道的方法的上下文对象。
*/
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
final String remoteAddr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.debug("NETTY SERVER PIPLINE: channelUnregistered {}", remoteAddr);
super.channelUnregistered(ctx);
}
/**
* 当通道变为活跃状态,即连接到远程节点时被调用。
*
* @param ctx 提供了操作网络通道的方法的上下文对象。
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
final String remoteAddr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.debug("NETTY SERVER PIPLINE: channelActive {}", remoteAddr);
super.channelActive(ctx);
}
/**
* 当通道变为不活跃状态,即不再连接远程节点时被调用。
*
* @param ctx 提供了操作网络通道的方法的上下文对象。
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
final String remoteAddr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.debug("NETTY SERVER PIPLINE: channelInactive {}", remoteAddr);
super.channelInactive(ctx);
}
/**
* 当用户自定义事件被触发时调用,例如,可以用来处理空闲状态检测事件。
*
* @param ctx 提供了操作网络通道的方法的上下文对象。
* @param evt 触发的用户事件。
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
// 检查事件是否为IdleStateEvent(空闲状态事件)
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
// 如果是所有类型的空闲事件,则关闭通道
if (event.state().equals(IdleState.ALL_IDLE)) {
final String remoteAddr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.warn("NETTY SERVER PIPLINE: userEventTriggered: IDLE {}", remoteAddr);
ctx.channel().close();
}
}
// 传递事件给下一个ChannelHandler
ctx.fireUserEventTriggered(evt);
}
/**
* 当处理过程中发生异常时调用,通常是网络层面的异常。
*
* @param ctx 提供了操作网络通道的方法的上下文对象。
* @param cause 异常对象。
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
final String remoteAddr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
// 记录警告信息和异常堆栈
log.warn("NETTY SERVER PIPLINE: remoteAddr: {}, exceptionCaught {}", remoteAddr, cause);
// 发生异常时关闭通道
ctx.channel().close();
}
}
实现 NettyProcessor
主要提供了处理网络请求的具体实现
- process(HttpRequestWrapper wrapper) :用于处理网络请求。它接收一个 HttpRequestWrapper 类型的参数,这个参数是对原始 HTTP 请求的封装,提供了更方便的方法来访问请求的各个部分。
- start():用于启动处理器。在这个阶段,处理器开始执行其主要功能,如监听网络请求、开始处理数据等。
- shutDown():用于关闭处理器。在这个阶段,通常会进行一些清理工作,如释放资源、停止服务等。
/**
* @author yu
* @description Netty请求处理器
*/
public interface NettyProcessor {
/**
* 处理请求
*/
void process(HttpRequestWrapper wrapper);
/**
* 启动
*/
void start();
/**
* 关闭
*/
void shutDown();
}
实现 NettyCoreProcessor
实现 NettyProcessor 接口,主要负责处理 HTTP 请求,包括处理请求中的异常,并将响应写回客户端。。
- process(HttpRequestWrapper wrapper):这个方法用于处理传入的 HTTP 请求。首先从 HttpRequestWrapper 中获取 FullHttpRequest 和 ChannelHandlerContext。然后,尝试创建并填充 GatewayContext 以保存有关传入请求的信息。如果在处理请求时发生已知异常(BaseException),会记录错误日志并发送适当的 HTTP 响应。如果发生未知异常,会记录错误日志并发送内部服务器错误响应。
- doWriteAndRelease(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse httpResponse):这个方法用于将 HTTP 响应写入通道并释放资源。首先将响应写入通道并刷新,然后添加一个监听器,在写入完成后关闭通道。最后,它释放与请求相关联的资源。
- start():这个方法用于启动 NettyCoreProcessor
- shutDown():这个方法用于关闭 NettyCoreProcessor。
/**
* @author yu
* @description NettyCoreProcessor 是负责在基于 Netty 的服务器中处理 HTTP 请求的组件
*/
@Slf4j
public class NettyCoreProcessor implements NettyProcessor {
/**
* 处理传入的 HTTP 请求。
*
* @param wrapper 包含 FullHttpRequest 和 ChannelHandlerContext 的 HttpRequestWrapper。
*/
@Override
public void process(HttpRequestWrapper wrapper) {
FullHttpRequest request = wrapper.getRequest();
ChannelHandlerContext ctx = wrapper.getCtx();
try {
// 创建并填充 GatewayContext 以保存有关传入请求的信息。
GatewayContext gatewayContext = RequestHelper.doContext(request, ctx);
} catch (BaseException e) {
// 通过记录日志并发送适当的 HTTP 响应处理已知异常。
log.error("处理错误 {} {}", e.getCode().getCode(), e.getCode().getMessage());
FullHttpResponse httpResponse = ResponseHelper.getHttpResponse(e.getCode());
doWriteAndRelease(ctx, request, httpResponse);
} catch (Throwable t) {
// 通过记录日志并发送内部服务器错误响应处理未知异常。
log.error("处理未知错误", t);
FullHttpResponse httpResponse = ResponseHelper.getHttpResponse(ResponseCode.INTERNAL_ERROR);
doWriteAndRelease(ctx, request, httpResponse);
}
}
/**
* 将 HTTP 响应写入通道并释放资源。
*
* @param ctx 用于写入响应的 ChannelHandlerContext。
* @param request 从客户端接收的 FullHttpRequest。
* @param httpResponse 作为响应发送的 FullHttpResponse。
*/
private void doWriteAndRelease(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse httpResponse) {
// 发送响应后关闭通道
ctx.writeAndFlush(httpResponse).addListener(ChannelFutureListener.CLOSE);
// 释放与请求相关联的资源。
ReferenceCountUtil.release(request);
}
/**
* 启动 NettyCoreProcessor
*/
@Override
public void start() {
}
/**
* 关闭 NettyCoreProcessor
*/
@Override
public void shutDown() {
}
}
实现 NettyHttpClient
LifeCycle 接口,主要负责创建和管理基于 Netty 的异步 HTTP 客户端,包括初始化、启动和关闭客户端。
- NettyHttpClient(Config config, EventLoopGroup eventLoopGroupWorker):构造函数,创建 NettyHttpClient 的实例。
- init():初始化异步 HTTP 客户端,设置其配置参数。
- start():启动客户端,通常在这里进行资源分配和启动必要的服务。在这个方法中,使用 AsyncHttpHelper 单例模式初始化异步 HTTP 客户端。
- shutdown():关闭客户端,通常在这里进行资源释放和清理工作。如果客户端实例不为空,则尝试关闭它。
/**
* @author yu
* @description NettyHttpClient 类负责创建和管理基于Netty的异步HTTP客户端
*/
@Slf4j
public class NettyHttpClient implements LifeCycle {
/**
* 配置信息对象,包含HTTP客户端的配置参数
*/
private final Config config;
/**
* Netty的事件循环组,用于处理客户端的网络事件
*/
private final EventLoopGroup eventLoopGroupWorker;
/**
* 异步HTTP客户端实例
*/
private AsyncHttpClient asyncHttpClient;
/**
* 构造函数,创建NettyHttpClient的实例。
*
* @param config 包含客户端配置的对象。
* @param eventLoopGroupWorker 用于客户端事件处理的Netty事件循环组。
*/
public NettyHttpClient(Config config, EventLoopGroup eventLoopGroupWorker) {
this.config = config;
this.eventLoopGroupWorker = eventLoopGroupWorker;
init();
}
/**
* 初始化异步HTTP客户端,设置其配置参数。
*/
@Override
public void init() {
DefaultAsyncHttpClientConfig.Builder httpClientBuilder = new DefaultAsyncHttpClientConfig.Builder()
// 工作线程组
.setEventLoopGroup(eventLoopGroupWorker)
// 连接超时
.setConnectTimeout(config.getHttpConnectTimeout())
// 请求超时
.setRequestTimeout(config.getHttpRequestTimeout())
// 最大重试请求次数
.setMaxRequestRetry(config.getHttpMaxRetryTimes())
// 池化ByteBuf分配器
.setAllocator(PooledByteBufAllocator.DEFAULT)
.setCompressionEnforced(true)
// 最大连接数
.setMaxConnections(config.getHttpMaxConnections())
.setMaxConnectionsPerHost(config.getHttpMaxConnectionsPerHost())
.setPooledConnectionIdleTimeout(config.getHttpPooledConnectionIdleTimeout());
this.asyncHttpClient = new DefaultAsyncHttpClient(httpClientBuilder.build());
}
/**
* 启动客户端,通常在这里进行资源分配和启动必要的服务。
*/
@Override
public void start() {
// 使用AsyncHttpHelper单例模式初始化异步HTTP客户端
AsyncHttpHelper.getInstance().initialized(asyncHttpClient);
}
/**
* 关闭客户端,通常在这里进行资源释放和清理工作。
*/
@Override
public void shutdown() {
// 如果客户端实例不为空,则尝试关闭它
if (asyncHttpClient != null) {
try {
// 关闭客户端,并处理可能的异常
this.asyncHttpClient.close();
} catch (IOException e) {
// 记录关闭时发生的错误
log.error("NettyHttpClient shutdown error", e);
}
}
}
}
核心容器
主要职责是初始化和管理 NettyHttpServer,NettyHttpClient 和 NettyProcessor,包括它们的启动和关闭
/**
* @author yu
* @description Netty组件集合封装
*/
@Slf4j
public class Container implements LifeCycle{
private final Config config;
private NettyHttpServer nettyHttpServer;
private NettyHttpClient nettyHttpClient;
private NettyProcessor nettyProcessor;
public Container(Config config) {
this.config = config;
init();
}
@Override
public void init() {
NettyCoreProcessor nettyCoreProcessor = new NettyCoreProcessor();
this.nettyProcessor = nettyCoreProcessor;
this.nettyHttpServer = new NettyHttpServer(config, nettyProcessor);
// nettyClient、nettyServer 公用相同 work_threadGroup
this.nettyHttpClient = new NettyHttpClient(config, nettyHttpServer.getEventLoopGroupWorker());
}
@Override
public void start() {
nettyProcessor.start();
nettyHttpServer.start();
nettyHttpClient.start();
log.info("api gateway starting!");
}
@Override
public void shutdown() {
nettyProcessor.shutDown();
nettyHttpClient.shutdown();
nettyHttpServer.shutdown();
}
}