分布式的流处理平台Kafka

目录:

    • 一、简介
    • 二、基本概念
    • 三、生产者使用详解
    • 四、发送消息
    • 五、消费者代码示例

一、简介

ApacheKafka 是一个分布式的流处理平台。它具有以下特点:

  • 支持消息的发布和订阅,类似于 RabbtMQ、ActiveMQ 等消息队列;
  • 支持数据实时处理;
  • 能保证消息的可靠性投递;
  • 支持消息的持久化存储,并通过多副本分布式的存储方案来保证消息的容错;
  • 高吞吐率,单 Broker 可以轻松处理数千个分区以及每秒百万级的消息量。

二、基本概念

2.1 Messages And Batches
Kafka 的基本数据单元被称为 message(消息),为减少网络开销,提高效率,多个消息会被放入同一批次 (Batch) 中后再写入。

2.2 Topics And Partitions
Kafka 的消息通过 Topics(主题) 进行分类,一个主题可以被分为若干个 Partitions(分区),一个分区就是一个提交日志 (commit log)。消息以追加的方式写入分区,然后以先入先出的顺序读取。Kafka 通过分区来实现数据的冗余和伸缩性,分区可以分布在不同的服务器上,这意味着一个 Topic 可以横跨多个服务器,以提供比单个服务器更强大的性能。

由于一个 Topic 包含多个分区,因此无法在整个 Topic 范围内保证消息的顺序性,但可以保证消息在单个分区内的顺序性。
在这里插入图片描述
2.3 Producers And Consumers

  1. 生产者
    生产者负责创建消息。一般情况下,生产者在把消息均衡地分布到在主题的所有分区上,而并不关心消息会被写到哪个分区。如果我们想要把消息写到指定的分区,可以通过自定义分区器来实现。

  2. 消费者
    消费者是消费者群组的一部分,消费者负责消费消息。消费者可以订阅一个或者多个主题,并按照消息生成的顺序来读取它们。消费者通过检查消息的偏移量 (offset) 来区分读取过的消息。偏移量是一个不断递增的数值,在创建消息时,Kafka 会把它添加到其中,在给定的分区里,每个消息的偏移量都是唯一的。消费者把每个分区最后读取的偏移量保存在 Zookeeper 或 Kafka 上,如果消费者关闭或者重启,它还可以重新获取该偏移量,以保证读取状态不会丢失。
    在这里插入图片描述
    一个分区只能被同一个消费者群组里面的一个消费者读取,但可以被不同消费者群组中所组成的多个消费者共同读取。多个消费者群组中消费者共同读取同一个主题时,彼此之间互不影响。
    在这里插入图片描述
    2.4 Brokers And Clusters
    一个独立的 Kafka 服务器被称为 Broker。Broker 接收来自生产者的消息,为消息设置偏移量,并提交消息到磁盘保存。Broker 为消费者提供服务,对读取分区的请求做出响应,返回已经提交到磁盘的消息。

Broker 是集群 (Cluster) 的组成部分。每一个集群都会选举出一个 Broker 作为集群控制器 (Controller),集群控制器负责管理工作,包括将分区分配给 Broker 和监控 Broker。

在集群中,一个分区 (Partition) 从属一个 Broker,该 Broker 被称为分区的首领 (Leader)。一个分区可以分配给多个 Brokers,这个时候会发生分区复制。这种复制机制为分区提供了消息冗余,如果有一个 Broker 失效,其他 Broker 可以接管领导权。
在这里插入图片描述

三、生产者使用详解

2.1 项目依赖

本项目采用 Maven 构建,想要调用 Kafka 生产者 API,需要导入 kafka-clients 依赖,如下:

<dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>2.2.0</version>
</dependency>

2.2 创建生产者

创建 Kafka 生产者时,以下三个属性是必须指定的:

  • bootstrap.servers :指定 broker 的地址清单,清单里不需要包含所有的 broker 地址,生产者会从给定的broker 里查找 broker 的信息。不过建议至少要提供两个 broker 的信息作为容错;
  • key.serializer :指定键的序列化器;
  • value.serializer :指定值的序列化器。

创建的示例代码如下:

public class SimpleProducer {public static void main(String[] args) {String topicName = "Hello-Kafka";Properties props = new Properties();props.put("bootstrap.servers", "hadoop001:9092");props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");/*创建生产者*/Producer<String, String> producer = new KafkaProducer<>(props);for (int i = 0; i < 10; i++) {ProducerRecord<String, String> record = new ProducerRecord<>(topicName, "hello" + i, "world" + i);/* 发送消息*/producer.send(record);}/*关闭生产者*/producer.close();}
}

2.3 测试

  1. 启动Kakfa
    Kafka 的运行依赖于 zookeeper,需要预先启动,可以启动 Kafka 内置的 zookeeper,也可以启动自己安装的:
# zookeeper启动命令
bin/zkServer.sh start# 内置zookeeper启动命令
bin/zookeeper-server-start.sh config/zookeeper.properties
启动单节点 kafka 用于测试:# bin/kafka-server-start.sh config/server.properties
# 创建用于测试主题
bin/kafka-topics.sh --create \--bootstrap-server hadoop001:9092 \--replication-factor 1 --partitions 1 \--topic Hello-Kafka# 查看所有主题bin/kafka-topics.sh --list --bootstrap-server hadoop001:9092
  1. 启动消费者
启动一个控制台消费者用于观察写入情况,启动命令如下:# bin/kafka-console-consumer.sh --bootstrap-server hadoop001:9092 --topic Hello-Kafka --from-beginning

在这里插入图片描述
2.4 可能出现的问题
在这里可能出现的一个问题是:生产者程序在启动后,一直处于等待状态。这通常出现在你使用默认配置启动 Kafka 的情况下,此时需要对 server.properties 文件中的 listeners 配置进行更改:

# hadoop001 为我启动kafka服务的主机名,你可以换成自己的主机名或者ip地址
listeners=PLAINTEXT://hadoop001:9092

四、发送消息

上面的示例程序调用了 send 方法发送消息后没有做任何操作,在这种情况下,我们没有办法知道消息发送的结果。想要知道消息发送的结果,可以使用同步发送或者异步发送来实现。

2.1 同步发送
在调用 send 方法后可以接着调用 get() 方法,send 方法的返回值是一个 Future对象,RecordMetadata 里面包含了发送消息的主题、分区、偏移量等信息。改写后的代码如下:

for (int i = 0; i < 10; i++) {try {ProducerRecord<String, String> record = new ProducerRecord<>(topicName, "k" + i, "world" + i);/*同步发送消息*/RecordMetadata metadata = producer.send(record).get();System.out.printf("topic=%s, partition=%d, offset=%s \n",metadata.topic(), metadata.partition(), metadata.offset());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}
}

此时得到的输出如下:偏移量和调用次数有关,所有记录都分配到了 0 分区,这是因为在创建 Hello-Kafka 主题时候,使用 --partitions 指定其分区数为 1,即只有一个分区。

topic=Hello-Kafka, partition=0, offset=40 
topic=Hello-Kafka, partition=0, offset=41 
topic=Hello-Kafka, partition=0, offset=42 
topic=Hello-Kafka, partition=0, offset=43 
topic=Hello-Kafka, partition=0, offset=44 
topic=Hello-Kafka, partition=0, offset=45 
topic=Hello-Kafka, partition=0, offset=46 
topic=Hello-Kafka, partition=0, offset=47 
topic=Hello-Kafka, partition=0, offset=48 
topic=Hello-Kafka, partition=0, offset=49 

2.2 异步发送
通常我们并不关心发送成功的情况,更多关注的是失败的情况,因此 Kafka 提供了异步发送和回调函数。 代码如下:

for (int i = 0; i < 10; i++) {ProducerRecord<String, String> record = new ProducerRecord<>(topicName, "k" + i, "world" + i);/*异步发送消息,并监听回调*/producer.send(record, new Callback() {@Overridepublic void onCompletion(RecordMetadata metadata, Exception exception) {if (exception != null) {System.out.println("进行异常处理");} else {System.out.printf("topic=%s, partition=%d, offset=%s \n",metadata.topic(), metadata.partition(), metadata.offset());}}});
}

三、自定义分区器
Kafka 有着默认的分区机制:

如果键值为 null, 则使用轮询 (Round Robin) 算法将消息均衡地分布到各个分区上;
如果键值不为 null,那么 Kafka 会使用内置的散列算法对键进行散列,然后分布到各个分区上。
某些情况下,你可能有着自己的分区需求,这时候可以采用自定义分区器实现。这里给出一个自定义分区器的示例:

3.1 自定义分区器

/*** 自定义分区器*/
public class CustomPartitioner implements Partitioner {private int passLine;@Overridepublic void configure(Map<String, ?> configs) {/*从生产者配置中获取分数线*/passLine = (Integer) configs.get("pass.line");}@Overridepublic int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {/*key 值为分数,当分数大于分数线时候,分配到 1 分区,否则分配到 0 分区*/return (Integer) key >= passLine ? 1 : 0;}@Overridepublic void close() {System.out.println("分区器关闭");}
}
需要在创建生产者时指定分区器,和分区器所需要的配置参数:public class ProducerWithPartitioner {public static void main(String[] args) {String topicName = "Kafka-Partitioner-Test";Properties props = new Properties();props.put("bootstrap.servers", "hadoop001:9092");props.put("key.serializer", "org.apache.kafka.common.serialization.IntegerSerializer");props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");/*传递自定义分区器*/props.put("partitioner.class", "com.heibaiying.producers.partitioners.CustomPartitioner");/*传递分区器所需的参数*/props.put("pass.line", 6);Producer<Integer, String> producer = new KafkaProducer<>(props);for (int i = 0; i <= 10; i++) {String score = "score:" + i;ProducerRecord<Integer, String> record = new ProducerRecord<>(topicName, i, score);/*异步发送消息*/producer.send(record, (metadata, exception) ->System.out.printf("%s, partition=%d, \n", score, metadata.partition()));}producer.close();}
}

3.2 测试
需要创建一个至少有两个分区的主题:

 bin/kafka-topics.sh --create \--bootstrap-server hadoop001:9092 \--replication-factor 1 --partitions 2 \--topic Kafka-Partitioner-Test

此时输入如下,可以看到分数大于等于 6 分的都被分到 1 分区,而小于 6 分的都被分到了 0 分区。

score:6, partition=1, 
score:7, partition=1, 
score:8, partition=1, 
score:9, partition=1, 
score:10, partition=1, 
score:0, partition=0, 
score:1, partition=0, 
score:2, partition=0, 
score:3, partition=0, 
score:4, partition=0, 
score:5, partition=0, 

分区器关闭
四、生产者其他属性
上面生产者的创建都仅指定了服务地址,键序列化器、值序列化器,实际上 Kafka 的生产者还有很多可配置属性,如下:

  1. acks

acks 参数指定了必须要有多少个分区副本收到消息,生产者才会认为消息写入是成功的:

acks=0 : 消息发送出去就认为已经成功了,不会等待任何来自服务器的响应; acks=1 :
只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应; acks=all
:只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。

  1. buffer.memory

设置生产者内存缓冲区的大小。

  1. compression.type

默认情况下,发送的消息不会被压缩。如果想要进行压缩,可以配置此参数,可选值有 snappy,gzip,lz4。

  1. retries

发生错误后,消息重发的次数。如果达到设定值,生产者就会放弃重试并返回错误。

  1. batch.size

当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。

  1. linger.ms

该参数制定了生产者在发送批次之前等待更多消息加入批次的时间。

  1. clent.id

客户端 id,服务器用来识别消息的来源。

  1. max.in.flight.requests.per.connection

指定了生产者在收到服务器响应之前可以发送多少个消息。它的值越高,就会占用越多的内存,不过也会提升吞吐量,把它设置为 1
可以保证消息是按照发送的顺序写入服务器,即使发生了重试。

  1. timeout.ms, request.timeout.ms & metadata.fetch.timeout.ms

timeout.ms 指定了 borker 等待同步副本返回消息的确认时间; request.timeout.ms
指定了生产者在发送数据时等待服务器返回响应的时间; metadata.fetch.timeout.ms
指定了生产者在获取元数据(比如分区首领是谁)时等待服务器返回响应的时间。

  1. max.block.ms

指定了在调用 send() 方法或使用 partitionsFor()方法获取元数据时生产者的阻塞时间。当生产者的发送缓冲区已满,或者没有可用的元数据时,这些方法会阻塞。在阻塞时间达到 max.block.ms 时,生产者会抛出超时异常。

  1. max.request.size

该参数用于控制生产者发送的请求大小。它可以指发送的单个消息的最大值,也可以指单个请求里所有消息总的大小。例如,假设这个值为 1000K,那么可以发送的单个最大消息为 1000K ,或者生产者可以在单个请求里发送一个批次,该批次包含了 1000 个消息,每个消息大小为 1K。

  1. receive.buffer.bytes & send.buffer.byte

这两个参数分别指定 TCP socket 接收和发送数据包缓冲区的大小,-1 代表使用操作系统的默认值。

五、消费者代码示例

在创建消费者的时候以下以下三个选项是必选的:

  • bootstrap.servers :指定 broker 的地址清单,清单里不需要包含所有的 broker 地址,生产者会从给定的
    broker 里查找 broker 的信息。不过建议至少要提供两个 broker 的信息作为容错;
  • key.deserializer :指定键的反序列化器;
  • value.deserializer :指定值的反序列化器。

除此之外你还需要指明你需要想订阅的主题,可以使用如下两个 API :

  • consumer.subscribe(Collection topics) :指明需要订阅的主题的集合;
  • consumer.subscribe(Pattern pattern) :使用正则来匹配需要订阅的集合


最后只需要通过轮询 API(poll) 向服务器定时请求数据。一旦消费者订阅了主题,轮询就会处理所有的细节,包括群组协调、分区再均衡、发送心跳和获取数据,这使得开发者只需要关注从分区返回的数据,然后进行业务处理。 示例如下:

String topic = "Hello-Kafka";
String group = "group1";
Properties props = new Properties();
props.put("bootstrap.servers", "hadoop001:9092");
/*指定分组 ID*/
props.put("group.id", group);
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);/*订阅主题 (s)*/
consumer.subscribe(Collections.singletonList(topic));try {while (true) {/*轮询获取数据*/ConsumerRecords<String, String> records = consumer.poll(Duration.of(100, ChronoUnit.MILLIS));for (ConsumerRecord<String, String> record : records) {System.out.printf("topic = %s,partition = %d, key = %s, value = %s, offset = %d,\n",record.topic(), record.partition(), record.key(), record.value(), record.offset());}}
} finally {consumer.close();
}

4.1 同步提交
通过调用 consumer.commitSync() 来进行同步提交,不传递任何参数时提交的是当前轮询的最大偏移量。

while (true) {ConsumerRecords<String, String> records = consumer.poll(Duration.of(100, ChronoUnit.MILLIS));for (ConsumerRecord<String, String> record : records) {System.out.println(record);}/*同步提交*/consumer.commitSync();
}

如果某个提交失败,同步提交还会进行重试,这可以保证数据能够最大限度提交成功,但是同时也会降低程序的吞吐量。基于这个原因,Kafka 还提供了异步提交的 API。

4.2 异步提交
异步提交可以提高程序的吞吐量,因为此时你可以尽管请求数据,而不用等待 Broker 的响应。代码如下:

while (true) {ConsumerRecords<String, String> records = consumer.poll(Duration.of(100, ChronoUnit.MILLIS));for (ConsumerRecord<String, String> record : records) {System.out.println(record);}/*异步提交并定义回调*/consumer.commitAsync(new OffsetCommitCallback() {@Overridepublic void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {if (exception != null) {System.out.println("错误处理");offsets.forEach((x, y) -> System.out.printf("topic = %s,partition = %d, offset = %s \n",x.topic(), x.partition(), y.offset()));}}});
}

异步提交存在的问题是,在提交失败的时候不会进行自动重试,实际上也不能进行自动重试。假设程序同时提交了 200 和 300 的偏移量,此时 200 的偏移量失败的,但是紧随其后的 300 的偏移量成功了,此时如果重试就会存在 200 覆盖 300 偏移量的可能。同步提交就不存在这个问题,因为在同步提交的情况下,300 的提交请求必须等待服务器返回 200 提交请求的成功反馈后才会发出。基于这个原因,某些情况下,需要同时组合同步和异步两种提交方式。

注:虽然程序不能在失败时候进行自动重试,但是我们是可以手动进行重试的,你可以通过一个 Map<TopicPartition, Integer> offsets 来维护你提交的每个分区的偏移量,然后当失败时候,你可以判断失败的偏移量是否小于你维护的同主题同分区的最后提交的偏移量,如果小于则代表你已经提交了更大的偏移量请求,此时不需要重试,否则就可以进行手动重试。

4.3 同步加异步提交
下面这种情况,在正常的轮询中使用异步提交来保证吞吐量,但是因为在最后即将要关闭消费者了,所以此时需要用同步提交来保证最大限度的提交成功。

try {while (true) {ConsumerRecords<String, String> records = consumer.poll(Duration.of(100, ChronoUnit.MILLIS));for (ConsumerRecord<String, String> record : records) {System.out.println(record);}// 异步提交consumer.commitAsync();}
} catch (Exception e) {e.printStackTrace();
} finally {try {// 因为即将要关闭消费者,所以要用同步提交保证提交成功consumer.commitSync();} finally {consumer.close();}
}

其他内容参考链接

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

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

相关文章

熵_相对熵_散度

1 信息量 意外越大&#xff0c;越不可能发生&#xff0c;概率就越小&#xff0c;信息量也就越大&#xff0c;也就是信息越多。比如说“今天肯定会天黑”&#xff0c;实现概率100%&#xff0c;说了和没说差不多&#xff0c;信息量就是0。 详见&#xff1a;2. 信息量 1.1 公式 …

流批一体计算引擎-4-[Flink]消费kafka实时数据

Python3.6.9 Flink 1.15.2消费Kafaka Topic PyFlink基础应用之kafka 通过PyFlink作业处理Kafka数据 1 环境准备 1.1 启动kafka (1)启动zookeeper zkServer.sh start(2)启动kafka cd /usr/local/kafka/ nohup ./bin/kafka-server-start.sh ./config/server.properties >&g…

【仿牛客网笔记】 Kafka,构建TB级异步消息系统——发送系统通知、显示系统通知

定义时间主题 判断消息内容是否为空&#xff0c;消息格式是否错误。 系统通知是后台发给用户 发送站内通知 构造一个Message对象 设置站内的值 判断是否有数据&#xff0c;然后放入到message中 对CommentController、LikeController、FollowController进行处理。 需要注入…

kafka集群压测与优化

影响kafka集群性能的因数有多个&#xff0c;网络带宽、cpu、内存、磁盘读写速度、副本数、分区数、broker数量、内存缓存等因素都会影响kafka集群的性能 1.优化kafka集群配置 server.properties配置文件优化 num.network.threads4 num.io.threads4 socket.send.buffer.bytes…

熵,信息熵,香农熵,微分熵,交叉熵,相对熵

2019-07-13 https://blog.csdn.net/landstream/article/details/82383503 https://blog.csdn.net/pipisorry/article/details/51695283 https://www.zhihu.com/question/41252833 https://cloud.tencent.com/developer/article/1397504 按顺序查看更容易理解 0、背景 在信息论中…

【Hive】云任务大量卡住故障分析

项目场景&#xff1a; 上一章节我们简单介绍到了JVM调优相关的知识&#xff0c;本章节结合日常故障处理进一步说明相关的使用 问题描述 在云上&#xff0c;hive任务出现大面积卡住的现象&#xff0c;但并无任何报错信息&#xff0c;具体如下&#xff1a; 原因分析&#xff1…

Linux服务器出现异常和卡顿排查思路和步骤

目录 前言一、查看内存使用情况二、查看磁盘使用情况三、top命令3.1 jmap分析堆内存配置信息和使用情况3.2 jstack分析线程的执行情况3.3 jstat查看各个区域占堆百分比 四、其他指令总结 前言 Linux 服务器出现异常和卡顿的原因有很多&#xff0c;以下是一些常见的原因&#x…

熵、交叉熵和散度

熵 自信息 I(x) - log p(x) 对于分布为P(x)的随机变量X&#xff0c;自信息的数学期望 即熵H(X)定义为&#xff1a; 熵越高&#xff0c;随机变量信息越高&#xff0c;反之越少。不同概率分布对应熵如下&#xff1a;P p()熵10001/21/41/41/31/31/3 概率分布越均匀&#xff0…

【腾讯轻量应用服务器上部署kafka并通过flink读取kafka数据】

环境准备 经过1个月的摸索&#xff0c;最终选择在腾讯云上搭建一个学习环境。当时选择原因还是新用户有优惠(150左右3年)&#xff0c;但现在看1核2g的配置勉强够用&#xff0c;建议后续小伙伴选择时最好是2核4g配置。 由于是单节点安装&#xff0c;需要准备如下资源&#xff1…

【Twitter Storm系列】flume-ng+Kafka+Storm+HDFS 实时系统搭建

技术交流群&#xff1a;59701880 深圳广州hadoop好友会 微信公众号&#xff1a;后续博客的文档都会转到微信公众号中。 一直以来都想接触Storm实时计算这块的东西&#xff0c;最近在群里看到上海一哥们罗宝写的FlumeKafkaStorm的实时日志流系统的搭建文档&#xff0c;自己也跟…

学习笔记之信息量、熵、KL散度、交叉熵的一些介绍

文章目录 信息量熵KL散度&#xff08;相对熵&#xff09;交叉熵参考 信息量 以前我也一直只是知道信息量的计算公式&#xff0c;也有想过为什么会是这样&#xff0c;但是因为要学的东西太多了&#xff0c;就没怎么深究&#xff0c;直到看了“交叉熵”如何做损失函数&#xff1…

AI产品经理是如何理解机器学习的(附注释)

本期目的&#xff1a;回顾之前敲代码的日常&#xff0c;利用生活场景帮助AI产品经理认识机器学习及学习过程中常用算法和评价指标。 适用读者&#xff1a;AI/数据产品经理。 阅读重点&#xff1a;品&#xff0c;细品&#xff0c;结合班级故事再品。 最近看了很多关于机器学习…

卢伟冰称小米今年库存会回到更健康水位 将加大门店整合调整

雷递网 乐天 3月25日 小米集团&#xff08;股票代码为&#xff1a;1810&#xff09;今日发布财报&#xff0c;财报显示&#xff0c;小米集团2022年营收为2800亿元&#xff0c;较上年同期的3283亿元下降14.7%。小米2022年经调净利为85亿元。 小米2022年第四季度营收为660.47亿元…

小米年营收2800亿:经调整利润85亿 电动汽车业务投入31亿

雷递网 雷建平 3月24日 小米集团&#xff08;股票代码为&#xff1a;1810&#xff09;今日发布财报&#xff0c;财报显示&#xff0c;小米集团2022年营收为2800亿元&#xff0c;较上年同期的3283亿元下降14.7%。 2022年&#xff0c;小米集团的境外市场收入为1378亿元&#xff0…

什么是低代码开发平台(apaas)?低代码开发平台的价值有哪些

手码6500字&#xff0c;带你快速看懂&#xff1a;什么是低代码开发平台&#xff08;apaas&#xff09;&#xff0c;低代码有哪些价值&#xff0c;以及低代码平台的使用逻辑和心得。 一、什么是低代码开发平台&#xff08;apaas&#xff09;&#xff1f; 低代码开发平台是一种a…

详解如何在ChatGPT内构建一个Python解释器

这篇文章主要为大家详细介绍了如何在ChatGPT内构建一个Python解释器&#xff0c;文中的示例代码讲解详细&#xff0c;具有一定的学习价值&#xff0c;需要的可以参考一下 目录 引用&#xff1a;Art Kulakov 《How to Build a Python Interpreter Inside ChatGPT》 这个灵感来自…

如何画各种“图”?

我们在写文档过程中&#xff0c;多多少少都会画一些图来说明程序的功能&#xff0c;这篇文章就来说明一些常用的"图"怎么画。 1&#xff0c;画图工具 常用的有这么几种 Visio&#xff1a;这是很常用的&#xff0c;基本上的图都可以用这个来画。StarUML&#xff1a…

patreon cg人物插画作品合集分享

1、wlop大神作品4K-8K精选无水印图片http://theme.chengxuz.com/265.html 2、加拿大女画师sakimi chan作品1-98期插画http://theme.chengxuz.com/250.html 3、画师Mirco Cabbia插画作品合集欣赏http://theme.chengxuz.com/306.html 4、画师Sciamano240插画作品分享http://th…

如何画场景插画?场景插画的起稿、构图技巧!

如何画场景插画&#xff1f;绘画初学者如何构图&#xff1f;绘画初学者如何起稿&#xff1f;学习绘画难吗&#xff1f;怎样才能学好绘画&#xff1f;想必这些都是绘画初学者们经常在想的问题吧&#xff0c;就是不知道如何才能绘画好一个场景&#xff0c;不知道如何起稿也不知道…

手把手教你实现手绘风格图形

大家好&#xff0c;我是 漫步&#xff0c;今天分享一篇高难度的图形绘制文章。 Rough.js[1]是一个手绘风格的图形库&#xff0c;提供了一些基本图形的绘制能力&#xff0c;比如&#xff1a;虽然笔者是个糙汉子&#xff0c;但是对这种可爱的东西都没啥抵抗力&#xff0c;这个库的…