mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-30 23:26:43 +08:00
879 lines
24 KiB
HTML
879 lines
24 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
|
||
<head>
|
||
|
||
<head>
|
||
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
|
||
<link rel="icon" href="/static/favicon.png">
|
||
|
||
<title>00 开篇词 优秀的程序员,你的技术栈中不能只有“增删改查”.md.html</title>
|
||
|
||
<!-- Spectre.css framework -->
|
||
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
|
||
<!-- theme css & js -->
|
||
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
|
||
</head>
|
||
|
||
|
||
|
||
<body>
|
||
|
||
|
||
|
||
<div class="book-container">
|
||
|
||
<div class="book-sidebar">
|
||
|
||
<div class="book-brand">
|
||
|
||
<a href="/">
|
||
|
||
<img src="/static/favicon.png">
|
||
|
||
<span>技术文章摘抄</span>
|
||
|
||
</a>
|
||
|
||
</div>
|
||
|
||
<div class="book-menu uncollapsible">
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="../">上一级</a></li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li>
|
||
|
||
|
||
|
||
<a class="current-tab" href="/专栏/消息队列高手课/00 开篇词 优秀的程序员,你的技术栈中不能只有“增删改查”.md">00 开篇词 优秀的程序员,你的技术栈中不能只有“增删改查”.md.html</a>
|
||
|
||
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/00 预习 怎样更好地学习这门课?.md">00 预习 怎样更好地学习这门课?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/01 为什么需要消息队列?.md">01 为什么需要消息队列?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/02 该如何选择消息队列?.md">02 该如何选择消息队列?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/03 消息模型:主题和队列有什么区别?.md">03 消息模型:主题和队列有什么区别?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/04 如何利用事务消息实现分布式事务?.md">04 如何利用事务消息实现分布式事务?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/05 如何确保消息不会丢失.md">05 如何确保消息不会丢失.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/06 如何处理消费过程中的重复消息?.md">06 如何处理消费过程中的重复消息?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/07 消息积压了该如何处理?.md">07 消息积压了该如何处理?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/08 答疑解惑(一) 网关如何接收服务端的秒杀结果?.md">08 答疑解惑(一) 网关如何接收服务端的秒杀结果?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/09 学习开源代码该如何入手?.md">09 学习开源代码该如何入手?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/10 如何使用异步设计提升系统性能?.md">10 如何使用异步设计提升系统性能?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/11 如何实现高性能的异步网络传输?.md">11 如何实现高性能的异步网络传输?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/12 序列化与反序列化:如何通过网络传输结构化的数据?.md">12 序列化与反序列化:如何通过网络传输结构化的数据?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/13 传输协议:应用程序之间对话的语言.md">13 传输协议:应用程序之间对话的语言.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/14 内存管理:如何避免内存溢出和频繁的垃圾回收?.md">14 内存管理:如何避免内存溢出和频繁的垃圾回收?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/15 Kafka如何实现高性能IO?.md">15 Kafka如何实现高性能IO?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/16 缓存策略:如何使用缓存来减少磁盘IO?.md">16 缓存策略:如何使用缓存来减少磁盘IO?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/17 如何正确使用锁保护共享数据,协调异步线程?.md">17 如何正确使用锁保护共享数据,协调异步线程?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/18 如何用硬件同步原语(CAS)替代锁?.md">18 如何用硬件同步原语(CAS)替代锁?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/19 数据压缩:时间换空间的游戏.md">19 数据压缩:时间换空间的游戏.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/20 RocketMQ Producer源码分析:消息生产的实现过程.md">20 RocketMQ Producer源码分析:消息生产的实现过程.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/21 Kafka Consumer源码分析:消息消费的实现过程.md">21 Kafka Consumer源码分析:消息消费的实现过程.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/22 Kafka和RocketMQ的消息复制实现的差异点在哪?.md">22 Kafka和RocketMQ的消息复制实现的差异点在哪?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/23 RocketMQ客户端如何在集群中找到正确的节点?.md">23 RocketMQ客户端如何在集群中找到正确的节点?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/24 Kafka的协调服务ZooKeeper:实现分布式系统的“瑞士军刀”.md">24 Kafka的协调服务ZooKeeper:实现分布式系统的“瑞士军刀”.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/25 RocketMQ与Kafka中如何实现事务?.md">25 RocketMQ与Kafka中如何实现事务?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/26 MQTT协议:如何支持海量的在线IoT设备.md">26 MQTT协议:如何支持海量的在线IoT设备.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/27 Pulsar的存储计算分离设计:全新的消息队列设计思路.md">27 Pulsar的存储计算分离设计:全新的消息队列设计思路.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/28 答疑解惑(二):我的100元哪儿去了?.md">28 答疑解惑(二):我的100元哪儿去了?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/29 流计算与消息(一):通过Flink理解流计算的原理.md">29 流计算与消息(一):通过Flink理解流计算的原理.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/30 流计算与消息(二):在流计算中使用Kafka链接计算任务.md">30 流计算与消息(二):在流计算中使用Kafka链接计算任务.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/31 动手实现一个简单的RPC框架(一):原理和程序的结构.md">31 动手实现一个简单的RPC框架(一):原理和程序的结构.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/32 动手实现一个简单的RPC框架(二):通信与序列化.md">32 动手实现一个简单的RPC框架(二):通信与序列化.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/33 动手实现一个简单的RPC框架(三):客户端.md">33 动手实现一个简单的RPC框架(三):客户端.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/34 动手实现一个简单的RPC框架(四):服务端.md">34 动手实现一个简单的RPC框架(四):服务端.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/35 答疑解惑(三):主流消息队列都是如何存储消息的?.md">35 答疑解惑(三):主流消息队列都是如何存储消息的?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/加餐 JMQ的Broker是如何异步处理消息的?.md">加餐 JMQ的Broker是如何异步处理消息的?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/消息队列高手课/结束语 程序员如何构建知识体系?.md">结束语 程序员如何构建知识体系?.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() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.add('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
function remove_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.remove('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
function sidebar_toggle() {
|
||
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let content = document.querySelector('.off-canvas-content')
|
||
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
|
||
sidebar_toggle.classList.remove('extend')
|
||
|
||
sidebar.classList.remove('hide')
|
||
|
||
content.classList.remove('extend')
|
||
|
||
} else { // hide
|
||
|
||
sidebar_toggle.classList.add('extend')
|
||
|
||
sidebar.classList.add('hide')
|
||
|
||
content.classList.add('extend')
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
function open_sidebar() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.add('show')
|
||
|
||
overlay.classList.add('show')
|
||
|
||
}
|
||
|
||
function hide_canvas() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.remove('show')
|
||
|
||
overlay.classList.remove('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
</script>
|
||
|
||
|
||
|
||
<div class="off-canvas-content">
|
||
|
||
<div class="columns">
|
||
|
||
<div class="column col-12 col-lg-12">
|
||
|
||
<div class="book-navbar">
|
||
|
||
<!-- For Responsive Layout -->
|
||
|
||
<header class="navbar">
|
||
|
||
<section class="navbar-section">
|
||
|
||
<a onclick="open_sidebar()">
|
||
|
||
<i class="icon icon-menu"></i>
|
||
|
||
</a>
|
||
|
||
</section>
|
||
|
||
</header>
|
||
|
||
</div>
|
||
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
|
||
overflow-x: auto;
|
||
|
||
overflow-y: hidden;">
|
||
|
||
<div class="book-post">
|
||
|
||
<p id="tip" align="center"></p>
|
||
|
||
<div><h1>00 开篇词 优秀的程序员,你的技术栈中不能只有“增删改查”</h1>
|
||
|
||
<p>你好,我是李玥,目前在京东技术架构部任职架构师。最近几年,我一直从事 PaaS 层基础中间件产品的设计和开发相关工作,通俗地说就是在“造轮子”。</p>
|
||
|
||
<p>2018 年,作为首席架构师,我和我的团队一起重构了京东自研的消息队列产品:JMQ(即将开源)。我们在京东实现了超过 2000 个节点的超大规模集群部署,经过 2018 年“11.11”和 2019 年“618”两次大促的实战考验,JMQ 很好地承载了大促期间超过万亿的流量洪峰。</p>
|
||
|
||
<p>在设计开发 JMQ 和其他中间件系统的过程中,我读了很多优秀开源软件的文档和源代码,从中学习和借鉴了很多优秀的设计思想与编码技巧,当然也融入了很多自己的思考和创新。这段经历对我而言弥足珍贵,我也希望能通过这一系列课程,把我学到和悟到的这些底层、通用、有深度的技术分享给你。</p>
|
||
|
||
<h2>底层技术知识,给你深入解决业务问题的能力</h2>
|
||
|
||
<p>你可能会问,我是普通程序员, 工作中只会使用消息队列等“轮子”来实现业务,并没有机会参与到“轮子”的开发,那么学习这些底层的技术知识对我有用吗?当然有用。</p>
|
||
|
||
<p>消息队列几乎是每个后端程序员都会用到的中间件,它在你的技术栈中重要程度不言而喻。消息队列的功能很简单,就是收发消息,你肯定可以看一下文档,几分钟就写出一个用消息队列收发消息的 Demo。但是,把消息队列真正应用到生产系统中,就没那么简单了。</p>
|
||
|
||
<p>在使用消息队列的过程中,你会遇到很多问题,比如选择哪款消息队列更适合你的业务系统?如何保证系统的高可靠、高可用和高性能?如何保证消息不重复、不丢失?如何做到水平扩展?诸如此类的问题,<strong>每一个问题想要解决好,都不太容易。</strong></p>
|
||
|
||
<p>比如说面对消息丢失这个问题,你会怎么解决呢?如果你对消息队列不熟悉,常规的做法可能是去搜索引擎上查看一下错误信息,然后照着别人的解决方案尝试下,能不能解决取决于运气。</p>
|
||
|
||
<p>如果你有一些消息队列使用经验,对于常见的问题,可以根据经验来判断问题所在,而对于一些没见过的问题,那就无能为力了。但如果你掌握了消息队列的实现原理,无论你使用任何一种消息队列,遇到任何问题,都可以从原理层面来分析它的原因,再简单看一下它的 API 和相关配置项,就能很快知道该如何配置消息队列,写出高性能并且可靠的程序。</p>
|
||
|
||
<p>当然**,从职业发展,保持市场竞争力的角度来看,掌握一些底层技术,深耕个人技术栈的深度,实现从“用轮子”到“造轮子”的技术提升,也是一个非常明智的选择。**为什么这么说呢?</p>
|
||
|
||
<p>可以看到,技术圈的风向一直在变,大数据、云的热度已经在慢慢消退,现在当红的是 AI 和 IoT。这些火热的概念,它最终要从论文和 PPT 落地,变成真正能解决问题的系统,否则就是一个空中楼阁。那不变的是什么?</p>
|
||
|
||
<p>无论 AI 还是 IoT,都是一个分布式系统,都要处理海量的数据,都要应对海量并发,它们需要解决的底层问题是一样的。所以,不管技术圈的风向如何变化,那些掌握这些底层技术的程序员,永远都是最受欢迎的人。这也是我开设这个课程的目的之一。</p>
|
||
|
||
<p>消息队列也确实是非常适合拿来展开做源码分析的技术。不难发现,消息队列作为使用最广泛、生命力最旺盛的中间件,无论技术如何发展,都离不开分布式系统的最基本需求:通信。它涉及的底层技术是非常全面的,比如:高性能通信、海量数据存储、高并发等。并且,消息队列具有功能简洁、结构清晰的特点,入门简单但具有足够的深度,适合用来进行深入地分析和学习。</p>
|
||
|
||
<p>从“上古”的 ActiveMQ,如今被广泛使用的 RocketMQ、Kafka,直到最近推出的 Pulsar,伴随着技术的持续发展,一代又一代的消息队列不断推陈出新,性能越来越强大,功能也日臻丰富完善。</p>
|
||
|
||
<p>在这门课程中,我将拿着“显微镜”和你一起分析这些开源消息队列的源码,学习其优秀的设计思路、高超的优化技巧,以及巧妙的代码结构。</p>
|
||
|
||
<p>希望通过这次系统学习,你的收获不仅仅是学会并精通消息队列的使用和原理,而且通过和我一起分析这些优秀开源软件的源代码,透过代码领会到其中最本质的、精髓的东西,将你的技术深度和代码能力再提升一个层次,这也是我希望交付给你的终极收获。</p>
|
||
|
||
<p>总结起来,通过这次系列课程的学习,你可以达成三个成就:</p>
|
||
|
||
<ul>
|
||
|
||
<li>成为消息队列领域的“技术高手”;</li>
|
||
|
||
<li>掌握从源码分析、解决问题的方法;</li>
|
||
|
||
<li>将你的综合技术能力提升到一个新的高度,具备成为开源软件项目开发者的能力。</li>
|
||
|
||
</ul>
|
||
|
||
<p>如果你愿意的话,强烈建议你参与某个开源软件项目,成为它的贡献者。</p>
|
||
|
||
<h2>课程设置</h2>
|
||
|
||
<p>我将这个课程设置为三部分:基础篇、进阶篇和案例篇。</p>
|
||
|
||
<p><strong>基础篇</strong>,以讲解消息队列的使用方法和最佳实践为主,包括消息队列基础知识、技术选型、高级功能等,给出消息队列应用过程中常见问题的解决策略。通过基础篇的学习,希望你能对消息队列和相关生态系统有比较深入的认识,成为消息队列“小达人”。</p>
|
||
|
||
<p><strong>进阶篇</strong>,是这个课程的核心内容,我们会深入到源码中去,探讨消息队列的实现原理,帮助你拓展知识深度。</p>
|
||
|
||
<p>在这个模块的前半部分,每篇会围绕一个知识点来深入探讨,比如像异步模型、高性能的底层网络通信等,**其中每一个知识点不仅是中间件开发人员必须掌握的,而且是各大厂面试题中的常考内容,**希望你每个知识点都不要放过。</p>
|
||
|
||
<p>后半部分我会带你分析一些开源消息队列的源代码,每篇选择一个开源的消息队列,针对一个功能特性,来一起分析它的源码是如何实现的,理解这个功能特性的实现原理,同时带你学习源代码中优秀的设计思想和一些好的编程技巧。</p>
|
||
|
||
<p>希望通过进阶篇的学习,能够帮助你理解消息队列的设计思想,<strong>学会从源码分析、解决问题的方法,掌握这些可复用到其他领域的底层技术。</strong></p>
|
||
|
||
<p><strong>案例篇</strong>,我会和你一起做两个微型的项目,带你体验实际的代码开发。这两个微项目会用到我们在基础篇和进阶篇中学习的知识。</p>
|
||
|
||
<ul>
|
||
|
||
<li>第一个微项目,一起用消息队列和流计算框架来实现一个流计算任务;</li>
|
||
|
||
<li>第二个微项目,一起来实现一个最简单的 RPC 框架,因为开发中间件用到的很多技术都是互通的,开发消息队列的技术同样可以用于开发 RPC 框架。</li>
|
||
|
||
</ul>
|
||
|
||
<p>希望你通过这两个微项目的实际编码,做到学以致用,同时也检验一下自己的学习效果。</p>
|
||
|
||
<h2>写在最后</h2>
|
||
|
||
<p>虽然说这是一门有点儿技术难度的课程,但只要你坚持学习,完整跟下来我们的课程,课后多思考,多练习(所有的知识点最终还是要落实到代码上),我相信你对消息队列的掌握情况、代码能力和架构能力都将会有一个质的飞跃。</p>
|
||
|
||
<p>最后,**我希望你在留言区立个 Flag,写下你的学习计划或目标,**我们所有人一起互相监督,互相鼓励,好的学习方法和心得也可以互相借鉴。当你完整学完所有内容之后,再来这里回顾当初的目标和计划,相信你会为自己的这段学习旅程感到骄傲。</p>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
|
||
|
||
<div style="float: right">
|
||
|
||
<a href="/专栏/消息队列高手课/00 预习 怎样更好地学习这门课?.md">下一页</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
|
||
</div>
|
||
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997943ad443cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
|
||
</body>
|
||
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
|
||
<script>
|
||
|
||
window.dataLayer = window.dataLayer || [];
|
||
|
||
|
||
|
||
function gtag() {
|
||
|
||
dataLayer.push(arguments);
|
||
|
||
}
|
||
|
||
|
||
|
||
gtag('js', new Date());
|
||
|
||
gtag('config', 'G-NPSEEVD756');
|
||
|
||
var path = window.location.pathname
|
||
|
||
var cookie = getCookie("lastPath");
|
||
|
||
console.log(path)
|
||
|
||
if (path.replace("/", "") === "") {
|
||
|
||
if (cookie.replace("/", "") !== "") {
|
||
|
||
console.log(cookie)
|
||
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
setCookie("lastPath", path)
|
||
|
||
}
|
||
|
||
|
||
|
||
function setCookie(cname, cvalue) {
|
||
|
||
var d = new Date();
|
||
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
|
||
var expires = "expires=" + d.toGMTString();
|
||
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
|
||
}
|
||
|
||
|
||
|
||
function getCookie(cname) {
|
||
|
||
var name = cname + "=";
|
||
|
||
var ca = document.cookie.split(';');
|
||
|
||
for (var i = 0; i < ca.length; i++) {
|
||
|
||
var c = ca[i].trim();
|
||
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
|
||
}
|
||
|
||
return "";
|
||
|
||
}
|
||
|
||
|
||
|
||
</script>
|
||
|
||
|
||
|
||
</html>
|
||
|