This commit is contained in:
周伟
2022-05-11 19:04:14 +08:00
parent 9440ac7291
commit d9c5ffd627
826 changed files with 0 additions and 481675 deletions

View File

@@ -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(&quot;unchecked&quot;)
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&lt;String&gt; {
@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&lt;Object&gt; 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&lt;Objec
<p>首先,我们看下 ByteToMessageDecoder 定义的抽象方法:</p>
<pre><code>public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List&lt;Object&gt; out) throws Exception;
protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List&lt;Object&gt; out) throws Exception {
if (in.isReadable()) {
decodeRemovalReentryProtection(ctx, in, out);
}
}
}
</code></pre>
@@ -1005,141 +609,39 @@ protected void encode(ChannelHandlerContext ctx, CharSequence msg, List&lt;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&lt;Object&gt; out) {
// 判断 ByteBuf 可读取字节
if (in.readableBytes() &lt; 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() &lt; 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&lt;Object&g
</div>
</div>
</div>
</div>
@@ -1181,9 +680,6 @@ public final void decode(ChannelHandlerContext ctx, ByteBuf in, List&lt;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&lt;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&lt;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&lt;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&lt;Object&g
return "";
}
</script>
</html>