Netty实战应用学习笔记-中级拓展篇(七)

bugstack虫洞栈 | netty4.1案例文章列表汇总,跟着案例学Netty itstack-demo-netty netty4.x案例 微信公众号:bugstack虫洞栈,欢迎您的关注&获取更多案例与源码!博客栈:https://bugstack.cn 本专题针对netty4.x分为从;基础入门篇(九篇)、中级拓展篇(八篇)、高级应用篇(三章),由浅入深实现netty服务功能,介绍netty如何从上手到实战。 基础入门篇(九篇) netty案例,netty4.1... 阅读详情

🔄 项目七:请求响应同步通信(RPC基础)

项目概述

项目名称:itstack-demo-netty-2-07
核心功能:实现类似RPC的同步请求-响应通信模式
技术要点:Future模式、CountDownLatch、请求ID映射、超时控制

为什么需要这个项目?

实际问题:

  • Netty默认是异步通信,无法像HTTP一样同步等待响应
  • RPC框架需要同步调用(发送请求→等待响应→返回结果)
  • 多个请求同时发送,响应可能乱序返回
  • 需要超时控制,避免无限等待

解决方案:

  • ✅ Future模式:异步转同步,阻塞等待响应
  • ✅ CountDownLatch:线程阻塞和唤醒机制
  • ✅ 请求ID映射:准确匹配请求和响应
  • ✅ 超时控制:避免无限等待,提升系统稳定性

适用场景:

  • RPC框架(Dubbo、gRPC的核心原理)
  • 同步调用远程服务
  • 需要等待响应结果的场景
  • 分布式事务(需要确认操作结果)

核心问题

为什么需要同步通信?

普通的Socket通信是异步的(像QQ聊天,发送消息后不需要立即等待回复),但RPC框架需要同步通信(像HTTP调用,必须等待响应返回或超时)。

异步通信(Netty默认):
客户端发送消息 → 继续执行其他代码 → 某个时刻收到响应

同步通信(RPC需要):
客户端发送消息 → 阻塞等待 → 收到响应或超时 → 继续执行

核心知识点

1. Future模式实现同步等待

WriteFuture接口

public interface WriteFuture<T> extends Future<T> {
    Throwable cause();                    // 获取异常
    void setCause(Throwable cause);       // 设置异常
    boolean isWriteSuccess();             // 是否发送成功
    void setWriteResult(boolean result);  // 设置发送结果
    String requestId();                   // 获取请求ID
    T response();                         // 获取响应
    void setResponse(Response response);  // 设置响应
    boolean isTimeout();                  // 是否超时
}

SyncWriteFuture实现类

public class SyncWriteFuture implements WriteFuture<Response> {
    private CountDownLatch latch = new CountDownLatch(1);  // 核心:阻塞工具
    private final long begin = System.currentTimeMillis();
    private long timeout;
    private Response response;
    private final String requestId;
    private boolean writeResult;
    private Throwable cause;
    private boolean isTimeout = false;
    
    // 阻塞等待响应
    public Response get(long timeout, TimeUnit unit) {
        if (latch.await(timeout, unit)) {  // 阻塞等待
            return response;
        }
        return null;  // 超时返回null
    }
    
    // 设置响应,唤醒等待线程
    public void setResponse(Response response) {
        this.response = response;
        latch.countDown();  // 唤醒等待的线程
    }
}

CountDownLatch工作原理:

线程A:发送请求 → latch.await() → 阻塞等待
                                    ↓
线程B:收到响应 → setResponse() → latch.countDown() → 唤醒线程A
2. 请求ID映射机制

全局映射表

public class SyncWriteMap {
    // 存储 requestId → Future 的映射关系
    public static Map<String, WriteFuture> syncKey = 
        new ConcurrentHashMap<>();
}

为什么需要请求ID?

在Netty中,多个请求可能同时发送,响应返回的顺序可能与发送顺序不同。通过请求ID可以准确匹配请求和响应。

客户端发送:
请求1(ID=uuid-001) → 服务器
请求2(ID=uuid-002) → 服务器
请求3(ID=uuid-003) → 服务器

服务器响应(可能乱序):
响应2(ID=uuid-002) → 客户端(通过ID找到请求2的Future)
响应1(ID=uuid-001) → 客户端(通过ID找到请求1的Future)
响应3(ID=uuid-003) → 客户端(通过ID找到请求3的Future)
3. 完整通信流程

SyncWrite.java - 发送请求并等待响应

public class SyncWrite {
    public Response writeAndSync(Channel channel, Request request, 
                                long timeout) throws Exception {
        // 1. 生成唯一请求ID
        String requestId = UUID.randomUUID().toString();
        request.setRequestId(requestId);
        
        // 2. 创建Future并存入全局Map
        WriteFuture<Response> future = new SyncWriteFuture(requestId);
        SyncWriteMap.syncKey.put(requestId, future);
        
        // 3. 发送请求并阻塞等待响应
        Response response = doWriteAndSync(channel, request, timeout, future);
        
        // 4. 清理映射关系
        SyncWriteMap.syncKey.remove(requestId);
        return response;
    }
    
    private Response doWriteAndSync(Channel channel, Request request, 
                                    long timeout, WriteFuture<Response> writeFuture) {
        // 发送请求,添加监听器
        channel.writeAndFlush(request).addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture future) {
                writeFuture.setWriteResult(future.isSuccess());
                writeFuture.setCause(future.cause());
                // 发送失败,移除映射
                if (!writeFuture.isWriteSuccess()) {
                    SyncWriteMap.syncKey.remove(writeFuture.requestId());
                }
            }
        });
        
        // 阻塞等待响应
        Response response = writeFuture.get(timeout, TimeUnit.MILLISECONDS);
        if (response == null) {
            if (writeFuture.isTimeout()) {
                throw new TimeoutException();
            } else {
                throw new Exception(writeFuture.cause());
            }
        }
        return response;
    }
}

服务端Handler - 处理请求并返回响应

@Override
public void channelRead(ChannelHandlerContext ctx, Object obj) {
    Request msg = (Request) obj;
    
    // 构建响应,保持相同的requestId(关键!)
    Response response = new Response();
    response.setRequestId(msg.getRequestId());
    response.setParam(msg.getResult() + " 请求成功");
    
    // 发送响应
    ctx.writeAndFlush(response);
}

客户端Handler - 接收响应并唤醒等待线程

@Override
public void channelRead(ChannelHandlerContext ctx, Object obj) {
    Response msg = (Response) obj;
    String requestId = msg.getRequestId();
    
    // 根据requestId找到对应的Future
    SyncWriteFuture future = (SyncWriteFuture) SyncWriteMap.syncKey.get(requestId);
    if (future != null) {
        future.setResponse(msg);  // 设置响应,唤醒等待线程
    }
}
4. 完整时序图
客户端线程                    Netty IO线程                  服务端
   |                              |                           |
   |--1.生成requestId------------>|                           |
   |--2.创建Future并存入Map------->|                           |
   |--3.发送请求----------------->|--4.网络传输-------------->|
   |--5.阻塞等待(latch.await)     |                           |
   |                              |                           |--6.处理请求
   |                              |<--7.返回响应(带requestId)--|
   |                              |                           |
   |<-8.根据requestId找到Future---|                           |
   |<-9.setResponse唤醒线程-------|                           |
   |--10.返回结果                 |                           |

项目总结

核心技术:

  • ✅ CountDownLatch:实现线程阻塞和唤醒
  • ✅ 请求ID映射:准确匹配请求和响应
  • ✅ Future模式:异步转同步
  • ✅ 超时控制:避免无限等待

适用场景:

  • RPC框架(Dubbo、gRPC)
  • 同步调用远程服务
  • 需要等待响应结果的场景

关键点:

  • 每个请求必须有唯一ID
  • 服务端必须原样返回requestId
  • 使用ConcurrentHashMap保证线程安全
  • 及时清理映射关系,避免内存泄漏

netty案例,netty4.1中级拓展篇七《Netty请求响应同步通信》 前言介绍 在我们实现开发RPC框架的时候,需要选择socket的通信方式。而我们知道一般情况下socket通信类似与qq聊天,发过去消息,什么时候回复都可以。但是我们RPC框架通信,从感觉上类似http调用,需要在一定时间内返回,否则就会发生超时断开。这里我们选择netty作为我们的socket框架,采用future方式进行通信。 Dubbo:国内最早开源的 RPC 框架,由阿里巴巴公司开发并于... 阅读详情

相关推荐

netty案例,netty4.1中级拓展篇一《Netty与SpringBoot整合》

前言介绍 在实际的开发中,我们需要对netty服务进行更多的操作,包括;获取它的状态信息、启动/停止、对客户端用户强制下线等等,为此我们需要把netty服务加入到web系统中,那么本章节介绍如何将Netty与SpringBoot整合。 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使...

bugstack虫洞栈 2642

Netty实战应用学习笔记-中级拓展篇(一)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 1072

netty案例,netty4.1中级拓展篇五《基于Netty搭建WebSocket,模仿微信聊天页面》

前言介绍 本章节我们模仿微信聊天页面,开发一个基于Netty搭建WebSocket通信案例。Netty的应用方面非常广;聊天、MQ、RPC、数据等等,在5G到来的时候更加需要大量数据传输,Netty的应用也会更加广阔。 1、这个案例使用SpringBoot+Netty+WebSocket搭建功能。 2、使用Netty提供的HttpServerCodec、HttpObjectAggregator、...

bugstack虫洞栈 3072

Netty实战应用学习笔记-中级拓展篇(六)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 630

netty案例,netty4.1中级拓展篇三《Netty传输Java对象》

前言介绍 Netty在实际应用级开发中,有时候某些特定场景下会需要使用Java对象类型进行传输,但是如果使用Java本身序列化进行传输,那么对性能的损耗比较大。为此我们需要借助protostuff-core的工具包将对象以二进制形式传输并做编码解码处理。与直接使用protobuf二进制传输方式不同,这里不需要定义proto文件,而是需要实现对象类型编码解码器,用以传输自定义Java对象。 pro...

bugstack虫洞栈 2142

netty案例,netty4.1中级拓展篇四《Netty传输文件、分片发送、断点续传》

前言介绍 1、在实际应用中我们经常使用到网盘服务,他们可以高效的上传下载较大文件。那么这些高性能文件传输服务,都需要实现的分片发送、断点续传功能。 2、在Java文件操作中有RandomAccessFile类,他可以支持文件的定位读取和写入,这样就满足了我们对文件分片的最基础需求。 3、Netty服务端启动后,可以向客户端发送文件传输指令;允许接收文件、控制读取位点、记录传输标记、文件接收完成。 ...

bugstack虫洞栈 3229

Dapper 快速上手指南:30 秒把数据库查询映射成 C 对象

Dapper 是 .NET 里一款轻量级 ORM / 对象映射器,核心就一件事:用最少的代码,把数据库查询结果映射成 C# 对象。它扩展 ADO.NET 的 `IDbConnection` 接口,不生成额外代码、不改变你的 SQL 写法,装好就能用。这篇 Dapper 教程带你从安装、配置到跑通第一次查询,顺带避开最常见的几个坑。 [![Dapper .NET 轻量 ORM 数据库映射项目主图]

gitblog_00439的博客 401

netty案例,netty4.1中级拓展篇二《Netty使用Protobuf传输数据》

前言介绍 在netty数据传输过程中可以有很多选择,比如;字符串、json、xml、java对象,但为了保证传输的数据具备;良好的通用性、方便的操作性和传输的高性能,我们可以选择protobuf作为我们的数据传输格式。目前protobuf可以支持;C++、C#、Dart、Go、Java、Python等,也可以在JS里使用。知识点;ProtobufDecoder、ProtobufEncoder、Pr...

bugstack虫洞栈 2080

Netty实战应用学习笔记-中级拓展篇(十二)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 1087

netty案例,netty4.1中级拓展篇八《Netty心跳服务与断线重连》

前言介绍 在我们使用netty中,需要监测服务是否稳定以及在网络异常链接断开时候可以自动重连。需要实现监听;f.addListener(new MyChannelFutureListener()) 环境准备 1、jdk1.8【jdk1.7以下只能部分支持netty】 2、Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 代码示例 itstack-...

bugstack虫洞栈 2069

netty案例,netty4.1中级拓展篇十二《Netty流量整形数据流速率控制分析与实战》

作者:付政委 | 关注微信公众号:bugstack虫洞栈,获取专题案例源码 前言介绍 你是否使用过某盘,在前几年我们使用的时候速度飞快,上传下载嗖嗖嗖。但是近年来只要不办会员,速度慢的像蜗牛,当然人家也得赚钱我们能理解。那么这样的限速是怎么实现的呢,我们这个案例使用Netty的流量整形进行限速传输,测试过程中当你把流量整形功能去掉后你就是年费VIP。 流量整形(Traffic Shaping...

bugstack虫洞栈 3696

Netty实战应用学习笔记-中级拓展篇(九)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 1063

TI C2000 DSP CLA实战:用F28069的协处理器实现ADC采样与PWM相位实时联动控制

本文详细介绍了如何利用TI C2000系列DSP的CLA协处理器在TMS320F28069上实现ADC采样与PWM相位的实时联动控制。通过CLA的零周期上下文切换和硬件级任务触发特性,显著提升了电机控制和数字电源应用的实时性能,特别适用于需要亚微秒级响应的场景。

weixin_30932215的博客 3968

Netty实战应用学习笔记-中级拓展篇(八)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 640

Simpack实战:5分钟搞定轨道车辆车轮不圆的3种定义方法(附详细参数设置)

本文详细介绍了在Simpack中定义轨道车辆车轮不圆的3种高效方法:Radius deviations、Fourier coefficients和Harmonic function,并提供了详细的参数设置指南。通过实战案例和参数优化技巧,帮助工程师快速掌握车轮不圆度的定义与仿真,提升轨道车辆动力学分析的准确性和效率。

weixin_29292535的博客 203

从课堂笔记到动手实践:用Multisim仿真高频谐振功放(丙类)的完整流程

本文详细介绍了使用Multisim仿真高频谐振功放(丙类)的完整流程,从仿真环境准备到电路搭建、偏置网络设计、信号注入与波形观测,再到效率优化与性能验证。通过14个关键操作步骤,帮助读者将高频电子线路的理论知识转化为动手实践,深入理解丙类功放的非线性工作状态和效率优化技巧。

weixin_30701575的博客 521

Netty实战应用学习笔记-中级拓展篇(十三)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 570

实现 PDF 添加水印的五种方式

PDF(Portable Document Format,便携式文档格式)是一种流行的文件格式,它可以在多个操作系统和应用程序中进行查看和打印。在某些情况下,我们需要对 PDF 文件添加水印,以使其更具有辨识度或者保护其版权。本文将介绍如何使用 Spring Boot 来实现 PDF 添加水印的方式。

逸轩 6474

Netty实战应用学习笔记-中级拓展篇(五)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 636

rk3568 Android12 默认授予预装app权限

默认授予预装应用权限可以避免用户在每次使用应用时都要重复同意权限请求。尽管在Android 12中,用户对权限的管理有了更高的控制权,但这也可能导致用户在频繁使用预装应用时感到繁琐和不必要的干扰。通过默认授权,减少了这种麻烦。一些厂商可能会根据其定制的Android系统(如Samsung的One UI、Xiaomi的MIUI)要求某些预装应用默认获得权限,以便应用能够提供厂商特定的功能。这也有助于设备厂商提供统一和稳定的服务,不至于因为权限问题导致某些功能无法使用。

weixin_35723192的博客 661

SAP_ABAP_SD_BAPI清单案例教程——销售订单开票_BAPI_BILLINGDOC_CREATEMULTIPLE_VF01_S4版本

SAP BAPI(Business Application Programming Interface)是SAP系统中的一组规范和标准,用于从SAP系统中获取数据、输入数据和更新数据。这些API允许开发者以编程方式与SAP系统交互,而无需编写大量的自定义代码。2.1 Copy代码 直接可使用。2.2 VF01开票。

java_zhong1990的专栏 2214

getbook netty实战_netty案例,netty4.1中级拓展篇七《Netty请求响应同步通信》

小傅哥 | https://bugstack.cn沉淀、分享、成长,让自己和他人都能有所收获。专注于原创专题案例编写,目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例、源码分析等。你用剑 、我用刀 ,好的代码都很烧,望你不吝出招!一、前言介绍在我们实现开发RPC框架的时候,需要选择socket的通信方式。而...

weixin_39838328的博客 136

CANoe/CAPL ,远程邮件通知

CANoe/CAPL ,远程邮件通知

蚂蚁小兵 3038

Netty 4.1 使用 Protobuf 传输数据:itstack-demo-netty 中级拓展篇二实战解析

本篇技术指南以 CodeGuide 开源仓库中 itstack-demo-netty 中级拓展篇二的完整案例为蓝本,讲解如何在 Netty 4.1 服务端与客户端之间使用 Google Protocol Buffers(Protobuf)作为数据传输格式,涵盖 `MsgInfo.proto` 协议定义、protoc 编译、`ProtobufDecoder` / `ProtobufEncoder`

gitblog_00156的博客 248

EMB电子机械制动执行器五大核心部件选型解析

本文介绍了EMB电子机械制动执行器五大核心部件的选型方案:采用无刷直流电机作为驱动源,兼具高扭矩和快速响应特性;选用滚珠丝杠传动机构,实现高效旋转-直线转换;行星齿轮减速器满足紧凑空间内的增扭需求;QT450-QT550材质的钳体确保刚度要求;应变片式压力传感器实现夹紧力精确测量。各部件选型综合考虑了性能、可靠性和空间限制,为EMB系统设计提供了完整解决方案。

m0_59012156的博客 1743

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

上一篇: Netty实战应用学习笔记-中级拓展篇(六)
下一篇: Netty实战应用学习笔记-中级拓展篇(八)
不早睡不改名@
博客等级 码龄5年 536粉丝 306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值