哥们儿!有遇到Kafka数据丢失问题的问题吗,你是如何解决的?今天的文章,V哥来详细解释一下,整理了12种解决策略,希望可以帮助你解决项目中的问题:以下是一些常见的解决方案和最佳实践。
生产者确认机制
:生产者可以使用 Kafka 的确认机制来确保消息成功发送到 Kafka 集群。生产者可以选择等待 Kafka 的确认响应(acks)或使用同步发送方式,以确保消息不会丢失。增加副本因子
:通过增加 Kafka 主题的副本因子,可以提高消息的可靠性。副本因子决定了每个分区的副本数量,增加副本数量可以提高消息的冗余度,降低消息丢失的风险。监控和警报
:设置监控和警报系统,及时发现和处理消息丢失的问题。可以监控生产者和消费者的指标,如发送速率、确认率和消费速率等,以及 Kafka 集群的状态和健康状况。合理的配置和容量规划
:根据应用程序的需求和负载情况,合理配置 Kafka 集群和主题的参数。确保足够的存储空间、网络带宽和处理能力,以避免由于资源不足而导致的消息丢失。设置生产者的 acks 参数为 "all"
:这将确保生产者在收到所有副本的确认后才认为消息发送成功,从而实现零丢失的配置。调整日志存储空间和最大消息大小
:根据实际需求调整 Kafka 集群的参数,如日志存储空间、最大消息大小、最大连接数等。使用压缩
:Kafka 支持 GZip 和 Snappy 压缩,这可以减少网络和磁盘 IO,同时缓解因资源限制导致的数据丢失问题。关闭自动提交 offset
:在消费者端,关闭自动更新 offset,等到数据被处理后再手动更新 offset,以避免数据丢失。确保 broker 配置正确
:broker 能接收消息的最大字节数的设置一定要比消费端能消费的最大字节数要小,以避免 broker 因为消费端无法使用这个消息而挂起。使用同步复制
:当配置了同步复制之后,多个副本的数据都在 PageCache 里面,出现多个副本同时挂掉的概率就很小了。调整 flush 间隔
:通过 log.flush.interval.messages 和 log.flush.interval.ms 配置 flush 间隔,以减少因 flush 间隔设置不当导致的数据丢失。避免使用 unclean leader 选举
:关闭 unclean.leader.election.enable,以避免非 ISR 中的副本被选举为 leader,这可能导致数据丢失。
通过这些方法,可以显著减少 Kafka 中的数据丢失问题,并提高系统的可靠性和稳定性。
下面,V哥针对12个策略再详细介绍实现步骤,并结合业务场景分析和示例代码来讲解,希望给你一个全面细致的了解。
1. 生产者确认机制
在 Kafka 中,生产者确认机制是指生产者在发送消息到 Kafka 集群后,根据配置的确认级别(acks)等待来自 Kafka 集群的响应。这是确保消息不会丢失的关键步骤。以下是生产者确认机制的具体实现步骤和 Java 示例:
1.1 生产者确认机制的实现步骤:
配置生产者属性:在生产者配置中设置 acks 参数,这个参数决定了生产者在发送消息后需要从 Kafka 集群接收多少确认。
发送消息:生产者发送消息到 Kafka 主题。
等待确认:根据 acks 参数的设置,生产者会等待来自 Kafka 集群的相应数量的确认。
处理确认响应:生产者根据收到的确认响应来确定消息是否成功发送。
错误处理:如果消息发送失败,生产者可以根据配置的重试策略进行重试。
1.2 Kafka 生产者确认级别(acks 参数):
- acks=0:生产者不等待任何来自服务器的确认,继续发送下一条。
- acks=1:只要集群的 leader 接收到消息,生产者就会收到确认。
- acks=all:只有当所有同步副本(ISR,In-Sync Replicas)都接收到消息时,生产者才会收到确认。
1.3 Java 示例:
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class KafkaProducerExample {
public static void main(String[] args) {
// 设置生产者配置
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.ACKS_CONFIG, "all"); // 设置确认级别为 all
props.put(ProducerConfig.RETRIES_CONFIG, 3); // 设置重试次数
props.put(ProducerConfig.RETRY_BACKOFF_MS_CONFIG, 100); // 设置重试间隔
// 创建 Kafka 生产者实例
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// 创建消息
String topicName = "exampleTopic";
String message = "Hello, Kafka!";
// 发送消息
try {
producer.send(new ProducerRecord<>(topicName, message), new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception e) {
if(e != null) {
e.printStackTrace();
} else {
System.out.println("Sent message with offset: " + metadata.offset());
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
// 关闭生产者
producer.close();
}
}
1.4 实际业务场景解释:
假设你正在开发一个电子商务平台,需要确保用户订单信息能够可靠地发送到 Kafka 主题中,以便后续的订单处理服务能够消费这些信息。
配置生产者:在生产者配置中设置 acks=all,确保所有副本都接收到消息,从而减少数据丢失的风险。
发送订单消息:当用户下单时,系统将订单信息封装为一个 ProducerRecord 对象,并发送到 Kafka。
等待确认:生产者等待来自 Kafka 集群的所有副本的确认,确保消息已经被安全存储。
记录发送结果:通过 Callback 接口,系统可以记录消息发送的结果,如果发送失败,可以根据重试策略进行重试。
关闭生产者:在应用程序关闭时,确保生产者资源被正确释放。
通过这种方式,即使在网络不稳定或 Kafka 集群内部出现问题的情况下,也能够最大程度地保证订单数据的可靠性和完整性。
2. 增加副本因子
增加副本因子是提高 Kafka 主题数据可靠性的重要手段。副本因子(replication factor)指的是每个分区的数据备份数量。增加副本因子可以减少数据丢失的风险,因为即使某些 broker 宕机,数据仍然可以从其他副本中恢复。以下是增加副本因子的具体实现步骤和 Java 示例:
2.1 增加副本因子的实现步骤:
评估当前副本因子:检查现有主题的副本因子,确定是否需要增加。
修改主题配置:使用 Kafka 提供的命令行工具或 Java API 来增加主题的副本因子。
监控副本同步:确保新的副本能够与 leader 保持同步。
调整集群配置:如果需要,调整集群的配置以支持更多的副本,例如增加 broker 数量或提高存储容量。
测试:在生产环境部署之前,测试新的配置以确保系统的稳定性和性能。
监控和优化:在生产环境中监控副本的同步状态,并根据需要进行优化。
2.2 Java 示例:
在 Java 中,可以使用 Kafka 的 AdminClient API 来修改主题的副本因子。以下是一个示例代码,展示了如何使用 Java API 增加主题的副本因子:
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.AlterConfigsResult;
import org.apache.kafka.clients.admin.Config;
import org.apache.kafka.clients.admin.ConfigEntry;
import org.apache.kafka.common.config.ConfigResource;
import java.util.Collections;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
public class IncreaseReplicationFactorExample {
public static void main(String[] args) {
// 设置 AdminClient 配置
Properties props = new Properties();
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
// 创建 AdminClient 实例
try (AdminClient admin = AdminClient.create(props)) {
// 定义要修改的主题和新的副本因子
String topicName = "exampleTopic";
int newReplicationFactor = 3;
// 创建配置资源
ConfigResource resource = new ConfigResource(ConfigResource.Type.TOPIC, topicName);
// 创建新的配置
Properties newProps = new Properties();
newProps.put("replication.factor", String.valueOf(newReplicationFactor));
// 构建配置项
Config newConfig = new Config(Collections.singletonList(new ConfigEntry("replication.factor", String.valueOf(newReplicationFactor))));
// 修改主题配置
AlterConfigsResult alterResult = admin.alterConfigs(Collections.singletonMap(resource, newConfig));
// 等待操作完成
alterResult.values().get(resource).get();
System.out.println("Replication factor increased to " + newReplicationFactor);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
2.3 实际业务场景解释:
假设你管理着一个金融服务平台,该平台使用 Kafka 来处理交易数据。为了确保数据的高可用性和可靠性,你需要将主题的副本因子从 1 增加到 3。
评估和计划:首先,评估当前主题的副本因子,确定它是否满足业务需求。如果副本因子为 1,那么任何 broker 的故障都可能导致数据丢失。
修改副本因子:使用上述 Java 代码示例,通过 AdminClient API 增加主题的副本因子到 3。这将确保每个分区的数据都有两个额外的备份。
监控副本同步:在增加副本因子后,监控新的副本是否能够成功同步数据。这可以通过 Kafka 的监控工具或自定义的监控脚本来完成。
测试和验证:在生产环境部署之前,在一个测试环境中验证新的副本因子配置是否按预期工作。
部署和监控:在生产环境中部署更改,并持续监控 Kafka 集群的性能和副本同步状态,确保系统的稳定性和数据的可靠性。
通过这种方式,即使在部分硬件故障的情况下,金融服务平台的交易数据也能够保持可用和一致,从而提高整个系统的可靠性。
3. 监控和警报
使用监控和警报是确保 Kafka 集群健康运行并及时发现问题的关键措施。以下是使用监控和警报的具体实现步骤和 Java 示例,以及结合实际业务场景的详细解释:
3.1 使用监控和警报的实现步骤:
选择监控工具:选择适合的监控工具,例如 Kafka 自带的 JMX 监控,或者使用第三方监控系统如 Prometheus、Grafana。
配置监控指标:确定需要监控的关键指标,如生产者和消费者的发送速率、确认率、消费速率,以及 broker 的内存使用、磁盘 IO 等。
集成监控系统:将 Kafka 集群与所选的监控系统集成,确保可以收集到所需的监控数据。
设置警报规则:根据业务需求和系统性能基线,设置警报规则,如当消息积压超过阈值或 broker 宕机时触发警报。
测试警报系统:在生产环境部署之前,测试警报系统以确保其按预期工作。
监控和响应:在生产环境中持续监控 Kafka 集群,并在收到警报时及时响应。
优化和调整:根据监控数据和警报反馈,不断优化 Kafka 集群配置和业务逻辑。
3.2 Java 示例:
在 Java 中,可以通过 JMX(Java Management Extensions)来监控 Kafka 的运行情况。以下是一个简单的示例,展示了如何使用 JMX 连接到 Kafka 的 JMX 端口并获取监控数据:
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class KafkaMonitoringExample {
public static void main(String[] args) {
try {
// Kafka JMX 连接 URL,端口号根据实际情况进行替换
String jmxURL = "service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi";
JMXServiceURL serviceURL = new JMXServiceURL(jmxURL);
// 创建 JMX 连接
Map<String, Object> env = new HashMap<>();
JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceURL, env);
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
// 获取特定的 MBean,这里以 Kafka 的 Controller 为例
ObjectName objectName = new ObjectName("kafka.controller:type=ControllerStats");
Set<ObjectName> names = mBeanServerConnection.queryNames(objectName, null);
for (ObjectName name : names) {
// 获取并打印 MBean 的属性值
String activeControllerCount = mBeanServerConnection.getAttribute(name, "ActiveControllerCount").toString();
System.out.println("ActiveControllerCount: " + activeControllerCount);
}
// 关闭 JMX 连接
jmxConnector.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.3 实际业务场景解释:
假设你负责一个大型电商平台的 Kafka 集群,该集群用于处理用户行为数据和订单信息。
选择监控工具:选择 Grafana 作为前端展示,Prometheus 作为后端数据收集的监控系统。
配置监控指标:配置 Prometheus 来收集 Kafka 集群的监控指标,如生产者吞吐量、消费者延迟、副本同步状态等。
集成监控系统:将 Kafka 的 JMX 端口暴露给 Prometheus,以便 Prometheus 可以抓取 Kafka 的监控数据。
设置警报规则:在 Grafana 中设置警报规则,例如当消费者队列积压超过一定数量或 broker 宕机时,通过邮件或短信通知运维团队。
测试警报系统:在生产环境部署之前,模拟故障情况来测试警报系统是否能够及时触发。
监控和响应:运维团队通过 Grafana 监控 Kafka 集群的运行状态,并在收到警报时快速响应,排查并解决问题。
优化和调整:根据监控数据和警报反馈,调整 Kafka 集群的配置,优化业务逻辑,提高系统的稳定性和可靠性。
通过这种方式,电商平台的 Kafka 集群可以保持高效运行,及时响应潜在的问题,确保用户数据和订单信息的实时处理和分析。
4. 合理的配置和容量规划
合理的配置和容量规划是确保 Kafka 集群高效、稳定运行的关键。以下是具体的实现步骤和一些 Java 示例,以及结合实际业务场景的详细解释:
4.1 合理的配置和容量规划的实现步骤:
需求分析:分析业务数据的类型、大小、生产和消费速率,以及数据保留策略。
确定硬件规格:根据需求分析的结果,确定所需的硬件规格,包括 CPU、内存、存储和网络带宽。
集群设计:设计 Kafka 集群的拓扑结构,包括 broker 数量、主题数量、分区数量和副本因子。
配置参数调优:根据业务需求调整 Kafka 配置参数,如 message.max.bytes、replica.fetch.max.bytes、log.flush.interval.messages 等。
性能测试:在测试环境中对 Kafka 集群进行性能测试,验证配置和容量是否满足业务需求。
监控和评估:在生产环境中监控 Kafka 集群的性能,定期评估并根据实际运行情况进行调整。
扩展策略:制定集群扩展策略,以便在业务增长时能够水平扩展集群。
4.2 Java 示例:
在 Java 应用程序中,合理配置 Kafka 生产者和消费者是确保高效处理消息的关键。以下是一个简单的 Java 示例,展示了如何配置生产者和消费者:
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Collections;
import java.util.Properties;
public class KafkaConfigurationExample {
public static void main(String[] args) {
// 生产者配置
Properties producerProps = new Properties();
producerProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
producerProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
producerProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
producerProps.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384); // 批量大小
producerProps.put(ProducerConfig.LINGER_MS_CONFIG, 5); // 延迟时间
producerProps.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432); // 缓冲区内存
// 创建生产者实例
KafkaProducer<String, String> producer = new KafkaProducer<>(producerProps);
// 消费者配置
Properties consumerProps = new Properties();
consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "example-consumer-group");
consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); // 偏移量重置策略
consumerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); // 关闭自动提交偏移量
// 创建消费者实例
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(consumerProps);
// 订阅主题
consumer.subscribe(Collections.singletonList("exampleTopic"));
// 发送消息示例
producer.send(new ProducerRecord<>("exampleTopic", "key", "value"));
// 接收消息示例
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
}
// 关闭生产者和消费者
producer.close();
consumer.close();
}
}
4.3 实际业务场景解释:
假设你负责一个实时数据流分析平台,该平台使用 Kafka 来收集和处理用户行为数据。
需求分析:分析用户行为数据的生成速率、数据大小和保留时间。
确定硬件规格:根据数据量和处理速率,确定 Kafka 集群所需的服务器规格。
集群设计:设计 Kafka 集群,包括 broker 数量、主题和分区的划分,以支持高吞吐量和并行处理。
配置参数调优:调整生产者和消费者的配置参数,如批量大小、延迟时间、缓冲区内存等,以优化消息的发送和接收效率。
性能测试:在测试环境中对 Kafka 集群进行压力测试,确保配置满足高负载情况下的性能要求。
监控和评估:在生产环境中监控 Kafka 集群的性能指标,如消息延迟、吞吐量和系统资源使用情况,并根据监控结果进行调整。
扩展策略:制定集群扩展计划,以便在用户量增长或数据量增加时,能够及时扩展集群以保持性能。
通过这种方式,实时数据流分析平台可以高效地处理大量用户行为数据,确保数据的实时分析和业务决策的准确性。
5. 设置生产者的 acks 参数为 "all"
设置生产者的 acks 参数为 "all" 确保了 Kafka 生产者在所有同步副本(ISR,In-Sync Replicas)都确认接收到消息之后才认为消息发送成功。这是实现零数据丢失的关键配置之一。以下是设置 acks 参数为 "all" 的具体实现步骤和 Java 示例,以及结合实际业务场景的详细解释:
5.1 设置 acks 参数为 "all" 的实现步骤:
理解业务需求:评估业务对数据一致性的要求,确定是否需要设置 acks 为 "all"。
修改生产者配置:在生产者配置中设置 acks 参数为 "all"。
调整副本因子:确保主题的副本因子大于 1,以便有多个副本可以接收消息。
配置重试机制:设置生产者的重试策略,以便在发送失败时能够自动重试。
监控发送结果:监控生产者的消息发送情况,确保消息成功发送到所有副本。
测试配置:在测试环境中验证 acks 参数的设置是否按预期工作。
部署到生产环境:在生产环境中部署更改,并持续监控其效果。
5.2 Java 示例:
以下是一个 Java 示例,展示了如何配置 Kafka 生产者以设置 acks 参数为 "all":
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class AllAcksProducerExample {
public static void main(String[] args) {
// 设置生产者配置
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.ACKS_CONFIG, "all"); // 设置 acks 为 "all"
props.put(ProducerConfig.RETRIES_CONFIG, 5); // 设置重试次数
props.put(ProducerConfig.RETRY_BACKOFF_MS_CONFIG, 100); // 设置重试间隔
// 创建 Kafka 生产者实例
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// 创建消息
String topicName = "criticalDataTopic";
String message = "Critical Data Message";
// 发送消息
try {
producer.send(new ProducerRecord<>(topicName, message),
(metadata, exception) -> {
if (exception != null) {
exception.printStackTrace();
} else {
System.out.println("Message sent with offset: " + metadata.offset());
}
});
} catch (Exception e) {
e.printStackTrace();
}
// 关闭生产者
producer.close();
}
}
5.3 实际业务场景解释:
假设你正在开发一个金融服务应用,该应用需要确保所有交易记录都准确无误地记录在日志系统中。
理解业务需求:由于交易数据的重要性,任何数据丢失都可能导致严重的后果,因此需要设置 acks 为 "all"。
修改生产者配置:在生产者的配置文件或代码中设置 acks 参数为 "all",确保所有副本都确认接收到消息。
调整副本因子:在创建 Kafka 主题时,设置适当的副本因子,例如 3,以确保高可用性和数据冗余。
配置重试机制:设置生产者的重试策略,以便在遇到暂时性错误时能够自动重试发送消息。
监控发送结果:通过回调函数或监控系统监控消息的发送情况,确保每条消息都成功发送。
测试配置:在测试环境中模拟不同的故障情况,验证 acks 参数的设置是否能够保证数据不丢失。
部署到生产环境:在确认配置无误后,将更改部署到生产环境,并持续监控其效果。
通过这种方式,金融服务应用可以确保交易数据的完整性和一致性,降低数据丢失的风险。
6. 调整日志存储空间和最大消息大小
第6点提到的调整日志存储空间和最大消息大小是 Kafka 性能调优的重要组成部分。以下是具体的实现步骤和 Java 示例,以及结合实际业务场景的详细解释:
6.1 调整日志存储空间和最大消息大小的实现步骤:
评估存储需求:根据业务数据量和增长趋势,评估所需的存储空间。
配置日志存储路径:在 Kafka 配置文件中设置 log.dirs 属性,指定日志文件的存储路径。
调整日志段大小:设置 log.segment.bytes 属性,控制日志段文件的最大大小。
设置最大消息大小:调整 message.max.bytes 和 replica.fetch.max.bytes 属性,以适应最大消息大小的需求。
配置日志保留策略:根据数据保留需求,配置 log.retention.hours、log.retention.bytes 和 log.retention.check.interval.ms。
监控磁盘使用情况:监控 Kafka 集群的磁盘使用情况,确保有足够的存储空间。
测试配置:在测试环境中测试新的配置,确保它们满足业务需求。
部署和监控:在生产环境中部署配置更改,并持续监控其效果。
6.2 Java 示例:
Java 示例主要涉及生产者和消费者配置的调整,因为 Kafka 的日志存储配置是在 broker 的配置文件中设置的,而不是通过 Java 代码。
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class KafkaProducerWithMaxMessageSizeExample {
public static void main(String[] args) {
// 设置生产者配置
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, 10485760); // 最大请求大小,例如 10MB
// 创建 Kafka 生产者实例
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// 创建消息
String topicName = "largeMessageTopic";
String largeMessage = "A very large message that may exceed the default max message size.";
// 发送消息
try {
producer.send(new ProducerRecord<>(topicName, largeMessage));
} catch (Exception e) {
e.printStackTrace();
}
// 关闭生产者
producer.close();
}
}
6.3 实际业务场景解释:
假设你负责一个 IoT 平台,该平台收集来自传感器设备的大量数据。
评估存储需求:根据传感器数据的生成速率和数据保留策略,评估所需的存储空间。
配置日志存储路径:在 Kafka 配置文件中设置多个 log.dirs 路径,以分散存储负载并提高可用性。
调整日志段大小:根据业务需求和存储设备的性能,设置合适的 log.segment.bytes 值。
设置最大消息大小:考虑到 IoT 设备可能发送较大的数据包,调整 message.max.bytes 和 replica.fetch.max.bytes,以确保这些大消息可以被处理。
配置日志保留策略:根据数据的重要性和合规性要求,配置日志的保留时间和大小。
监控磁盘使用情况:使用监控工具监控 Kafka 集群的磁盘使用情况,确保及时扩展存储资源。
测试配置:在测试环境中模拟 IoT 设备的数据发送,验证配置是否满足需求。
部署和监控:在生产环境中部署配置更改,并持续监控 Kafka 集群的性能和存储使用情况。
通过这种方式,IoT 平台可以有效地处理来自传感器设备的大量数据,同时确保数据的可靠性和系统的稳定性。
7. 使用压缩
使用压缩是 Kafka 中减少网络传输量和存储需求的有效手段,尤其适用于消息体较大或者消息产生频率很高的场景。以下是使用压缩的具体实现步骤和 Java 示例,以及结合实际业务场景的详细解释:
7.1 使用压缩的实现步骤:
选择压缩算法:Kafka 支持多种压缩算法,如 GZIP、Snappy、LZ4 和 Zstd。根据业务数据的特点选择合适的压缩算法。
配置生产者压缩:在生产者配置中设置 compression.type 属性,以启用压缩。
调整批处理参数:优化 batch.size 和 linger.ms 参数,以实现更有效的批处理和压缩。
配置消费者解压缩:确保消费者能够正确处理压缩的消息。
监控压缩效果:监控压缩后的消息大小和系统性能,评估压缩效果。
测试配置:在测试环境中测试压缩配置,确保它满足业务需求并且不引入新的问题。
部署和监控:在生产环境中部署压缩配置,并持续监控其效果。
7.2 Java 示例:
以下是一个 Java 示例,展示了如何配置 Kafka 生产者以使用 GZIP 压缩:
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class KafkaProducerWithCompressionExample {
public static void main(String[] args) {
// 设置生产者配置
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "gzip"); // 启用 GZIP 压缩
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384); // 批处理大小
props.put(ProducerConfig.LINGER_MS_CONFIG, 5); // 延迟时间,以便进行批处理
// 创建 Kafka 生产者实例
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// 创建消息
String topicName = "compressedDataTopic";
String message = "This is a message that will be compressed.";
// 发送消息
try {
producer.send(new ProducerRecord<>(topicName, message));
} catch (Exception e) {
e.printStackTrace();
}
// 关闭生产者
producer.close();
}
}
7.3 实际业务场景解释:
假设你负责一个日志收集系统,该系统从多个服务实例收集日志数据。
选择压缩算法:考虑到日志数据通常具有高度冗余性,选择 GZIP 压缩算法,因为它提供了良好的压缩率。
配置生产者压缩:在生产者配置中启用 GZIP 压缩,减少发送到 Kafka 的数据量。
调整批处理参数:通过调整批处理大小和延迟时间,优化消息的压缩效率。
配置消费者解压缩:由于 Kafka 消费者会自动处理压缩的消息,确保消费者配置正确,无需额外设置。
监控压缩效果:监控压缩后的消息大小,评估存储节省和网络传输效率。
测试配置:在测试环境中模拟日志收集过程,验证压缩配置的有效性。
部署和监控:在生产环境中部署压缩配置,并持续监控系统性能和压缩效果。
通过这种方式,日志收集系统可以有效地减少网络传输量和存储需求,同时保持数据的完整性和可读性。
8. 关闭自动提交 offset
关闭自动提交 offset 是 Kafka 消费者的一个重要配置,它允许消费者在完全处理完消息之后才手动提交 offset,从而避免在消息处理过程中发生故障导致消息丢失。以下是关闭自动提交 offset 的具体实现步骤和 Java 示例,以及结合实际业务场景的详细解释:
8.1 关闭自动提交 offset 的实现步骤:
理解业务需求:评估业务对消息处理的可靠性要求,确定是否需要关闭自动提交 offset。
修改消费者配置:在消费者配置中设置 enable.auto.commit 为 false,以禁用自动提交 offset。
处理消息:编写消息处理逻辑,确保每条消息都被完全处理。
手动提交 offset:在消息处理完成后,手动提交该消息的 offset。
异常处理:在消息处理过程中捕获异常,并根据业务需求决定是否回滚已提交的 offset。
测试配置:在测试环境中测试新的配置,确保消息处理和 offset 提交的逻辑正确。
部署到生产环境:在生产环境中部署更改,并持续监控其效果。
8.2 Java 示例:
以下是一个 Java 示例,展示了如何配置 Kafka 消费者以关闭自动提交 offset 并手动提交:
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.util.Collections;
import java.util.Properties;
public class KafkaConsumerManualOffsetCommitExample {
public static void main(String[] args) {
// 设置消费者配置
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "manual-offset-commit-group");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); // 关闭自动提交 offset
// 创建 Kafka 消费者实例
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
// 订阅主题
consumer.subscribe(Collections.singletonList("exampleTopic"));
while (true) {
// 接收消息
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
try {
// 处理消息
System.out.printf("Received message: key=%s, value=%s, offset=%d%n",
record.key(), record.value(), record.offset());
// 消息处理逻辑(根据业务需求编写)
// 手动提交 offset
consumer.commitSync(Collections.singletonMap(record, new OffsetAndMetadata(record.offset() + 1)));
} catch (Exception e) {
// 异常处理逻辑(根据业务需求编写)
// 可以选择手动提交 offset 到错误发生前的值,或者进行其他错误恢复操作
}
}
}
}
}
8.3 实际业务场景解释:
假设你正在开发一个订单处理系统,该系统需要从 Kafka 主题中消费订单消息并进行处理。
理解业务需求:订单处理系统要求消息处理的高可靠性,不能容忍消息丢失。
修改消费者配置:在消费者的配置中设置 enable.auto.commit 为 false,禁用自动提交 offset。
处理消息:编写订单处理逻辑,确保每条订单消息都被完全处理。
手动提交 offset:在订单处理完成后,手动提交该订单消息的 offset,确保消息不会被重复处理。
异常处理:在处理过程中捕获异常,根据异常类型和业务需求决定是否回滚 offset 或采取其他恢复措施。
测试配置:在测试环境中模拟订单消息的处理过程,验证消息处理和 offset 提交的逻辑正确。
部署到生产环境:在确认配置无误后,将更改部署到生产环境,并持续监控订单处理系统的性能和可靠性。
通过这种方式,订单处理系统可以确保每条订单消息都被可靠地处理,即使在发生故障的情况下也不会丢失消息。
9. 确保 broker 配置正确
第9点提到的确保 broker 配置正确是 Kafka 集群稳定性和性能的关键。以下是确保 broker 配置正确的具体实现步骤和一些概念性 Java 示例,以及结合实际业务场景的详细解释:
9.1 确保 broker 配置正确的实现步骤:
审核当前配置:检查现有的 Kafka broker 配置,了解每个参数的当前值和作用。
规划配置调整:根据业务需求和集群性能,规划需要调整的配置项。
修改 broker 配置:在 Kafka 的配置文件中(通常是 server.properties),修改相应的配置项。
监控配置影响:在更改配置后,监控 broker 的性能和资源使用情况,确保配置更改没有负面影响。
测试配置:在测试环境中测试新的配置,确保它们满足业务需求并且不引入新的问题。
滚动更新:如果集群中有多个 broker,采用滚动更新的方式应用配置更改,以避免集群中断。
文档化配置:更新系统文档,记录配置更改的详细信息和原因。
部署和监控:在生产环境中部署配置更改,并持续监控其效果。
9.2 Kafka broker 配置示例:
以下是一些常见的 Kafka broker 配置项及其说明:
- num.partitions: 默认分区数,适用于新创建的主题。
- log.retention.hours: 日志文件的保留时间。
- log.retention.bytes: 每个主题日志文件的最大大小。
- message.max.bytes: broker 能接收的最大消息大小。
- replica.fetch.max.bytes: 副本之间同步的最大消息大小。
注意:Java 代码本身不用于直接修改 broker 配置,broker 配置是在 Kafka 服务器的配置文件中设置的。以下是一个概念性的 Java 示例,展示如何使用 Java 代码连接到具有特定配置的 Kafka 集群:
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class KafkaProducerExample {
public static void main(String[] args) {
// 设置生产者配置
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); // 连接到 Kafka 集群
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
// 可以设置其他生产者特定的配置
// 创建 Kafka 生产者实例
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// 使用生产者发送消息...
}
}
9.3 实际业务场景解释:
假设你管理着一个处理大量日志数据的 Kafka 集群。
审核当前配置:检查 broker 的 message.max.bytes 和 replica.fetch.max.bytes 配置,确保它们足够大以处理大型日志消息。
规划配置调整:根据日志数据的大小和集群的性能,决定是否需要增加这些配置的值。
修改 broker 配置:在 server.properties 文件中调整 message.max.bytes 和 replica.fetch.max.bytes。
监控配置影响:更改配置后,监控 broker 的性能,确保没有因为配置更改而出现处理瓶颈或资源问题。
测试配置:在测试环境中模拟日志数据的生成和处理,验证新的 broker 配置是否有效。
滚动更新:逐个重启 broker 实例以应用配置更改,监控每个 broker 重启后的状态。
文档化配置:记录配置更改的详细信息,包括更改的配置项、更改的值和更改的原因。
部署和监控:在生产环境中部署配置更改,并持续监控集群的性能和稳定性。
通过这种方式,你可以确保 Kafka 集群的 broker 配置正确,能够高效、稳定地处理大量日志数据。
10. 使用同步复制
第10点提到的使用同步复制(也称为同步提交或同步副本提交)是 Kafka 提供的一个功能,用于确保消息在提交给消费者之前已经被所有同步副本(ISR)确认。这可以提高数据的耐久性,但可能会影响吞吐量。以下是使用同步复制的具体实现步骤和概念性 Java 示例,以及结合实际业务场景的详细解释:
10.1 使用同步复制的实现步骤:
评估业务需求:确定业务是否需要高数据耐久性,以及是否可以接受同步复制可能带来的性能影响。
配置 unclean.leader.election.enable:设置 unclean.leader.election.enable 为 false 以防止非同步副本成为 leader。
配置 min.insync.replicas:设置 min.insync.replicas 属性,定义消息需要被多少个同步副本确认。
配置生产者:在生产者配置中设置 acks 为 all,确保消息被所有同步副本确认。
监控集群性能:在应用同步复制配置后,监控集群的性能和吞吐量,确保它们符合业务要求。
测试配置:在测试环境中测试同步复制配置,确保它按预期工作并且没有引入新的问题。
部署到生产环境:在生产环境中部署更改,并持续监控其效果。
10.2 Kafka 配置示例:
同步复制的配置主要在 Kafka 服务器的配置文件中(通常是 server.properties)进行设置。以下是一些相关的配置项:
- unclean.leader.election.enable=false:禁用非同步副本的 leader 选举。
- min.insync.replicas=2:设置至少需要 2 个同步副本来确认消息。
注意:Java 代码本身不用于直接修改 Kafka 集群的同步复制配置,这些配置是在 Kafka 服务器的配置文件中设置的。
10.3 Java 示例:
以下是一个 Java 示例,展示了如何配置 Kafka 生产者以使用同步复制:
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class KafkaProducerWithSyncReplicationExample {
public static void main(String[] args) {
// 设置生产者配置
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.ACKS_CONFIG, "all"); // 确保所有同步副本确认消息
// 可以设置其他生产者特定的配置
// 创建 Kafka 生产者实例
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// 创建消息
String topicName = "syncReplicationTopic";
String message = "Message that requires synchronous replication.";
// 发送消息
producer.send(new ProducerRecord<>(topicName, message),
(metadata, exception) -> {
if (exception != null) {
exception.printStackTrace();
} else {
System.out.println("Message sent with offset: " + metadata.offset());
}
});
// 关闭生产者
producer.close();
}
}
10.4 实际业务场景解释:
假设你负责一个金融服务应用,该应用需要确保交易数据的高耐久性。
评估业务需求:由于金融服务对数据的准确性和耐久性要求极高,决定使用同步复制。
配置 unclean.leader.election.enable 和 min.insync.replicas:在 Kafka 集群的配置文件中设置这些参数,以确保消息的同步复制。
配置生产者:在生产者的配置中设置 acks=all,确保消息在提交给消费者之前已经被所有同步副本确认。
监控集群性能:在应用同步复制配置后,监控集群的性能,确保同步复制没有导致不可接受的延迟或吞吐量下降。
测试配置:在测试环境中模拟交易数据的发送和处理,验证同步复制配置的有效性。
部署到生产环境:在确认配置无误后,将更改部署到生产环境,并持续监控金融服务应用的性能和数据耐久性。
通过这种方式,金融服务应用可以确保交易数据的高耐久性,减少数据丢失的风险,即使在发生故障的情况下也能保证数据的完整性。
11. 调整 flush 间隔
第11点提到的调整 flush 间隔是指设置 Kafka 生产者和消费者在内存中缓存数据后,多久将数据刷新(flush)到磁盘的时间间隔或消息数量间隔。这可以通过 log.flush.interval.messages 和 log.flush.interval.ms 配置项来实现。以下是调整 flush 间隔的具体实现步骤和 Java 示例,以及结合实际业务场景的详细解释:
11.1 调整 flush 间隔的实现步骤:
评估业务需求:根据业务对数据持久性和延迟的要求,评估是否需要调整 flush 间隔。
配置生产者 flush 间隔:在生产者配置中设置 log.flush.interval.messages 和 log.flush.interval.ms。
配置消费者 fetch 间隔:在消费者配置中,通过 max.poll.interval.ms 配置消费者在两次轮询之间的最大时间间隔。
监控性能影响:在更改配置后,监控生产者和消费者性能,确保 flush 间隔的调整没有负面影响。
测试配置:在测试环境中测试新的配置,确保它们满足业务需求并且不引入新的问题。
部署到生产环境:在生产环境中部署更改,并持续监控其效果。
优化和调整:根据监控结果和业务发展,持续优化和调整 flush 间隔配置。
11.2 Java 示例:
Java 示例主要涉及生产者和消费者配置的调整,因为 Kafka 的 flush 间隔配置是在 broker 的配置文件中设置的,而不是通过 Java 代码。
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class KafkaProducerFlushIntervalExample {
public static void main(String[] args) {
// 设置生产者配置
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
// 可以设置其他生产者特定的配置
// 创建 Kafka 生产者实例
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// 创建消息
String topicName = "flushIntervalTopic";
String message = "Message with adjusted flush interval.";
// 发送消息
producer.send(new ProducerRecord<>(topicName, message));
// 关闭生产者
producer.close();
}
}
11.3 实际业务场景解释:
假设你负责一个需要高吞吐量和低延迟的实时数据分析平台。
评估业务需求:由于业务需要快速处理大量数据,同时保证数据的持久性,决定调整 flush 间隔。
配置生产者 flush 间隔:在 Kafka broker 的配置文件中设置 log.flush.interval.messages 和 log.flush.interval.ms,例如,可以设置为每 10000 条消息或每 1000 毫秒 flush 一次。
配置消费者 fetch 间隔:在消费者配置中,根据消费者的处理能力,设置合适的 max.poll.interval.ms。
监控性能影响:在应用新配置后,监控生产者和消费者性能,确保 flush 间隔的调整没有导致延迟增加或吞吐量下降。
测试配置:在测试环境中模拟实时数据分析流程,验证 flush 间隔配置的有效性。
部署到生产环境:在确认配置无误后,将更改部署到生产环境,并持续监控平台的性能。
优化和调整:根据监控数据和业务发展,持续优化 flush 间隔配置,以满足不断变化的业务需求。
通过这种方式,实时数据分析平台可以在保证数据持久性的同时,实现高吞吐量和低延迟的消息处理。
12. 避免使用 unclean leader 选举
第12点提到的避免使用 unclean leader 选举是确保 Kafka 数据不丢失的一种策略。Unclean leader 选举指的是在某些副本(follower)还没有完全同步数据的情况下,这些副本被选举为 leader。这可能导致数据丢失,因为这些未同步的数据不会被提交给客户端。以下是避免使用 unclean leader 选举的具体实现步骤和概念性 Java 示例,以及结合实际业务场景的详细解释:
12.1 避免使用 unclean leader 选举的实现步骤:
理解 ISR 机制:了解 Kafka 的 In-Sync Replicas (ISR) 机制,确保 leader 选举只在同步副本中进行。
配置 unclean.leader.election.enable:设置 unclean.leader.election.enable 参数为 false,以禁止未同步副本成为 leader。
配置 min.insync.replicas:设置 min.insync.replicas 参数,确保至少有指定数量的副本需要与 leader 保持同步。
监控副本同步状态:监控 Kafka 集群的副本同步状态,确保没有副本落后。
测试配置:在测试环境中测试这些配置,确保它们按预期工作并且不引入新的问题。
部署到生产环境:在生产环境中部署更改,并持续监控其效果。
处理异常情况:在监控过程中,如果发现副本同步延迟或故障,及时处理以避免 unclean leader 选举。
12.2 Kafka broker 配置示例:
以下是一些相关的 Kafka broker 配置项:
- unclean.leader.election.enable=false:禁用 unclean leader 选举。
- min.insync.replicas=2:设置至少需要 2 个同步副本才能进行 leader 选举。
这些配置是在 Kafka 服务器的配置文件中(通常是 server.properties)进行设置的。
12.3 Java 示例:
Java 示例主要涉及生产者和消费者配置的使用,因为避免 unclean leader 选举的配置是在 Kafka 服务器端进行的。以下是一个 Java 示例,展示如何配置 Kafka 生产者以确保生产者不会触发 unclean leader 选举:
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class KafkaProducerAvoidUncleanLeaderExample {
public static void main(String[] args) {
// 设置生产者配置
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.ACKS_CONFIG, "all"); // 确保所有同步副本确认消息
// 创建 Kafka 生产者实例
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// 创建消息
String topicName = "avoidUncleanLeaderTopic";
String message = "Message requiring clean leader election.";
// 发送消息
producer.send(new ProducerRecord<>(topicName, message));
// 关闭生产者
producer.close();
}
}
12.4 实际业务场景解释:
假设你负责一个电子商务平台的订单处理系统,该系统依赖 Kafka 来确保订单数据的准确性和完整性。
理解 ISR 机制:了解 Kafka 的副本同步机制,确保订单数据在多个副本间同步。
配置 unclean.leader.election.enable 和 min.insync.replicas:在 Kafka 集群配置中设置这些参数,以避免数据丢失。
监控副本同步状态:定期检查副本的同步状态,确保没有副本落后,及时处理同步问题。
测试配置:在测试环境中模拟订单处理流程,确保配置正确,没有 unclean leader 选举发生。
部署到生产环境:将配置部署到生产环境,并监控订单处理系统的性能和数据完整性。
处理异常情况:如果监控到副本同步延迟或故障,及时采取措施,比如增加副本数量或优化网络条件。
通过这种方式,电子商务平台的订单处理系统可以确保订单数据的高可靠性,避免因 unclean leader 选举导致的数据丢失问题。
最后
以上这些策略对于解决 kafka 数据丢失问题很有帮助,如果你正在使用 kafka,或者正在学习 kafka,V 哥觉得你都应该把这12种策略收藏起来并消化掉,这对你在大型项目应用中非常有用。欢迎关注威哥爱编程,一起向技术大神进发。