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 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 class="current-tab" 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">
@@ -635,37 +407,13 @@ function hide_canvas() {
<p>FileChannel#transferTo() 的使用也非常简单,我们直接看如下的代码示例,通过 transferTo() 将 from.data 传输到 to.data(),等于实现了文件拷贝的功能。</p>
<pre><code>public void testTransferTo() throws IOException {
RandomAccessFile fromFile = new RandomAccessFile(&quot;from.data&quot;, &quot;rw&quot;);
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile(&quot;to.data&quot;, &quot;rw&quot;);
FileChannel toChannel = toFile.getChannel();
long position = 0;
long count = fromChannel.size();
fromChannel.transferTo(position, count, toChannel);
}
</code></pre>
@@ -713,13 +461,7 @@ function hide_canvas() {
<p>CompositeByteBuf 是 Netty 中实现零拷贝机制非常重要的一个数据结构CompositeByteBuf 可以理解为一个虚拟的 Buffer 对象,它是由多个 ByteBuf 组合而成,但是在 CompositeByteBuf 内部保存着每个 ByteBuf 的引用关系,从逻辑上构成一个整体。比较常见的像 HTTP 协议数据可以分为<strong>头部信息 header</strong><strong>消息体数据 body</strong>,分别存在两个不同的 ByteBuf 中,通常我们需要将两个 ByteBuf 合并成一个完整的协议数据进行发送,可以使用如下方式完成:</p>
<pre><code>ByteBuf httpBuf = Unpooled.buffer(header.readableBytes() + body.readableBytes());
httpBuf.writeBytes(header);
httpBuf.writeBytes(body);
</code></pre>
@@ -727,9 +469,6 @@ httpBuf.writeBytes(body);
<p>可以看出,如果想实现 header 和 body 这两个 ByteBuf 的合并,需要先初始化一个新的 httpBuf然后再将 header 和 body 分别拷贝到新的 httpBuf。合并过程中涉及两次 CPU 拷贝,这非常浪费性能。如果使用 CompositeByteBuf 如何实现类似的需求呢?如下所示:</p>
<pre><code>CompositeByteBuf httpBuf = Unpooled.compositeBuffer();
httpBuf.addComponents(true, header, body);
</code></pre>
@@ -741,37 +480,13 @@ httpBuf.addComponents(true, header, body);
<p>从图上可以看出CompositeByteBuf 内部维护了一个 Components 数组。在每个 Component 中存放着不同的 ByteBuf各个 ByteBuf 独立维护自己的读写索引,而 CompositeByteBuf 自身也会单独维护一个读写索引。由此可见Component 是实现 CompositeByteBuf 的关键所在,下面看下 Component 结构定义:</p>
<pre><code>private static final class Component {
final ByteBuf srcBuf; // 原始的 ByteBuf
final ByteBuf buf; // srcBuf 去除包装之后的 ByteBuf
int srcAdjustment; // CompositeByteBuf 的起始索引相对于 srcBuf 读索引的偏移
int adjustment; // CompositeByteBuf 的起始索引相对于 buf 的读索引的偏移
int offset; // Component 相对于 CompositeByteBuf 的起始索引位置
int endOffset; // Component 相对于 CompositeByteBuf 的结束索引位置
// 省略其他代码
}
</code></pre>
@@ -805,9 +520,6 @@ httpBuf.addComponents(true, header, body);
<p>ByteBuf 提供了两个 slice 切分方法:</p>
<pre><code>public ByteBuf slice();
public ByteBuf slice(int index, int length);
</code></pre>
@@ -815,13 +527,7 @@ public ByteBuf slice(int index, int length);
<p>假设我们已经有一份完整的 HTTP 数据,可以通过 slice 方法切分获得 header 和 body 两个 ByteBuf 对象,对应的内容分别为 &quot;header&quot;&quot;body&quot;,实现方式如下:</p>
<pre><code>ByteBuf httpBuf = ...
ByteBuf header = httpBuf.slice(0, 6);
ByteBuf body = httpBuf.slice(6, 4);
</code></pre>
@@ -835,101 +541,29 @@ ByteBuf body = httpBuf.slice(6, 4);
<p>在 Netty 源码的 example 包中,提供了 FileRegion 的使用示例,以下代码片段摘自 FileServerHandler.java。</p>
<pre><code>@Override
public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
RandomAccessFile raf = null;
long length = -1;
try {
raf = new RandomAccessFile(msg, &quot;r&quot;);
length = raf.length();
} catch (Exception e) {
ctx.writeAndFlush(&quot;ERR: &quot; + e.getClass().getSimpleName() + &quot;: &quot; + e.getMessage() + '\n');
return;
} finally {
if (length &lt; 0 &amp;&amp; raf != null) {
raf.close();
}
}
ctx.write(&quot;OK: &quot; + raf.length() + '\n');
if (ctx.pipeline().get(SslHandler.class) == null) {
// SSL not enabled - can use zero-copy file transfer.
ctx.write(new DefaultFileRegion(raf.getChannel(), 0, length));
} else {
// SSL enabled - cannot use zero-copy file transfer.
ctx.write(new ChunkedFile(raf));
}
ctx.writeAndFlush(&quot;\n&quot;);
}
</code></pre>
@@ -937,121 +571,37 @@ public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception
<p>从 FileRegion 的使用示例可以看出Netty 使用 FileRegion 实现文件传输的零拷贝。FileRegion 的默认实现类是 DefaultFileRegion通过 DefaultFileRegion 将文件内容写入到 NioSocketChannel。那么 FileRegion 是如何实现零拷贝的呢?我们通过源码看看 FileRegion 到底使用了什么黑科技。</p>
<pre><code>public class DefaultFileRegion extends AbstractReferenceCounted implements FileRegion {
private final File f; // 传输的文件
private final long position; // 文件的起始位置
private final long count; // 传输的字节数
private long transferred; // 已经写入的字节数
private FileChannel file; // 文件对应的 FileChannel
@Override
public long transferTo(WritableByteChannel target, long position) throws IOException {
long count = this.count - position;
if (count &lt; 0 || position &lt; 0) {
throw new IllegalArgumentException(
&quot;position out of range: &quot; + position +
&quot; (expected: 0 - &quot; + (this.count - 1) + ')');
}
if (count == 0) {
return 0L;
}
if (refCnt() == 0) {
throw new IllegalReferenceCountException(0);
}
open();
long written = file.transferTo(this.position + position, count, target);
if (written &gt; 0) {
transferred += written;
} else if (written == 0) {
validate(this, position);
}
return written;
}
// 省略其他代码
}
</code></pre>
@@ -1085,9 +635,6 @@ public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception
</div>
</div>
</div>
</div>
@@ -1095,9 +642,6 @@ public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
@@ -1113,17 +657,11 @@ public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
@@ -1149,9 +687,6 @@ public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
@@ -1163,9 +698,6 @@ public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
@@ -1183,12 +715,6 @@ public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception
return "";
}
</script>
</html>