【自研网关系列】Netty 搭建

🌈Yu-Gateway:基于 Netty 与原生 Java 实现,使用 Nacos 作为注册与配置中心。项目实现多种过滤器,包含路由、负载均衡、鉴权、灰度发布等过滤器。

🌈项目代码地址:https://github.com/YYYUUU42/YuGateway-master

如果该项目对你有帮助,可以在 github 上点个 ⭐ 喔 🥰🥰

🌈自研网关系列:可以点开专栏,参看完整的文档

目录

1. Netty 概述

2. Netty 核心概念

3. 项目中的结构

生命周期接口

实现 NettyHttpService

实现 NettyHttpServerHandler

实现 NettyServerConnectManagerHandler

实现 NettyProcessor

实现 NettyCoreProcessor

实现 NettyHttpClient

核心容器

1. Netty 概述

在基于Netty进行设计之前,我们先按照老方法,介绍一下Netty,以及它的作用和使用场景。

  1. Netty 是什么: Netty 是一个高性能的、异步事件驱动的网络应用程序框架,支持快速开发可维护的高性能协议服务器和客户端。它是一个在 Java NIO 的基础上构建的网络编程框架,提供了易于使用的API。
  2. Netty 的具体功能:
  3. 异步和事件驱动:Netty 提供了一个多线程的事件循环,用于处理所有网络事件,例如连接、数据发送和接收。
  4. 支持多协议:它可以支持多种传输协议,包括 TCP、UDP,以及更高级的协议如 HTTP、HTTPS、WebSocket。
  5. 高度可定制:可以通过ChannelHandler来定制处理网络事件的逻辑,支持编解码器、拦截器等。
  6. 性能优化:利用池化和复用技术来减少资源消耗,减少GC压力,优化内存使用。
  7. 安全性:内置了对 SSL/TLS 协议的支持,确保数据传输安全。
  8. Netty 中的核心概念:
  • Boss 和 Worker 线程:在 Netty 的服务器端,"Boss" 线程负责处理连接的建立,而 "Worker" 线程负责处理已连接的通道的IO操作。这种模型允许Boss线程迅速处理新的连接,并将数据传输的处理任务委托给Worker线程。
  • Channel:代表一个到远程节点的开放连接,可以进行读写操作。
  • EventLoop:用于处理连接的生命周期中的所有事件,每个Channel都分配给了一个EventLoop。
  • ChannelHandler:核心处理器,可以响应入站和/或出站事件和数据。
  1. Netty 的使用场景:
  • Web服务器和客户端:使用Netty作为底层通信组件来构建自己的Web服务器和HTTP客户端。
  • 实时通信系统:如在线游戏的服务器、聊天服务器,因为Netty支持WebSocket和TCP协议,适合需要低延迟和大量并发连接的应用。

2. Netty 核心概念

在基于 Netty设计服务端之前我们首先需要了解一下几个Netty中的核心概念:

  1. EventLoopGroup: 这是Netty中的一个核心组件,负责处理所有的I/O操作。EventLoopGroup是一个包含多个EventLoop的组,每个EventLoop都是一个单线程循环,负责处理连接的生命周期内的所有事件。其分为boss和worker两种类型的线程组。boss线程组通常负责接受新的客户端连接,而worker线程组负责处理boss线程组接受的连接的后续I/O操作。
  2. ServerBootstrap: 这个类是一个帮助类,用于设置服务器。它允许我们设置服务器所需的所有参数,如端口、使用的EventLoopGroup等。ServerBootstrap还允许为新接受的连接以及连接后的通道设置属性和处理程序。
  3. Channel: Channel接口代表一个到远程节点的开放连接,可以进行读写操作。在Netty中,Channel是网络通信的基础组件,每个连接都会创建一个新的Channel
  4. ChannelInitializer: 这是一个特殊的处理程序,用于配置新注册的ChannelChannelPipeline,它提供了一个容易扩展的方式来初始化Channel,一旦Channel注册到EventLoop上,就会调用ChannelInitializer
  5. ChannelPipeline: 这个接口表示一个ChannelHandler的链表,用于处理或拦截入站和出站操作。它使得可以容易地添加或删除处理程序。
  6. ChannelHandler: 接口定义了很多事件处理方法,你可以通过实现这些方法来进行自定义的事件处理。事件可以是入站也可以是出站的,例如数据读取、写入、连接开启和关闭。
  7. ChannelHandlerContext: 提供了一个接口,用于在ChannelHandler中进行交互操作。通过这个上下文对象,处理程序可以传递事件、修改管道、存储处理信息等。
  8. ChannelOptionChannelConfig: 这些类和接口用于配置Channel的参数,如连接超时、缓冲区大小等。
  9. NioEventLoopGroupEpollEventLoopGroup: 这些类是EventLoopGroup的实现,分别对应于使用Java NIO和Epoll(只在Linux上可用)作为传输类型。Netty自动选择使用哪个实现,通常基于操作系统的能力和应用程序的需求。
  10. NioServerSocketChannelEpollServerSocketChannel: 这些是Channel实现,表示服务器端的套接字通道。选择哪个实现通常取决于你选择的EventLoopGroup
  11. 编解码器(Codec: Netty提供了一系列的编解码器用于数据的编码和解码,例如HttpServerCodec用于HTTP协议的编码和解码。

3. 项目中的结构

这六个类在项目中扮演了不同的角色,它们之间的关系如下:

  1. NettyHttpServer:这个类是服务器类,负责初始化和启动服务器。它使用 NettyServerHandler 和 NettyServerConnectManagerHandler 来处理网络事件。
  2. NettyHttpServerHandler:这个类是一个网络事件处理器,继承自 ChannelInboundHandlerAdapter,用于处理入站的网络事件,如接收到的数据。使用 NettyProcessor 来处理具体的业务逻辑。
  3. NettyServerConnectManagerHandler:这个类也是一个网络事件处理器,继承自 ChannelDuplexHandler,可以处理入站和出站的网络事件,用于管理网络连接的整个生命周期。
  4. NettyProcessor:这是一个接口,定义了处理业务逻辑的方法。NettyServerHandler 使用实现了这个接口的类来处理具体的业务逻辑。
  5. NettyCoreProcessor:这个类实现了 NettyProcessor 接口,提供了处理业务逻辑的具体实现。
  6. 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();
    }
}
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值