diff --git a/lab-40/lab-40-kafka/pom.xml b/lab-40/lab-40-kafka/pom.xml
new file mode 100644
index 00000000..6050f8d9
--- /dev/null
+++ b/lab-40/lab-40-kafka/pom.xml
@@ -0,0 +1,68 @@
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.11.RELEASE
+
+
+ 4.0.0
+
+ lab-40-kafka
+
+
+
+
+
+ org.springframework.kafka
+ spring-kafka
+ 2.2.11.RELEASE
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+
+ io.zipkin.brave
+ brave
+
+
+ io.zipkin.reporter2
+ zipkin-sender-okhttp3
+
+
+
+
+
+ io.zipkin.brave
+ brave-instrumentation-spring-webmvc
+
+
+
+ io.zipkin.brave
+ brave-instrumentation-kafka-clients
+
+
+
+
+
+
+
+
+ io.zipkin.brave
+ brave-bom
+ 5.9.1
+ pom
+ import
+
+
+
+
+
diff --git a/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/KafkaApplication.java b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/KafkaApplication.java
new file mode 100644
index 00000000..0213566a
--- /dev/null
+++ b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/KafkaApplication.java
@@ -0,0 +1,13 @@
+package cn.iocoder.springboot.lab40.skywalkingdemo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class KafkaApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(KafkaApplication.class, args);
+ }
+
+}
diff --git a/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/config/SpringMvcConfiguration.java b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/config/SpringMvcConfiguration.java
new file mode 100644
index 00000000..09fd2000
--- /dev/null
+++ b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/config/SpringMvcConfiguration.java
@@ -0,0 +1,25 @@
+package cn.iocoder.springboot.lab40.skywalkingdemo.config;
+
+import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+@Import(SpanCustomizingAsyncHandlerInterceptor.class) // 创建拦截器 SpanCustomizingAsyncHandlerInterceptor Bean
+public class SpringMvcConfiguration implements WebMvcConfigurer {
+
+ @Autowired
+ public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;
+
+ /**
+ * Decorates server spans with application-defined web tags
+ */
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) { // 记录 SpringMVC 相关信息到 Span 中
+ registry.addInterceptor(webMvcTracingCustomizer);
+ }
+
+}
diff --git a/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/config/ZipkinConfiguration.java b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/config/ZipkinConfiguration.java
new file mode 100644
index 00000000..93c74652
--- /dev/null
+++ b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/config/ZipkinConfiguration.java
@@ -0,0 +1,138 @@
+package cn.iocoder.springboot.lab40.skywalkingdemo.config;
+
+import brave.CurrentSpanCustomizer;
+import brave.SpanCustomizer;
+import brave.Tracing;
+import brave.http.HttpTracing;
+import brave.kafka.clients.KafkaTracing;
+import brave.servlet.TracingFilter;
+import org.apache.kafka.clients.consumer.Consumer;
+import org.apache.kafka.clients.producer.Producer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaProducerFactory;
+import org.springframework.kafka.core.ProducerFactory;
+import zipkin2.Span;
+import zipkin2.reporter.AsyncReporter;
+import zipkin2.reporter.Sender;
+import zipkin2.reporter.okhttp3.OkHttpSender;
+
+import javax.servlet.Filter;
+import java.util.Properties;
+
+@Configuration
+public class ZipkinConfiguration {
+
+ // ==================== 通用配置 ====================
+
+ /**
+ * Configuration for how to send spans to Zipkin
+ */
+ @Bean
+ public Sender sender() { // Sender 采用 HTTP 通信方式
+ return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans");
+ }
+
+ /**
+ * Configuration for how to buffer spans into messages for Zipkin
+ */
+ @Bean
+ public AsyncReporter spanReporter() { // 异步 Reporter
+ return AsyncReporter.create(sender());
+ }
+
+ /**
+ * Controls aspects of tracing such as the service name that shows up in the UI
+ */
+ @Bean
+ public Tracing tracing(@Value("${spring.application.name}") String serviceName) {
+ return Tracing.newBuilder()
+ .localServiceName(serviceName) // 应用名
+ .spanReporter(this.spanReporter()).build();
+ }
+
+ /**
+ * Allows someone to add tags to a span if a trace is in progress
+ */
+ @Bean
+ public SpanCustomizer spanCustomizer(Tracing tracing) {
+ return CurrentSpanCustomizer.create(tracing);
+ }
+
+ // ==================== HTTP 相关 ====================
+
+ /**
+ * Decides how to name and tag spans. By default they are named the same as the http method
+ */
+ @Bean
+ public HttpTracing httpTracing(Tracing tracing) {
+ return HttpTracing.create(tracing);
+ }
+
+ /**
+ * Creates server spans for http requests
+ */
+ @Bean
+ public Filter tracingFilter(HttpTracing httpTracing) { // 拦截请求,记录 HTTP 请求的链路信息
+ return TracingFilter.create(httpTracing);
+ }
+
+ // ==================== SpringMVC 相关 ====================
+ // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) 。因为 SpanCustomizingAsyncHandlerInterceptor 未提供 public 构造方法
+
+ // ==================== Kafka 相关 ====================
+
+ @Bean
+ public KafkaTracing kafkaTracing(Tracing tracing) {
+ return KafkaTracing.newBuilder(tracing)
+ .remoteServiceName("demo-mq-kafka")
+ .build();
+ }
+
+ @Bean
+ public ProducerFactory, ?> kafkaProducerFactory(KafkaProperties properties, KafkaTracing kafkaTracing) {
+ DefaultKafkaProducerFactory, ?> factory = new DefaultKafkaProducerFactory(properties.buildProducerProperties()) {
+
+ @Override
+ public Producer createProducer() {
+ // 创建默认的 Producer
+ Producer, ?> producer = super.createProducer();
+ // 创建可链路追踪的 Producer
+ return kafkaTracing.producer(producer);
+ }
+
+ };
+
+ String transactionIdPrefix = properties.getProducer().getTransactionIdPrefix();
+ if (transactionIdPrefix != null) {
+ factory.setTransactionIdPrefix(transactionIdPrefix);
+ }
+
+ return factory;
+ }
+
+ @Bean
+ public ConsumerFactory, ?> kafkaConsumerFactory(KafkaProperties properties, KafkaTracing kafkaTracing) {
+ return new DefaultKafkaConsumerFactory(properties.buildConsumerProperties()) {
+
+ @Override
+ public Consumer, ?> createConsumer(String groupId, String clientIdPrefix, String clientIdSuffix) {
+ return this.createConsumer(groupId, clientIdPrefix, clientIdSuffix, null);
+ }
+
+ @Override
+ public Consumer, ?> createConsumer(String groupId, String clientIdPrefix, final String clientIdSuffixArg, Properties properties) {
+ // 创建默认的 Consumer
+ Consumer, ?> consumer = super.createConsumer(groupId, clientIdPrefix, clientIdSuffixArg, properties);
+ // 创建可链路追踪的 Consumer
+ return kafkaTracing.consumer(consumer);
+ }
+
+ };
+ }
+
+}
diff --git a/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/consumer/DemoConsumer.java b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/consumer/DemoConsumer.java
new file mode 100644
index 00000000..ccb1b38f
--- /dev/null
+++ b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/consumer/DemoConsumer.java
@@ -0,0 +1,20 @@
+package cn.iocoder.springboot.lab40.skywalkingdemo.consumer;
+
+import cn.iocoder.springboot.lab40.skywalkingdemo.message.DemoMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DemoConsumer {
+
+ private Logger logger = LoggerFactory.getLogger(getClass());
+
+ @KafkaListener(topics = DemoMessage.TOPIC,
+ groupId = "demo-consumer-group-" + DemoMessage.TOPIC)
+ public void onMessage(DemoMessage message) {
+ logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message);
+ }
+
+}
diff --git a/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/controller/DemoController.java b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/controller/DemoController.java
new file mode 100644
index 00000000..3d89fdb3
--- /dev/null
+++ b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/controller/DemoController.java
@@ -0,0 +1,28 @@
+package cn.iocoder.springboot.lab40.skywalkingdemo.controller;
+
+import cn.iocoder.springboot.lab40.skywalkingdemo.producer.DemoProducer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.concurrent.ExecutionException;
+
+@RestController
+@RequestMapping("/demo")
+public class DemoController {
+
+ @Autowired
+ private DemoProducer producer;
+
+ @GetMapping("/kafka")
+ public String echo() throws ExecutionException, InterruptedException {
+ this.sendMessage(1);
+ return "kafka";
+ }
+
+ public void sendMessage(Integer id) throws ExecutionException, InterruptedException {
+ producer.syncSend(id);
+ }
+
+}
diff --git a/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/message/DemoMessage.java b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/message/DemoMessage.java
new file mode 100644
index 00000000..90050c82
--- /dev/null
+++ b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/message/DemoMessage.java
@@ -0,0 +1,31 @@
+package cn.iocoder.springboot.lab40.skywalkingdemo.message;
+
+/**
+ * 示例 Message 消息
+ */
+public class DemoMessage {
+
+ public static final String TOPIC = "DEMO";
+
+ /**
+ * 编号
+ */
+ private Integer id;
+
+ public DemoMessage setId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ @Override
+ public String toString() {
+ return "DemoMessage{" +
+ "id=" + id +
+ '}';
+ }
+
+}
diff --git a/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/producer/DemoProducer.java b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/producer/DemoProducer.java
new file mode 100644
index 00000000..2d49cccf
--- /dev/null
+++ b/lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/skywalkingdemo/producer/DemoProducer.java
@@ -0,0 +1,25 @@
+package cn.iocoder.springboot.lab40.skywalkingdemo.producer;
+
+import cn.iocoder.springboot.lab40.skywalkingdemo.message.DemoMessage;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.kafka.support.SendResult;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.concurrent.ExecutionException;
+
+@Component
+public class DemoProducer {
+
+ @Resource
+ private KafkaTemplate