本文为分布式系列文章的集锦汇总,长期保持置顶及更新,读者可以在本文中更好的学习到某个具体的系列。

注: 转载本博客文章请注明出处,原创不易,洗文可耻。

我说分布式事务系列

文章链接
我说分布式事务之TCC
我说分布式事务之最大努力通知型事务
我说分布式事务之可靠消息最终一致性事务1-原理及实现
我说分布式事务之消息一致性事务2-rocketmq的实现
【汇总】我说分布式事务系列
分布式事务之聊聊TCC
分布式事务最终一致性常用方案
TCC-Transaction源码解析之事务执行
TCC-Transaction源码解析之事务补偿
自己写分布式事务框架之原理篇[详解本地消息表]

跟我学RocketMQ

文章链接
[1-1]安装RocketMQ
[1-2]安装RocketMQ-Console管理平台
[1-3]发送普通消息及封装DefaultMQProducer支持spring
[1-4]消费消息及封装DefaultMQPushConsumer支持spring
[1-5]发送事务消息实现分布式事务及封装TransactionMQProducer支持spring
[2-0]跟我学RocketMQ之消息重试
[2-1]跟我学RocketMQ之消息幂等
[2-2]跟我学RocketMQ之消息轨迹实战与源码分析
[2-3]跟我学RocketMQ之消息发送源码解析
[2-4]跟我学RocketMQ之批量消息发送源码解析
[2-5]跟我学RocketMQ之消息消费源码解析-p1
[2-6]跟我学RocketMQ之消息消费源码解析-p2
[2-7]跟我学RocketMQ之订阅关系一致性源码讨论
[2-8]跟我学RocketMQ之消息拉取源码解析
[2-9]跟我学RocketMQ之开源客户端混合云实践与案例解析
[2-10]跟我学RocketMQ之事务消息发送源码解析
[2-10]跟我学RocketMQ之事务消息存储源码解析
[2-11]跟我学RocektMQ之事务消息提交及回查源码解析
[2-12]跟我学RocketMQ之定时消息源码解析
[2-13]跟我学RocektMQ之理解长轮询机制
[2-14]跟我学RocketMQ之消息持久化原理与Mmap
[2-15]跟我学RocketMQ之拉模式消费的两种方式
[2-16]跟我学RocketMQ之聊聊死信队列

分库分表

文章链接
我说分布式之分库分表
跟我学shardingjdbc之shardingjdbc入门
跟我学shardingjdbc之使用jasypt加密数据库连接密码
跟我学shardingjdbc之分布式主键及其自定义
跟我学shardingjdbc之自定义分库分表策略-复合分片算法自定义实现

Read More

本文将主要记录在日常开发中遇到的各种问题。以技术类别进行章节划分,作为个人的编码备忘录随时进行查阅,并长期进行置顶。

问题排查

CPU异常飙高排查思路

cpu占用高如何排查

  • 查看占用cpu高的进程: 通过 top 命令找到 CPU 消耗最高的进程,并记住进程 ID {pid}。

    top -M -n 2 -d 3 >{pid}/top.txt 查看top

  • 再次通过 top -Hp {pid} 找到 CPU 消耗最高的线程 ID,并记住线程 ID(十进制).
  • 通过 JDK 提供的 jstack 工具 dump 线程堆栈信息到指定文件中。

    jstack {pid} >{pid}/jstack_1.txt 一次堆栈快照 备用

    jstack {pid} >{pid}/jstack_2.txt 两次堆栈快照 备用

  • 由于刚刚的线程 ID 是十进制的,而堆栈信息中的线程 ID 是16进制的,因此我们需要将10进制的转换成16进制的,并用这个线程 ID 在堆栈中查找。

    使用 printf “%x\n” [十进制数字] ,可以将10进制转换成16进制。

  • 通过刚刚转换的16进制数字从堆栈信息里找到对应的线程堆栈。就可以从该堆栈中看出端倪。

  • 通过top查看当前进程cpu占用情况,找到cpu使用最高的进程PID
  • 查看子进程情况

    top -p 4606 -H

  • 将 子进程id 转换成16进制

    printf “%x \n” 4648 结果为1228

  • 使用jstack查询具体出现问题的代码位置

    jstack 4606|grep 1228 -C 30

  • 根据打印结果定位到具体代码位置

JavaCore相关

该模块主要记录JavaCore相关的技术点

bigdecimal四舍五入

BigDecimal.ROUND_HALF_UP: 遇到.5的情况时往上近似,例: 1.5 ->;2
BigDecimal.ROUND_HALF_DOWN : 遇到.5的情况时往下近似,例: 1.5 ->;1

bigDecimal转换为百分比,保留若干小数

DecimalFormat decimalFormat = new DecimalFormat("0.00%");
BigDecimal decimal = new BigDecimal(count.intValue()).divide(new BigDecimal(allCount), 5, ROUND_HALF_UP);
String formatted = decimalFormat.format(sdPercent);

bigDecimal精确度

BigDecimal.setScale(5,  BigDecimal.ROUND_HALF_UP)  -->保留五位小数,最后一位遇到.5的情况时往上近似

Read More

在2006 年,爱德华·李 (Edward A. Lee) 撰写了一篇题为“线程中的问题”的技术报告。 在技术报告中,他有以下说法:

线程,是非常不确定的一种计算模型。而程序员的工作就是要修剪这种不确定性。

尽管许多技术研究通过提供更加有效的裁减措施来改进线程模型,但我(爱德华本人)认为这是在开倒车。

我们应该从本质上构建确定性的、可组合的组件,而不是去裁减这种非确定性。

应该在需要的时候明确并且明智地引入非确定性,而不是在不需要它的地方将其删除。

对于Aeron而言,Agrona Agents(Agrona代理) 和 Idle Strategies(空闲策略) 是实现爱德华的建议的一种实现方式。

当与 Aeron 一起使用时,Agrona Agents允许基于开发人员以易于推理的方式,安全地构建确定性的资源,从而管理线程。

Agents

Agrona Agents是在应用程序逻辑中执行的,具备工作周期的容器。

例如处理 Aeron 中订阅的消息。

Agents的工作周期间隔和CPU消耗地增加,由空闲策略进行控制。 Agents可以调度在专用的线程上,当然也可以作为一组复合Agents(a composite group of agents)的一部分在单个线程上运行。

典型的Agents工作周期是通过轮询Agents的 doWork 函数实现的,一旦doWork方法返回零,就会回调选中的空闲策略,代码逻辑如下。

1
2
3
4
5
6
7
8
9
10
11
12
public final class Sample implements Agent
{
...
public int doWork()
{
int workCount = 0;
workCount += dutyCyclePart1();
workCount += dutyCyclePart2();
return workCount;
}
...
}

Yielding Idle Strategy (空闲让步策略)

下面这段代码是 Yielding Idle Strategy (中译为:空闲让步策略)的关键部分,它展示了对workCount值进行校验的关键逻辑模板。如果 workCount 结果小于等于零,则Agents线程将由“运行态Running”转为就绪态“Runnable”,让渡cpu时间片供自己及其余同优先级线程进行时间片抢占。

1
2
3
4
5
6
7
8
9
10
public final class YieldingIdleStrategy implements IdleStrategy
{
...
public void idle(int workCount) {
if (workCount <= 0) {
Thread.yield();
}
}
...
}

接着这些 Agent 和 IdleStrategy 会在 AgentRunner 中组合在一起,代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class AgentRunner implements Runnable, AutoCloseable
{
...
public void run()
{
...
// 当线程运行态
while (isRunning)
{
// 执行工作周期的逻辑
doDutyCycle(idleStrategy, agent);
}
...
}
private void doDutyCycle(final IdleStrategy idleStrategy, final Agent agent)
{
...
// 执行agent的doWork方法,即业务逻辑
final int workCount = agent.doWork();
// 根据返回值决定是否要进行执行空闲策略
idleStrategy.idle(workCount);
...
}
...
}

Idle Strategies(空闲策略)

Agrona 提供了一系列默认的空闲策略,但是如果以后需要,开发者很容易实现自定义的空闲策略。

默认空闲策略及其解释如下:

  • SleepingIdleStrategy(休眠空闲策略): 通过使用 parkNanos 将线程阻塞在给定的时间段内实现休眠;

    1
    2
    3
    4
    5
    public void idle(int workCount) {
    if (workCount <= 0) {
    LockSupport.parkNanos(this.sleepPeriodNs);
    }
    }
  • SleepingMillisIdleStrategy(另一种休眠空闲策略):这是基于thread.sleep(millseconds)方法实现的在某个给定时间段内使线程阻塞的空闲策略。 适用于在低规格机器上进行本地开发或者在运行了大量进程的机器上使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public void idle(int workCount) {
    if (workCount <= 0) {
    try {
    Thread.sleep(this.sleepPeriodMs);
    } catch (InterruptedException var3) {
    Thread.currentThread().interrupt();
    }
    }
    }
  • YieldingIdleStrategy(让步空闲策略):使用 thread.yield 来让出线程控制权的策略,在上文我们已经专门解释过;

    1
    2
    3
    4
    5
    public void idle(int workCount) {
    if (workCount <= 0) {
    Thread.yield();
    }
    }
  • BackoffIdleStrategy(退避空闲策略): 这是一种激进的策略,它的机制为先是自旋,然后过渡到让步,然后再过渡到阻塞在可进行配置的纳秒时间内的一种复合策略。 这是 Aeron Cluster 的默认策略。可以看到它的idle()方法是随着运行不断升级空闲处理策略的(是不是类似synchronized锁膨胀)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public void idle() {
    switch (this.state) {
    case 0:
    this.state = 1;
    // 进入自旋逻辑
    ++this.spins;
    break;
    case 1:
    // 自旋
    ThreadHints.onSpinWait();
    if (++this.spins > this.maxSpins) {
    this.state = 2;
    this.yields = 0L;
    }
    break;
    case 2:
    // 让步cpu
    if (++this.yields > this.maxYields) {
    this.state = 3;
    this.parkPeriodNs = this.minParkPeriodNs;
    } else {
    Thread.yield();
    }
    break;
    case 3:
    // 阻塞
    LockSupport.parkNanos(this.parkPeriodNs);
    this.parkPeriodNs = Math.min(this.parkPeriodNs << 1, this.maxParkPeriodNs);
    }
    }
  • NoOpIdleStrategy:这是目前可用的最激进的空闲策略。顾名思义,什么操作都不做,也就是根本不会空闲。

1
2
3
4
5
public void idle(int workCount) {
}
public void idle() {
}
  • BusySpinIdleStrategy: 这将调用 java.lang.Thread.onSpinWait()方法。 这个策略在Java 9+版本的JVM运行时可用。 它向 CPU 提供了一个微弱的标识:如果线程处于一个循环中并且正忙于等待某个操作,那么CPU可能会在不执行OS系统级调度的前提下将额外的资源分配给另一个线程。(也就是CPU会对这种处于循环忙等的线程进行资源调度,分配资源给其他线程)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void idle(int workCount) {
if (workCount <= 0) {
ThreadHints.onSpinWait();
}
}
// org.agrona.hints.ThreadHints#onSpinWait
public static void onSpinWait() {
if (null != ON_SPIN_WAIT_METHOD_HANDLE) {
try {
ON_SPIN_WAIT_METHOD_HANDLE.invokeExact();
} catch (Throwable var1) {
}
}
}

实现自定义空闲策略

如果你想要实现自定义空闲策略,则只需要实现 IdleStrategy 接口,即:

1
2
3
4
5
public interface IdleStrategy {
void idle(int count);
void idle();
void reset();
}

Scheduling Agents(代理调度机制)

如果要开启Agent的生命周期,那么你需要决定如何对它进行调度。

  • 你可以让Agent在 Agrona 提供的一个线程上运行;
  • 你也可以提供一个线程工厂(thread factory)来运行你的Agent;
  • 您可以从一组Agent(a collection of agents)中构造一个 CompositeAgent,然后你就可以将这些Agent作为一个调度单元进行调度。

注意: 并不是所有的空闲策略都是线程安全的。因此通常我们建议为每个被调度的Agent制定不同的空闲策略。(专策略专用)

一旦你已经决定了如何执行一个Agent,那么就可以使用 Agent Runner 启动它。

下面是代码示例:

1
2
3
final AgentRunner runner =
new AgentRunner(idleStrategy, errorHandler, errorCounter, agent);
AgentRunner.startOnThread(runner);

构造一个AgentRunner实例,构造参数中,

  • idleStrategy可以设置为上述任何空闲策略之一,
  • 最后一个参数为一个Agent引用实例。
  • errorHandler(错误处理器)通常会接受字符串,并捕获引发的错误。
  • errorCounter(错误计数器)用于计算该Agent引发的错误。

最后通过调用AgentRunner.startOnThread 告诉 Agrona 将Agent调度在一个新线程上。

小结

到此我们就基本了解了Aeron中的Agrona Agent及其空闲策略的分类及基本使用,如果需要进一步学习,请自行参考完整代码示例:https://aeroncookbook.com/aeron-cookbook/ipc/

复习:java线程生命周期

最后我们一起简单复习一下Java中线程的生命周期,温故知新。

  • 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new Thread();

  • 就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

  • 运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

  • 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

    • 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

    • 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

    • 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  • 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

下一篇

频道,流,以及会话 (Channels, Streams and Sessions)

附录

本文原文链接:https://aeroncookbook.com/agrona/agents-idle-strategies/



版权声明:

原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

本文我们重点介绍Aeron核心组件中的Media Driver(媒体驱动)。、

在继续之前,读者应该熟悉代理和空闲策略部分。由于我们是按照顺序进行学习,因此关于代理与空闲策略(Agents & Idle Strategies),将在下一篇文章讲解。

什么是Media Driver

Media Driver (媒体驱动程序) 组件负责管理通过UDP 或 IPC(这两种方式我们成为Media)发送和接收数据,用于任何活动的发布和/或订阅场景。

Media Driver很灵活,它可以通过修改配置以适应大多数场景,包括但不限于:具有很小延迟的高性能场景,乃至对资源要求极为宽松的低资源环境。

虽然Media Driver在概念上类似于分布式消息代理(消息队列的broker,比如kafka、RocketMQ的broker组件),但它缺少一些broker所具备的功能。 因此最好将 Aeron 的Media Driver与消息代理broker区别对待(但是可以类比进行理解)。

下图显示了构成嵌入在应用程序中的Media Driver的核心组件。

可以看到,发布与订阅均借助Media Driver实现,而Media Driver之间通过Media进行通讯(UDP或者IPC)

Media Driver核心组件解析

Driver Conductor(驱动协调器)

Driver Conductor 接受来自 Aeron 客户端内发布者和订阅者的命令,并协调Media Driver的操作。此外,Driver Conductor 还负责名字解析任务(类比域名解析);

Receiver(接收器)

Receiver管理连接Media Driver的所有数据接收过程。数据传输轮询器(Data Transport Poller)接收 UDP 数据,并使用 Java NIO 与操作系统的网络栈进行交互。

除了从媒体(IPC/UDP)接收数据外,Receiver还管理接收到的images(快照),根据需要发送 NAK 和状态消息。

Sender(发送器)

发送者管理媒体(IPC/UDP)上的数据传输。

Client Conductor(客户端协调器)

Client Conductor负责与Driver Conductor进行通信。

Media Driver目录结构

对于生产环境而言,Media Driver目录文件夹应位于 \dev\shm 路径下。 您需要确保文件系统有足够的存储空间来存储所有镜像(images)和发布内容。

有关更多详细信息,请参阅 发布和订阅 (https://aeroncookbook.com/aeron/publications-subscriptions/#implications-on-devshm)。

上图中展示了一个Media Driver文件夹,其中打开了一个 IPC 发布内容。 Media Driver文件夹包含以下内容:

  • blank.template:它是一个空的日志缓冲区文件,对于一个新的发布过程具备复制支持
  • cnc.dat: 这是在 Aeron 客户端和Media Driver之间共享的 命令-控制内存映射文件。 同时它也是 AeronStat 读取的 Aeron 计数器的所在位置。
  • images (文件夹) :此文件夹包含所有远程发布的打开镜像(image)
  • loss-report.dat:该文件存储了 Aeron 丢包的所有详细信息。 对于了解一段时间内的网络行为很有用。 LossStat 工具用于读取它的内容。
  • Publications(文件夹):此文件夹包含所有打开的发布日志缓冲区。

配置和运行时部署选项

Media Driver可以嵌入在进程中运行或在进程外运行。 当进程用完时,您可以使用 Java 或 C 版本的Media Driver。

除了Media Driver,在 Aeron源代码中还发现了另外两个变体:

  • LowLatencyMediaDriver:在示例文件夹中可以找到,它使用低延迟的最佳设置来设置MediaDriver
  • JavaTestMediaDriver:在测试文件夹中可以找到。通过引入损失能力设置MediaDriver

Media Driver线程模式

在Media Driver中选择正确的资源分配,应当考虑进程中可用的资源和性能要求。

如果将超出可用资源的资源过度分配给 Aeron 进程可能对应用程序的稳定性和性能非常不利。(系统资源需要对Aeron进行合理分配)

Dedicated模式(适用于线上环境or资源充足的环境中)

在专用线程模式(ThreadingMode.DEDICATED)下,Media Driver 将使用 3 个线程,每个线程都有特定的空闲策略:

  • Sender线程(使用 senderIdleStrategy策略);
  • Receiver线程(使用receiverIdleStrategy策略);
  • Driver Conductor线程(使用conductorIdleStrategy策略)。

这是默认的线程模式,最好在关注性能且系统有足够资源的场景下使用。

如果要进一步提高性能,请将线程绑定(pin)到特定的 CPU 核心上。

Shared Network模式(折中场景)

运行在Shared Network模式下(ThreadingMode.SHARED_NETWORK) 将线程数减少到两个:

  • Sender 和 Receiver 被安排在一个复合代理中( [sender-receiver线程],使用 sharedNetworkIdleStrategy策略);
  • Driver Conductor(driver-conductor线程,使用conductorIdleStrategy策略)

Shared模式(适合开发及资源较少场景)

运行在共享模式 (ThreadingMode.SHARED) 下,将线程数减少到一个。

复合代理托管了 Sender、Receiver 和 Conductor 代理。

这可以看作是线程dump中的 [sender-receiver-driver-conductor]线程,并利用了 Media Driver Context 中定义的 sharedIdleStrategy策略。 这最适合开发或运行在可用资源较少的运行环境中。

扩展:C Media Driver

通过用 C 语言编写Media Driver,Real Logic 可以尽可能多地控制Media Driver运行的硬件和操作系统,并且它允许团队从 Java 虚拟机和垃圾收集器中删除对性能敏感的组件。

从 Java 方面来看,使用 C Media Driver通常是透明的。 开发者所需要做的就是将自己的 Aeron 客户端指向 C Media Driver 的文件夹,理论上,一切都会像以前一样工作。

与在独立模式下运行的 Java Media Driver一样,如果多个 Aeron 客户端都在同一台物理机器上,C Media Driver可以与它们一起运行。

获取 C Media Driver

C 媒体驱动程序必须从源代码构建(因操作系统而异):

https://aeroncookbook.com/cookbook-content/build-macos-cpp-driver/

https://aeroncookbook.com/cookbook-content/build-ubuntu-cpp-driver/

https://aeroncookbook.com/cookbook-content/build-windows-cpp-driver/

构建完成会同时构建几个 C语言 版本的 Aeron Sample Tools(AeronStat、ErrorStat、LossStat)。

配置 C Media Driver

默认情况下,C 媒体驱动程序运行需要以下条件:

  • 具有繁忙自旋线程(busy spin thread)优先级的三个线程(sender, receiver, driver conductor线程)
  • 十秒驱动程序超时
  • 16mb缓冲区长度。

如果需要覆盖设置,通常有两个选择:

  • 从文件或 URL 加载参数
  • 使用 -D 参数(与 Java 中相同)通过命令行传入参数,将 .更换为 _。例如
    • -Daeron.driver.timeout=30000 (C版本的支持点号)
    • -Daeron_driver_timeout=30000(java版本的传参不支持点号)

下一篇

代理与空闲策略 (https://aeroncookbook.com/agrona/agents-idle-strategies/)



版权声明:

原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

本系列基于Aeron cookbook编写,通过对原版英文文档进行翻译,加入笔者的一些思考,努力为Aeron提供一份可参考可阅读的中文参考资料。

话不多说,开始我们的学习之旅。

1.Aeron是什么

Aeron是一个开源高性能消息传输机制(单向),它支持高效可靠的UDP单播、UDP多播和IPC消息传输。

Aeron 以可预测的延迟,有效地跨进程或跨网络边界复制有序日志缓冲区。
Aeron API 提供了三个主要交互点

  1. Media Driver 媒体驱动程序
  2. Publications 发布
  3. Subscriptions 订阅

Media Driver 当前支持通过 UDP 和 IPC 进行传输。

UDP套接字交互可以卸载到 Open Onload,如果必要的驱动程序和硬件可供使用,

除了核心 IPC 和 UDP 功能外,Aeron 还提供:

  • Aeron Archive,它为流提供磁盘支持的存储
  • Aeron Cluster,允许开发人员构建基于 RAFT 协议的容错服务

思考
由于Aeron天然支持RAFT协议,并且具备高性能的消息处理能力,因此多使用在金融交易领域,它同时提供了高性能和高可靠的特性。(事实上,Aeron正是real-logic开发的,该团队的另一款代表作disruptor在金融高频交易场景被大规模应用,其超高性能超低延时得到了业界的普遍赞誉。)

从这里也可以想到,Aeron本身属于有状态服务的范畴,所以学习曲线、开发曲线、运维难度都高于传统的无状态服务框架;

2.Aeron架构

Media Driver 和 Aeron 客户端 API 构成了 Aeron 的核心。

Aeron Archive 和 Aeron Cluster 添加其他组件,如存档概览集群概览中所述。

基本代码案例

下面是一个完整的跨IPC发送消息的方法。

为了让它尽可能简单,发布者和订阅者在同一个线程上——但这在现实世界的应用程序中是没有意义的。

尽管如此,它还是很有用的,因为它显示了最小 Aeron 应用程序的完整设置。

该代码可以在 Aeron-ipc 项目的 SimplestCase.java 文件中找到。

作为示例的一部分,我们将构建 4 个部分:

  • Aeron API;
  • Media Driver 媒体驱动程序;
  • a Subscription; 一个发布者
  • a Publication 一个订阅者

完整的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class AeronSimpleDemo {
public static void main(String[] args) {
String channel = "aeron:ipc";
String message = "this is a new Message.";
IdleStrategy idleStrategy = new SleepingIdleStrategy();
UnsafeBuffer unsafeBuffer = new UnsafeBuffer(ByteBuffer.allocate(256));
try (
MediaDriver driver = MediaDriver.launch();
Aeron aeron = Aeron.connect();
Subscription sub = aeron.addSubscription(channel, 10);
Publication pub = aeron.addPublication(channel, 10);
) {
while (!pub.isConnected()) {
// 连接失败,阻塞
idleStrategy.idle();
}
unsafeBuffer.putStringAscii(0, message);
System.out.println("sending:" + message);
// 尝试发布
while (pub.offer(unsafeBuffer) < 0) {
idleStrategy.idle();
}
FragmentHandler handler = ((buffer, offset, length, header) -> {
System.out.println("received:" + buffer.getStringAscii(offset));
});
while (sub.poll(handler, 1) <= 0) {
idleStrategy.idle();
}
}
}
}

运行结果

1
2
sending:this is a new Message.
received:this is a new Message.

代码剖析

参数构造

首先进行参数构造,主要是这几行

1
2
3
4
String channel = "aeron:ipc";
String message = "this is a new Message.";
IdleStrategy idleStrategy = new SleepingIdleStrategy();
UnsafeBuffer unsafeBuffer = new UnsafeBuffer(ByteBuffer.allocate(256));
  • 第 1 行指定 Aeron 发布者和订阅者进行通信的通道。这是使用 Aeron URL 方案定义的,本示例中的通道为IPC 通道。另请参阅有关频道配置的 Aeron Wiki https://github.com/real-logic/aeron/wiki/Channel-Configuration
  • 第 2 行指定发布者要发送的消息内容。
  • 第 3 行指定要使用的空闲策略,在本例中是 SleepingIdleStrategy,该策略将线程停顿 1 微秒(默认)。
  • 第 4 行提供了一个堆外缓冲区,该缓冲区(UnsafeBuffer)是不安全的缓冲区,它主要用来保存要发送的消息。为简单起见,我们为它分配了 256 个字节,当然,这个大小是可以调整的。

另请参阅:代理和空闲策略-Agents & Idle Strategies

构造Aeron对象

1
2
3
4
5
try (
MediaDriver driver = MediaDriver.launch();
Aeron aeron = Aeron.connect();
Subscription sub = aeron.addSubscription(channel, 10);
Publication pub = aeron.addPublication(channel, 10);

这几行代码创建了用于发送和接收数据的关键Aeron对象。

  • 第 1 行创建媒体驱动程序(MediaDriver)。 Media Driver负责所有的IPC和网络活动,后面会详细讨论
  • 第 2 行创建 Aeron 对象。这是应用程序中用于与 Aeron 交互的主要 API
  • 第 3 行创建消息的订阅者,用于轮询接收消息
  • 第 4 行创建消息发布者,用于消息的发布

另请参阅:媒体驱动程序-Media Driver

发送消息

1
2
3
4
5
6
7
8
9
10
1. while (!pub.isConnected()) {
// 连接失败,阻塞
2. idleStrategy.idle();
3. }
4. unsafeBuffer.putStringAscii(0, message);
5. System.out.println("sending:" + message);
6. // 尝试发布
7. while (pub.offer(unsafeBuffer) < 0) {
8. idleStrategy.idle();
9. }

这些代码行负责发布消息。

首先,应用程序等待发布者(Subscription)达到连接状态。 当连接成功之后,将消息写入不安全缓冲区(unsafeBuffer),最后将缓冲区提供给发布者。

下面对关键代码行进行解释:

  • 仅在连接建立成功后,第 1 行(pub.isConnected())才返回 true。 这是在一个while循环中轮询的过程,每个循环操作之间有1微秒的暂停,直到连接成功(底层代码为LockSupport.park实现的)。
  • 第 7 行(pub.offer(unsafeBuffer) < 0)为发布者提供了缓冲区,发布者通过offer方法将message发布出去。 当返回的值小于零时,某种机制会阻止发布者往缓冲区写入数据。 空闲策略再次轮询直到发布者可以继续写入缓冲区。

接收消息

1
2
3
4
5
6
1. FragmentHandler handler = ((buffer, offset, length, header) -> {
2. System.out.println("received:" + buffer.getStringAscii(offset));
3. });
4. while (sub.poll(handler, 1) <= 0) {
5. idleStrategy.idle();
6. }

代码的最后一部分负责接受来自订阅的消息。

首先,声明一个 FragmentHandler 用于接受消息,然后订阅者持续进行轮询,直到 Aeron将消息传递过来。

下面对关键行进行分析:

  • 第 1-2 行构造了 FragmentHandler。 在这个场景下,我们知道它是一个简单的小消息体,因此我们使用了最基本的FragmentHandler来接受消息。 如果消息体比较大,那么可以使用FragmentAssembler重新组装消息。
  • 第 4 行轮询数据订阅。 与发布过程一样,poll的返回值若等于或小于零表明没有收到消息,则以微秒空闲时间进行轮询。

下一篇

Aeron-CookBook学习之路-MediaDriver概述



版权声明:

原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

历史的车轮滚滚向前,当代互联网也早已过了而立之年。

各种优秀的理念,各种优秀的实践,提出,兴起,推广,衰落。你方唱罢我登场。

而这其中,最为人津津乐道的当属–敏捷开发。

唠一唠敏捷

2001年,当时软件领域的多位大佬在一起开了个会,大家聊得很开心,各种理念碰撞交汇,意趣相投,相见恨晚。

于是这几位好朋友在一起把讨论的结果总结为一份“敏捷宣言”。寥寥数语,包含4条价值观和12条原则,奠定了敏捷开发的基调。

12条原则

1
2
3
4
5
6
7
8
9
10
11
12
13
我们遵循以下原则:
01.我们最重要的目标,是通过持续不断地及早交付有价值的软件使客户满意。
02.欣然面对需求变化,即使在开发后期也一样。为了客户的竞争优势,敏捷过程掌控变化。
03.经常地交付可工作的软件,相隔几星期或一两个月,倾向于采取较短的周期。
04.业务人员和开发人员必须相互合作,项目中的每一天都不例外。
05.激发个体的斗志,以他们为核心搭建项目。提供所需的环境和支援,辅以信任,从而达成目标。
06.不论团队内外,传递信息效果最好效率也最高的方式是面对面的交谈。
07.可工作的软件是进度的首要度量标准。
08.敏捷过程倡导可持续开发。责任人、开发人员和用户要能够共同维持其步调稳定延续。
09.坚持不懈地追求技术卓越和良好设计,敏捷能力由此增强。
10.以简洁为本,它是极力减少不必要工作量的艺术。
11.最好的架构、需求和设计出自自组织团队。
12.团队定期地反思如何能提高成效,并依此调整自身的举止表现。

是的,我承认贴上宣言的内容是为了占篇幅的,你可以直接跳过,这样我的歉意也会少一些。

但是我仍旧想让你看一下它的四条价值观的内容。

4条价值观

1
2
3
4
5
6
7
8
9
10
11
我们一直在实践中探寻更好的软件开发方法,身体力行的同时也帮助他人。由此我们建立了如下价值观:
个体和互动高于流程和工具
工作的软件高于详尽的文档
客户合作高于合同谈判
响应变化高于遵循计划
也就是说,尽管右项有一定的价值,我们更重视左项的价值。

寥寥数语,道出了敏捷的核心价值。

  • 流程和工具固然重要,但作为工具而言他们的目的在于调动个体,提升成员间的互动;
  • 文档详尽对加深理解和解释清楚问题而言的确意义非凡,但是能够交付可工作的软件才是更高目的;
  • 开会,沟通,交流,是弄明白需求的重要步骤,这其中不免发生博弈,大概率会出现观点的交锋,刁难,疑问,甚至情绪化。但是不同角色间的合作更为重要,围绕着解决问题本身触发的交流才是更为有价值的交流,而这种善意的交流也体现了团队价值和尊重;
  • 只要有需求,先聊排期,先列计划。这是避免犯错、把控节奏的有效措施,但是变化会导致计划调整,变化会使得计划失效,拥抱变化不仅仅是裁员时候才反复强调的口号,更应当在平时的工作中体现出来,它不是一种“恐吓”,它应该是一种人文关怀。

那么中式敏捷团队的特点是什么?

中式软件开发团队,尤其是互联网开发团队,喜欢吸收年轻血液,乐于引入先进的经验和实践。这是好事。

于是我们看到,看板用起来了,泳道列出来了,迭代跑起来了,站会也开起来了。

工作开始,第一件事,每日站会,大家机械式的发言,我今天要做ABCD这几件事,如果精力足够,我将继续做EFGH这几件事;

进入工作状态,又一个需求过来了,哦,天哪,这是啥,没听过啊。看看多会儿要,wow,产品说要倒排,明天就评审,今天就得给排期。

只能硬着头皮用经验去估计了,没办法,咱也没参与需求讨论,咱也没参与预评审,但是我们是一个敏捷的团队。敏捷的团队从来都是扁平化的,每个人都是资源池中的一个可自由调度的资源,轮到你做就得你做,当仁不让。

一天时间了解下需求背景,产品说发布时间是两周后,那就是说还有10个人日的时间,开发3天,单侧1.5天,联调1天,测试3天,预发1.5天。

先这么排吧,你问为啥单测要1.5天?我只能说这叫经验。

我们采用了业界领先的TDD模式,要编写完善的测试用例,指令覆盖率要达到90%以上,分支覆盖率要达到70%以上。

这都是套话,实际上是因为你不得不写这么多用例,否则代码提交都提交不上去,流水线直接卡住你。到时候耽误了代码合并,影响提测,就等着复盘吧。
关于复盘,也是一个有意思的事情,我们下一篇文章再说。

吭哧吭哧写了一通设计文档,编码,单测写了一通,又自然地迎来了敏捷开发团队的每日必修课:晚会站会。

此时,看板出场了。

看板里,井井有条地罗列着A B C D E … 等一堆事项,每个事项下又有着1 2 3 4 5…个任务。每个开发像勤劳的蚂蚁一样,将任务从一个泳道,搬到了另一个泳道,并战战兢兢地在任务进度上了小心翼翼地增加了若干百分比。

会议上,所有参与者挨个介绍着自己所负责任务的进度,嗯,一片和谐。

看啊,多么精干的敏捷团队,多么高效的工作方式。

听啊,这件事情和我有多少关系?这个百分比有多少可信度?
哎呀,又有一个会要参加了
得嘞,饭点儿快到了。

我们不禁提问,这样的节奏是敏捷的么?这样的team是敏捷的么?

相信你自有答案在心中。

用了看板/jira/站会就是敏捷了?

这里其实是一个哲学问题,神与形的边界在哪里。

哲学里有一个“忒休斯之船”的概念,说的是有一艘叫做“忒休斯”的木船,工人们每天更换一个船上的零件,日复一日,直到有一日,船上最后一个零件被更换掉了。

那么问题是,此时这艘船,还是忒休斯吗?

我们说,在水手的心中,它还是忒休斯。

人有其魂,船有其神。

这艘的信念和意志不灭,于是在水手的心中,忒休斯永存。

将目光收回,我们问,是否用了看板/jira/站会,乃至所谓TDD,就是在实践敏捷开发了?

明确回答,不是。

敏捷宣言的价值观中,有这样一条

个体和互动高于流程和工具

如果仅仅是引入了工具链,即便再先进,投入的资源再多,也不过是流于表面。

真正的敏捷是要调用个体之间的互动,而这种互动必然是良性的,平等的,没有信息差的,友好共享的。

你知道我在说什么。

于是我们可以多聊几句,我们聊聊层级官僚制,或者说科层制。

科层制在人类发展中,被认为是最为理性的组织结构。

一个理想的科层制架构,命令从上往下,层层传达,职责层层系分,每一层的人都知道自己要干什么,井井有条。像是一个精密的仪器,永不出错。

这里我只说几句,毕竟这个话题不能聊深。

我想说的是,在传统的zz领域和传统实体行业,科层制尚存在低效、长臂管辖、信息丢失等现象,试问在讲究效率与变化的互联网/IT行业,它就能保证不出查错,高效运转吗?

我想,没有人敢站出来,拍着胸脯说,你是错的,这是对的。

人是有思想的动物,信息越透明,人的思想越广阔。反之,信息差越多,越激发误解与不满情绪,而这种不透明,绝对多数是来自自上而下的,而这种自上而下往往会在基层集中体现。

我不谈什么皈依者狂热,那太绝对化,我只想表达一个观点,基层的公平性与信息的透明性应当如何体现?

聊回资源池。

所谓资源池,是指对于一个团队的成员而言,每个个体都是可调度的资源单位。
理想情况下,若团队中每个个体的认知水平相差不多,原则上,凡是涉及到该团队的需求和问题,任意调度团队中此时有时间投入新需求/新问题的个体,都能够cover。

资源池可以说是中式软件开发,在多年迭代与探索中,为了提高人力资源利用率而总结出的一套可行手段。

那么,保证这个模式能够健康良性运行的前提,便是消除信息差。

敏捷与信息差之间的矛盾是否可调和?

自然地,我们提出这样一个问题。

两者矛盾,是否可调和?

我们说可以,解决方式在于消除信息差。

有人可能要站出来反对了,你这意思是直接取消基层的调度管理节点吗?

兄弟,坐下说话,别激动。

那我们聊聊,基层调度节点的设立吧。

设立基层调度节点,无外乎因为人多了,一级管理节点精力有限,难以顾及方方面面,因此需要设立基层的调度节点来协调工作。

这没问题,这不正是经典的分治法么。

那问题在哪里?问题在于,将原本繁琐的事情进行某个维度的划分,再分担给基层调度节点,就万事大吉了么?

对于一级管理节点,的确减轻了压力,但是对于基层的资源池角色呢?

要知道,基层调度节点可是从资源池里选出来的,也就是说,在全局上,能够切实干活儿的实际资源池角色减少了。这是一方面。

另一方面,将新增的调度节点去作为对接任务的分发器,对于真正干活儿的资源池角色而言,何时介入需求,变成了该调度角色决定的事情。

何时介入,以及介入何种需求,都是黑盒子。

我们分开说。

何时介入有影响么

当然有,一个需求,或者说一个问题,前期沟通不是你,前期讨论你没参与。直接丧失了接触第一手资料的机会;这其中有多少信息丢失?

进一步,等你介入的时候,都已经到了切实的落地环节,怎么做已经有人帮你想好了,甚至可能连设计大纲都写出来了。

你要做的就是照着这个图纸,修房子。与戴着镣铐跳舞,何其相似。

对于解决一个具有一定规模的问题而言,这种基于他人的思路解决问题的方式,从根源上就限制作为一个独立个体的思考能力。

万事俱备,就差一个程序员了。

魔幻现实主义。

写代码,或者说,软件开发,是一种创造。如果你要创造,那就从头到尾都是你作为核心在创造,你的想法,你的设计,你的代码。一气呵成。

当然,对于复杂问题,多人参与也是应该的,但是至少这其中至少存在这么一个角色,他参与了整个问题的生命周期,我想这对于少犯错,提升健壮性,意义巨大。

为何要你的想法,你的设计,再变成我的代码?这其中要损失多少个脑细胞,换来多少句亲切的问候?不敢多想。

那,如何破局?

山人有补药和苦药两剂,且听山人道来。

补药,温顺,我们的目的是为了减少信息差,提高参与度与个体互动,那不妨在问题早期讨论阶段就让可能的参与者参与到讨论中,在这其中,挑选时间、精力、能力均能胜任的一人或多人参与后期的设计和编码。

你会发现,作为调度节点,每个问题上,都不再是单打独斗,总有人会和你一起分担来自问题/需求的压力;作为资源池中的角色,有想法,有一手知识,有你的设计,最后你将自己的想法和设计付诸落地,一件美事。

补药如此精妙,那苦药又如何?

既是苦药,怕不会太好下咽。从资源池中来,回资源池中去吧。

足下可否明示?

我是说,去TMD的调度节点吧。

调度节点,本质上还是资源池中的一员,选出来作为调度节点,是为了更好的协调复杂事项,做好问题的预调研,预分配。

但是实际上呢?好像,调度成了主要职责了,dispatch各种事项给资源池中的executor,在各个上下文中灵活切换,然后不时回调一下,看看进度如何,以便灵活组织语言进行汇总上报。

精彩,精彩,精彩。

作为调度节点,你可能委屈,“我的精力都被各种事项占满,再让我去负起资源池角色的责任,臣妾做不到啊”。

可以理解,但是请让事情的透明度更高一些,让executor们的参与度更高一些,停止你的所谓好心行为,你设计,你讨论,为什么要executor来买单?

你设计,你讨论,那就你来开发。毕竟,我们是一个敏捷团队,而这,是最敏捷的,最少上下文切换的。

除此之外,作为调度节点,别忘了自己的基类还是一个executor,本质上是executor implements Dispatcher,那么,请继续保持作为executor的公平性。

该值的班,该复的盘,请不要躲避,听我说谢谢你。

不患寡而患不均,这其中的利弊,我想作为资源池中的翘楚,调度节点比我更清楚。

如何更好的发挥资源池的优势,提升敏捷度?

资源池这种实践模式,确实被实践验证为能够切实解决问题,落地想法的模式,虽然存在不合理之处,但是不掩盖它的光芒。

那么基于资源池模式不改变的前提,如何发挥它的优势,更好服务于敏捷软件开发流程呢?

这里提供一个场景,供君品鉴。

产品同学有一个好的点子,想为系统添加一个新的产品线,该产品线能够带来日PV增加百分之5的效果。于是产品同学编写prd,并向需求池中投递需求,并期望支持的人力资源。

资源池中进行调度的开发同学,根据自己在甘特图上的排期及任务分配情况,对需求池中的需求进行主动拣选,并进行预排期。

一旦锁定了该任务,该同学便会成为该需求的owner,后续的讨论、设计、开发、测试均需要参与,并负责到上线。

有会必邀,邀必参与,自己想,自己参与沟通,自己落地,自己追结果,自己负责。出问题,自己复盘,问心无愧。

康威定律的启示

康威定律告诉我们,设计系统的架构受制于产生这些设计的组织的沟通结构。也就是说,产品必然是其(人员)组织沟通结构的缩影。

你的组织架构是什么样的,如如何运作的,基本上你的业务架构也是这么运作的。

业务架构,技术架构在迭代,组织架构也需要迭代,否则滞后冗赘的组织架构必然带来畸形抵消的业务架构、技术架构。

一个敏捷的团队,是自革新的,是自迭代的。

让前线的特种部队能够呼唤集团炮火。这是日渐销声匿迹的中台遗留下的一句经典。

你可以说中台是失败的,但是你不能否认这句话的积极意义。

与业务交互的团队,尤其是自诩敏捷的团队,一定是一个精悍的特种部队,扁平化,高效化,信息透明化,专家化。

如果这个团队把整个后勤部门都携带在身边,那么行军效率一定不会很高。

让后勤待在后勤的位置,让部队前出侦查。这才是现代化战争的作战形态。这也是康威定律给我的启示。

拥抱变化,不是自上而下的指示,而是推己及人的感悟,是整个团队所乐于拥抱的态度,是历史螺旋上升的坐标。

结语:敏捷之于个人又如何?

之于个人,敏捷的背后,也许是内卷。

关于内卷,我不想多聊,我不反对内卷,这是一种选择,它也许是一种无奈,也许是一种随波逐流,也许是发自内心的自我鞭策。

都无所谓,只要是自己的想法,自己的选择,内卷或者躺平,无关风月。

纷繁的会议里,你知道哪个是重要的;琐碎的事务中,你知道优先级是如何排列的。

并行的需求中,你能够把握结果走向;复杂的问题里,你知道问题就在那里,答案就在那里。

你知道,你有一群好队友,你知道,你有一个好team。

你知道,你有一颗清醒、独立的内心;你知道,你知道。

你知道,你不知道。

心存敬畏,踏实向前,不迎合所谓的文化,不沉溺于所谓的氛围,不抱怨所谓的现状,不停止思考的大脑。

敏捷,发乎于内心,行乎于个人,成乎于群体。

中式软件开发的现状,已经是世俗化的,利益导向化的,实用主义化的。对于敏捷团队而言,始终是服务于业务增长的,要深知,当代中式互联网软件开发,几无技术驱动型,放眼望去,皆为业务驱动型。

那么想明白这个道理,再去实践,就会少一些压力,多一些豁达。

不妥协,不偏激,不随波,不逐流。

有想法,便会踏实,不轻易追逐内卷节奏;

有目标,便会沉稳,不停止自我迭代脚步。

其实,软件开发里,根本没有敏捷。



版权声明:

原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

对于韭菜而言,哪里有赚钱的可能就一股脑儿钻进哪里。也不管是否有一套自己的交易系统。

范围缩小到股市交易和虚拟货币市场,也是同样的道理。

合格的韭菜是要有一套体系化的交易系统,较为全面的交易术语体系的,作为扫盲,也作为警示,本文对金融交易中的一些术语进行讲解。

声明:

文中的概念和场景仅作为学习使用,不作为投资建议。

市场有风险,入市需谨慎!

闲话就说到这里,我们进入正题。

Read More

概述常见的服务状态形式,

无状态的演进过程 好处 及 劣势

为何要有状态

状态的核心之道

状态与一致性(分片与sequence进度追赶,水位)

wal机制

异步化 无锁化 disruptor

各种分片

上下文传递 context

持久化相关,推拉,启动reload

共识机制



版权声明:

原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

有没有想过,有这么一门课程。他不是简单的念PPT,他的老师是计算机史上五大黑客之一的罗伯特·塔潘-莫里斯(RobertTappanMorris);

有没有想过,有这么一门课程,他会带你阅读GFS、Map Reduce、RAFT、Big Table论文;

来看看他的四个实验作业题:

  • 基于论文思路,实现一个简单的 MapReduce 框架。这个作业的实现前提是你得了解 Unix Domain socket,知道goalng plugin(也就是动态链接)。
  • 实现一个完整的Raft协议。这部分拆成了三个部分,它会分别让你实现 Leader Election、AppendEntry RPC 和 Raft persist。
    • 在这一部分里,我们将使用提供的类库 labrpc 来完成完整的Raft。其中, labrpc 是一个模拟网络环境的 rpc 框架,它是基于 UDP实现的(注意这一点)。
  • 根据自己实现的Raft,写一个KV数据库。同时实现 Raft 协议的 Snapshot RPC。
  • 在之前的作业基础上,为KV数据库实现Sharding分片功能,同时实现 multi Raft功能。

笔者在看到这部分的时候,惊为天人。这课程,硬核!

这门课就是MIT6.824,由罗伯特·塔潘-莫里斯教授教学。

MIT 6.824 是一门讲解分布式系统的课程。

课程内容包括Golang的学习、分布式系统研究动机、一致性协议算法、著名分布式系统软件论文讲解和一致性算法 (Raft算法)的实现机制及实验。

是一门不可多得的实践性质比较强的课程。

虽说这门课程受众主要是MIT研究生,但是笔者认为该课程对有一定工作经验的工程师有着更多的指导意义和参考价值。

他可以帮助工程师对自己的知识体系有一个系统性的整理,如果你恰巧是一个动手能力、自学能力强的人,那么这门课程一定适合你。

学习资料

课程是纯英文的,因此对于大多数人而言可能是比较陡峭的门槛,幸运的是,已经有无私的翻译者提供了人工翻译的版本。

人工翻译版本

B站地址:MIT 6.824 Distributed Systems Spring 2020 分布式系统 中文翻译版合集

除此之外,官方课件的地址在着这里,包括了上文提到的四个实验:

6.824 Schedule:Spring 2022

Raft作业难度高?不要怕,这里已经网友贡献的学生作业指南:

Raft学生指南:一

Raft学生指南:二

Raft学生指南:三

Raft算法不好理解,不够直观?我想这两个可视化呈现Raft算法的网站是你想要的:

https://raft.github.io/ 在该页面的Raft Visualization部分,提供了一个可交互的Raft集群,你可以与该Raft集群进行交互以理解算法。

动画呈现Raft算法的主要细节与设计思想

Raft论文中文版:寻找一种易于理解的一致性算法

除了课程本身外,如果你还觉得卷地不够爽,那么推荐一本书,可以搭配这门课程一起看,那就是经典的:《DDIA》,全名是《Design Data-Intensive Application》,中文翻译版本名为:《数据密集型应用系统设计》

笔者曾经在工作早期看过几页,奈何功力不够,且内容硬核,遂放弃。现在笔者准备捡起来,努力往后看。

如果确实没有很多时间看视频,也没有关系,已经有大佬把视频内容用文字+图片方式整理出来,以gitbook方式分享了出来。

MIT 6.824 文字+图片整理版本

作者将每一节课拆分成若干小节翻译并整理出来,在翻译的同时还加入了自己的理解,可以说是相当诚意的作品了。

上几张图简单感受一下:

学习建议

简单说一下学习建议。

论文是一定得读的!论文是一定得读的!论文是一定得读的!

重要的话说三遍,因为课堂上,莫里斯教授是以思路讲解和答疑为主的,这都是建立在提前阅读了相关论文资料的前提上开展的,如果压根没有读过相关的论文,那么我相信基本上就是在听天书了。

论文的英文版PDF都能够在课程官网下载到,相关的中译版本也都能通过搜索找到,这里笔者就不提供了,相信有能力的读者都能够自己解决。

课程的schedule页面在 https://pdos.csail.mit.edu/6.824/schedule.html,该页面上提供了所有必须的课程资料链接、论文pdf,以及lab作业的资料,甚至是课程视频,都有链接,读者可以按需求下载。

关于LAB作业

关于如何完成作业,及视频中提到的LAB,笔者简单提供几点建议。

笔者看的版本为2020版本,它的开发语言为Golang,需要学习者提前学习一些go的语法和必须的概念知识。因此开发环境一定需要安装go。莫里斯教授在课堂上也对go语言的设计思想进行了讲解,可以作为参考。

快速学习上手go语言,可以使用以下线上学习指南,你可以在页面上直接写代码并看到直观的运行效果。

https://tour.go-zh.org/welcome/1\

操作系统建议使用类Unix环境(linux、mac均可),使用Windows需要你做额外的工作以去除环境相关的依赖,既繁琐又不一定保证成功。

建议使用Goland作为IDE开发环境,笔者是坚定的JetBrains粉。

结语

题目本身是一种调侃,卷八股文也没什么坏处,尤其是对于换工作的同学而言。

但是从长期看,我们还是需要整理一下自己的知识体系,通过学习一些优秀的论文、课程,做一些有代表性的练习来夯实基础。我想,这应当是破除程序员中年诅咒的方式之一吧。

废话不多说,让我们以梦为马,不负韶华。

学就对了!

(后续笔者也会不定时的发表MIT6.824相关的学习笔记和心得,同时欢迎公众号读者积极投稿,共同交流成长。)



版权声明:

原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

Fork me on GitHub