Netty + Akka 搭建tcp通信高性能服务端

Netty 和 AKKA 的分工

Akka主要关注于actor模型和并发编程,而Netty专注于网络通信和事件驱动的编程模型。

Akka 的核心价值

1、消息驱动的业务处理*

        Netty中的 I/O 事件(如 channelReadwrite)和非IO事件会由 EventLoop 线程 按顺序依次处理不会并发执行。所以所有耗时操作(无论是否 I/O)都应异步化,保持 EventLoop 仅处理轻量级任务。使用AKKA将每个客户端请求被封装为消息,由 Akka Actor 异步处理,避免阻塞 Netty 的 EventLoop导致其他channel长时间等待。

2、并发与状态管理

        Actor 模型天然支持线程安全的状态共享(每个 Actor 串行处理消息,无需锁)

3、分布式扩展

        通过 Akka Cluster 可以轻松实现横向扩展,将业务逻辑分布到多台机器。

客户端 -> Netty -> AKKA 交互流程

步骤 1:客户端建立连接
  • 客户端 发起 TCP 连接请求(如 WebSocket、HTTP)。

  • Netty 的 bossEventLoop(单线程)接受连接,创建 Channel 并注册到 workerEventLoopGroup 中的一个 EventLoop

  •  每个 Channel 绑定到一个固定的 EventLoop(线程)。
步骤 2:Netty 接收请求
  • Netty 的 workerEventLoop 从 Channel 读取数据,触发 channelRead 事件。

  • Netty Handler 进行协议解码(如 HTTP 解析、Protobuf 反序列化),生成业务请求对象。

步骤 3:Netty → Akka 消息转发
  • Akka Router Actor 接收请求,根据业务规则(如请求类型、用户 ID)路由到具体的业务 Actor。

步骤 4:Akka 处理业务逻辑
  • 业务 Actor 执行核心逻辑(如订单处理、用户认证),可能调用外部服务(数据库、第三方 API)。

步骤 5:Akka → Netty 返回响应
  • 业务 Actor 将处理结果封装为响应消息,发送回原始请求的 ChannelHandlerContext(通过消息传递)。

  • Netty Handler 收到 Akka 响应后,通过 EventLoop 线程写回客户端:

步骤 6:Netty 发送响应
  • Netty 的 workerEventLoop 执行 writeAndFlush,将数据编码后通过 Channel 发送给客户端。

示例代码

创建用于桥接AkkaHandler

维护一个成员变量routerActor,用于向routerActor转发消息(在启动类启动时赋值)

//继承SimpleChannelInboundHandler类指定泛型为Protobuf请求的生成类
public class NettyAkkaBridgeHandler extends SimpleChannelInboundHandler<MyRequest> {
    private final ActorRef routerActor;

    public NettyAkkaBridgeHandler(ActorRef routerActor) {
        this.routerActor = routerActor;
    }

    @Override
    protected void channelRead(ChannelHandlerContext ctx, MyRequest request) {
        // 1. 将 Netty 请求转为 Akka 消息
        AkkaRequest akkaRequest = new AkkaRequest(ctx, request);
        
        // 2. 发送给 Akka Router Actor
        routerActor.tell(akkaRequest, ActorRef.noSender());
        
        // 注意:这里不需要手动释放 request,Netty 会自动处理
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 异常处理(如日志、关闭连接)
        ctx.close();
    }
}

创建akka的路由Actor

netty将消息发给路由actor之后根据不同的AkkaRequest转发给对应的actor,其中receiveBuilder().match() 方法中,两个参数分别表示 消息类型匹配规则 和 消息处理逻辑。

下面的代码收到一条消息时,Akka 会检查该消息是否是 AkkaRequest的实例,如果是则执行第二个参数里的逻辑

public class RouterActor extends AbstractActor {
    @Override
    public Receive createReceive() {
        return receiveBuilder()
            .match(AkkaRequest.class, req -> {
                //获取到对应actor的actorSelection
                ActorSelection targetActorSelection = selectTargetActor(req);
                if(targetActorSelection != null){
                    //转发
                    targetActorSelection .tell(req, getSender());
                }else{
                    log.info("#$%^&^RE@!");
                }
            })
            .build();
    }

    //根据req请求返回需要处理这个请求的actor的ActorSelection 
    private ActorSelection selectTargetActor(AkkaRequestMessage req){
        int cmdId = req.getMsgBody().getCmdId();
        switch (cmdId ) {
            case 1:
                return context().actorSelection("/user/" + "actor的名字1");
            case 2:
                return context().actorSelection("/user/" + "actor的名字2");
            default:
                return null;
        }
    }
}

创建处理业务逻辑的Actor

public class MyWorkerActor extends AbstractActor {
    //处理db操作的线程池
    private final ExecutorService dbExecutor = Executors.newFixedThreadPool(4);
    @Override
    public Receive createReceive() {

        return receiveBuilder()
                .match(AkkaRequestMessage.class, req -> {

                    // 处理耗时操作(如数据库查询)
                    CompletableFuture.supplyAsync(() -> {
                        return queryDatabase(req); // 阻塞操作,在独立线程池执行
                    }, dbExecutor).thenAccept(result -> {
                        // 将结果封装为响应,回到 Netty 的 EventLoop 线程写回
                        req.getChannelContext().executor().execute(() -> {
                            req.getChannelContext().writeAndFlush(req);
                        });
                    });

                }).build();

    }

    private String queryDatabase(AkkaRequestMessage request) {
        // 模拟数据库查询
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "Order-" + request.getId() + ": processed";
    }
}

启动类中初始化Netty和AKKA

// 初始化 Akka
//创建akka系统
ActorSystem system = ActorSystem.create("BusinessSystem");
//往系统中添加已经创建好的actor
ActorRef router = system.actorOf(Props.create(RouterActor.class), "routerActor");
system.actorOf(Props.create(WorkerActor1.class), "workerActorr1");
//其中WorkerActor2安排3个实例,使用轮询的策略负载均衡
system.actorOf(Props.create(WorkerActor2.class).withRouter(new RoundRobinPool(3)), "workerActorr2");

// 初始化 Netty 
//bossGroup配置1个线程,workerGroup默认cpu核心数*2
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

ServerBootstrap bootstrap = new ServerBootstrap()
    .group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)
    .childHandler(new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(SocketChannel ch) {
            ch.pipeline()
                //解码为 MyRequest
                .addLast(new ProtobufDecoder(MyRequest.getDefaultInstance()))
                //添加已经创建好的Handler
                //并将router(用于路由的actor)设置为这个Handler的成员变量用于tell其他actor操作
                .addLast(new NettyAkkaBridgeHandler(router));
        }
    });

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值