京东的Netty实践,京麦TCP网关长连接容器架构

2023-11-11


背景

早期京麦搭建 HTTP 和 TCP 长连接功能主要用于消息通知的推送,并未应用于 API 网关。随着逐步对 NIO 的深入学习和对 Netty 框架的了解,以及对系统通信稳定能力越来越高的要求,开始有了采用 NIO 技术应用网关实现 API 请求调用的想法,最终在 2016 年实现,并完全支撑业务化运行。

由于诸多的改进,包括 TCP 长连接容器、Protobuf 的序列化、服务泛化调用框架等等,性能比 HTTP 网关提升 10 倍以上,稳定性也远远高于 HTTP 网关。


为什么选择Netty


      了解了Socket通信(IO/NIO/AIO)编程,对于通信模型已经有了一个基本的认识。其实上一篇文章中,我们学习的仅仅是一个模型,如果想把这些真正的用于实际工作中,那么还需要不断的完善、扩展和优化。比如经典的TCP读包写包问题,或者是数据接收的大小,实际的通信处理与应答的处理逻辑等等一些细节问题需要认真的去思考,而这些都需要大量的时间和经历,以及丰富的经验。所以想学好Socket通信不是件容易事,那么接下来就来学习一下新的技术Netty,为什么会选择Netty?因为它简单!使用Netty不必编写复杂的逻辑代码去实现通信,再也不需要去考虑性能问题,不需要考虑编码问题,半包读写等问题。强大的Netty已经帮我们实现好了,我们只需要使用即可。

Netty是最流行的NIO框架,它的健壮性、功能、性能、可定制性和可扩展性在同类框架都是首屈一指的。它已经得到成百上千的商业/商用项目验证,如Hadoop的RPC框架Avro、RocketMQ以及主流的分布式通信框架Dubbox等等。

netty简介

      Netty是基于Java NIO client-server的网络应用框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一种新的方式来开发网络应用程序,这种新的方式使它很容易使用和具有很强的扩展性。Netty的内部实现是很复杂的,但是Netty提供了简单易用的API从网络处理代码中解耦业务逻辑。Netty是完全基于NIO实现的,所以整个Netty都是异步的。

网络应用程序通常需要有较高的可扩展性,无论是Netty还是其他的基于Java Nio的框架,都会提供可扩展性的解决方案。Netty中一个关键组成部分是它的异步特性,本片文章将讨论同步(阻塞)和异步(非阻塞)的IO来说明为什么使用异步代码解决扩展性问题以及如何使用异步。

Netty的特性

 

设计

统一的API,适用于不同的协议(阻塞和非阻塞)

基于灵活、可扩展的事件驱动模型

高度可定制的线程模型

可靠的无连接数据Socket支持(UDP)

 

性能

更好的吞吐量,低延迟

更省资源

尽量减少不必要的内存拷贝

 

安全

完整的SSL/TLS和STARTTLS的支持

能在Applet与Android的限制环境运行良好

 

健壮性

不再因过快、过慢或超负载连接导致OutOfMemoryError

不再有在高速网络环境下NIO读写频率不一致的问题

架构

netty架构 

先放上一张漂亮的Netty总体结构图,下面的内容也主要围绕该图上的一些核心功能做分析,但对如Container Integration及Security Support等高级可选功能,本文不予分析。

下面谈一下netty框架的实现,主要针对主reactor多线程这个模型作为示例。 
其流程如下: 

 一、网络结构


客户端通过域名 + 端口访问 TCP 网关,域名不同的运营商对应不同的 VIP,VIP 发布在 LVS 上,LVS 将请求转发给后端的 HAProxy,再由 HAProxy 把请求转发给后端的 Netty 的 IP+Port。

LVS 转发给后端的 HAProxy,请求经过 LVS,但是响应是 HAProxy 直接反馈给客户端的,这也就是 LVS 的 DR 模式。


 二、TCP 网关长连接容器架构


TCP 网关的核心组件是 Netty,而 Netty 的 NIO 模型是 Reactor 反应堆模型(Reactor 相当于有分发功能的多路复用器 Selector)。每一个连接对应一个 Channel(多路指多个 Channel,复用指多个连接复用了一个线程或少量线程,在 Netty 指 EventLoop),一个 Channel 对应唯一的 ChannelPipeline,多个 Handler 串行的加入到 Pipeline 中,每个 Handler 关联唯一的 ChannelHandlerContext。

TCP 网关长连接容器的 Handler 就是放在 Pipeline 的中。我们知道 TCP 属于 OSI 的传输层,所以建立 Session 管理机制构建会话层来提供应用层服务,可以极大的降低系统复杂度。

所以,每一个 Channel 对应一个 Connection,一个 Connection 又对应一个 Session,Session 由 Session Manager 管理,Session 与 Connection 是一一对应的,Connection 保存着 ChannelHandlerContext(ChannelHanderContext 可以找到 Channel),Session 通过心跳机制来保持 Channel 的 Active 状态。

每一次 Session 的会话请求(ChannelRead)都是通过 Proxy 代理机制调用 Service 层,数据请求完毕后通过写入 ChannelHandlerConext 再传送到 Channel 中。数据下行主动推送也是如此,通过 Session Manager 找到 Active 的 Session,轮询写入 Session 中的 ChannelHandlerContext,就可以实现广播或点对点的数据推送逻辑。

Netty 的应用实践


京麦 TCP 网关使用 Netty Channel 进行数据通信,使用 Protobuf 进行序列化和反序列化,每个请求都将被封装成 Byte 二进制字节流,在整个生命周期中,Channel 保持长连接,而不是每次调用都重新创建 Channel,达到链接的复用。


 一、TCP 网关的线程模型


TCP 网关使用 Netty 的线程池,共三组线程池,分别为 BossGroup、WorkerGroup 和 ExecutorGroup。其中,BossGroup 用于接收客户端的 TCP 连接,WorkerGroup 用于处理 I/O、执行系统 Task 和定时任务,ExecutorGroup 用于处理网关业务加解密、限流、路由,及将请求转发给后端的抓取服务等业务操作。

NioEventLoop 是 Netty 的 Reactor 线程,其角色:

  1. Boss Group:作为服务端 Acceptor 线程,用于 accept 客户端链接,并转发给 WorkerGroup 中的线程。

  2. Worker Group:作为 IO 线程,负责 IO 的读写,从 SocketChannel 中读取报文或向 SocketChannel 写入报文。

  3. Task Queue/Delay Task Queue:作为定时任务线程,执行定时任务,例如链路空闲检测和发送心跳消息等。


 二、TCP 网关 Netty Server 的 IO 模型


  1. 创建 ServerBootstrap,设定 BossGroup 与 WorkerGroup 线程池。

  2. bind 指定的 port,开始侦听和接受客户端链接。(如果系统只有一个服务端 port 需要监听,则 BossGroup 线程组线程数设置为 1。)

  3. 在 ChannelPipeline 注册 childHandler,用来处理客户端链接中的请求帧。


 三、TCP 网关执行时序图


其中步骤一至步骤九是 Netty 服务端的创建时序,步骤十至步骤十三是 TCP 网关容器创建的时序。

  • 步骤一:创建 ServerBootstrap 实例,ServerBootstrap 是 Netty 服务端的启动辅助类。

  • 步骤二:设置并绑定 Reactor 线程池,EventLoopGroup 是 Netty 的 Reactor 线程池,EventLoop 负责所有注册到本线程的 Channel。

  • 步骤三:设置并绑定服务器 Channel,Netty Server 需要创建 NioServerSocketChannel 对象。

  • 步骤四:TCP 链接建立时创建 ChannelPipeline,ChannelPipeline 本质上是一个负责和执行 ChannelHandler 的职责链。

  • 步骤五:添加并设置 ChannelHandler,ChannelHandler 串行的加入 ChannelPipeline 中。

  • 步骤六:绑定监听端口并启动服务端,将 NioServerSocketChannel 注册到 Selector 上。

  • 步骤七:Selector 轮训,由 EventLoop 负责调度和执行 Selector 轮询操作。

  • 步骤八:执行网络请求事件通知,轮询准备就绪的 Channel,由 EventLoop 执行 ChannelPipeline。

  • 步骤九:执行 Netty 系统和业务 ChannelHandler,依次调度并执行 ChannelPipeline 的 ChannelHandler。

  • 步骤十:通过 Proxy 代理调用后端服务,ChannelRead 事件后,通过发射调度后端 Service。

  • 步骤十一:创建 Session,Session 与 Connection 是相互依赖关系。

  • 步骤十二:创建 Connection,Connection 保存 ChannelHandlerContext。

  • 步骤十三:添加 SessionListener,SessionListener 监听 SessionCreate 和 SessionDestory 等事件。


 四、TCP 网关源码分析

1. 心跳

心跳是用来检测保持连接的客户端是否还存活着,客户端每间隔一段时间就会发送一次心跳包上传到服务端,服务端收到心跳之后更新 Session 的最后访问时间。在服务端长连接会话检测通过轮询 Session 集合判断最后访问时间是否过期,如果过期则关闭 Session 和 Connection,包括将其从内存中删除,同时注销 Channel 等。

通过源码分析,在每个 Session 创建成功之后,都会在 Session 中添加 TcpHeartbeatListener 这个心跳检测的监听,TcpHeartbeatListener 是一个实现了 SessionListener 接口的守护线程,通过定时休眠轮询 Sessions 检查是否存在过期的 Session,如果轮训出过期的 Session,则关闭 Session。

同时,注意到 session.connect 方法,在 connect 方法中会对 Session 添加的 Listeners 进行添加时间,它会循环调用所有 Listner 的 sessionCreated 事件,其中 TcpHeartbeatListener 也是在这个过程中被唤起。

2. Session 管理

Session 是客户端与服务端建立的一次会话链接,会话信息中保存着 SessionId、连接创建时间、上次访问事件,以及 Connection 和 SessionListener,在 Connection 中保存了 Netty 的 ChannelHandlerContext 上下文信息。Session 会话信息会保存在 SessionManager 内存管理器中。


创建 Session 的源码


通过源码分析,如果 Session 已经存在销毁 Session,但是这个需要特别注意,创建 Session 一定不要创建那些断线重连的 Channel,否则会出现 Channel 被误销毁的问题。因为如果在已经建立 Connection(1) 的 Channel 上,再建立 Connection(2),进入 session.close 方法会将 cxt 关闭,Connection(1) 和 Connection(2) 的 Channel 都将会被关闭。在断线之后再建立连接 Connection(3),由于 Session 是有一定延迟,Connection(3) 和 Connection(1/2) 不是同一个,但 Channel 可能是同一个。

所以,如何处理是否是断线重练的 Channel,具体的方法是在 Channel 中存入 SessionId,每次事件请求判断 Channel 中是否存在 SessionId,如果 Channel 中存在 SessionId 则判断为断线重连的 Channel。


3. 数据下行

数据下行通过 MQ 广播机制到所有服务器,所有服务器收到消息后,获取当前服务器所持有的所有 Session 会话,进行数据广播下行通知。如果是点对点的数据推送下行,数据也是先广播到所有服务器,每天服务器判断推送的端是否是当前服务器持有的会话,如果判断消息数据中的信息是在当前服务,则进行推送,否则抛弃。

通过源码分析,数据下行则通过 NotifyProxy 的方式发送数据,需要注意的是 Netty 是 NIO,如果下行通知需要获取返回值,则要将异步转同步,所以 NotifyFuture 是实现 java.util.concurrent.Future 的方法,通过设置超时时间,在 channelRead 获取到上行数据之后,通过 seq 来关联 NotifyFuture 的方法。

下行的数据通过 TcpConnector 的 send 方法发送,send 方式则是通过 ChannelHandlerContext 的 writeAndFlush 方法写入 Channel,并实现数据下行,这里需要注意的是,之前有另一种写法就是 cf.await,通过阻塞的方式来判断写入是否成功,这种写法偶发出现 BlockingOperationException 的异常。


使用阻塞获取返回值的写法


关于 BlockingOperationException 的问题我在 StackOverflow 进行提问,非常幸运的得到了 Norman Maurer(Netty 的核心贡献者之一)的解答。

最终结论大致分析出,在执行 write 方法时,Netty 会判断 current thread 是否就是分给该 Channe 的 EventLoop,如果是则行线程执行 IO 操作,否则提交 executor 等待分配。当执行 await 方法时,会从 executor 里 fetch 出执行线程,这里就需要 checkDeadLock,判断执行线程和 current threads 是否时同一个线程,如果是就检测为死锁抛出异常 BlockingOperationException。

4. 数据上行

数据上行特指从客户端发送数据到服务端,数据从 ChannelHander 的 channelRead 方法获取数据。数据包括创建会话、发送心跳、数据请求等。这里注意的是,channelRead 的数据包括客户端主动请求服务端的数据,以及服务端下行通知客户端的返回数据,所以在处理 object 数据时,通过数据标识区分是请求 - 应答,还是通知 - 回复。

总结


本篇文章粗浅地向大家介绍了京麦 TCP 网关中使用 Netty 实现长连接容器的架构,对涉及 TCP 长连接容器搭建的关键点一一进行了阐述,以及对源码进行简单地分析。在京麦发展过程里 Netty 还有很多的实践应用,例如 Netty4.11+HTTP2 实现 APNs 的消息推送等等。

大型网站架构技术

程序员修炼之道

大型web系统数据缓存设计

基于 Redis 实现分布式应用限流

Cache缓存技术全面解析

京东到家库存系统分析

Nginx 缓存引发的跨域惨案

浅谈Dubbo服务框架

数据库中间件架构 | 架构师之路

MySQL优化精髓



看完本文有收获?请转发分享给更多人


欢迎关注“畅聊架构”,我们分享最有价值的互联网技术干货文章,助力您成为有思想的全栈架构师,我们只聊互联网、只聊架构!打造最有价值的架构师圈子和社区。


长按下方的二维码可以快速关注我们


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

京东的Netty实践,京麦TCP网关长连接容器架构 的相关文章

  • Java基础知识之笔记总结分享(超详细)

    给大家分享一篇我之前在学习java过程中的关于java基础部分的笔记 比较详细 内容也比较多 如有问题请指出以便修改 谢谢 篇幅较长建议收藏浏览 1 环境变量配置 JAVA HOME jdk路径 Path 要把jdk的bin目录路径 添加到
  • 深入理解 relocating 对Elasticsearch集群的影响

    rebalance 用于将集群中的分片进行均衡 保持各个节点的分片数量大致相等 当集群扩容或缩容 掉一个节点的时候 这过程会自动完成 直观的感觉他应该是在后台默默干活的过程 最多占用带宽和磁盘 io 资源 应该感受不到他的存在 但实际情况是
  • 项目经理职责与权利

    项目经理有权按工程承包合同的规定 根据项目随时出现的人 财 物等资源变化情况进行指挥调度 对于施工组织设计和网络计划 也有权在保证总目标不变的前提下进行优化和调整 一 项目经理的权力 1 生产指挥权 项目经理有权按工程承包合同的规定 根据项
  • HAProxy--理论--03--配置文件中的关键字参考

    HAProxy 理论 03 配置文件中的关键字参考 1 balance balance balance url param check post 可用于 defaults listen 和 backend 定义负载均衡算法 用于在负载均衡场
  • 圆石重磅

    Datawhale干货 分享人 武卿 刘伟博士 人工智能的飞速发展 为未来增加了诸多不确定性 未来的世界更加不可预测 ChatGPT问世以来 在全球范围内掀起了一场科技革命 人工智能的飞速发展 为未来增加了诸多不确定性 未来的世界更加不可预
  • java 数组继承_关于对象的行为、数组、继承和类的高级概念(Java)

    1 对象的行为 1 方法调用栈 所有的方法调用都维护在一个称为调用栈的结构中 第一个被调用的方法就是main 该方法是Jvm调用的 因此main 方法总是在调用栈的底部 2 调用方法 一个方法被调用时该方法就放在调用栈的顶部 直到方法执行完
  • 一张图告诉你:今年上市的独角兽,股票表现都还好吗?

    西雅图IT圈 seattleit 今日作者 栗头蓝 一个充满着人文情怀的 web前端相声演员 2019年是独角兽公司上市的大年 定义独角兽公司的一条标准是 估值超过十亿美元的创业公司 截止到6月中旬 美国共有177家这样的独角兽 相比十年前
  • SSM项目中的Layui登陆

    该登陆的的具体结构如图下 在WEB INF jsp admin目录 下创建login jsp页面 写下所需要的登陆页面html代码 在com bdqn controller admin包下创建SystemController控制器层 写页面
  • java21天打卡 day10-字符串2

    字符串2 1 截取子字符串 1 取从第三个字符开始到最后 2 取第二到第四个字符 2 分割字符串 public class Day10 public static void main String args 字符串2 1 截取子字符串 1
  • websocket协议简介

    概念介绍 单工通信 数据传输只允许在一个方向上传输 只能一方发送数据 另一方接收数据并发送 半双工 数据传输允许两个方向上的传输 但在同一时间内 只可以有一方发送或接收数据 全双工 同时可进行双向数据传输 websocket介绍 WebSo
  • 微信小程序java登录授权解密获取unionId(填坑)

    官方流程图 第一步 获取code 说明 小程序调用wx login 获取 临时登录凭证code 并回传到开发者服务器 开发者服务器以code换取 用户唯一标识openid 和 会话密钥session key 之后开发者服务器可以根据用户标识
  • 数据科学与大数据分析项目练习-2使用R进行K-means聚类分析

    使用R进行K means聚类分析 使用Rstudio读取grades km input csv并进行练习 yearly sales csv包含620条数据 包含4种变量 student English Math 和 Science 首先还是
  • 目标检测之Generalized Focal Loss介绍

    Generalized Focal Loss介绍 论文地址 https arxiv org abs 2006 04388 mmdetection已经实现了GFL 简单的说是继承的onestage loss改成作者提出的qfl dfl 正负样
  • DELL服务器R230 RIAD1创建

    DELL服务器R230 RIAD1创建 服务器开机后 按Ctrl R键 进入配置RIAD 第一步 删除虚拟硬盘中的硬盘 可以看到Virtual Disk中显示的2个硬盘都是1 8T的 但前面1和2都是Non RAID 说明这两块硬盘没有进行
  • java异步编程

    java异步调用 定义 Java异步调用是指在调用某个方法时 不需要等待该方法执行完毕才能继续执行下面的代码 而是通过多线程或回调函数等方式 让该方法在后台执行 同时允许程序继续执行下面的代码 这种方式可以提高程序的并发性和响应速度 特别是
  • 【翻译】Attention Is All You Need

    Attention Is All You Need 注意力是你所需要的一切 论文地址 https proceedings neurips cc paper 2017 file 3f5ee243547dee91fbd053c1c4a845aa
  • x86汇编指令学习

    ltr 使用方法 ltr ax 意义 将寄存器ax中的值加载到任务寄存器 TR 中 jnz 和 jne jnz or jne 是指令测试之后的条件跳转 jnz 如果Zero flag ZF 被清零 它将跳转到指定位置 jnz 通常用于显示测
  • Python 程序设计与算法基础教程(第二版)第八章上机实践

    第八章上机实践部分参考答案 2 求阶乘 3 Fibonacci 斐波那契 数列 4 利用可变参数定义求任意个数数值的最小值的函数min n a b c 5 利用元组作为函数的返回值 求最大值 最小值和元素个数 函数后缀带D为递归哦 2 求阶
  • opencv 识别图片和视频中的人脸

    识别图片中的人脸 import cv2 加载图像并创建一个人脸识别的级联分类器 image cv2 imread test jpeg face cascade cv2 CascadeClassifier D pyTest venv Lib
  • wireshark常见提示错误

    TCP dup ack 重复应答 TCP dup ack XXX X 表示第几次重新请求某一个包 XXX表示第几个包 不是Seq X表示第几次请求 丢包或者乱序的情况下 会出现该标志 RST ACK 重置 一般问题不大 TCP Retran

随机推荐

  • 关键词爬取图片(Python)

    废话不多说 直接上代码 测试了好几遍 确认管用才发出来 import requests import os import re headers User Agent Mozilla 5 0 Windows NT 10 0 WOW64 App
  • pandas 获取不符合条件/不包含某个字符串的dataframe

    工作中数据的处理中往往会遇到筛选出不符合条件的或者不包含某个字符的dataframe 我们会如何去做呢 可能你会想到用python写一个函数 然后用panda的apply函数或者map函数来进行处理 不可否认这是一中方法 但是实际上pand
  • JS面向对象编程浅析

    在AJAX兴起以前 很多人写JS可以说都是毫无章法可言的 基本上是想到什么就写什么 就是一个接一个的函数function 遇到重复的还得copy 如果一不小心函数重名了 还真不知道从何开始查找错误 因为大家总是用面向过程的编程思想来写JS代
  • Hexo个人博客主题配置

    系列文章目录 1 Hexo Github Gitee 搭建个人博客 2 Hexo个人博客主题配置 目录 系列文章目录 配置站点信息 修改主题 Next主题配置 样式 favicon avatar rss 代码块 回到页面顶部 阅读进度条 G
  • AI商业化如何落地?看设计师如何利用AI细化工作流

    自从AI爆火之后 人类是否会被AI取代一直都是打工人格外关注的问题 而最近 在小编深入探索到我们用户的使用情况后 发现已经有人拿神采PromeAI直接实现了商业应用 将AI的设计创意应用得淋漓尽致 并且直接进军房地产及家装行业 让我们一起来
  • CSS样式–被忽略的前端基础(二)

    CSS样式 被忽略的前端基础 二 前言 写css的时候有没有觉得属性值千奇百怪 有数值 有带单位的数值 有百分百 有函数 有颜色 red fff rgba 255 255 255 0 CSS属性值种类 1 数值 长度值 用于指定例如元素宽度
  • react-router-dom

    进入项目目录 使用npm安装react router dom npm install react router dom save dev 然后我们新建俩个页面 分别命名为 home 和 detail 在页面中编写如下代码 home js i
  • 一个都不能少: DevOps的3大核心基础架构

    DevOps的涵盖面非常广 因为这个概念的火热 又有很多文章和技术都在把DevOps的帽子扣在自己头上 让很多人迷惑不解 其实 DevOps的知识体系如果从顶层上来分解 只有2块 方法论和工具链 方法论这块 因为DevOps的很多理念脱胎于
  • rank/row_number() OVER ()使用

    row number OVER PARTITION BY COL1 ORDER BY COL2 表示根据COL1分组 在分组内部根据 COL2排序 而此函数计算的值就表示每组内部排序后的顺序编号 组内连续的唯一的 与rownum的区别在于
  • Qt 打开文件夹并选择该文件

    打开文件夹 并选择指定文件 打开文件夹 并且选择指定文件 QString fileName ui gt lineEdit gt text if fileName isEmpty return QProcess process fileNam
  • SQL数据库插入、修改、删除及删除表中数据操作示例(insert、update)

    目录 1 数据库的连接 创建 2 对字段的操作 alter table 3 SQL数据库插入 修改 删除操作 4 数据库的查询操作大全 select 5 多表查询 join on 6 约束操作 三 SQL数据库插入 修改 删除 1 插入数据
  • linux-arm电源管理

    一文搞懂ARM SoC功耗控制架构
  • kube-apiserver启动时报错并且不能操作etcd

    kube apiserver启动时报错 错误信息如下 Flag kubelet port has been deprecated kubelet port is deprecated and will be removed W0914 15
  • 软构第九天:OOP编程语法

    文章目录 前言 一 OOP的基本概念 二 基本概念 类 对象 属性 方法 三 接口 四 继承与重写 五 多态 子类型 重载 总结 前言 上节课说了ADT的相关内容 这节课老师讲述了ADT思想在编程语言中的具体体现 OOP OOP思想我们已经
  • 操作系统之虚拟化CPU(一)介绍

    但凡学习过计算机的人 一定都听说过一个问题 并发和并行的区别 此处引用知乎用户的关于吃饭的形象比喻 是的 你就是任劳任怨的CPU 而吃饭和接电话是你要执行的多个任务 你吃饭吃到一半 电话来了 你一直到吃完了才去接 说明你不支持并发也不支持并
  • (3)使用uboot界面通过tftp安装kernel,rootfs

    板子已经通过Hitool烧录U boot 参数配置 1 单板上电后 敲任意键进入u boot 设置serverip 即tftp服务器的ip ipaddr 单板ip 和ethaddr 单板的MAC地址 hisilicon setenv ser
  • ldap登录 AD域登录工具类

    1 依赖包
  • RabbitMQ 消息确认机制、补偿机制、消息幂等性实践

    1 场景 先看这么几个面试题 如何保证消息的可靠性投递 即如何确定消息是否发送成功 如果失败如何处理 补偿机制 如何保证消息不被重复消费 或者说 如何保证消息消费时的幂等性 2 消息的可靠性投递 消息确认 消息确认包括主要生产者发送确认和消
  • ChatGPT认知课07:ChatGPT AI时代,还需要专门学写作么?

    5 天前 ChatGPT云炬学长 关注 我认为ChatGPT出现后 我们更需要学写作了 这节课就聊聊这个话题 01ChatGPT会像作家一样写出好作品么 自媒体人 职业写作者会失业么 ChatGPT可以高效生成文本 语言表达能力确实牛逼 但
  • 京东的Netty实践,京麦TCP网关长连接容器架构

    背景 早期京麦搭建 HTTP 和 TCP 长连接功能主要用于消息通知的推送 并未应用于 API 网关 随着逐步对 NIO 的深入学习和对 Netty 框架的了解 以及对系统通信稳定能力越来越高的要求 开始有了采用 NIO 技术应用网关实现