当前位置: 首页 > news >正文

Kafka集群调优+能力探底

一、前言

我们需要对4个规格的kafka能力进行探底,即其可以承载的最大吞吐;4个规格对应的单节点的配置如下:

  • 标准版: 2C4G

  • 铂金版: 4C8G

  • 专业版: 8C16G

  • 企业版: 16C32G

另外,一般来讲,在同配置下,kafka的读性能是要优于写性能的,写操作时,数据要从网卡拷贝至堆内存,然后进行一堆数据校验、解析后,会将数据拷贝至堆外内存,然后再拷贝至操作系统的page cache,最后操作系统异步刷盘至设备中。而读操作时,kafka使用了零拷贝技术,数据会从disk或page cache直接拷贝到网卡,节省了大量的内存拷贝。因此我们这次探底将聚焦于链路的短板,即kafka的写操作进行压测

注:本文不是专业的压测报告,而是针对不同集群调优,以获取其最大的吞吐能力

二、磁盘能力探底

在真正开始对kafka压测前,我们首先对磁盘的能力进行一个摸底。因为kafka是典型的数据型应用,是强依赖磁盘性能的,一旦有了这个数据,那么这个就是kafka的性能天花板。如果磁盘是传统的机械磁盘,那么瓶颈毫无悬念一般都会落在磁盘上;但如果磁盘类型是SSD,而且性能很高的话,操作系统会极力压榨cpu,从而获取一个最大刷盘吞吐,因此瓶颈在哪就很难讲了

要测试磁盘吞吐的话,2个变参的影响较大:

  1. 单次写入磁盘的数据量

  1. 写盘的线程数

2.1、单次刷盘大小

现在的硬件厂商,对于磁盘的优化,基本上都是4K对齐的,因此我们的参数一般也要设置为4K的整数倍,例如4K\8K\16K... 如果单次写入量小于4K,例如只写了10byte,那么底层刷盘的时候,也会刷4K的量,这就是臭名昭著的写放大

而具体单次写多少数据量能达到最优呢? 这就需要不断的benchmark了

2.2、刷盘线程数

我们知道kafka的broker通过参数num.io.threads来控制io的线程数量,通常是cpu * 2,不过这个参数并不能真实反应在同一时刻写盘的线程数,因此我们探底的时候,也需要动态修改这个参数,从而获取磁盘真实的吞吐

2.3、探底工具

public class DiskMain2 {private static final long EXE_KEEP_TIME = 30 * 1000;public static void main(String[] args) throws Exception {new DiskMain2().begin(args);}private void begin(String[] args) throws Exception {AtomicLong totalLen = new AtomicLong();long begin = System.currentTimeMillis();int threadNum = args.length > 0 ? Integer.parseInt(args[0]) : 4;int msgK = args.length > 1 ? Integer.parseInt(args[1]) : 16;int size = msgK * 1024;List<Thread> threadList = new ArrayList<>();for (int j = 0; j < threadNum; j++) {Thread thread = new Thread(() -> {try {File file = new File("/bitnami/kafka/" + UUID.randomUUID() + ".txt");file.createNewFile();FileChannel channel = FileChannel.open(Paths.get(file.getPath()), StandardOpenOption.WRITE);ByteBuffer byteBuffer = ByteBuffer.allocateDirect(size);for (int i = 0; ; i++) {byteBuffer.clear();byteBuffer.position(size);byteBuffer.flip();channel.write(byteBuffer);if (i % 100 == 0) {long cost = System.currentTimeMillis() - begin;if (cost > EXE_KEEP_TIME) {break;}}}channel.force(false);totalLen.addAndGet(file.length());file.delete();} catch (IOException e) {throw new RuntimeException(e);}});thread.start();threadList.add(thread);}for (Thread thread : threadList) {thread.join();}long cost = (System.currentTimeMillis() - begin) / 1000;System.out.println((totalLen.get() / 1024 / 1024) + " MB");System.out.println(cost + " sec");System.out.println(totalLen.get() / cost / 1024 / 1024 + " MB/sec");}
}

简单描述下这个工具做的事儿:启动M(可配)个线程,每个线程单次往磁盘中写入N(可配)K的数据,整个过程持续30秒,然后统计所有写入文件的总大小,最后除以时间,从而计算磁盘吞吐

几个注意点:

  • 大块的磁盘写入,一定要使用FileChannel,与kafka中的log写入对齐

  • 尽量减少cpu的使用,将压力下放给磁盘,demo中使用的ByteBuffer,通过修改position的值模拟大块数据

  • 使用DirectByteBuffer,减少一次堆内存 --> 对外内存的拷贝

2C4G

[root@jmc-pod kafka]# java DiskMain 2 4 802 MB/sec

[root@jmc-pod kafka]# java DiskMain 2 8 1016 MB/sec

[root@jmc-pod kafka]# java DiskMain 2 16 1105 MB/sec

[root@jmc-pod kafka]# java DiskMain 2 32 942 MB/sec

[root@jmc-pod kafka]# java DiskMain 4 4 1013 MB/sec

[root@jmc-pod kafka]# java DiskMain 4 8 941 MB/sec

[root@jmc-pod kafka]# java DiskMain 4 16 1062 MB/sec

[root@jmc-pod kafka]# java DiskMain 4 32 1076 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 4 916 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 8 993 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 16 1035 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 32 982 MB/sec

4C8G

[root@jmc-pod kafka]# java DiskMain 2 16 1320 MB/sec

[root@jmc-pod kafka]# java DiskMain 4 4 2172 MB/sec

[root@jmc-pod kafka]# java DiskMain 4 8 2317 MB/sec

[root@jmc-pod kafka]# java DiskMain 4 16 2580 MB/sec

[root@jmc-pod kafka]# java DiskMain 4 32 2271 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 4 2150 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 8 2315 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 16 2498 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 32 2536 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 64 2434 MB/sec

8C16G

[root@jmc-pod kafka]# java DiskMain 4 16 2732 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 4 3440 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 8 3443 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 16 3531 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 32 3561 MB/sec

[root@jmc-pod kafka]# java DiskMain 8 64 3562 MB/sec

[root@jmc-pod kafka]# java DiskMain 16 4 3515 MB/sec

[root@jmc-pod kafka]# java DiskMain 16 8 3573 MB/sec

[root@jmc-pod kafka]# java DiskMain 16 16 3659 MB/sec

[root@jmc-pod kafka]# java DiskMain 16 32 3673 MB/sec

[root@jmc-pod kafka]# java DiskMain 16 64 3674 MB/sec

[root@jmc-pod kafka]# java DiskMain 12 16 3672 MB/sec

16C32G

[root@jmc-pod kafka]# java DiskMain 16 16 3918 MB/sec

[root@jmc-pod kafka]# java DiskMain 16 8 3814 MB/sec

[root@jmc-pod kafka]# java DiskMain 16 32 3885 MB/sec

[root@jmc-pod kafka]# java DiskMain 16 64 3894 MB/sec

[root@jmc-pod kafka]# java DiskMain 24 8 4053 MB/sec

[root@jmc-pod kafka]# java DiskMain 24 16 4039 MB/sec

[root@jmc-pod kafka]# java DiskMain 24 32 4080 MB/sec

[root@jmc-pod kafka]# java DiskMain 24 64 4050 MB/sec

[root@jmc-pod kafka]# java DiskMain 32 16 4042 MB/sec

[root@jmc-pod kafka]# java DiskMain 32 32 4078 MB/sec

通过反复压测,得出如下结论:

在kafka 3副本的经典协议下,上述表格便是其吞吐量的天花板。其中16C32G在算力上虽然比8C16G强了1倍,但其落盘速度却基本持平,在大数据的压力下,瓶颈终究会落在磁盘,因此可以大胆预测,其性能不会比8C16G高出太多

分析上述数据可知:

  • 2C4G:磁盘的吞吐量约1G/s,远没有达到上限,此时的瓶颈在cpu

  • 4C8G:吞吐量虽上升了一倍不止,不过cpu飚满,瓶颈还在cpu

  • 8C16G:吞吐量约为3.5G/s,相比较4C8G并没有翻倍,后端的cpu几乎吃满,光看这组数据不好定位瓶颈

  • 16C32G:终于探到磁盘的底了,在cpu还有大量剩余的前提下,磁盘明显写不动了

磁盘的性能是真好,居然能压出 4 GB/s的速率,叹叹

三、Kafka吞吐量概述

一般描述某个kafka集群的吞吐量时,通常写为 3*n MB/s,例如 3*100 MB/s。 之所以习惯这样描述,是基于kafka自身的3副本协议,即1主2备的模式,leader收到业务流量n后,2个follower还需要从leader将数据同步过来,这样在集群角度看来,是一共处理了3*n流量

某个topic所拥有的副本数,理论上是不能大于整个集群的broker数量的,因为副本本质上是做高可用的,当topic的副本数大于整个集群的broker数量后,那势必某个broker存在2个及以上副本,这样也就丧失了高可用的初衷

3.1、集群横向扩容

所谓集群横向扩容,是指为集群添加同构broker,集群的broker数量初始为3,扩容后可能变为了6,这里的broker数量与topic的replica不是同一个概念,注意区分。

某个topic副本数过多,将带来集群内部大量的数据流转,而副本数过少,例如单副本,又存在一些高可用的风险,因此即便随着broker数量的增多,kafka最佳实践还是建议将topic的副本数设置为3,这样每增加3个broker,集群的能力将会得到横向的扩容

这里的横向扩容出来的能力跟broker数量是严格呈线性关系的,本文不会对横向扩容进行压测对比

3.2、集群纵向扩容

纵向扩容是指集群的broker数量不变,但是提升broker的配置。例如之前集群是3 * 2C4G的规格,进行纵向扩容后,集群将变为3 * 4C8G

broker的配置线性提升了,其提供的吞吐能力也会随着线性提升吗? 答案是否定的;如果磁盘用的是机械磁盘,我们可能很快能够断言瓶颈将卡在disk上,但SSD的高吞吐也是非常吃cpu的,内容比较复杂,内存、磁盘、cpu等都息息相关,这里没有很好的捷径,只能benchmark

纵向压测、对比也是本文的重心

四、发压准备

4.1、客户端准备

4.1.1、发压程序

发压程序使用官方的工具kafka-producer-perf-test.sh,这个工具实际调用的是kafka内核中的类:

org.apache.kafka.tools.ProducerPerformance

当然,单个Producer的pool、开辟内存、与server端的连接等都是有上限的,因此真正发压时,需要启动多个发压进程。发压脚本如下

bash kafka-producer-perf-test.sh \
--producer-props \bootstrap.servers=10.0.0.10:9094,10.0.0.11:9094,10.0.0.12:9094 \acks=1  \buffer.memory=134217728 \
--producer.config=admin5.conf \
--topic topic_6 \
--throughput=-1  \
--num-records 100000000 \
--record-size 1024000

admin5.conf 配置如下(因为开启了ACL认证,因此需要申明SASL配置)

security.protocol=SASL_PLAINTEXT
sasl.mechanism=SCRAM-SHA-512
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="kafka-a9asfbx5pl" password="a9asfbxmsn";

关于发压脚本的参数配置做一下说明

  • bootstrap.servers 连接集群的接入点

  • acks 响应方式,这个对性能影响非常大,这里使用默认的1。有3种配置

  • 1 : leader收到消息后便返回成功

  • 0 : 不需要等待任何副本确认

  • -1 : 生产者将等待所有的副本接收到消息并进行确认

  • buffer.memory 这里是设置了producer的128M的缓冲区,默认为32M

  • producer.config 因为目标集群开启了acl,这里需要存放一些相关配置

  • throughput 发送流量的一个上限值,-1表示不设置上限

  • num-records 发送消息的条数,因为要压测,所以这里放一个大值

  • record-size 每条消息的大小。这里配置的1M,因为需要压测集群的极限值,这里直接设置一个相对大的值

  • 注意,如果消息大小配置较小的话,可以通过调整batch.size及linger.ms参数来控制攒批

4.1.2、发压机器

因为kafka实例是被k8s孵化出来的,因此独立开辟了 5台ECS发压,其配置

因为发压程序不会占用大量cpu及内存,当开启多进程压测时,只要网络带宽不是瓶颈就OK

4.2、服务端准备

4.2.1、常规压测配置

集群新建出来后,有一些关键的配置还是需要留意设置一下的,否则性能会打很大的折扣

4.2.2、副本同步

前文说过,kafka选择不同的副本同步策略、同步副本数量,对性能影响很大;如果选择单副本的话,那么最大吞吐就是上文使用工具测出来的磁盘性能,而如果选择多副本的话,则整体吞吐的计算公式是是业务流量*副本数,后文我们将针对不同acksnum.replica.fetchers参数以及不同的副本数分别进行压测,最终得出一个多维参考值

4.2.3、服务端监控

服务端监控主要是查看集群整体流量、broker cpu、内存参数。我们通过top命令可以快速获取cpu、memory参数,而集群整体流量,为了快速获取,可通过JMX去拉取kafka原生监控项

public class JMXMain {public static void main(String[] args) throws Exception {new JMXMain().begin(args);}private void begin(String[] args) throws Exception {String metricName = "kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec";List<MBeanServerConnection> connectionList = initConnectionList(args);while (true) {long total = 0L;long[] arr = new long[3];int index = 0;for (MBeanServerConnection connection : connectionList) {Optional<ObjectName> first = connection.queryNames(new ObjectName(metricName), null).stream().findFirst();if (first.isPresent()) {Object oneMinuteRate = connection.getAttribute(first.get(), "OneMinuteRate");long single = transToM(oneMinuteRate);arr[index++] = single;total += single;} else {System.out.println("none");}}System.out.println(Arrays.toString(arr));System.out.println("rate is " + total + " MB/sec");Thread.sleep(10000);}}private List<MBeanServerConnection> initConnectionList(String[] args) throws Exception {// 10.0.0.21:9094,10.0.0.22:9094,10.0.0.23:9094List<String> connList = new ArrayList<>();if (args != null) {for (String ip : args) {String s = "service:jmx:rmi:///jndi/rmi://" + ip + ":5555/jmxrmi";connList.add(s);}}List<MBeanServerConnection> resultList = new ArrayList<>();
//        String[] arr = {"service:jmx:rmi:///jndi/rmi://10.0.0.19:5555/jmxrmi", "service:jmx:rmi:///jndi/rmi://10.0.0.20:5555/jmxrmi", "service:jmx:rmi:///jndi/rmi://10.0.0.21:5555/jmxrmi"};for (String jmxUrl : connList) {System.out.println(jmxUrl);JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(jmxUrl));MBeanServerConnection connection = connector.getMBeanServerConnection();resultList.add(connection);}return resultList;}public static long transToM(Object count) {if (count == null) {return 0;} else {double v = Double.parseDouble(count.toString()) / 1024 / 1024;return (long) v;}}
}

每隔10秒打印一下集群整体的流量及每个broker各自流量,例如:

[406, 407, 405]
rate is 1218 MB/sec
[409, 409, 407]
rate is 1225 MB/sec
[408, 408, 408]
rate is 1224 MB/sec
[406, 406, 405]
rate is 1217 MB/sec

注意:这里打印的流量,仅包含leader处理的业务流量,不包括follower从leader同步的备份流量,例如,我创建一个单partition,3副本的topic,然后向集群写入100MB/s的流量,因为设置了3副本,因此虽然只会向其中某个broker发送数据,但是另外2个broker中同时也均会有100MB/s的备份流量,但是使用上述工具则只会打印leader的流量: [100, 0, 0]

五、发压

5.1、2C4G

5.1.1、单partition/单副本

最小配置,首先测试一下单partition、单replica 的性能,从而与磁盘极限性能做个对比;这个值体现了kafka单broker的极限能力

创建topic:【1 partition、1 replica、acks=1】

发压命令

bash /root/kafka_2.12-2.8.2/bin/kafka-producer-perf-test.sh \ 
--producer-props \bootstrap.servers=10.0.0.10:9094,10.0.0.11:9094,10.0.0.12:9094 \acks=0 \max.request.size=2048000   \
--producer.config=/root/kafka_2.12-2.8.2/bin/admin5.conf \
--topic topic_1_1 \
--throughput=-1  --num-records 100000000 --record-size 1924000

当启动了8个producer客户端后,监控集群的吞吐峰值来到了 550MB/s;其实启动了4个客户端后,吞吐量就达到了530,后续通过增加客户端数量带来的收益越来越小,说明broker端能力已达上限

[0, 546, 0]
rate is 546 MB/sec
[0, 545, 0]
rate is 545 MB/sec
[0, 550, 0]
rate is 550 MB/sec

简单做个对比

在开始对磁盘用工具进行压测时候,2C4G的规格就因为cpu成为了短板,压测工具自身没有消耗cpu的逻辑,几乎全量的cpu都消耗在了刷盘的操作中

而kafka的构成要比磁盘工具复杂很多,涉及内存的数据拷贝、数据解析、正确性验证、刷盘等,而这些操作无疑会消耗大量cpu,cpu本身就是短板,因此压测出来的kafka吞吐量会比理论值低很多

因此当前2C4G的3节点的极限能力是 3 * 550MB/s

5.1.2、多partition/三副本/all acks

当选项acks设置为all时,代表只有当3个副本的消息都落盘后,才会response,这个设置也是严格的保证了数据的高可用,不会有任何数据的丢失,同时这种配置也是效率最低的,我们创建一个 6 partition,3副本的topic,同时将acks设置为all,再查看此时的性能,做一个对比

创建topic:【6 partition、3 replica、acks=all】

发压命令

bash /root/kafka_2.12-2.8.2/bin/kafka-producer-perf-test.sh \
--producer-props \bootstrap.servers=10.0.0.10:9094,10.0.0.11:9094,10.0.0.12:9094 \acks=-1 \max.request.size=2048000   \
--producer.config=/root/kafka_2.12-2.8.2/bin/admin5.conf \
--topic topic_6_3 \
--throughput=-1  --num-records 100000000 --record-size 1924000

启动了4个producer客户端后,监控集群的吞吐峰值来到了 3 * 320MB/s

[106, 107, 105]
rate is 318 MB/sec
[107, 107, 106]
rate is 320 MB/sec
[108, 108, 106]
rate is 322 MB/sec
[108, 107, 107]
rate is 322 MB/sec
[108, 107, 107]
rate is 322 MB/sec
[108, 106, 107]
rate is 321 MB/sec

4个客户端的延迟都已经很高,达到了400ms左右,说明数据都积攒到了broker端排队处理,4个客户端数据采样:

terminal-1:
230 records sent, 45.8 records/sec (84.07 MB/sec), 395.2 ms avg latency, 2228.0 ms max latency.
223 records sent, 44.4 records/sec (81.56 MB/sec), 437.7 ms avg latency, 1771.0 ms max latency.terminal-4:
216 records sent, 43.1 records/sec (79.11 MB/sec), 429.5 ms avg latency, 1628.0 ms max latency.
225 records sent, 45.0 records/sec (82.54 MB/sec), 399.1 ms avg latency, 1279.0 ms max latency.

可见,acks参数的不同,对最终结果的影响甚大

因cpu核数只有2,因此调整num.replica.fetchers参数对最终的压测影响不大,后续等cpu核数增加后可以考虑增加此值

5.1.3、多partition/三副本/leader

然而我们实际生产中,通常既不会将topic的副本数设置为1,也不会将acks设置为all,那么这个时候的最大流量值,体现的便是集群能够处理业务流量的峰值,一旦这个值超过了550MB/s,那么follower一定出现不同程度的落后leader的现象,等流量回落后,follower再逐步进行追赶,因此这个值也是具有相当重要的参考价值

创建topic:【3 partition、3 replica、acks=1】

发压命令

bash /root/kafka_2.12-2.8.2/bin/kafka-producer-perf-test.sh --producer-props bootstrap.servers=10.0.0.10:9094,10.0.0.11:9094,10.0.0.12:9094 acks=1 max.request.size=2048000   --producer.config=/root/kafka_2.12-2.8.2/bin/admin5.conf --topic topic_3_3 --throughput=-1  --num-records 100000000 --record-size 1924000

启动了12个producer客户端后,监控集群的业务流量峰值来到约了 1GB/s

[347, 349, 343]
rate is 1039 MB/sec
[340, 345, 342]
rate is 1027 MB/sec
[339, 344, 341]
rate is 1024 MB/sec
[343, 347, 344]
rate is 1034 MB/sec

客户端的延迟都已经达到了400ms左右,说明瓶颈不在客户端侧,客户端数据采样:

terminal-1:
228 records sent, 45.6 records/sec (83.60 MB/sec), 412.7 ms avg latency, 1173.0 ms max latency.
268 records sent, 53.5 records/sec (98.11 MB/sec), 334.0 ms avg latency, 774.0 ms max latency.terminal-3:
218 records sent, 43.4 records/sec (79.62 MB/sec), 394.7 ms avg latency, 1422.0 ms max latency.
252 records sent, 50.3 records/sec (92.35 MB/sec), 377.1 ms avg latency, 1226.0 ms max latency.

注意:我这里并没有使用 3 * 1GB/s 的描述,是因为虽然leader确实已经接受了1GB/s的流量,但是其并没有在同一时刻事实同步给follower,事实上,随着时间的推移,follower已经落后的越来越多

5.1.4、整理总结

5.2、4C8G

相关认证配置 admin7.conf

security.protocol=SASL_PLAINTEXT
sasl.mechanism=SCRAM-SHA-512
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="kafka-acegzesojh" password="acegzesfmj";

5.2.1、单partition/单副本

创建topic:【1 partition、1 replica、acks=1】

当启动了8个producer端时,集群的流量来到580 MB/s左右,这个值与2C4G的基本持平,难道它们两个的性能相当吗?其实不尽然,因为单partition、单副本的case,注定broker将会是同时写入1个文件,此时的瓶颈将落在IO上,因此,单纯的加cpu是不会提升吞吐的

[0, 584, 0]
rate is 584 MB/sec
[0, 589, 0]
rate is 589 MB/sec
[0, 586, 0]
rate is 586 MB/sec

看一下cpu的使用率

cpu基本维持在180%上下,而4C的上限是400%

5.2.2、multi 【单partition/单副本】

创建topic:4 * 【1 partition、1 replica、acks=1】

既然1个topic无法探知broker的上限,那我们就创建多个【单partition/单副本】的topic,使其落在同一个broker上,然后再向这个broker发压即可。(也可以通过手动指定将某个topic的分区都分布在1台broker上实现)

查看topic_1_1的ISR分布情况:

bash /root/kafka_2.12-2.8.2/bin/kafka-topics.sh \
--bootstrap-server 10.0.0.5:9094,10.0.0.9:9094,10.0.0.18:9094 \
--command-config  /root/kafka_2.12-2.8.2/bin/admin7.conf  \
--describe --topic topic_1_1

返回结果

Topic: topic_1_1	Partition: 0	Leader: 1000	Replicas: 1000	Isr: 1000

通过这种方式,选取4个topic:topic_1_1、topic_i_1_1、topic_j_1_1、topic_n_1_1,然后每个topic启动4个producer进行发压,也就是一共开启了16个client端发压

首先看一下broker端的流量统计指标,单broker的流量来到了 1GB/s

[0, 1009, 0]
rate is 1009 MB/sec
[0, 1016, 0]
rate is 1016 MB/sec
[0, 1021, 0]
rate is 1021 MB/sec
[0, 1016, 0]
rate is 1016 MB/sec
[0, 1015, 0]
rate is 1015 MB/sec

再观察一下cpu利用率,维持在400%,已经打满

客户端的监控日志采样。延迟也高达500ms,说明压力已经完全给到了broker

196 records sent, 38.8 records/sec (71.24 MB/sec), 457.4 ms avg latency, 560.0 ms max latency.
167 records sent, 33.4 records/sec (61.21 MB/sec), 536.9 ms avg latency, 649.0 ms max latency.
147 records sent, 29.1 records/sec (53.35 MB/sec), 623.0 ms avg latency, 782.0 ms max latency.
184 records sent, 36.6 records/sec (67.19 MB/sec), 489.4 ms avg latency, 588.0 ms max latency.
189 records sent, 37.8 records/sec (69.33 MB/sec), 478.6 ms avg latency, 563.0 ms max latency.

至此,探知当前配置的单broker的处理上限为 1 GB/s,因此集群的最大吞吐为 3 * 1 GB/s

5.2.3、多partition/三副本/all acks

创建topic:【6 partition、3 replica、acks=-1】

发压命令

bash /root/kafka_2.12-2.8.2/bin/kafka-producer-perf-test.sh \
--producer-props \bootstrap.servers=10.0.0.27:9094,10.0.0.28:9094,10.0.0.29:9094 \acks=-1 max.request.size=2048000 \
--producer.config=/root/kafka_2.12-2.8.2/bin/admin77.conf  \
--topic topic_a_6_3 --throughput=-1  \
--num-records 100000000 --record-size 1924000

吞吐停留在330 MB/s,怎么跟2C4G的相差不大呢?

[112, 111, 111]
rate is 334 MB/sec
[112, 111, 111]
rate is 334 MB/sec
[112, 111, 112]
rate is 335 MB/sec

这里别忘了一个参数num.replica.fetchers,这个参数默认为1,调大这个参数将加快follower从leader拉取数据的速率;我们首先看下当前这个参数的设置:

sh kafka-configs.sh \
--bootstrap-server 10.0.0.27:9094,10.0.0.28:9094,10.0.0.29:9094 \
--command-config  admin77.conf --all \
--entity-type brokers --describe  |  grep "num.replica.fetchers"

最终返回

num.replica.fetchers=1 sensitive=false synonyms={DEFAULT_CONFIG:num.replica.fetchers=1}
num.replica.fetchers=1 sensitive=false synonyms={DEFAULT_CONFIG:num.replica.fetchers=1}
num.replica.fetchers=1 sensitive=false synonyms={DEFAULT_CONFIG:num.replica.fetchers=1}

这个参数是可以调用命令进行直接修改的,我们将其修改为cpu核数

sh kafka-configs.sh \
--bootstrap-server 10.0.0.27:9094,10.0.0.28:9094,10.0.0.29:9094 \
--command-config  admin77.conf \
--alter --entity-type brokers --entity-default \
--add-config 'num.replica.fetchers=4' 

最终broker的性能提升至了550 MB/s

[181, 182, 182]
rate is 545 MB/sec
[183, 184, 183]
rate is 550 MB/sec
[183, 184, 183]
rate is 550 MB/sec

cpu利用率也相当低,大量的耗时都停留在三副本sync上

5.2.4、多partition/三副本/leader

将参数“num.replica.fetchers”调整为默认值后,同时将acks设置为1,再次发压

bash /root/kafka_2.12-2.8.2/bin/kafka-producer-perf-test.sh \
--producer-props \bootstrap.servers=10.0.0.27:9094,10.0.0.28:9094,10.0.0.29:9094 \acks=1 max.request.size=2048000 \
--producer.config=/root/kafka_2.12-2.8.2/bin/admin77.conf  \
--topic topic_a_6_3 --throughput=-1  \
--num-records 100000000 --record-size 1924000

共启动了16个客户端,流量来到了2200 MB/s

[734, 737, 733]
rate is 2204 MB/sec
[734, 736, 744]
rate is 2214 MB/sec
[737, 745, 741]
rate is 2223 MB/sec

同时cpu被打满

部分发压程序日志采样。随着producer的增多,吞吐量维持在恒定值

432 records sent, 86.2 records/sec (158.12 MB/sec), 198.7 ms avg latency, 1601.0 ms max latency.
378 records sent, 75.4 records/sec (138.33 MB/sec), 238.2 ms avg latency, 1297.0 ms max latency.
413 records sent, 82.4 records/sec (151.17 MB/sec), 222.9 ms avg latency, 1315.0 ms max latency.
353 records sent, 70.5 records/sec (129.39 MB/sec), 268.1 ms avg latency, 1442.0 ms max latency.

5.2.5、整理总结

5.3、8C16G

用到的配置信息 admin6.conf

security.protocol=SASL_PLAINTEXT
sasl.mechanism=SCRAM-SHA-512
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="kafka-acntgcoin9" password="acntgcozqb";

5.3.1、multi 【单partition/单副本】

创建topic:【12 partition、1 replica、acks=1】

通过命令创建topic,将12个分区全部都放在第一个broker上

bash /root/kafka_2.12-2.8.2/bin/kafka-topics.sh \
--bootstrap-server 10.0.0.5:9094,10.0.0.6:9094,10.0.0.7:9094 \
--command-config  /root/kafka_2.12-2.8.2/bin/admin6.conf  \
--create --topic topic_1_1 \
--replica-assignment 1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000

启动28个producer端发压客户端后,broker流量趋于稳定

[1964, 0, 0]
rate is 1964 MB/sec
[1959, 0, 0]
rate is 1959 MB/sec
[1962, 0, 0]
rate is 1962 MB/sec

查看对应pod的cpu使用率,已经打满

最终得出结论,单台broker的吞吐上限为 1.9 GB/s

5.3.2、多partition/三副本/all acks

创建topic:【12 partition、3 replica、acks=-1】

当前规格配置较高,需要创建更多的partition以压榨更多的cpu资源

查看参数num.replica.fetchers

sh kafka-configs.sh \
--bootstrap-server 10.0.0.5:9094,10.0.0.6:9094,10.0.0.7:9094 \
--command-config  admin6.conf --all \
--entity-type brokers --describe  |  grep "num.replica.fetchers"

返回

  num.replica.fetchers=1 sensitive=false synonyms={DEFAULT_CONFIG:num.replica.fetchers=1}num.replica.fetchers=1 sensitive=false synonyms={DEFAULT_CONFIG:num.replica.fetchers=1}num.replica.fetchers=1 sensitive=false synonyms={DEFAULT_CONFIG:num.replica.fetchers=1}

用同样的方法查看参数replica.fetch.max.bytes,返回

replica.fetch.max.bytes=30240000 sensitive=false synonyms={STATIC_BROKER_CONFIG:replica.fetch.max.bytes=30240000, DEFAULT_CONFIG:replica.fetch.max.bytes=1048576}
replica.fetch.max.bytes=30240000 sensitive=false synonyms={STATIC_BROKER_CONFIG:replica.fetch.max.bytes=30240000, DEFAULT_CONFIG:replica.fetch.max.bytes=1048576}
replica.fetch.max.bytes=30240000 sensitive=false synonyms={STATIC_BROKER_CONFIG:replica.fetch.max.bytes=30240000, DEFAULT_CONFIG:replica.fetch.max.bytes=1048576}

将参数num.replica.fetchers修改为cpu核数

sh kafka-configs.sh \
--bootstrap-server 10.0.0.5:9094,10.0.0.6:9094,10.0.0.7:9094 \
--command-config  admin6.conf \
--alter --entity-type brokers --entity-default \
--add-config 'num.replica.fetchers=8' 

发压

bash /root/kafka_2.12-2.8.2/bin/kafka-producer-perf-test.sh \
--producer-props \bootstrap.servers=10.0.0.5:9094,10.0.0.6:9094,10.0.0.7:9094 \acks=-1  \
max.request.size=2048000   \
--producer.config=/root/kafka_2.12-2.8.2/bin/admin6.conf \
--topic topic_a_12_3 --throughput=-1  --num-records 100000000 --record-size 1924000

一共启动了24个producer压测,强劲的cpu发挥了作用,写入速率达到了950 M/s

[315, 315, 315]
rate is 945 MB/sec
[315, 317, 318]
rate is 950 MB/sec
[313, 317, 317]
rate is 947 MB/sec

5.3.3、多partition/三副本/leader

创建topic:【12 partition、3 replica、acks=1】

按照常规,我们压一下三副本写leader的case;此时num.replica.fetchers同样设置为8

启动28个压测客户端后,流量趋于稳定

1034, 1032, 1028]
rate is 3094 MB/sec
[1034, 1038, 1034]
rate is 3106 MB/sec
[1036, 1039, 1040]
rate is 3115 MB/sec

客户端延迟1s+

426 records sent, 85.1 records/sec (156.14 MB/sec), 204.5 ms avg latency, 1791.0 ms max latency.

broker cpu使用率接近饱和

至此,可以得出结论,陡增业务流量可承接 3.1 GB/s

5.3.4、整理总结

当前规格,cpu还是短板,无法触及磁盘的3.6GB/s

5.4、16C32G

这个配置规格与另外3个规格有本质的区别,当前规格在使用磁盘压测工具时,已经窥探到了磁盘的上限,我们继续benchmark,看它的压测数据。用到的配置信息 admin0.conf

security.protocol=SASL_PLAINTEXT
sasl.mechanism=SCRAM-SHA-512
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="kafka-acrk9ciqkx" password="acrk9cihnz";

5.4.1、multi 【单partition/单副本】

创建topic:【24 partition、1 replica、acks=1】

通过命令创建topic,将24个分区全部都放在第一个broker上

bash /root/kafka_2.12-2.8.2/bin/kafka-topics.sh \
--bootstrap-server 10.0.0.5:9094,10.0.0.6:9094,10.0.0.7:9094 \
--command-config  /root/kafka_2.12-2.8.2/bin/admin0.conf  \
--create --topic topic_1_1 \
--replica-assignment \
1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000

启动35个producer端发压客户端后,broker流量趋于稳定

[0, 2714, 0]
rate is 2714 MB/sec
[0, 2717, 0]
rate is 2717 MB/sec
[0, 2719, 0]
rate is 2719 MB/sec

查看对应pod的cpu使用率

最终得出结论,单台broker的吞吐上限为 2.7 GB/s

不对啊,这个与预期不符的,单broker能力上限的探测,8C16G已经达到了 1.9G/s,而当前配置预期可以达到 4G/s的,可无论怎么压,顶多到达 2.7+ GB/s,到底哪里是瓶颈呢?

  • cpu ? cpu明显不是瓶颈,当前broker的cpu使用率还有富余

  • 磁盘? 2.7 GB/s 显然没有达到disk的上限,上文已经探知其上限为 4GB/s

  • 发压端? 发压端为了压测最大规格的实例,又额外申请了一个16C32G的压测机,而且即便是将客户端数量提升至40个,依旧没有变化,甚至可能有小幅度的下掉

既然都不是瓶颈,为什么吞吐上不去呢?难道瓶颈落在了网卡上?

5.4.2、网卡带宽探测--iperf3

这里我们还是很有必要探测一下网卡带宽的上限,使用的压测工具是

「iperf3」

  • 启动server端程序

  • iperf3 -s

  • 这里-s是-server的缩写,标明当前启动的是server端程序,默认监听5201端口

  • 启动client端

  • iperf3 -c 21.100.18.151 -P 8

  • -c 指启动client端,后面的IP是server端IP

  • -P 8 启动并发连接数。这个要额外注意,如果不指定的话,默认是启动1个链接测试,这个时候网卡是打不满的,具体设置为多少能打满,需要反复不断测试

当将并发连接数设置为8时,达到网络传输的峰值

server端日志

[SUM]   0.00-10.04  sec  23.6 GBytes  20.2 Gbits/sec                  receiver

client端日志

...
[SUM]   6.00-7.00   sec  2.64 GBytes  22.7 Gbits/sec    0             
....[SUM]   0.00-10.00  sec  23.7 GBytes  20.3 Gbits/sec  214             sender
[SUM]   0.00-10.04  sec  23.6 GBytes  20.2 Gbits/sec                  receiveriperf Done.

虽然压测的平均速率是20.3 Gbits/sec,但其曾经最高速率达到了22.7 Gbits/sec,因此我们就那这个进行计算。因为这个的单位是bit,我们需要将其转换为byte

22.7Gbits/8 = 2.8 GB/s

果然与我们压测的 2.72 GB 对应上了,压力实实在在的给到了网卡

5.4.3、多partition/三副本/all acks

创建topic:【24 partition、3 replica、acks=-1】

根据之前的磁盘压测工具已经判断出,当前的规格实例的cpu已经不是短板,因此我们这里将partition数量设置大一些。另外一些配置的修改

  • 将参数num.replica.fetchers修改为cpu核数

  • 将参数replica.fetch.max.bytes设置为20M

这样做的目的是加快follower同步leader数据的速率,经过一段时间发压后,broker端的速率来到了1350 MB/s

[448, 450, 446]
rate is 1344 MB/sec
[451, 450, 450]
rate is 1351 MB/sec
[452, 451, 450]
rate is 1353 MB/sec

8C16G的速率接近 1GB/s,而当前规格虽然配置翻倍了,但是速率却没能翻倍,为什么呢?其实这里也是符合预期的,因为8C16G的集群已经非常接近磁盘上限了,压出了3580 MB/s的吞吐,而16C32G也只能压出4GB/s的吞吐,因此瓶颈已经悄悄转移至磁盘上了(acks=all的情况,很大一部分开销在3副本的sync上)

其实这里我对不同partition的速率也做了压测,这里简单公布结论

5.4.4、整理总结

因为已经将短板确定为网卡,我们这里不再对峰值进行赘述

六、总结

此处我们还是有必要对流量规格再做个说明,以2C4G来举例,我们对外宣讲的流量规格是

「 3 * 550 MB/s 」

即集群可以处理 550 MB/s的吞吐,且在集群内部做好了经典三副本同步,因此站在partiton leader的视角,我们脑海中的画面可能是这样的:

而实际结果很有可能是这样的

甚至这样

这取决于follower从leader的拉取策略,比如参数num.replica.fetchers、replica.fetch.max.bytes等的配置,但在磁盘吞吐较高的背景下,又加剧了同步的不稳定性;以下分别以低IO及高IO进行阐述

  • 低IO:集群的磁盘吞吐并不高,例如50 MB/s,集群的吞吐瓶颈会落在了IO上,因此当数据进来后会首先进入page cache中,然后再由操作系统异步刷盘到磁盘中。因为吞吐不高,因此新数据在page cache中存活的时间相对较长,当follower从leader拉取数据时,数据大概率还在cache当中,此时调用kafka的零拷贝,将数据copy走,性能快,速率稳定,且不占用磁盘IO

  • 高IO:就像我们现在部署的集群,IO的吞吐非常高,拿2C4G的规格来讲,单broker的写入速率可达 550 MB/s,而内存只有4个G,其中Java堆内存又会占用2.5个G,以及操作系统本身的占用,留给page cache的空间满打满算也就1G左右,那数据进入在page cache中停留的时间不会超过2秒,等follower来拉取数据时,大概率是需要从disk设备中进行二次读取的,这就占用了磁盘的带宽,也让整个拉取的过程变得相对不稳定

但我们同样测出了acks=all场景中,流量的一个相对精确的保底值,供大家参考

总结各规格集群吞吐如下

测试多规格kafka集群的极限吞吐是一个艰巨的任务,其中至少涉及

  • 网络

  • cpu

  • 磁盘/网络 IO

  • kafka攒批模式/消息体大小

  • 刷盘策略

  • 不同规格的partition控制

  • replica副本同步策略

  • 发压端控制

  • 网络线程/IO线程搭配

  • 等等。。

这些环节中,只要有1个掉链子,那整个链路的吞吐也会拉胯;另外上述所有case,Java堆空间只分配了系统一半的内存,另一半留给了page cache

文章转载自:昔久

原文链接:https://www.cnblogs.com/xijiu/p/17878078.html

相关文章:

Kafka集群调优+能力探底

一、前言 我们需要对4个规格的kafka能力进行探底&#xff0c;即其可以承载的最大吞吐&#xff1b;4个规格对应的单节点的配置如下&#xff1a; 标准版&#xff1a; 2C4G 铂金版&#xff1a; 4C8G 专业版&#xff1a; 8C16G 企业版&#xff1a; 16C32G 另外&#xff0c;一般…...

netcore swagger 错误 Failed to load API definition

后端接口报错如下&#xff1a; 前端nswag报错如下&#xff1a; 根据网上查询到的资料说明&#xff0c;说一般swagger这种错误都是控制器里有接口代码异常造成的&#xff0c;通常是接口没有加属性Attribute&#xff0c; 比如[HttpPost("Delete")]、[HttpGet("Del…...

UDP Socket API 的讲解,以及回显服务器客户端的实现

文章目录 UDPDatagramSocktet APIDatagramPacket API UDP 客户端服务器实现 UDP 先来认识一下 UDP 的 socket api&#xff0c;两个核心的类&#xff1a;DatagramSocket、DatagramPacket. DatagramSocktet API 是一个 socket 对象。 什么是 socket&#xff1f; 操作系统&…...

数据结构与算法-D7栈实现及应用

顺序栈 具有顺序表同样的存储结构&#xff0c;由数组定义&#xff0c;配合用数组下标表示的栈顶指针top完成操作 sqstack.h stack_creat stack_push stack_empty stack_full 1、判断栈是否为空 2、top--&#xff0c;取&#xff1a;data[top1] stack_top stack_clear stack_fre…...

蓝桥杯真题:分巧克力(二分法)-Java版

由题目可知,该题的最终结果具有单调性,边长越大,可分蛋糕越少 可以用二分模板的向右找: 整数二分 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class Main {static int n,k; //n个块蛋糕,k个学生static int N 10…...

c++面试题

1.static的使用 1&#xff09;修饰局部变量&#xff1a;在函数内部使用static修饰局部变量&#xff0c;会使它成为静态局部变量。静态局部变量只会被初始化一次&#xff0c;且只有在第一次调用该函数时才会被初始化&#xff0c;之后每次调用该函数时都会保留上一次的值.从原来…...

高精度加法,减法,乘法,除法(上)(C语言)

前言 加&#xff0c;减&#xff0c;乘&#xff0c;除这些运算我们自然信手捏来&#xff0c;就拿加法来说&#xff0c;我们要用c语言编程算ab的和&#xff0c;只需让sum ab即可&#xff0c;可是这是局限的&#xff0c;我们都知道int的表示的最大值为2147483647&#xff08;32位…...

C++新经典模板与泛型编程:SFINAE特性的信息萃取

用成员函数重载实现is_default_constructible 首先介绍一个C标准库提供的可变参类模板std::is_default_constructible。这个类模板的主要功能是判断一个类的对象是否能被默认构造&#xff08;所谓默认构造&#xff0c;就是构造一个类对象时&#xff0c;不需要给该类的构造函数…...

java单人聊天

服务端 package 单人聊天;import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import…...

nodejs环境安装

node安装 wget https://mirrors.tuna.tsinghua.edu.cn/nodejs-release/v20.8.0/node-v20.8.0-linux-x64.tar.gz tar xf node-v20.8.0-linux-x64.tar.xz -C /usr/local/ ln -s node-v20.8.0-linux-x64 nodevim /etc/profile.d/node.sh export PATH$PATH:/usr/local/node/binnp…...

R语言进行正态分布检验

查了很多资料&#xff0c;还是比较模糊 Kolmogorov-Smirnov检验&#xff08;K-S检验&#xff09;广泛用于正态性检验和其他分布的拟合检验。适用于中等到大样本。 Lilliefors检验是K-S检验的一种变体&#xff0c;专门为小样本设计。其通过使用更准确的临界值来提高对小样本的适…...

什么是SPA(Single Page Application)?它的优点和缺点是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...

由于找不到xinput1_3.dll,无法继续执行代码的多种解决方法指南,xinput1_3.dll文件修复

当玩家或用户在启动某些游戏和应用程序时&#xff0c;可能会遭遇到一个系统错误提示&#xff1a;“由于找不到xinput1_3.dll,无法继续执行代码l”。这种情况通常指出系统中DirectX组件存在问题。以下我们将介绍几种常用的解决方法&#xff0c;并提供详细的操作步骤。 一.找不到…...

Vue---Echarts

项目需要用echarts来做数据展示&#xff0c;现记录vue3引入并使用echarts的过程。 1. 使用步骤 安装 ECharts&#xff1a;使用 npm 或 yarn 等包管理工具安装 ECharts。 npm install echarts 在 Vue 组件中引入 ECharts&#xff1a;在需要使用图表的 Vue 组件中&#xff0c;引入…...

uni-app实现返回刷新上一页

方案一 通过监听器实现 page1 uni.$on("refresh", function(data) {if(data.page "page2") {this.reload()} })page2 methods: {handleBack() {uni.$emit("refresh", {page: "page2"})uni.navigateBack()} }方案二 通过页面实例实…...

centos服务器安装docker和Rabbitmq

centos服务器 一 centos安装docker1 安装docker所需要的依赖包2配置yum源3查看仓库中所有的docker版本4安装docker5 设置docker为开机自启6验证docker是否安装成功 二 使用docker安装RabbitMQ拉取RabbitMQ镜像创建并运行容器 一 centos安装docker 1 安装docker所需要的依赖包 …...

【Redis】Redis高级特性和应用(慢查询、Pipeline、事务、Lua)

目录 Redis的慢查询 慢查询配置 慢查询操作命令 慢查询建议 Pipeline 事务 Redis的事务原理 Redis的watch命令 Pipeline和事务的区别 Lua Lua入门 安装Lua Lua基本语法 注释 标示符 关键词 全局变量 Lua中的数据类型 Lua 中的函数 Lua 变量 Lua中的控制语句…...

【pytorch】深度学习入门一:pytorch的安装与配置(Windows版)

请支持原创&#xff0c;认准DannisTang&#xff08;tangweixuan1995foxmail.com&#xff09; 文章目录 第〇章 阅读前提示第一章 准备工作第一节 Python下载第二节 Python安装第三节 Python配置第四节 Pycharm下载第五节 Pycharm安装第六节 CUDA的安装 第二章 Anaconda安装与配…...

安装postgresql驱动及python使用pyodbc指定postgresql驱动调用postgresql

注&#xff1a;Python解释器版本(32位/64位)和postgresql驱动版本(32位/64位)需一致。 一、安装postgresql驱动 https://www.postgresql.org/ftp/odbc/versions/msi/ &#xff08;1&#xff09;32位&#xff1a; &#xff08;2&#xff09;64位&#xff1a; 双击安装。全程默…...

【OpenCV】计算机视觉图像处理基础知识

目录 前言 推荐 1、OpenCV礼帽操作和黑帽操作 2、Sobel算子理论基础及实际操作 3、Scharr算子简介及相关操作 4、Sobel算子和Scharr算子的比较 5、laplacian算子简介及相关操作 6、Canny边缘检测的原理 6.1 去噪 6.2 梯度运算 6.3 非极大值抑制 6.4 滞后阈值 7、Ca…...

Course1-Week3-分类问题

Course1-Week3-分类问题 文章目录 Course1-Week3-分类问题1. 逻辑回归1.1 线性回归不适用于分类问题1.2 逻辑回归模型1.3 决策边界 2. 逻辑回归的代价函数3. 实现梯度下降4. 过拟合与正则化4.1 线性回归和逻辑回归中的过拟合4.2 解决过拟合的三种方法4.3 正则化4.4 用于线性回归…...

Dockerfile 指令的最佳实践

这些建议旨在帮助您创建一个高效且可维护的Dockerfile。 一、FROM 尽可能使用当前的官方镜像作为镜像的基础。Docker推荐Alpine镜像&#xff0c;因为它受到严格控制&#xff0c;体积小&#xff08;目前不到6 MB&#xff09;&#xff0c;同时仍然是一个完整的Linux发行版。 FR…...

Drools 入门:折扣案例

1. 安装 在idea软件中安装Drools 插件&#xff0c;我这里是直接搜索Drools就可以搜到 2. 实现入门案例 2.1 配置pom.xml文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi&q…...

微信小程序中生命周期钩子函数

微信小程序 App 的生命周期钩子函数有以下 7 个&#xff1a; onLaunch(options)&#xff1a;当小程序初始化完成时&#xff0c;会触发 onLaunch&#xff08;全局只触发一次&#xff09;。onShow(options)&#xff1a;当小程序启动或从后台进入前台显示时&#xff0c;会触发 on…...

“无忧文件安全!上海迅软DSE文件加密软件助您轻松管控分公司数据!

许多大型企业集团由于旗下有着分布在不同城市的分支机构&#xff0c;因此在规划数据安全解决方案时&#xff0c;不适合采用市面上常见的集中式部署方式来管控各分部服务器&#xff0c;而迅软DSE文件加密软件支持采用分布式部署的方式来解决这一问题。 企业用户只需在总部内部署…...

详解线段树

前段时间写过一篇关于树状数组的博客树状数组&#xff0c;今天我们要介绍的是线段树&#xff0c;线段树比树状数组中的应用场景更加的广泛。这些问题也是在leetcode 11月的每日一题频繁遇到的问题&#xff0c;实际上线段树就和红黑树 、堆一样是一类模板&#xff0c;但是标准库…...

C语言——指针的运算

1、指针 - 整数 #include<stdio.h> #define N_VALUES 5 int main() {flout values[N_VALUES];flout *vp;for(vp&values[0];vp<&values[N_VALUES]&#xff1b;) //指针的关系运算{*vp0; //指针整数} } 2、指针 - 指针 #include<stdio.h> int main() …...

Apache Hive(部署+SQL+FineBI构建展示)

Hive架构 Hive部署 VMware虚拟机部署 一、在node1节点安装mysql数据库 二、配置Hadoop 三、下载 解压Hive 四、提供mysql Driver驱动 五、配置Hive 六、初始化元数据库 七、启动Hive(Hadoop用户) chown -R hadoop:hadoop apache-hive-3.1.3-bin hive 阿里云部…...

python入门级简易教程

Python是一种高级编程语言&#xff0c;由Guido van Rossum于1991年创建。它是一种通用的、解释型的、高级的、动态的、面向对象的编程语言。 Python的编程哲学是简洁明了&#xff0c;强调代码的可读性和简洁性&#xff0c;使开发人员能够快速开发出正确的代码。Python被广泛用…...

模拟一个集合 里面是设备号和每日的日期

问题&#xff1a; 需要模拟一个集合 里面是设备号和每日的日期 代码如下&#xff1a; static void Main(string[] args){string equipmentCodePar "";DateTime time DateTime.Now; // 获取当前时间DateTime startDate time.AddDays(1 - time.Day);//获取当前月第一…...

antdesign前端一直加载不出来

antdesign前端一直加载不出来 报错&#xff1a;Module “./querystring” does not exist in container. while loading “./querystring” from webpack/container/reference/mf at mf-va_remoteEntry.js:751:11 解决方案&#xff1a;Error: Module “xxx“ does not exist …...

排序算法介绍(一)插入排序

0. 简介 插入排序&#xff08;Insertion Sort&#xff09; 是一种简单直观的排序算法&#xff0c;它的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。插入排序在实现上&#xff0c;通常…...

2023新优化应用:RIME-CNN-LSTM-Attention超前24步多变量回归预测算法

程序平台&#xff1a;适用于MATLAB 2023版及以上版本。 霜冰优化算法是2023年发表于SCI、中科院二区Top期刊《Neurocomputing》上的新优化算法&#xff0c;现如今还未有RIME优化算法应用文献哦。RIME主要对霜冰的形成过程进行模拟&#xff0c;将其巧妙地应用于算法搜索领域。 …...

RNN:文本生成

文章目录 一、完整代码二、过程实现2.1 导包2.2 数据准备2.3 字符分词2.4 构建数据集2.5 定义模型2.6 模型训练2.7 模型推理 三、整体总结 采用RNN和unicode分词进行文本生成 一、完整代码 这里我们使用tensorflow实现&#xff0c;代码如下&#xff1a; # 完整代码在这里 imp…...

Rust UI开发(五):iced中如何进行页面布局(pick_list的使用)?(串口调试助手)

注&#xff1a;此文适合于对rust有一些了解的朋友 iced是一个跨平台的GUI库&#xff0c;用于为rust语言程序构建UI界面。 这是一个系列博文&#xff0c;本文是第五篇&#xff0c;前四篇链接&#xff1a; 1、Rust UI开发&#xff08;一&#xff09;&#xff1a;使用iced构建UI时…...

Linux学习笔记2

web服务器部署&#xff1a; 1.装包&#xff1a; [rootlocalhost ~]# yum -y install httpd 2.配置一个首页&#xff1a; [rootlocalhost ~]# echo i love yy > /var/www/html/index.html 启动服务&#xff1a;[rootlocalhost ~]# systemctl start httpd Ctrl W以空格为界…...

数据结构算法-插入排序算法

引言 玩纸牌 的时候。往往 需要将牌从乱序排列变成有序排列 这就是插入排序 插入排序算法思想 先看图 首先第一个元素 我默认已有序 那我们从第二个元素开始&#xff0c;依次插入到前面已有序的部分中。具体来说&#xff0c;我们将第二个元素与第一个元素比较&#xff0c;…...

安装Kuboard管理K8S集群

目录 第一章.安装Kuboard管理K8S集群 1.安装kuboard 2.绑定K8S集群&#xff0c;完成信息设定 3.内网安装 第二章.kuboard-spray安装K8S 2.1.先拉镜像下来 2.2.之后打开后&#xff0c;先熟悉功能&#xff0c;注意版本 2.3.打开资源包管理&#xff0c;选择符合自己服务器…...

网络安全行业大模型调研总结

随着人工智能技术的发展&#xff0c;安全行业大模型SecLLM&#xff08;security Large Language Model&#xff09;应运而生&#xff0c;可应用于代码漏洞挖掘、安全智能问答、多源情报整合、勒索情报挖掘、安全评估、安全事件研判等场景。 参考&#xff1a; 1、安全行业大模…...

Linux AMH服务器管理面板本地安装与远程访问

最近&#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念&#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助&#xff0c;所以我在此分享。点击这里跳转到网站。 文章目录 1. Linux 安装AMH 面板2. 本地访问AMH 面板3. Linux安装…...

Sharding-Jdbc(3):Sharding-Jdbc分表

1 分表分库 LogicTable 数据分片的逻辑表&#xff0c;对于水平拆分的数据库(表)&#xff0c;同一类表的总称。 订单信息表拆分为2张表,分别是t_order_0、t_order_1&#xff0c;他们的逻辑表名为t_order。 ActualTable 在分片的数据库中真实存在的物理表。即上个示例中的t_…...

zookeeper集群 +kafka集群

1.zookeeper kafka3.0之前依赖于zookeeper zookeeper是一个开源&#xff0c;分布式的架构&#xff0c;提供协调服务&#xff08;Apache项目&#xff09; 基于观察者模式涉及的分布式服务管理架构 存储和管理数据&#xff0c;分布式节点上的服务接受观察者的注册&#xff0c…...

2022年全国大学生数据分析大赛医药电商销售数据分析求解全过程论文及程序

2022年全国大学生数据分析大赛 医药电商销售数据分析 原题再现&#xff1a; 问题背景   20 世纪 90 年代是电子数据交换时代&#xff0c;中国电子商务开始起步并初见雏形&#xff0c;随后 Web 技术爆炸式成长使电子商务处于蓬勃发展阶段&#xff0c;目前互联网信息碎片化以…...

Python版本与opencv版本的对应关系

python版本要和opencv版本相对应&#xff0c;否则安装的时候会报错。 可以到Links for opencv-python上面查看python版本和opencv版本的对应关系&#xff0c;如图&#xff0c;红框内是python版本&#xff0c;绿框内是opencv版本。 查看自己的python版本后&#xff0c;使用下面…...

【开源视频联动物联网平台】LiteFlow

LiteFlow是一个轻量且强大的国产规则引擎框架&#xff0c;可用于复杂的组件化业务的编排领域。它基于规则文件来编排流程&#xff0c;支持xml、json、yml三种规则文件写法方式&#xff0c;再复杂的逻辑过程都能轻易实现。LiteFlow于2020年正式开源&#xff0c;2021年获得开源中…...

家用智能门锁——智能指纹锁方案

智能指纹锁产品功能&#xff1a; 1&#xff1a;指纹识别技术&#xff1a;光学传感器、半导体传感器或超声波传感器等。 2&#xff1a;指纹容量&#xff1a;智能指纹锁可以存储的指纹数量&#xff0c;通常在几十到几百个指纹之间。 3&#xff1a;解锁时间&#xff1a;指纹识别和…...

Qt6 QRibbon 一键美化Qt界面

强烈推荐一个 github 项目&#xff1a; https://github.com/gnibuoz/QRibbon 作用&#xff1a; 在几乎不修改任何你自己代码的情况下&#xff0c;一键美化你的 UI 界面。 代码环境&#xff1a;使用 VS2019 编译 Qt6 GUI 程序&#xff0c;继承 QMainWindow 窗口类 一、使用方法 …...

JAVA IO:NIO

1.阻塞 IO 模型 ​ 最传统的一种 IO 模型&#xff0c;即在读写数据过程中会发生阻塞现象。当用户线程发出 IO 请求之后&#xff0c;内核会去查看数据是否就绪&#xff0c;如果没有就绪就会等待数据就绪&#xff0c;而用户线程就会处于阻塞状态&#xff0c;用户线程交出 CPU。当…...

Python 在控制台打印带颜色的信息

#格式&#xff1a;  设置颜色开始 &#xff1a;\033[显示方式;前景色;背景色m #说明&#xff1a; 前景色 背景色 颜色 --------------------------------------- 30 40 黑色 31 41 红色 32 …...

SQL Server 数据库,创建触发器避免数据被更改

5.4触发器 触发器是一种特殊类型的存储过程&#xff0c;当表中的数据发生更新时将自动调用&#xff0c;以响应INSERT、 UPDATE 或DELETE 语句。 5.4.1什么是触发器 1.触发器的概念 触发器是在对表进行插入、更新或删除操作时自动执行的存储过程&#xff0c;触发器通常用于强…...