跳到主要内容RocketMQ 顺序消息详解:全局与分区实现及最佳实践 | 极客日志Javajava
RocketMQ 顺序消息详解:全局与分区实现及最佳实践
RocketMQ 顺序消息机制旨在解决分布式系统中的数据一致性问题,分为全局顺序与分区顺序。全局顺序要求单一队列处理所有消息,适用于低吞吐场景;分区顺序则基于业务键将消息路由至特定队列,兼顾顺序性与并发性能。实现关键在于生产者使用 MessageQueueSelector 确保同键消息入队一致性,消费者采用 ConsumeOrderlyListener 单线程消费。此外,需妥善处理消费失败重试与死信策略,避免阻塞后续消息。
MqEngine1 浏览 RocketMQ 顺序消息详解:全局与分区实现及最佳实践
在现代分布式系统中,消息中间件扮演着至关重要的角色。它不仅能够解耦系统组件、削峰填谷,还能保证数据的可靠传输。Apache RocketMQ 作为一款高性能、高可用、高可靠的消息中间件,在金融、电商、物流等多个领域得到了广泛应用。而在众多消息特性中,顺序消息(Ordered Message)是一个既重要又容易被误解的概念。
本文将深入探讨 RocketMQ 中的顺序消息机制,包括其工作原理、实现方式、使用场景以及最佳实践。我们将通过详细的 Java 代码示例来展示如何在实际项目中正确使用顺序消息,并分析全局顺序与分区顺序的区别与适用场景。
什么是顺序消息?
在讨论 RocketMQ 的顺序消息之前,我们首先需要明确'顺序消息'的定义。简单来说,顺序消息是指消息的消费顺序与发送顺序保持一致。这意味着如果生产者按照 A → B → C 的顺序发送三条消息,那么消费者也必须按照 A → B → C 的顺序进行消费。
然而,在分布式系统中实现严格的全局顺序是非常困难的,甚至是不现实的。原因如下:
- 性能瓶颈:全局顺序要求所有消息都通过同一个队列处理,这会严重限制系统的吞吐量。
- 单点故障:如果只有一个队列负责处理所有消息,一旦该队列所在的 Broker 出现故障,整个消息系统就会瘫痪。
- 扩展性差:无法通过增加队列数量来水平扩展系统处理能力。
因此,RocketMQ 采用了分区顺序(Partitioned Ordering)的策略,即在特定的业务维度上保证消息的顺序性,而不是在整个消息系统中保证全局顺序。
RocketMQ 顺序消息的工作原理
RocketMQ 的顺序消息实现基于其核心架构中的 Topic-Queue 模型。在 RocketMQ 中,每个 Topic 可以包含多个 MessageQueue(通常简称为 Queue),而每个 Queue 是一个 FIFO(先进先出)的队列。
全局顺序 vs 分区顺序
全局顺序(Global Ordering)是指在整个 Topic 范围内,所有消息都按照发送顺序被消费。要实现全局顺序,Topic 必须只包含一个 Queue。这样所有的消息都会被发送到同一个 Queue 中,从而保证了严格的顺序性。
分区顺序(Partitioned Ordering)是指在特定的业务键(如订单 ID、用户 ID 等)范围内保证消息的顺序性。不同的业务键可以对应不同的 Queue,从而在保证局部顺序的同时获得更好的并发性能。
让我们通过一个 mermaid 图表来直观地理解这两种顺序模式的区别:
graph TD
subgraph GlobalOrder
direction TB
T[Topic: OrderTopic] --> Q0[Queue: 0]
Q0 --> M1[消息 1]
Q0 --> M2[消息 2]
Q0 --> M3[消息 3]
Q0 --> M4[消息 4]
end
subgraph PartitionOrder
direction TB
T2[Topic: OrderTopic] --> Q1[Queue: 0]
T2 --> Q2[Queue: 1]
T2 --> Q3[Queue: 2]
Q1 --> O1A[订单 A: 消息 1 -> 消息 2]
Q2 --> O2B[订单 B: 消息 1 -> 消息 2]
Q3 --> O3C[订单 C: 消息 1 -> 消息 2]
end
从图中可以看出,全局顺序将所有消息都塞入同一个 Queue,而分区顺序则根据业务键将消息分散到不同的 Queue 中,每个 Queue 内部保持顺序。
RocketMQ 顺序消息的核心机制
RocketMQ 实现顺序消息的关键在于以下几点:
- 消息路由策略:生产者需要确保同一业务键的消息总是被发送到同一个 Queue。
- 单线程消费:消费者对每个 Queue 必须使用单线程进行消费,避免多线程并发导致的顺序混乱。
- 失败重试机制:当消费失败时,RocketMQ 会将消息重新放回原 Queue 的头部,确保顺序不被破坏。
全局顺序消息的实现
虽然全局顺序在实际应用中较少使用,但了解其实现方式有助于我们更好地理解 RocketMQ 的顺序消息机制。
全局顺序的配置要求
- Topic 只有一个 Queue:这是最关键的条件,可以通过 RocketMQ 控制台或命令行工具创建单 Queue 的 Topic。
- 生产者发送策略:由于只有一个 Queue,生产者无需特殊处理,所有消息自然都会进入同一个 Queue。
- 消费者单线程消费:消费者必须使用单线程处理消息,不能开启并发消费。
Java 代码示例:全局顺序消息
首先,我们需要创建一个只包含一个 Queue 的 Topic。假设我们已经通过 RocketMQ 控制台创建了名为 GlobalOrderTopic 的 Topic,并且只分配了一个 Queue。
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
public class GlobalOrderProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("GlobalOrderProducerGroup");
producer.setNamesrvAddr("localhost:9876");
producer.start();
try {
for (int i = 0; i < 10; i++) {
String messageBody = "全局顺序消息 - " + i;
Message msg = new Message("GlobalOrderTopic", "GlobalOrderTag", messageBody.getBytes(RemotingHelper.DEFAULT_CHARSET));
producer.send(msg);
System.out.println("发送消息:" + messageBody);
Thread.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
producer.shutdown();
}
}
}
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
public class GlobalOrderConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("GlobalOrderConsumerGroup");
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("GlobalOrderTopic", "*");
consumer.setMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
for (MessageExt msg : msgs) {
try {
String messageBody = new String(msg.getBody(), "UTF-8");
System.out.println("消费消息:" + messageBody + ", QueueId: " + msg.getQueueId() + ", Offset: " + msg.getQueueOffset());
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
System.out.println("全局顺序消费者启动成功");
}
}
全局顺序的局限性
虽然上述代码能够实现全局顺序,但在实际生产环境中存在明显的局限性:
- 性能瓶颈:所有消息都通过单个 Queue 处理,无法利用 RocketMQ 的并行处理能力。
- 可用性风险:如果该 Queue 所在的 Broker 宕机,整个消息系统将不可用。
- 扩展性差:无法通过增加 Queue 数量来提升系统吞吐量。
因此,全局顺序通常只适用于消息量非常小、对顺序要求极其严格的场景,比如某些金融交易系统的日志记录。
分区顺序消息的实现
分区顺序是 RocketMQ 中更常用、更实用的顺序消息实现方式。它通过将消息按照业务键进行分组,每组消息在各自的 Queue 中保持顺序,从而在保证业务逻辑正确性的同时获得良好的性能表现。
分区顺序的设计思路
分区顺序的核心思想是:相同业务键的消息必须发送到同一个 Queue,不同业务键的消息可以发送到不同的 Queue。
例如,在电商系统中,我们可以使用订单 ID 作为业务键。这样,同一个订单的所有操作(创建、支付、发货、完成)都会被发送到同一个 Queue,保证了该订单操作的顺序性。而不同订单的操作可以并行处理,提高了系统的整体吞吐量。
Java 代码示例:分区顺序消息
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import java.util.List;
public class PartitionOrderProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("PartitionOrderProducerGroup");
producer.setNamesrvAddr("localhost:9876");
producer.start();
String[] orderIds = {"ORDER_001", "ORDER_002", "ORDER_003"};
try {
for (String orderId : orderIds) {
String[] operations = {"CREATE", "PAY", "SHIP", "COMPLETE"};
for (String operation : operations) {
String messageBody = orderId + " - " + operation;
Message msg = new Message("PartitionOrderTopic", "OrderTag", messageBody.getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
String orderId = (String) arg;
int index = Math.abs(orderId.hashCode()) % mqs.size();
return mqs.get(index);
}
}, orderId);
System.out.println("发送消息:" + messageBody + ", QueueId: " + sendResult.getMessageQueue().getQueueId());
Thread.sleep(100);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
producer.shutdown();
}
}
}
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
public class PartitionOrderConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("PartitionOrderConsumerGroup");
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("PartitionOrderTopic", "*");
consumer.setMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
for (MessageExt msg : msgs) {
try {
String messageBody = new String(msg.getBody(), "UTF-8");
System.out.println("消费消息:" + messageBody + ", QueueId: " + msg.getQueueId() + ", Offset: " + msg.getQueueOffset() + ", Thread: " + Thread.currentThread().getName());
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
System.out.println("分区顺序消费者启动成功");
}
}
分区顺序的关键要点
- MessageQueueSelector 的使用:这是实现分区顺序的关键。通过自定义的
MessageQueueSelector,我们可以控制消息的路由策略,确保相同业务键的消息总是被发送到同一个 Queue。
- 业务键的选择:选择合适的业务键非常重要。业务键应该能够唯一标识需要保证顺序的业务实体,比如订单 ID、用户 ID、设备 ID 等。
- Queue 数量的影响:Topic 的 Queue 数量会影响分区顺序的效果。Queue 数量越多,并发度越高,但每个 Queue 的负载越轻。需要根据实际业务需求进行权衡。
顺序消息的消费机制详解
RocketMQ 的顺序消息消费机制是其保证顺序性的关键。理解这一机制对于正确使用顺序消息至关重要。
ConsumeOrderlyStatus 枚举
在顺序消息的消费过程中,消费者必须返回 ConsumeOrderlyStatus 枚举值来指示消费结果:
- SUCCESS:消费成功,继续消费下一条消息。
- SUSPEND_CURRENT_QUEUE_A_MOMENT:消费失败,暂停当前 Queue 的消费,稍后重试。
需要注意的是,顺序消息不支持并发消费。即使消费者配置了多个线程,RocketMQ 也会确保每个 Queue 的消息由单个线程顺序处理。
消费失败的处理机制
当顺序消息消费失败时,RocketMQ 会将消息重新放回原 Queue 的头部,并暂停该 Queue 的消费一段时间(默认 1 秒)。这种机制确保了:
- 顺序不被破坏:失败的消息不会被跳过,而是重新尝试消费。
- 避免雪崩效应:暂停消费可以防止因持续失败而导致系统资源耗尽。
但是,这也带来了一个潜在问题:如果某条消息一直消费失败,会导致后续消息被阻塞。因此,在实际应用中,我们需要考虑以下策略:
- 设置最大重试次数:通过
consumer.setMaxReconsumeTimes() 方法设置最大重试次数。
- 死信队列:将超过最大重试次数的消息转移到死信队列进行人工处理。
- 异常处理:在消费逻辑中加入完善的异常处理机制,避免因个别消息的问题影响整个 Queue。
并发消费 vs 顺序消费
- 并发消费(Concurrently):使用
MessageListenerConcurrently,支持多线程并发处理消息,但不保证顺序。
- 顺序消费(Orderly):使用
MessageListenerOrderly,单线程处理每个 Queue 的消息,保证顺序。
选择哪种模式取决于业务需求。如果业务逻辑对消息顺序有严格要求,必须使用顺序消费;否则,为了获得更好的性能,建议使用并发消费。
实际应用场景分析
理解了 RocketMQ 顺序消息的技术细节后,让我们来看看它在实际业务中的应用场景。
电商订单状态流转
这是最经典的顺序消息应用场景。在电商系统中,一个订单会经历多个状态:创建 → 支付 → 发货 → 完成。这些状态变更必须按照严格的顺序执行,否则会导致业务逻辑错误。
例如,如果系统先收到'发货'消息,再收到'支付'消息,就可能出现未支付就发货的情况,造成资损。
public class OrderStatusMessage {
private String orderId;
private String status;
private long timestamp;
}
在这种场景下,使用订单 ID 作为业务键,通过分区顺序消息保证每个订单的状态变更按序执行。
用户行为轨迹分析
在用户行为分析系统中,需要按照用户操作的时间顺序来分析用户行为路径。例如:浏览商品 → 加入购物车 → 下单 → 支付。
如果消息顺序错乱,分析结果就会失真。通过使用用户 ID 作为业务键,可以保证每个用户的行为轨迹按序处理。
数据库 binlog 同步
在数据库主从同步或数据迁移场景中,binlog 事件必须严格按照事务提交的顺序进行处理。如果顺序错乱,可能导致数据不一致。
RocketMQ 的顺序消息可以很好地支持这种场景,使用表名 + 主键作为业务键,确保同一记录的变更按序同步。
物联网设备指令下发
在物联网场景中,对同一设备的控制指令必须按序执行。例如:开机 → 设置参数 → 开始工作 → 关机。
如果指令顺序错乱,可能导致设备异常。通过使用设备 ID 作为业务键,可以保证指令的正确执行顺序。
性能优化与最佳实践
虽然顺序消息能够保证业务逻辑的正确性,但如果不当使用,可能会严重影响系统性能。以下是一些最佳实践和优化建议。
合理设计业务键
- 粒度适中:业务键的粒度不宜过大也不宜过小。过大(如使用固定字符串)会导致所有消息进入同一个 Queue,退化为全局顺序;过小(如使用 UUID)会导致消息过于分散,失去顺序意义。
- 稳定性好:业务键应该是稳定的,不会在消息生命周期内发生变化。
- 分布均匀:业务键的 hash 值应该尽可能均匀分布,避免某些 Queue 负载过高。
优化 Queue 数量
Topic 的 Queue 数量需要根据业务特点进行优化:
- 消息量大、业务键多:可以适当增加 Queue 数量,提高并发度。
- 消息量小、业务键少:Queue 数量不宜过多,避免资源浪费。
一般来说,Queue 数量可以设置为 Broker 数量的整数倍,以充分利用集群资源。
处理消费失败
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
for (MessageExt msg : msgs) {
try {
processMessage(msg);
} catch (BusinessException e) {
log.error("业务异常,跳过消息:{}", msg.getMsgId(), e);
return ConsumeOrderlyStatus.SUCCESS;
} catch (Exception e) {
log.error("系统异常,重试消息:{}", msg.getMsgId(), e);
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
}
return ConsumeOrderlyStatus.SUCCESS;
}
- 业务异常:如数据格式错误、业务规则不满足等,这类异常通常无法通过重试解决,可以直接跳过。
- 系统异常:如数据库连接失败、网络超时等,这类异常可能通过重试解决,应该返回重试状态。
监控与告警
- 消费延迟:监控每个 Queue 的消费延迟,及时发现消费阻塞问题。
- 失败率:监控消息消费失败率,及时发现业务逻辑问题。
- 重试次数:监控消息重试次数,避免无限重试导致的资源浪费。
RocketMQ 提供了丰富的监控指标,可以通过 Prometheus 和 Grafana 进行可视化监控。
常见问题与解决方案
在使用 RocketMQ 顺序消息的过程中,可能会遇到一些常见问题。以下是几个典型问题及其解决方案。
问题 1:消息顺序仍然错乱
现象:明明使用了顺序消息,但消费时发现顺序还是不对。
- 生产者没有正确使用
MessageQueueSelector,导致相同业务键的消息被发送到不同 Queue。
- 消费者使用了并发消费模式而不是顺序消费模式。
- 业务键选择不当,无法正确标识需要保证顺序的业务实体。
- 检查生产者的
MessageQueueSelector 实现,确保相同业务键的消息总是选择同一个 Queue。
- 确认消费者使用的是
MessageListenerOrderly 而不是 MessageListenerConcurrently。
- 重新审视业务键的设计,确保其能够正确标识业务实体。
问题 2:消费性能低下
- Queue 数量过少,无法充分利用多核 CPU 的并行处理能力。
- 消费逻辑过于复杂,单条消息处理时间过长。
- 消费失败导致频繁重试,影响整体吞吐量。
- 增加 Topic 的 Queue 数量,提高并发度。
- 优化消费逻辑,减少单条消息的处理时间。
- 合理处理消费失败,避免不必要的重试。
问题 3:消费阻塞
原因分析:
这是顺序消息的固有特性。由于顺序消息必须保证严格的顺序性,当某条消息消费失败时,RocketMQ 会暂停该 Queue 的消费,直到失败消息被成功处理。
- 在消费逻辑中加入完善的异常处理机制,尽量避免消费失败。
- 对于无法恢复的业务异常,可以选择跳过该消息(返回 SUCCESS),避免阻塞后续消息。
- 设置合理的最大重试次数,避免无限重试。
与其他消息中间件的对比
为了更好地理解 RocketMQ 顺序消息的特点,我们可以将其与其他主流消息中间件进行对比。
RocketMQ vs Kafka
Kafka 也支持顺序消息,但其实现方式与 RocketMQ 有所不同:
- Kafka:通过 Partition 保证顺序,相同 key 的消息会被发送到同一个 Partition。Kafka 的顺序保证是在 Partition 级别的,这与 RocketMQ 的 Queue 级别顺序类似。
- RocketMQ:提供了更灵活的顺序消息 API,通过
MessageQueueSelector 可以自定义路由策略。
- Kafka 的顺序保证更强,因为其底层存储是基于日志的,天然支持顺序读写。
- RocketMQ 的顺序消息 API 更加友好,更容易在 Java 应用中使用。
更多关于消息中间件的详细对比,可以参考 Apache 官方文档。
高级特性与扩展
除了基本的顺序消息功能,RocketMQ 还提供了一些高级特性来增强顺序消息的能力。
事务消息与顺序消息结合
在某些场景下,我们需要同时保证消息的顺序性和事务性。例如,在金融系统中,转账操作既要保证顺序(先扣款后入账),又要保证事务性(要么都成功,要么都失败)。
RocketMQ 支持事务消息,可以与顺序消息结合使用:
TransactionMQProducer producer = new TransactionMQProducer("TransactionOrderProducerGroup");
producer.setTransactionListener(new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
return LocalTransactionState.COMMIT_MESSAGE;
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
return LocalTransactionState.COMMIT_MESSAGE;
}
});
producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
String businessKey = (String) arg;
return mqs.get(Math.abs(businessKey.hashCode()) % mqs.size());
}
}, businessKey);
延迟消息与顺序消息
RocketMQ 还支持延迟消息,可以与顺序消息结合使用。例如,在订单超时取消场景中,我们需要在订单创建后 30 分钟检查是否已支付,如果没有支付则自动取消订单。
Message msg = new Message("OrderTimeoutTopic", "TimeoutTag", ("ORDER_001").getBytes(RemotingHelper.DEFAULT_CHARSET));
msg.setDelayTimeLevel(5);
producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
String orderId = (String) arg;
return mqs.get(Math.abs(orderId.hashCode()) % mqs.size());
}
}, "ORDER_001");
需要注意的是,延迟消息在延迟期间不保证顺序,只有在延迟结束后进入目标 Topic 时才开始保证顺序。
总结与展望
RocketMQ 的顺序消息机制为分布式系统中的顺序性需求提供了优雅的解决方案。通过分区顺序的设计,既保证了业务逻辑的正确性,又获得了良好的性能表现。
核心要点回顾
- 全局顺序:适用于消息量小、顺序要求严格的场景,但存在性能和可用性问题。
- 分区顺序:通过业务键将消息分组,在组内保证顺序,是更实用的方案。
- MessageQueueSelector:是实现分区顺序的关键,控制消息的路由策略。
- 顺序消费:必须使用
MessageListenerOrderly,单线程处理每个 Queue 的消息。
- 失败处理:顺序消息的失败处理需要特别注意,避免阻塞后续消息。
未来发展方向
随着云原生和微服务架构的普及,消息中间件也在不断演进。RocketMQ 5.0 引入了更多的云原生特性,如:
- Proxy 架构:简化客户端接入,提高系统的可维护性。
- 流式处理:支持更复杂的流式计算场景。
- 多语言支持:除了 Java,还提供了 Go、Python、C++ 等多种语言的客户端。
在顺序消息方面,未来可能会看到更多智能化的优化,比如:
- 自适应分区:根据消息流量和业务特征自动调整分区策略。
- 智能重试:根据失败原因智能选择重试策略,避免不必要的阻塞。
- 跨地域顺序:在多地域部署场景下保证全局顺序。
如果你想要深入了解 RocketMQ,以下是一些优质的学习资源:
通过本文的学习,相信你已经掌握了 RocketMQ 顺序消息的核心概念和实践技巧。在实际项目中,要根据具体的业务需求选择合适的顺序消息方案,并注意性能优化和异常处理,才能构建出稳定可靠的消息系统。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online