并发编程实践之公平有界阻塞队列实现

简介: JUC 工具包是 JAVA 并发编程的利器。本文讲述在没有 JUC 工具包帮助下,借助原生的 JAVA 同步原语, 如何实现一个公平有界的阻塞队列。希望你也能在文后体会到并发编程的复杂之处,以及 JUC 工具包的强。

image.png

作者 | 李新然
来源 | 阿里技术公众号

一 背景

JUC 工具包是 JAVA 并发编程的利器。

本文讲述在没有 JUC 工具包帮助下,借助原生的 JAVA 同步原语, 如何实现一个公平有界的阻塞队列。

希望你也能在文后体会到并发编程的复杂之处,以及 JUC 工具包的强大。

二 方法

本文使用到的基本工具:

  1. 同步监听器 synchronized ,方法基本和代码块级别;
  2. Object 基础类的 wait, notify, notifyAll;

基于以上基础工具,实现公平有界的阻塞队列,此处:

  1. 将公平的定义限定为 FIFO ,也就是先阻塞等待的请求,先解除等待;
  2. 并不保证解除等待后执行 Action 的先后顺序;
  3. 确保队列的大小始终不超过设定的容量;但阻塞等待的请求数不做限制;

三 实现

1 基础版本

首先,考虑在非并发场景下,借助 ADT 实现一个基础版本

interface Queue {boolean offer(Object obj);Object poll();}
class FairnessBoundedBlockingQueue implements Queue {// 当前大小protected int size;// 容量protected final int capacity;// 头指针,empty: head.next == tail == nullprotected Node head;// 尾指针protected Node tail;public FairnessBoundedBlockingQueue(int capacity) {this.capacity = capacity;this.head = new Node(null);this.tail = head;this.size = 0;}// 如果队列已满,通过返回值标识public boolean offer(Object obj) {if (size < capacity) {Node node = new Node(obj);tail.next = node;tail = node;++size;return true;}return false;}// 如果队列为空,head.next == null;返回空元素public Object poll() {if (head.next != null) {Object result = head.next.value;head.next.value = null;head = head.next; // 丢弃头结点--size;return result;}return null;}class Node {Object value;Node next;Node(Object obj) {this.value = obj;next = null;}}
}

以上

  1. 定义支持队列的两个基础接口, poll 和 offer;
  2. 队列的实现,采用经典实现;
  3. 考虑在队列空的情况下, poll 返回为空,非阻塞;
  4. 队列在满的情况下, offer 返回 false ,入队不成功,无异常;

需要注意的一点:在出队时,本文通过迁移头结点的方式实现,避免修改尾结点。
在下文实现并发版本时,会看到此处的用意。

2 并发版本

如果在并发场景下,上述的实现面临一些问题,同时未实现给定的一些需求。

通过添加 synchronized ,保证并发条件下的线程安全问题。

注意此处做同步的原因是为了保证类的不变式。

并发问题

在并发场景下,基础版本的实现面临的问题包括:原子性,可见性和指令重排的问题。

参考 JMM 的相关描述。

并发问题,最简单的解决方法是:通过 synchronized 加锁,一次性解决问题。

// 省略接口定义
class BoundedBlockingQueue implements Queue {// 当前大小protected int size;// 容量protected final int capacity;// 头指针,empty: head.next == tail == nullprotected Node head;// 尾指针protected Node tail;public BoundedBlockingQueue(int capacity) {this.capacity = capacity;this.head = new Node(null);this.tail = head;this.size = 0;}// 如果队列已满,通过返回值标识public synchronized boolean offer(Object obj) {if (size < capacity) {Node node = new Node(obj);tail.next = node;tail = node;++size;return true;}return false;}// 如果队列为空,head.next == null;返回空元素public synchronized Object poll() {if (head.next != null) {Object result = head.next.value;head.next.value = null;head = head.next; // 丢弃头结点--size;return result;}return null;}// 省略 Node 的定义
}

以上,简单粗暴的加 synchronized 可以解决问题,但会引入新的问题:系统活性问题(此问题下文会解决)。

同时,简单加 synchronized 同步是无法实现阻塞等待;即

  1. 如果队列为空,那么出队的动作还是会立即返回,返回为空;
  2. 如果队列已满,那么入队动作还是会立即返回,返回操作不成功;

实现阻塞等待,需要借助 JAVA 中的 PV 原语:wait, notify, notifyAll 。

参考:JDK 中对 wait, notify, notifyAll 的相关描述。

卫式方法

阻塞等待,可以通过简单的卫式方法来实现,此问题本质上可以抽象为:

  1. 任何一个方法都需要在满足一定条件下才可以执行;
  2. 执行方法前需要首先校验不变式,然后执行变更;
  3. 在执行完成后,校验是否满足后验不变式;
WHEN(condition) Object action(Object arg) {checkPreCondition();doAction(arg);checkPostCondition();
}

此种抽象 Ada 在语言层面上实现。在 JAVA 中,借助 wait, notify, notifyAll 可以翻译为:

// 当前线程
synchronized Object action(Object arg) {while(!condition) {wait();}// 前置条件,不变式checkPreCondition();doAction();// 后置条件,不变式checkPostCondition();
}// 其他线程
synchronized Object notifyAction(Object arg) {notifyAll();
}

需要注意:

  1. 通常会采用 notifyAll 发送通知,而非 notify ;因为如果当前线程收到 notify 通知后被中断,那么系统将一直等待下去。
  2. 如果使用了 notifyAll 那么卫式语句必须放在 while 循环中;因为线程唤醒后,执行条件已经不满足,虽然当前线程持有互斥锁。
  3. 卫式条件的所有变量,有任何变更都需要发送 notifyAll 不然面临系统活性问题

据此,不难实现简单的阻塞版本的有界队列,如下

interface Queue {boolean offer(Object obj) throws InterruptedException;Object poll() throws InterruptedException;}
class FairnessBoundedBlockingQueue implements Queue {// 当前大小protected int size;// 容量protected final int capacity;// 头指针,empty: head.next == tail == nullprotected Node head;// 尾指针protected Node tail;public FairnessBoundedBlockingQueue(int capacity) {this.capacity = capacity;this.head = new Node(null);this.tail = head;this.size = 0;}// 如果队列已满,通过返回值标识public synchronized boolean offer(Object obj) throws InterruptedException {while (size < capacity) {wait();}Node node = new Node(obj);tail.next = node;tail = node;++size;notifyAll(); // 可以出队return true;}// 如果队列为空,阻塞等待public synchronized Object poll() throws InterruptedException {while (head.next == null) {wait();}Object result = head.next.value;head.next.value = null;head = head.next; // 丢弃头结点--size;notifyAll(); // 可以入队return result;}// 省略 Node 的定义
}

以上,实现了阻塞等待,但也引入了更大的性能问题

  1. 入队和出队动作阻塞等待同一把锁,恶性竞争;
  2. 当队列变更时,所有阻塞线程被唤醒,大量的线程上下文切换,竞争同步锁,最终可能只有一个线程能执行;

需要注意的点:

  1. 阻塞等待 wait 会抛出中断异常。关于异常的问题下文会处理;
  2. 接口需要支持抛出中断异常;
  3. 队里变更需要 notifyAll 避免线程中断或异常,丢失消息;

3 锁拆分优化

以上第一个问题,可以通过锁拆分来解决,即:定义两把锁,读锁和写锁;读写分离。

// 省略接口定义
class FairnessBoundedBlockingQueue implements Queue {// 容量protected final int capacity;// 头指针,empty: head.next == tail == nullprotected Node head;// 尾指针protected Node tail;// guard: canPollCount, headprotected final Object pollLock = new Object();protected int canPollCount;// guard: canOfferCount, tailprotected final Object offerLock = new Object();protected int canOfferCount;public FairnessBoundedBlockingQueue(int capacity) {this.capacity = capacity;this.canPollCount = 0;this.canOfferCount = capacity;this.head = new Node(null);this.tail = head;}// 如果队列已满,通过返回值标识public boolean offer(Object obj) throws InterruptedException {synchronized(offerLock) {while(canOfferCount <= 0) {offerLock.wait();}Node node = new Node(obj);tail.next = node;tail = node;canOfferCount--;}synchronized(pollLock) {++canPollCount;pollLock.notifyAll();}return true;}// 如果队列为空,阻塞等待public Object poll() throws InterruptedException {Object result = null;synchronized(pollLock) {while(canPollCount <= 0) {pollLock.wait();}result = head.next.value;head.next.value = null;head = head.next;canPollCount--;}synchronized(offerLock) {canOfferCount++;offerLock.notifyAll();}return result;}// 省略 Node 定义
}

以上

  1. 定义了两把锁, pollLock 和 offerLock 拆分出队和入队竞争;
  2. 入队锁同步的变量为:callOfferCount 和 tail;
  3. 出队锁同步的变量为:canPollCount 和 head;
  4. 出队的动作:首先拿到 pollLock 卫式等待后,完成出队动作;然后拿到 offerLock 发送通知,解除入队的等待线程。
  5. 入队的动作:首先拿到 offerLock 卫式等待后,完成入队的动作;然后拿到 pollLock 发送通知,解除出队的等待线程。

以上实现

  1. 确保通过入队锁和出队锁,分别保证入队和出队的原子性;
  2. 出队动作,通过特别的实现,确保出队只会变更 head ,避免获取 offerLock;
  3. 通过 offerLock.notifyAll 和 pollLock.notifyAll 解决读写竞争的问题;

但上述实现还有未解决的问题:

当有多个入队线程等待时,一次出队的动作会触发所有入队线程竞争,大量的线程上下文切换,最终只有一个线程能执行。

即,还有 读与读 和 写与写 之间的竞争问题。

4 状态追踪解除竞争

此处可以通过状态追踪,解除读与读之间和写与写之间的竞争问题

class FairnessBoundedBlockingQueue implements Queue {// 容量protected final int capacity;// 头指针,empty: head.next == tail == nullprotected Node head;// 尾指针protected Node tail;// guard: canPollCount, headprotected final Object pollLock = new Object();protected int canPollCount;protected int waitPollCount;// guard: canOfferCount, tailprotected final Object offerLock = new Object();protected int canOfferCount;protected int waitOfferCount;public FairnessBoundedBlockingQueue(int capacity) {this.capacity = capacity;this.canPollCount = 0;this.canOfferCount = capacity;this.waitPollCount = 0;this.waitOfferCount = 0;this.head = new Node(null);this.tail = head;}// 如果队列已满,通过返回值标识public boolean offer(Object obj) throws InterruptedException {synchronized(offerLock) {while(canOfferCount <= 0) {waitOfferCount++;offerLock.wait();waitOfferCount--;}Node node = new Node(obj);tail.next = node;tail = node;canOfferCount--;}synchronized(pollLock) {++canPollCount;if (waitPollCount > 0) {pollLock.notify();}}return true;}// 如果队列为空,阻塞等待public Object poll() throws InterruptedException {Object result;synchronized(pollLock) {while(canPollCount <= 0) {waitPollCount++;pollLock.wait();waitPollCount--;}result = head.next.value;head.next.value = null;head = head.next;canPollCount--;}synchronized(offerLock) {canOfferCount++;if (waitOfferCount > 0) {offerLock.notify();}}return result;}// 省略 Node 的定义
}

以上

  1. 通过 waitOfferCount 和 waitPollCount 的状态追踪解决 读写内部的竞争问题;
  2. 当队列变更时,根据追踪的状态,决定是否派发消息,触发线程阻塞状态解除;

但,上述的实现在某些场景下会运行失败,面临活性问题,考虑

情况一:

  1. 初始状态队列为空 线程 A 执行出队动作,被阻塞在 pollLock , 此时 waitPollCount==1;
  2. 此时线程 A 在执行 wait 时被中断,抛出异常, waitPollCount==1 并未被重置;
  3. 阻塞队列为空,但 waitPollCount==1 类状态异常;

情况二:

  1. 初始状态队列为空 线程 A B 执行出队动作,被阻塞在 pollLock , 此时 waitPollCount==2;
  2. 线程 C 执行入队动作,可以立即执行,执行完成后,触发 pollLock 解除一个线程等待 notify;
  3. 触发的线程在 JVM 实现中是随机的,假设线程 A 被解除阻塞;
  4. 假设线程 A 在阻塞过程中已被中断,阻塞解除后 JVM 检查 interrupted 状态,抛出 InterruptedException 异常;
  5. 此时队列中有一个元素,但线程 A 仍阻塞在 pollLock 中,且一直阻塞下去;

以上为解除阻塞消息丢失的例子,问题的根源在与异常处理。

5 解决异常问题

解决线程中断退出的问题,线程校验中断状态的场景

  1. JVM 通常只会在有限的几个场景检测线程的中断状态, wait, Thread.join, Thread.sleep;
  2. JVM 在检测到线程中断状态 Thread.interrupted() 后,会清除中断标志,抛出 InterruptedException;
  3. 通常为了保证线程对中断及时响应, run 方法中需要自主检测中断标志,中断线程,特别是对中断比较敏感需要保持类的不变式的场景;
class FairnessBoundedBlockingQueue implements Queue {// 容量protected final int capacity;// 头指针,empty: head.next == tail == nullprotected Node head;// 尾指针protected Node tail;// guard: canPollCount, head, waitPollCountprotected final Object pollLock = new Object();protected int canPollCount;protected int waitPollCount;// guard: canOfferCount, tail, waitOfferCountprotected final Object offerLock = new Object();protected int canOfferCount;protected int waitOfferCount;public FairnessBoundedBlockingQueue(int capacity) {this.capacity = capacity;this.canPollCount = 0;this.canOfferCount = capacity;this.waitPollCount = 0;this.waitOfferCount = 0;this.head = new Node(null);this.tail = head;}// 如果队列已满,通过返回值标识public boolean offer(Object obj) throws InterruptedException {if (Thread.interrupted()) {throw new InterruptedException(); // 线程已中断,直接退出即可,防止中断线程竞争锁}synchronized(offerLock) {while(canOfferCount <= 0) {waitOfferCount++;try {offerLock.wait();} catch (InterruptedException e) {// 触发其他线程offerLock.notify();throw e;} finally {waitOfferCount--;}}Node node = new Node(obj);tail.next = node;tail = node;canOfferCount--;}synchronized(pollLock) {++canPollCount;if (waitPollCount > 0) {pollLock.notify();}}return true;}// 如果队列为空,阻塞等待public Object poll() throws InterruptedException {if (Thread.interrupted()) {throw new InterruptedException();}Object result = null;synchronized(pollLock) {while(canPollCount <= 0) {waitPollCount++;try {pollLock.wait();} catch (InterruptedException e) {pollLock.notify();throw e;} finally {waitPollCount--;}}result = head.next.value;head.next.value = 0;// ignore head;head = head.next;canPollCount--;}synchronized(offerLock) {canOfferCount++;if (waitOfferCount > 0) {offerLock.notify();}}return result;}// 省略 Node 的定义
}

以上

  1. 当等待线程中断退出时,捕获中断异常,通过 pollLock.notify 和 offerLock.notify 转发消息;
  2. 通过在 finally 中恢复状态追踪变量;

通过状态变量追踪可以解决读与读之间和写与写之间的锁竞争问题。

以下考虑如果解决读与读之间和写与写之间的公平性问题。

6 解决公平性

公平性的问题的解决需要将状态变量的追踪转换为:请求监视器追踪。

  1. 每个请求对应一个监视器;
  2. 通过内部维护一个 FIFO 队列,实现公平性;
  3. 在队列状态变更时,释放队列中的监视器;

以上逻辑可以统一抽象为

boolean needToWait;
synchronized(this) {needToWait = calculateNeedToWait();if (needToWait) {enqueue(monitor); // 请求对应的monitor}
}
if (needToWait) {monitor.doWait();
}

需要注意

  1. monitor.doWait() 需要在 this 的卫式语句之外,因为如果在内部, monitor.doWait 并不会释放 this锁;
  2. calculateNeedToWait() 需要在 this 的守卫之内完成,避免同步问题;
  3. 需要考虑中断异常的问题;

基于以上的逻辑抽象,实现公平队列

// 省略接口定义
class FairnessBoundedBlockingQueue implements Queue {// 容量protected final int capacity;// 头指针,empty: head.next == tail == nullprotected Node head;// 尾指针protected Node tail;// guard: canPollCount, head, pollQueueprotected final Object pollLock = new Object();protected int canPollCount;// guard: canOfferCount, tail, offerQueueprotected final Object offerLock = new Object();protected int canOfferCount;protected final WaitQueue pollQueue = new WaitQueue();protected final WaitQueue offerQueue = new WaitQueue();public FairnessBoundedBlockingQueue(int capacity) {this.capacity = capacity;this.canOfferCount = capacity;this.canPollCount = 0;this.head = new Node(null);this.tail = head;}// 如果队列已满,通过返回值标识public boolean offer(Object obj) throws InterruptedException {if (Thread.interrupted()) {throw new InterruptedException(); // 线程已中断,直接退出即可,防止中断线程竞争锁}WaitNode wait = null;synchronized(offerLock) {// 在有阻塞请求或者队列为空时,阻塞等待if (canOfferCount <= 0 || !offerQueue.isEmpty()) {wait = new WaitNode();offerQueue.enq(wait);} else {// continue.}}try {if (wait != null) {wait.doWait();}if (Thread.interrupted()) {throw new InterruptedException();}} catch (InterruptedException e) {offerQueue.doNotify();throw e;}// 确保此时线程状态正常,以下不会校验中断synchronized(offerLock) {Node node = new Node(obj);tail.next = node;tail = node;canOfferCount--;}synchronized(pollLock) {++canPollCount;pollQueue.doNotify();}return true;}// 如果队列为空,阻塞等待public Object poll() throws InterruptedException {if (Thread.interrupted()) {throw new InterruptedException();}Object result = null;WaitNode wait = null;synchronized(pollLock) {// 在有阻塞请求或者队列为空时,阻塞等待if (canPollCount <= 0 || !pollQueue.isEmpty()) {wait = new WaitNode();pollQueue.enq(wait);} else {// ignore}}try {if (wait != null) {wait.doWait();}if (Thread.interrupted()) {throw new InterruptedException();}} catch (InterruptedException e) {// 传递消息pollQueue.doNotify();throw e;}// 以下不会检测线程中断状态synchronized(pollLock) {result = head.next.value;head.next.value = 0;// ignore head;head = head.next;canPollCount--;}synchronized(offerLock) {canOfferCount++;offerQueue.doNotify();}return result;}class WaitQueue {WaitNode head;WaitNode tail;WaitQueue() {head = new WaitNode();tail = head;}synchronized void doNotify() {for(;;) {WaitNode node = deq();if (node == null) {break;} else if (node.doNotify()) {// 此处确保NOTIFY成功break;} else {// ignore, and retry.}}}synchronized boolean isEmpty() {return head.next == null;}synchronized void enq(WaitNode node) {tail.next = node;tail = tail.next;}synchronized WaitNode deq() {if (head.next == null) {return null;}WaitNode res = head.next;head = head.next;if (head.next == null) {tail = head; // 为空,迁移tail节点}return res;}}class WaitNode {boolean released;WaitNode next;WaitNode() {released = false;next = null;}synchronized void doWait() throws InterruptedException {try {while (!released) {wait();}             } catch (InterruptedException e) {if (!released) {released = true;throw e;} else {// 如果是NOTIFY之后收到中断的信号,不能抛出异常;需要做RELAY处理Thread.currentThread().interrupt();}}}synchronized boolean doNotify() {if (!released) {released = true;notify();// 明确释放了一个线程,返回truereturn true;} else {// 没有释放新的线程,返回falsereturn false;}}}// 省略 Node 的定义
}

以上

  1. 核心是替换状态追踪变量为同步节点, WaitNode;
  2. WaitNode 通过简单的同步队列组织实现 FIFO 协议,每个线程等待各自的 WaitNode 监视器;
  3. WaitNode 内部维持 released 状态,标识线程阻塞状态是否被释放,主要是为了处理中断的问题;
  4. WaitQueue 本身是全同步的,由于已解决了读写竞争已经读写内部竞争的问题, WaitQueue 同步并不会造成问题;
  5. WaitQueue 是无界队列,是一个潜在的问题;但由于其只做同步的追踪,而且追踪的通常是线程,通常并不是问题;
  6. 最终的公平有界队列实现,无论是入队还是出队,首先卫式语句判定是否需要入队等待,如果入队等待,通过公平性协议等待;

当信号释放时,借助读写锁同步更新队列;最后同样借助读写锁,触发队列更新消息;

7 等待时间的问题

并发场景下,等待通常会设置为限时等待 TIMED_WAITING ,避免死锁或损失系统活性;

实现同步队列的限时等待,并没想象的那么困难

class TimeoutException extends InterruptedException {}class WaitNode {boolean released;WaitNode next;WaitNode() {released = false;next = null;}synchronized void doWait(long milliSeconds) throws InterruptedException {try {long startTime = System.currentTimeMillis();long toWait = milliSeconds;for (;;) {wait(toWait);if (released) {return;}long now = System.currentTimeMillis();toWait = toWait - (now - startTime);if (toWait <= 0) {throw new TimeoutException();}}} catch (InterruptedException e) {if (!released) {released = true;throw e;} else {// 如果已经释放信号量,此处不抛出异常;但恢复中断状态Thread.currentThread().interrupt();}}}synchronized boolean doNotify() {if (!released) {released = true;notify();return true;} else {return false;}}

由于所有的等待都阻塞在 WaitNode 监视器,以上

  • 首先定义超时异常,此处只是为了方便异常处理,继承 InterruptedException;
  • 此处依赖于 wait(long timeout) 的超时等待实现,这通常不是问题;

最后,将 WaitNode 超时等待的逻辑,带入到 FairnessBoundedBlockingQueue 实现中,即可。

四 总结

本文通过一步步迭代,最终借助 JAVA 同步原语实现初版的公平有界队列。迭代实现过程中可以看到以下几点:

  1. 观念的转变,将调用一个类的方法思维转换为:在满足一定条件下方法才可以调用,在调用前需要满足不变式,调用后满足不变式;由于并发的问题很难测试,通常要采用卫式表达证明并发的正确性;
  2. 在迭代实现中会看到很多模式,比如,读写分离时,其实可以抽象为读锁和写锁;就得到了一个抽象的 Lock 的定义;比如,读写状态追踪,可以采用 Exchanger 抽象表达;
  3. 另外,本文的实现远非完善,还需要考虑支持 Iterator 遍历、状态查询及数据迁移等操作;

最后,相信大家再看 JUC 的工具包实现,定有不一样的体会。

原文链接
本文为阿里云原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/512050.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iOS App 启动优化

简介&#xff1a; 作为程序猿来说&#xff0c;“性能优化”是我们都很熟悉的词&#xff0c;也是我们需要不断努⼒以及持续进⾏的事情&#xff1b;其实优化是⼀个很⼤的课题&#xff0c;因为细分来说的话有⼤⼤⼩⼩⼗⼏种优化⽅向 &#xff0c;但是切忌在实际开发过程中不能盲⽬…

apache1.3 php编译,安装Apache1.3.29 - Linux+Apache+Mysql+PHP典型配置详解_Linux教程_Linux公社-Linux系统门户网站...

2.安装Apache1.3.29。我没有选择安装Apache2.0是我对他还是不放心&#xff0c;因为网上最新公布的apache的漏洞基本上是针对2.0&#xff0c;当然大家可以自己选择安装相应的版本。我这里讲的都是采用DSO动态编译的方法编译Apache.至于有关apache的编译方法&#xff0c;可以参考…

前后端、多语言、跨云部署,全链路追踪到底有多难?

简介&#xff1a; 完整的全链路追踪可以为业务带来三大核心价值&#xff1a;端到端问题诊断&#xff0c;系统间依赖梳理&#xff0c;自定义标记透传。 作者 | 涯海 全链路追踪的价值 链路追踪的价值在于“关联”&#xff0c;终端用户、后端应用、云端组件&#xff08;数据库…

供应商太多,怎么才能高效比价?

本篇文章暨 CSDN《中国 101 计划》系列数字化转型场景之一。 《中国 101 计划——探索企业数字化发展新生态》为 CSDN 联合《新程序员》、GitCode.net 开源代码仓共同策划推出的系列活动&#xff0c;寻访一百零一个数字化转型场景&#xff0c;聚合呈现并开通评选通道&#xff0…

7张图揭晓RocketMQ存储设计的精髓

简介&#xff1a; RocketMQ 作为一款基于磁盘存储的中间件&#xff0c;具有无限积压能力&#xff0c;并提供高吞吐、低延迟的服务能力&#xff0c;其最核心的部分必然是它优雅的存储设计。 存储概述 RocketMQ 存储的文件主要包括 Commitlog 文件、ConsumeQueue 文件、Index 文…

庖丁解InnoDB之UNDO LOG

简介&#xff1a; Undo Log是InnoDB十分重要的组成部分&#xff0c;它的作用横贯InnoDB中两个最主要的部分&#xff0c;并发控制&#xff08;Concurrency Control&#xff09;和故障恢复&#xff08;Crash Recovery&#xff09;&#xff0c;InnoDB中Undo Log的实现亦日志亦数据…

Ampere Altra Max 对比测试数据公布,性能能效双领先

在云计算领域&#xff0c;发展创新的脚步永不停歇。十多年前&#xff0c;伴随着虚拟化及高速网络的发展和成熟&#xff0c;云计算应运而生。在将工作负载迁移到云端的过程中&#xff0c;为了更好地适应云环境&#xff0c;软件架构得以重建&#xff0c;就如同搬进新家时&#xf…

钉钉宜搭入选Forrester《中国低代码平台市场分析报告》

简介&#xff1a; &#x1f389; 最新&#xff1a;钉钉宜搭入选Forrester《中国低代码平台市场分析报告》&#xff01; 11月12日&#xff0c;全球知名研究机构Forrester发布《中国低代码平台市场分析报告&#xff08;The State Of Low-Code Platforms In China&#xff09;》&…

被自己的行为蠢哭了,意识到原因后真香!

作者 | 零一来源 | 前端印象这两天在学习 node 相关的知识时&#xff0c;做出了一些错误的行为~在做用户登录相关业务时涉及到了 cookie、session 的存取&#xff0c;一搜就找到了 express-session 这个中间件&#xff0c;真香&#xff01;配几个配置就可以自动生成 cookie、se…

一种命令行解析的新思路(Go 语言描述)

简介&#xff1a; 本文通过打破大家对命令行的固有印象&#xff0c;对命令行的概念解构后重新梳理&#xff0c;开发出一种功能强大但使用极为简单的命令行解析方法。这种方法支持任意多的子命令&#xff0c;支持可选和必选参数&#xff0c;对可选参数可提供默认值&#xff0c;支…

云原生 DevOps,模型化应用交付能力很重要

简介&#xff1a; DevOps 文化及其支撑其落地实践的自动化工具与平台能力在云原生架构渐为普及的背后&#xff0c;发挥了关键的价值。 撰稿&#xff1a;溪洋 云原生正在成为企业业务创新和解决规模化挑战的加速器。 云原生带来的变革绝不限于基础设施和应用架构等技术层面&a…

如何在 Kubernetes Pod 内进行网络抓包

作者 | Addo Zhang来源 | 云原生指北使用 Kubernetes 时&#xff0c;经常会遇到一些棘手的网络问题需要对 Pod 内的流量进行抓包分析。然而所使用的镜像一般不会带有 tcpdump 命令&#xff0c;过去常用的做法简单直接暴力&#xff1a;登录到节点所在节点&#xff0c;使用 root …

EDAS 4.0 助力企业一站式实现微服务架构转型与 K8s 容器化升级

简介&#xff1a; EDAS 正式来到 4.0 时代&#xff0c;发布多项重磅新能力&#xff1b;同时联合新产品—云原生应用设计开发平台 ADD 1.0&#xff0c;一起发布云原生应用研发&运维 PaaS 产品家族&#xff0c;助力企业应用架构现代化升级。 作者&#xff1a;安绍飞 前言 …

如何用20分钟就能获得同款企业级全链路灰度能力?

简介&#xff1a; MSE 微服务引擎将推出服务治理专业版&#xff0c;提供开箱即用且完整专业的微服务治理解决方案&#xff0c;帮助企业更好地实现微服务治理能力。如果您的系统也可以像本文描述的那样&#xff0c;快速具备完整的全链路灰度能力&#xff0c;并基于该能力进行进一…

云桌面场景化升级新作,锐捷网络发布全新远程办公“U空间”

编辑 | 宋慧 出品 | CSDN云计算 远程办公真的来了。 在硅谷的科技公司远程办公常态化之后&#xff0c;国内的科技大厂也在跟进中&#xff0c;如携程正式宣布的32混合办公模式。根据iiMedia Research艾媒咨询数据显示&#xff0c;在2020年新春期间&#xff0c;中国远程办公人员…

细说双 11 直播背后的压测保障技术

简介&#xff1a; 阿里云 PTS 站在双 11 巨人的肩膀上&#xff0c;是阿里全链路压测的延伸。PTS 通过伸缩弹性&#xff0c;轻松发起用户百万级别的流量&#xff0c;免去机器、人力成本&#xff1b;PTS 对流量的控制&#xff0c;能够实时脉冲&#xff0c;精准控制&#xff1b; 是…

【SpringCloud-Alibaba系列教程】14.一文教你入门RocketMQ

<本文已参与 RocketMQ Summit 优秀案例征文活动&#xff0c;点此了解详情> MQ简介 MQ(Message Queue)是一种跨进程的通信机制&#xff0c;用于消息传递。通俗点说&#xff0c;就是一个先进先出的数据结构。 MQ应用场景 异步解耦 很多场景不使用MQ会产生各个应用见紧密…

独家 | 2021双11背后的数据库硬核科技

简介&#xff1a; 今年双11&#xff0c;阿里云数据库技术有什么不一样&#xff1f; 2021年&#xff0c;是阿里巴巴首个100%云上双11 双11峰值计算成本 相比去年下降50% 作为全球规模最大的数字工程之一 双11无疑是对阿里技术人的“大考” 在又一次技术“严考"面前 …

前沿分享|阿里云资深技术专家 魏闯先:AnalyticDB PostgreSQL年度新版本发布

简介&#xff1a; 本篇内容为2021云栖大会-云原生数据仓库AnalyticDB技术与实践峰会分论坛中&#xff0c;阿里云资深技术专家 魏闯先关于“AnalyticDB PostgreSQL年度新版本发布”的分享。 本篇内容将通过三个部分来介绍AnalyticDB PG年度新版本发布。 一、AnalyticDB PG云原生…

Apache RocketMQ在我司的最佳实践--智慧政务场景下的分布式消息与分布式事务

<本文已参与 RocketMQ Summit 优秀案例征文活动&#xff0c;点此了解详情> 缘起 对于Apache RocketMQ的了解&#xff0c;追溯起来&#xff0c;可以说是从开源初始&#xff0c;就认识到了它。那时候的它&#xff0c;还是个幼年&#xff0c;没有成熟的社区&#xff0c;也…