mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-11-15 21:53:50 +08:00
u
This commit is contained in:
@@ -25,13 +25,7 @@
|
||||
<meta name="generator" content="Hexo 4.2.0">
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
<div class="book-container">
|
||||
|
||||
<div class="book-sidebar">
|
||||
@@ -55,421 +49,214 @@
|
||||
<li><a href="/" class="current-tab">首页</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="../">上一级</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/00 学好 Netty,是你修炼 Java 内功的必经之路.md.html">00 学好 Netty,是你修炼 Java 内功的必经之路.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/01 初识 Netty:为什么 Netty 这么流行?.md.html">01 初识 Netty:为什么 Netty 这么流行?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/02 纵览全局:把握 Netty 整体架构脉络.md.html">02 纵览全局:把握 Netty 整体架构脉络.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/03 引导器作用:客户端和服务端启动都要做些什么?.md.html">03 引导器作用:客户端和服务端启动都要做些什么?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/04 事件调度层:为什么 EventLoop 是 Netty 的精髓?.md.html">04 事件调度层:为什么 EventLoop 是 Netty 的精髓?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/05 服务编排层:Pipeline 如何协调各类 Handler ?.md.html">05 服务编排层:Pipeline 如何协调各类 Handler ?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/06 粘包拆包问题:如何获取一个完整的网络包?.md.html">06 粘包拆包问题:如何获取一个完整的网络包?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/Netty 核心原理剖析与 RPC 实践-完/07 接头暗语:如何利用 Netty 实现自定义协议通信?.md.html">07 接头暗语:如何利用 Netty 实现自定义协议通信?.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/08 开箱即用:Netty 支持哪些常用的解码器?.md.html">08 开箱即用:Netty 支持哪些常用的解码器?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/09 数据传输:writeAndFlush 处理流程剖析.md.html">09 数据传输:writeAndFlush 处理流程剖析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/10 双刃剑:合理管理 Netty 堆外内存.md.html">10 双刃剑:合理管理 Netty 堆外内存.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/11 另起炉灶:Netty 数据传输载体 ByteBuf 详解.md.html">11 另起炉灶:Netty 数据传输载体 ByteBuf 详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/12 他山之石:高性能内存分配器 jemalloc 基本原理.md.html">12 他山之石:高性能内存分配器 jemalloc 基本原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/13 举一反三:Netty 高性能内存管理设计(上).md.html">13 举一反三:Netty 高性能内存管理设计(上).md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/14 举一反三:Netty 高性能内存管理设计(下).md.html">14 举一反三:Netty 高性能内存管理设计(下).md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/15 轻量级对象回收站:Recycler 对象池技术解析.md.html">15 轻量级对象回收站:Recycler 对象池技术解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/16 IO 加速:与众不同的 Netty 零拷贝技术.md.html">16 IO 加速:与众不同的 Netty 零拷贝技术.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/17 源码篇:从 Linux 出发深入剖析服务端启动流程.md.html">17 源码篇:从 Linux 出发深入剖析服务端启动流程.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/18 源码篇:解密 Netty Reactor 线程模型.md.html">18 源码篇:解密 Netty Reactor 线程模型.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/19 源码篇:一个网络请求在 Netty 中的旅程.md.html">19 源码篇:一个网络请求在 Netty 中的旅程.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/20 技巧篇:Netty 的 FastThreadLocal 究竟比 ThreadLocal 快在哪儿?.md.html">20 技巧篇:Netty 的 FastThreadLocal 究竟比 ThreadLocal 快在哪儿?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/21 技巧篇:延迟任务处理神器之时间轮 HashedWheelTimer.md.html">21 技巧篇:延迟任务处理神器之时间轮 HashedWheelTimer.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/22 技巧篇:高性能无锁队列 Mpsc Queue.md.html">22 技巧篇:高性能无锁队列 Mpsc Queue.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/23 架构设计:如何实现一个高性能分布式 RPC 框架.md.html">23 架构设计:如何实现一个高性能分布式 RPC 框架.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/24 服务发布与订阅:搭建生产者和消费者的基础框架.md.html">24 服务发布与订阅:搭建生产者和消费者的基础框架.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/25 远程通信:通信协议设计以及编解码的实现.md.html">25 远程通信:通信协议设计以及编解码的实现.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/26 服务治理:服务发现与负载均衡机制的实现.md.html">26 服务治理:服务发现与负载均衡机制的实现.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/27 动态代理:为用户屏蔽 RPC 调用的底层细节.md.html">27 动态代理:为用户屏蔽 RPC 调用的底层细节.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/28 实战总结:RPC 实战总结与进阶延伸.md.html">28 实战总结:RPC 实战总结与进阶延伸.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/29 编程思想:Netty 中应用了哪些设计模式?.md.html">29 编程思想:Netty 中应用了哪些设计模式?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/30 实践总结:Netty 在项目开发中的一些最佳实践.md.html">30 实践总结:Netty 在项目开发中的一些最佳实践.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Netty 核心原理剖析与 RPC 实践-完/31 结束语 技术成长之路:如何打造自己的技术体系.md.html">31 结束语 技术成长之路:如何打造自己的技术体系.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||||
|
||||
<div class="sidebar-toggle-inner"></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function add_inner() {
|
||||
@@ -479,9 +266,6 @@
|
||||
inner.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function remove_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
@@ -489,9 +273,6 @@
|
||||
inner.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function sidebar_toggle() {
|
||||
|
||||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||||
@@ -521,9 +302,6 @@
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function open_sidebar() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
@@ -547,13 +325,7 @@ function hide_canvas() {
|
||||
overlay.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="off-canvas-content">
|
||||
|
||||
<div class="columns">
|
||||
@@ -647,29 +419,11 @@ function hide_canvas() {
|
||||
<p>通过以上协议基本要素的学习,我们可以得到一个较为通用的协议示例:</p>
|
||||
|
||||
<pre><code>+---------------------------------------------------------------+
|
||||
|
||||
|
||||
|
||||
| 魔数 2byte | 协议版本号 1byte | 序列化算法 1byte | 报文类型 1byte |
|
||||
|
||||
|
||||
|
||||
+---------------------------------------------------------------+
|
||||
|
||||
|
||||
|
||||
| 状态 1byte | 保留字段 4byte | 数据长度 4byte |
|
||||
|
||||
|
||||
|
||||
+---------------------------------------------------------------+
|
||||
|
||||
|
||||
|
||||
| 数据内容 (长度不定) |
|
||||
|
||||
|
||||
|
||||
+---------------------------------------------------------------+
|
||||
|
||||
</code></pre>
|
||||
@@ -727,133 +481,37 @@ function hide_canvas() {
|
||||
<p>MessageToByteEncoder 用于将对象编码成<strong>字节流</strong>,MessageToByteEncoder 提供了唯一的 encode 抽象方法,我们只需要实现<strong>encode 方法</strong>即可完成自定义编码。那么encode() 方法是在什么时候被调用的呢?我们一起看下MessageToByteEncoder 的核心源码片段,如下所示。</p>
|
||||
|
||||
<pre><code>@Override
|
||||
|
||||
|
||||
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
|
||||
|
||||
|
||||
ByteBuf buf = null;
|
||||
|
||||
|
||||
|
||||
try {
|
||||
|
||||
|
||||
|
||||
if (acceptOutboundMessage(msg)) { // 1. 消息类型是否匹配
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
|
||||
|
||||
I cast = (I) msg;
|
||||
|
||||
|
||||
|
||||
buf = allocateBuffer(ctx, cast, preferDirect); // 2. 分配 ByteBuf 资源
|
||||
|
||||
|
||||
|
||||
try {
|
||||
|
||||
|
||||
|
||||
encode(ctx, cast, buf); // 3. 执行 encode 方法完成数据编码
|
||||
|
||||
|
||||
|
||||
} finally {
|
||||
|
||||
|
||||
|
||||
ReferenceCountUtil.release(cast);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (buf.isReadable()) {
|
||||
|
||||
|
||||
|
||||
ctx.write(buf, promise); // 4. 向后传递写事件
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
buf.release();
|
||||
|
||||
|
||||
|
||||
ctx.write(Unpooled.EMPTY_BUFFER, promise);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
buf = null;
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
ctx.write(msg, promise);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (EncoderException e) {
|
||||
|
||||
|
||||
|
||||
throw e;
|
||||
|
||||
|
||||
|
||||
} catch (Throwable e) {
|
||||
|
||||
|
||||
|
||||
throw new EncoderException(e);
|
||||
|
||||
|
||||
|
||||
} finally {
|
||||
|
||||
|
||||
|
||||
if (buf != null) {
|
||||
|
||||
|
||||
|
||||
buf.release();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
@@ -875,25 +533,10 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
|
||||
<p>编码器实现非常简单,不需要关注拆包/粘包问题。如下例子,展示了如何将字符串类型的数据写入到 ByteBuf 实例,ByteBuf 实例将传递给 ChannelPipeline 链表中的下一个 ChannelOutboundHandler。</p>
|
||||
|
||||
<pre><code>public class StringToByteEncoder extends MessageToByteEncoder<String> {
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
|
||||
|
||||
protected void encode(ChannelHandlerContext channelHandlerContext, String data, ByteBuf byteBuf) throws Exception {
|
||||
|
||||
|
||||
|
||||
byteBuf.writeBytes(data.getBytes());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
@@ -909,29 +552,11 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
|
||||
<p>MessageToMessageEncoder 常用的<strong>实现子类</strong>有 StringEncoder、LineEncoder、Base64Encoder 等。以 StringEncoder 为例看下 MessageToMessageEncoder 的用法。源码示例如下:将 CharSequence 类型(String、StringBuilder、StringBuffer 等)转换成 ByteBuf 类型,结合 StringDecoder 可以直接实现 String 类型数据的编解码。</p>
|
||||
|
||||
<pre><code>@Override
|
||||
|
||||
|
||||
|
||||
protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception {
|
||||
|
||||
|
||||
|
||||
if (msg.length() == 0) {
|
||||
|
||||
|
||||
|
||||
return;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
@@ -951,33 +576,12 @@ protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Objec
|
||||
<p>首先,我们看下 ByteToMessageDecoder 定义的抽象方法:</p>
|
||||
|
||||
<pre><code>public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {
|
||||
|
||||
|
||||
|
||||
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
|
||||
|
||||
|
||||
|
||||
protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
|
||||
|
||||
|
||||
if (in.isReadable()) {
|
||||
|
||||
|
||||
|
||||
decodeRemovalReentryProtection(ctx, in, out);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
@@ -1005,141 +609,39 @@ protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Objec
|
||||
<p>在实现协议编码器之前,我们首先需要清楚一个问题:如何判断 ByteBuf 是否存在完整的报文?最常用的做法就是通过读取消息长度 dataLength 进行判断。如果 ByteBuf 的可读数据长度小于 dataLength,说明 ByteBuf 还不够获取一个完整的报文。在该协议前面的消息头部分包含了魔数、协议版本号、数据长度等固定字段,共 14 个字节。固定字段长度和数据长度可以作为我们判断消息完整性的依据,具体编码器实现逻辑示例如下:</p>
|
||||
|
||||
<pre><code>/*
|
||||
|
||||
|
||||
|
||||
+---------------------------------------------------------------+
|
||||
|
||||
|
||||
|
||||
| 魔数 2byte | 协议版本号 1byte | 序列化算法 1byte | 报文类型 1byte |
|
||||
|
||||
|
||||
|
||||
+---------------------------------------------------------------+
|
||||
|
||||
|
||||
|
||||
| 状态 1byte | 保留字段 4byte | 数据长度 4byte |
|
||||
|
||||
|
||||
|
||||
+---------------------------------------------------------------+
|
||||
|
||||
|
||||
|
||||
| 数据内容 (长度不定) |
|
||||
|
||||
|
||||
|
||||
+---------------------------------------------------------------+
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
|
||||
|
||||
public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
|
||||
|
||||
|
||||
|
||||
// 判断 ByteBuf 可读取字节
|
||||
|
||||
|
||||
|
||||
if (in.readableBytes() < 14) {
|
||||
|
||||
|
||||
|
||||
return;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
in.markReaderIndex(); // 标记 ByteBuf 读指针位置
|
||||
|
||||
|
||||
|
||||
in.skipBytes(2); // 跳过魔数
|
||||
|
||||
|
||||
|
||||
in.skipBytes(1); // 跳过协议版本号
|
||||
|
||||
|
||||
|
||||
byte serializeType = in.readByte();
|
||||
|
||||
|
||||
|
||||
in.skipBytes(1); // 跳过报文类型
|
||||
|
||||
|
||||
|
||||
in.skipBytes(1); // 跳过状态字段
|
||||
|
||||
|
||||
|
||||
in.skipBytes(4); // 跳过保留字段
|
||||
|
||||
|
||||
|
||||
int dataLength = in.readInt();
|
||||
|
||||
|
||||
|
||||
if (in.readableBytes() < dataLength) {
|
||||
|
||||
|
||||
|
||||
in.resetReaderIndex(); // 重置 ByteBuf 读指针位置
|
||||
|
||||
|
||||
|
||||
return;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
byte[] data = new byte[dataLength];
|
||||
|
||||
|
||||
|
||||
in.readBytes(data);
|
||||
|
||||
|
||||
|
||||
SerializeService serializeService = getSerializeServiceByType(serializeType);
|
||||
|
||||
|
||||
|
||||
Object obj = serializeService.deserialize(data);
|
||||
|
||||
|
||||
|
||||
if (obj != null) {
|
||||
|
||||
|
||||
|
||||
out.add(obj);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
@@ -1171,9 +673,6 @@ public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object&g
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1181,9 +680,6 @@ public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object&g
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||||
|
||||
</div>
|
||||
@@ -1199,17 +695,11 @@ public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object&g
|
||||
<script>
|
||||
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
|
||||
|
||||
function gtag() {
|
||||
|
||||
dataLayer.push(arguments);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-NPSEEVD756');
|
||||
@@ -1235,9 +725,6 @@ public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object&g
|
||||
setCookie("lastPath", path)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setCookie(cname, cvalue) {
|
||||
|
||||
var d = new Date();
|
||||
@@ -1249,9 +736,6 @@ public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object&g
|
||||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getCookie(cname) {
|
||||
|
||||
var name = cname + "=";
|
||||
@@ -1269,12 +753,6 @@ public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object&g
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user