中间件里的异步编程

Q: 是怎么上账的,机器重启之后数据去哪了?

public boolean handleConfirm(TransferTxSideReq request, Builder respBuilder, boolean hasFrozen)
        throws ExecutionException, InterruptedException {
        // 业务检查,幂等检查
        // ... 
        // 创建划入确认单
        Order confirmOrder = createOrder(request, TRANSFER_IN_CONFIRM);

        // 提交raft wal
        raftApplyManager.futureRaftApply(new TransferOrderRaftClosure(confirmOrder)).get();
        
        // 发送MR到清算 doing
        clearOpenApi.submitMatchResultToClear(convertToMR(confirmOrder));

        return true;
    }

分析上面的流程,主要经过了几个阶段

1 数据验证准备阶段

2 写日志阶段 raft apply jraft调研

3 异步转同步等待阶段 .get()

4 写内存的阶段 ← 内存化系统

快照

1 定时、定量 保存快照

2 服务启动时,加载快照

    @Override
    public void onSnapshotSave(final SnapshotWriter writer, final Closure done) {
        log.info("开始生产快照,lastIndex={}", writer.getCurrentMeta().getLastIncludedIndex());
        byte[] memoryDataCenter = recoveryBackupOperation.syncBackup();
        snapshotFile.save(writer, done, memoryDataCenter);
    }

    @Override
    public boolean onSnapshotLoad(final SnapshotReader reader) {
        Optional<MemoryDataCenter> memoryDataCenterOptional = snapshotFile.load(reader);
        if (memoryDataCenterOptional.isPresent()) {
            recoveryBackupOperation.recovery(memoryDataCenterOptional.get());
            return true;
        }
        return false;
    }

回放

1 follow节点同步维护数据

2 服务启动时,回放数据

3 leader节点 会时时走到这里面吗 (todo 待测试)

@Override
    public void onApply(Iterator it) {
        int index = 0;
        int applied = 0;
        try {
            while (it.hasNext()) {
                RaftEntry raftEntry;
                final DefaultClosure closure = (DefaultClosure) it.done();
                if (!Objects.isNull(closure)) {
                    // 正常流程:leader节点
                    raftEntry = closure.getMemoryClosure().getRaftEntry();
                } else {
                    try {
                        // follower or restart
                        raftEntry = RaftEntry.parseFrom(it.getData().array());
                    } catch (Throwable t) {
                        throw new StoreCodecException("Decode operation error", t);
                    }
                }

                doMaintainMemory(raftEntry, closure, it.getIndex());
                ++index;
                it.next();
            }
        } catch (Throwable t) {
            log.error("StateMachine meet critical error: {}.", t);
            it.setErrorAndRollback(index - applied, new Status(RaftError.ESTATEMACHINE, "StateMachine meet critical error: %s.",
                t.getMessage()));
        }
    }

在上面的流程中,梳理弹药库

1 怎么理解同步和异步

2 异步转同步怎么做,通常的做法有什么,常见的产品有哪些

3 我们在聊内存话系统在聊什么

4 raft 在这个过程中起到的作用是什么

1 怎么理解同步与异步

tomcat, mysql, redis, netty, rocketmq

// org.apache.rocketmq.client.producer.MQProducer
SendResult send(final Message msg)
SendResult send(final Message msg, final long timeout)
void send(final Message msg, final SendCallback sendCallback) 
void send(final Message msg, final SendCallback sendCallback, final long timeout)
void sendOneway(final Message msg) 

GRPC是怎么做异步的

2 异步转同步是怎么做的

rocketmq 的异步转同步代码分析

public RemotingCommand invokeSyncImpl(final Channel channel, final RemotingCommand request,
        final long timeoutMillis)
        throws InterruptedException, RemotingSendRequestException, RemotingTimeoutException {
        final int opaque = request.getOpaque();

        try {
            // 创建一个 future
            final ResponseFuture responseFuture = new ResponseFuture(channel, opaque, timeoutMillis, null, null);
            // 保存到路由表
            this.responseTable.put(opaque, responseFuture);
            final SocketAddress addr = channel.remoteAddress();
            // tcp 双工,发送方向
            channel.writeAndFlush(request).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture f) throws Exception {
                    if (f.isSuccess()) {
                        responseFuture.setSendRequestOK(true);
                        return;
                    } else {
                        responseFuture.setSendRequestOK(false);
                    }

                    responseTable.remove(opaque);
                    responseFuture.setCause(f.cause());
                    responseFuture.putResponse(null);
                    log.warn("send a request command to channel <" + addr + "> failed.");
                }
            });
            // future 等待
            RemotingCommand responseCommand = responseFuture.waitResponse(timeoutMillis);
            if (null == responseCommand) {
                if (responseFuture.isSendRequestOK()) {
                    throw new RemotingTimeoutException(RemotingHelper.parseSocketAddressAddr(addr), timeoutMillis,
                        responseFuture.getCause());
                } else {
                    throw new RemotingSendRequestException(RemotingHelper.parseSocketAddressAddr(addr), responseFuture.getCause());
                }
            }

            return responseCommand;
        } finally {
            this.responseTable.remove(opaque);
        }
    }

3 我们在聊内存化系统时,在聊什么,为什么是10万TPS 而不是20 tps 

内存化系统的基本组成 wal + snapshot

Write-Ahead Logging (WAL) 和 Snapshot 是两种在分布式数据库和存储系统中常用的技术,它们各自发挥着重要作用,尤其是在确保数据一致性和持久性方面。在分布式环境中,WAL 和 Snapshot 的结合使用可以提供强大的数据恢复和复制能力。

Write-Ahead Logging (WAL)

WAL 是一种日志记录技术,其中所有数据修改首先被写入日志(通常是磁盘上的一个日志文件),然后再实际更新数据库中的数据页。这样做的目的是为了确保在发生崩溃或故障时,可以通过日志重放(Redo Log)来恢复数据到最近的一致状态。WAL 在分布式系统中的作用如下:

  1. 数据恢复:WAL 记录了所有事务的修改,即使在系统崩溃后,也可以通过重放日志来恢复数据到崩溃前的一致状态。
  2. 事务持久性:确保事务的原子性和持久性,即使在事务完成之后系统立即崩溃,事务的效果也会被保留。
  3. 复制和分发:在分布式数据库中,WAL 记录可以被复制到其他节点,用于数据同步和一致性保证。例如,主节点的事务日志可以被推送到从节点,以更新其数据集。

Snapshot

Snapshot 是数据库在某个时间点的快照,它捕获了数据库的状态。在分布式系统中,Snapshot 主要有以下几个作用:

  1. 一致性读取:提供了一种机制,使客户端能够在不干扰正在进行的写操作的情况下读取数据的一致视图。
  2. 数据恢复:Snapshot 可以作为数据恢复的基础,与 WAL 结合使用,可以快速恢复到某一状态,然后通过重放 WAL 来应用后续的修改。
  3. 数据分发:Snapshot 可以被用于初始化远程节点或用于数据复制。例如,可以先将 Snapshot 发送给远程节点,然后通过应用 WAL 来同步数据到最新状态。

结合使用

在分布式数据库中,WAL 和 Snapshot 经常结合使用,以提供高效的数据复制和恢复机制:

  • 初始数据加载:使用 Snapshot 快速初始化远程节点,避免长时间的数据复制。
  • 增量更新:通过持续发送 WAL 记录,可以最小化远程节点与主节点之间的数据差异,保持数据的一致性。
  • 数据恢复:在数据丢失或损坏时,可以从最近的 Snapshot 开始,然后重放 WAL 来恢复数据到最新状态。

这种组合使用在许多分布式数据库系统中都有体现,如 PostgreSQL 的物理复制、Cassandra 的 Snapshot 和 Repair 机制,以及像 ZNBase 这样的系统,它使用 WAL 和 Snapshot 来实现数据恢复至任意时间点的能力。

数据来源于 文心千问

对于普通的消费级SSD硬盘,IOPS(每秒输入/输出操作次数)的性能范围可以从几千到几万,具体数值取决于SSD的类型、接口、控制器以及闪存技术。

以下是几种常见SSD类型的IOPS性能概览:

SATA SSD:
SATA接口的SSD通常可以提供高达50,000至100,000 IOPS的随机读取性能,而随机写入性能略低,大约在40,000至80,000 IOPS。这些数字适用于4KB随机读写操作,这是行业标准测试条件。
PCIe SSD (NVMe):
使用PCIe接口的SSD,尤其是基于NVMe协议的SSD,可以提供显著更高的IOPS性能。这类SSD的随机读取IOPS可以达到250,000至500,000,随机写入IOPS同样在这一范围内,甚至更高。
企业级SSD:
专为企业级应用设计的SSD,如数据中心或服务器使用,可以提供更高的IOPS性能,通常在几十万到上百万的范围内。这些SSD通常采用PCIe/NVMe接口,并且具有优化的控制器和耐用性设计,以支持高IOPS和长时间运行的需求。
需要注意的是,IOPS性能会受到许多因素的影响,包括:

工作负载:IOPS测试通常在特定的工作负载条件下进行,如4KB随机读写,实际应用中的IOPS性能可能有所不同。
闪存类型:TLC、MLC或SLC NAND闪存的不同类型会影响IOPS性能。
驱动程序和固件:SSD的驱动程序和固件版本也会影响其IOPS性能。
系统瓶颈:如CPU、内存和主板的性能也会影响SSD的实际IOPS表现。
在选择SSD时,除了IOPS,还需要考虑其他因素,如容量、耐久性、延迟、成本和是否符合预期的应用场景。

Q : DMA是双工的吗?

其他可参考的文件顺序写

RocketMQ 的文件落盘过程 (单大文件)

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值