diff --git a/ReadMe.md b/ReadMe.md index 6aabce3..84572da 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -30,6 +30,8 @@

+

+ # Java基础 ## 基础知识 @@ -394,6 +396,25 @@ todo # 消息队列 ## Kafka +* [消息队列kafka详解:Kafka快速上手(Java版)](docs/mq/kafka/消息队列kafka详解:Kafka快速上手(Java版).md) +* [消息队列kafka详解:Kafka一条消息存到broker的过程](docs/mq/kafka/消息队列kafka详解:Kafka一条消息存到broker的过程.md) +* [消息队列kafka详解:消息队列kafka详解:Kafka介绍](docs/mq/kafka/消息队列kafka详解:Kafka介绍.md) +* [消息队列kafka详解:Kafka原理分析总结篇](docs/mq/kafka/消息队列kafka详解:Kafka原理分析总结篇.md) +* [消息队列kafka详解:Kafka常见命令及配置总结](docs/mq/kafka/消息队列kafka详解:Kafka常见命令及配置总结.md) +* [消息队列kafka详解:Kafka架构介绍](docs/mq/kafka/消息队列kafka详解:Kafka架构介绍.md) +* [消息队列kafka详解:Kafka的集群工作原理](docs/mq/kafka/消息队列kafka详解:Kafka的集群工作原理.md) +* [消息队列kafka详解:Kafka重要知识点+面试题大全](docs/mq/kafka/消息队列kafka详解:Kafka重要知识点+面试题大全.md) +* [消息队列kafka详解:如何实现延迟队列](docs/mq/kafka/消息队列kafka详解:如何实现延迟队列.md) +* [消息队列kafka详解:如何实现死信队列](docs/mq/kafka/消息队列kafka详解:如何实现死信队列.md) + +## RocketMQ +* [RocketMQ系列:事务消息(最终一致性)](docs/mq/RocketMQ/RocketMQ系列:事务消息(最终一致性).md) +* [RocketMQ系列:基本概念](docs/mq/RocketMQ/RocketMQ系列:基本概念.md) +* [RocketMQ系列:广播与延迟消息](docs/mq/RocketMQ/RocketMQ系列:广播与延迟消息.md) +* [RocketMQ系列:批量发送与过滤](docs/mq/RocketMQ/RocketMQ系列:批量发送与过滤.md) +* [RocketMQ系列:消息的生产与消费](docs/mq/RocketMQ/RocketMQ系列:消息的生产与消费.md) +* [RocketMQ系列:环境搭建](docs/mq/RocketMQ/RocketMQ系列:环境搭建.md) +* [RocketMQ系列:顺序消费](docs/mq/RocketMQ/RocketMQ系列:顺序消费.md) # 大后端 * [后端技术杂谈开篇:云计算,大数据与AI的故事](docs/backend/后端技术杂谈开篇:云计算,大数据与AI的故事.md) diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConfig.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConfig.md" index 67c9c5a..cb6a94c 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConfig.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConfig.md" @@ -821,4 +821,13 @@ config: 4\. ʹٴηʡhttp://localhost:3366/getConfigͼ ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10194222Y-15.png) -ͼ17Spring Cloud Bus ֪ͨ \ No newline at end of file +ͼ17Spring Cloud Bus ֪ͨ + + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConsul.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConsul.md" index 730cda5..85fcf19 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConsul.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConsul.md" @@ -239,3 +239,10 @@ Spring Cloud Consul # ĿԴַ https://github.com/macrozheng/springcloud-learning +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudEureka.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudEureka.md" index b1a2d68..960e5c7 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudEureka.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudEureka.md" @@ -1041,4 +1041,12 @@ enable-self-preservation: false # false ͼ 11 Կݣ * DS Replicas ѡ˺ɫϢEMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.ָϢ Eureka ұƴڿ״̬Ѿ -* micro-service-cloud-provider-dept-8001 ķϢȻ Eureka Server עУδƳ \ No newline at end of file +* micro-service-cloud-provider-dept-8001 ķϢȻ Eureka Server עУδƳ + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudGateway.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudGateway.md" index 8b7ad04..e99968d 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudGateway.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudGateway.md" @@ -496,9 +496,18 @@ eureka: 2\. ```micro-service-cloud-gateway-9527ʹʡhttp://eureka7001.com:9527/dept/listǻַᷢʱ 406 󣬿̨¡ ```2021-10-21 16:25:39.450 INFO 19116 --- [ctor-http-nio-4] net.biancheng.c.filter.MyGlobalFilter : Thu Oct 21 16:25:39 CST 2021Զȫֹ MyGlobalFilter -2021-10-21 16:25:39.451 INFO 19116 --- [ctor-http-nio-4] net.biancheng.c.filter.MyGlobalFilter : uname Ϊ null``` +2021-10-21 16:25:39.451 INFO 19116 --- [ctor-http-nio-4] net.biancheng.c.filter.MyGlobalFilter : uname Ϊ null +``` 3\. ʹʡhttp://eureka7001.com:9527/dept/list?uname=123,ͼ ![Զȫع](http://c.biancheng.net/uploads/allimg/211210/101P43096-6.png) -ͼ7Զȫع \ No newline at end of file +ͼ7Զȫع + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudHystrix.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudHystrix.md" index adddc7d..1a26d68 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudHystrix.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudHystrix.md" @@ -1109,4 +1109,12 @@ public class HystrixDashboardConfig { 8\. ʹηʡhttp://eureka7001.com:8004/dept/hystrix/circuit/1 http://eureka7001.com:8004/dept/hystrix/circuit/-1鿴 Hystrix ҳ棬ͼ ![Hystrix 8004 ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10162345J-15.png) -ͼ16Hystrix ط \ No newline at end of file +ͼ16Hystrix ط + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudLoadBalancer.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudLoadBalancer.md" index 9289412..1a35579 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudLoadBalancer.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudLoadBalancer.md" @@ -336,3 +336,11 @@ LoadBalancerΪ # ĿԴַ https://github.com/macrozheng/springcloud-learning/tree/master/nacos-loadbalancer-service + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudOpenFeign.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudOpenFeign.md" index d2e6a26..50c14d5 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudOpenFeign.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudOpenFeign.md" @@ -479,4 +479,11 @@ Logger.Level 2021-10-12 14:33:07.983 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] 2021-10-12 14:33:07.991 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] [{"deptNo":1,"deptName":"","dbSource":"bianchengbang_jdbc"},{"deptNo":2,"deptName":"²","dbSource":"bianchengbang_jdbc"},{"deptNo":3,"deptName":"","dbSource":"bianchengbang_jdbc"},{"deptNo":4,"deptName":"г","dbSource":"bianchengbang_jdbc"},{"deptNo":5,"deptName":"ά","dbSource":"bianchengbang_jdbc"}] 2021-10-12 14:33:07.991 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] <--- END HTTP (341-byte body)``` -``` \ No newline at end of file +``` +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudRibbon.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudRibbon.md" index 7d3f979..74eff0a 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudRibbon.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudRibbon.md" @@ -654,4 +654,12 @@ public class MicroServiceCloudConsumerDept80Application { ![Ƹؾ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10122152E-5.gif) ͼ6Ƹؾ -ͨͼ 6 dbSource ֶȡֵı仯ԿǶƵĸؾѾЧ \ No newline at end of file +ͨͼ 6 dbSource ֶȡֵı仯ԿǶƵĸؾѾЧ + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudSleuth.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudSleuth.md" index e3f2000..45bc82f 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudSleuth.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudSleuth.md" @@ -131,4 +131,12 @@ springcloud-learning ## [#](https://www.macrozheng.com/cloud/sleuth.html#%E9%A1%B9%E7%9B%AE%E6%BA%90%E7%A0%81%E5%9C%B0%E5%9D%80)ĿԴַ -[https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) \ No newline at end of file +[https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudZuul.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudZuul.md" index 7bca86e..9040110 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudZuul.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudZuul.md" @@ -384,3 +384,9 @@ Zuul #ĿԴַ https://github.com/macrozheng/springcloud-learning +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloud\346\246\202\350\277\260.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloud\346\246\202\350\277\260.md" index fb27ab5..a525595 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloud\346\246\202\350\277\260.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloud\346\246\202\350\277\260.md" @@ -134,4 +134,10 @@ Spring Boot } -```` \ No newline at end of file +```` +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\346\263\250\345\206\214.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\346\263\250\345\206\214.md" index 3569058..1a59865 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\346\263\250\345\206\214.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\346\263\250\345\206\214.md" @@ -346,4 +346,12 @@ task Ҳڶʼʱͽһ̳߳أȥnotifierӦķrunrunġͻʵʱ첽СдĺôǽдʹȫˡͨܵڴУ飬ĺô1 -![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e27c8e72b30845c3ab9346b89932fb42.png "image.png") \ No newline at end of file +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e27c8e72b30845c3ab9346b89932fb42.png "image.png") + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" index 0bc283c..dc517a0 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" @@ -491,4 +491,11 @@ NACOS ȻҲһЩɺӵȱ㣬磬ע͹٣뻹кܴعռ䣬tenantnamespaceʹá -Spring Cloud Alibaba NacosĽܵ˾ͽˣϣ \ No newline at end of file +Spring Cloud Alibaba NacosĽܵ˾ͽˣϣ +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\351\205\215\347\275\256\344\270\255\345\277\203.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\351\205\215\347\275\256\344\270\255\345\277\203.md" index 2ed31c8..7515d8b 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\351\205\215\347\275\256\344\270\255\345\277\203.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\351\205\215\347\275\256\344\270\255\345\277\203.md" @@ -256,4 +256,11 @@ public class ConfigServerDemo { ߣƵŶ ӣhttps://juejin.cn/post/6999814668390760484 Դϡ -ȨСҵתϵ߻Ȩҵתע \ No newline at end of file +ȨСҵתϵ߻Ȩҵתע +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" index 35fcfef..9be0e22 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -524,4 +524,11 @@ public synchronized void start() throws MQClientException { ߣҶ컨 ӣhttps://www.jianshu.com/p/8dd4cfeae39d Դ -ȨСҵתϵ߻Ȩҵתע \ No newline at end of file +ȨСҵתϵ߻Ȩҵתע +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSeata\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSeata\346\272\220\347\240\201\345\210\206\346\236\220.md" index 1287eef..19ce963 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSeata\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSeata\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -994,4 +994,12 @@ public BranchStatus branchRollback(BranchType branchType, String xid, long branc -ջعõUndoLogManager.undo(dataSourceProxy, xid, branchId);![SpringCloud AlibabaϵС17Seata ATģʽԴ£-Դ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4842a0701546824cf2720855d8310a1274c576.jpg "SpringCloud AlibabaϵС17Seata ATģʽԴ£-Դ")жundologǷڣɾӦundologһύseataATģʽԴϡ \ No newline at end of file +ջعõUndoLogManager.undo(dataSourceProxy, xid, branchId);![SpringCloud AlibabaϵС17Seata ATģʽԴ£-Դ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4842a0701546824cf2720855d8310a1274c576.jpg "SpringCloud AlibabaϵС17Seata ATģʽԴ£-Դ")жundologǷڣɾӦundologһύseataATģʽԴϡ + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" index 0d6c22c..9fc4976 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -650,3 +650,10 @@ public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudConfig\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudConfig\346\272\220\347\240\201\345\210\206\346\236\220.md" index 60d1f61..4ae8cc8 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudConfig\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudConfig\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -1038,4 +1038,12 @@ if (!remoteProperties.isAllowOverride() || (!remoteProperties.isOverrideNone() ߣӵ¶_to ӣhttps://www.jianshu.com/p/60c6ab0e79d5 Դ -ȨСҵתϵ߻Ȩҵתע \ No newline at end of file +ȨСҵתϵ߻Ȩҵתע + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudEureka\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudEureka\346\272\220\347\240\201\345\210\206\346\236\220.md" index 8ad8c31..0236a13 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudEureka\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudEureka\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -1773,4 +1773,12 @@ Value getValue(final Key key, boolean useReadOnlyCache) { } return payload; } -``` \ No newline at end of file +``` + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudGateway\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudGateway\346\272\220\347\240\201\345\210\206\346\236\220.md" index 9a6c22c..80db850 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudGateway\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudGateway\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -404,4 +404,12 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 6\. ִйе return chain.filter(exchange); } -``` \ No newline at end of file +``` + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudHystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudHystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" index c9323b5..87b59fb 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudHystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudHystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -1183,5 +1183,10 @@ circuitBreaker.allowRequest() յõԼҵ߼ - -ܽһҵ߼ͼ +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudLoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudLoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" index 9704860..f66f6ba 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudLoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudLoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -659,4 +659,12 @@ public class CustomLoadBalancerConfiguration { ![image-20220509003807968](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c72e3f02f7e0d5d3343f8ae9c464b69c.png) -![image-20220509003927550](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/59796bbbd6b3524e32759d42b622f1bc.png) \ No newline at end of file +![image-20220509003927550](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/59796bbbd6b3524e32759d42b622f1bc.png) + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudOpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudOpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" index 7a54f90..17db744 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudOpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudOpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -893,3 +893,10 @@ Response convertResponse(HttpURLConnection connection, Request request) throws I +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRibbon\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRibbon\346\272\220\347\240\201\345\210\206\346\236\220.md" index 57606b3..d06f945 100644 --- "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRibbon\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRibbon\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -944,4 +944,12 @@ public interface ServerList { getInitialListOfServersǻȡʼķб getUpdatedListOfServersǻȡµķб ServerListжʵ࣬õĸأ -EurekaRibbonClientConfigurationҵRibbonEurekaϵԶ࣬ĿǰûEurekaͨļãԻConfigurationBasedServerListࡣ \ No newline at end of file +EurekaRibbonClientConfigurationҵRibbonEurekaϵԶ࣬ĿǰûEurekaͨļãԻConfigurationBasedServerListࡣ + +# ο +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git a/docs/monitor/Spring Actuator.md b/docs/monitor/Spring Actuator.md new file mode 100644 index 0000000..a1ee5ef --- /dev/null +++ b/docs/monitor/Spring Actuator.md @@ -0,0 +1,578 @@ +## ǰ + +΢ϵͳܹУļDZزٵġĿǰ΢ӦǻSpring CloudϵУҲ˵ǻSpring BootϵеġʱʹSpring Boot Actuator΢ļأȫ棬ҷdz㡣 + +ƪ¡[Spring Boot Actuatorɣѵã](https://link.juejin.cn?target=https%3A%2F%2Fmp.weixin.qq.com%2Fs%2FBaNQWygQb8UXxktrXetOcw "https://mp.weixin.qq.com/s/BaNQWygQb8UXxktrXetOcw")ѾνActuatorɵSpring BootĿУҽԶEndpoint˵㣩˵룬ôƪأǽActuatorԭ˵Ĺܼʹó + +## Endpoints + +Actuatorν Endpoints Ϊ˵㣩ṩⲿӦóзʺͽĹܡ ˵/health˵ṩӦýϢmetrics ˵ṩӦóָ꣨JVM ڴʹáϵͳCPUʹõȣϢ + +ActuatorԭĶ˵ɷΪࣺ + +* ӦࣺȡӦóмصӦáԶñSpring BootӦصϢ +* ָࣺȡӦóйڼصĶָ꣬磺ڴϢ̳߳ϢHTTPͳƵȡ +* ࣺṩ˶ӦõĹرյȲ๦ܡ + +ͬ汾Actuatorṩԭ˵룬ʹõĹʹð汾ĹٷĵΪ׼ͬʱÿԭĶ˵㶼ͨĽûá + +Actuator 2.x Ĭ϶˵/actuatorǰ׺ͬʱĬֻ¶˵Ϊ/actuator/health /actuator/infoڶ˵㱩¶ãɲοǰһƪ¡Spring Boot 2.2.2.RELEASE汾ص㽲ÿ˵ĹܺӦó + +## actuator˵ + +Actuator 2.xĬ϶˵㣬չʾĿǰӦб¶Ķ˵ܣΪö˵Ŀ¼ + +URL[http://localhost:8080/actuator](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator "http://localhost:8080/actuator") Ӧչʾͼ + +![image-20230530233537559](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233537559.png) + +ֻչʾһֵĶ˵㣬ؽΪIJ-Handler˸ʽͨactuatorֱ۵ĿĿǰЩ˵㣬ԼЩ˵ƺ· + +ǾͰʾactuator˵չʾбһܡ + +## auditevents˵ + +auditevents˵ʾӦñ¶¼ (֤롢ʧ)ʹǴж˵㣬ĬҲǿ˵ġΪʹǰҪSpringдһΪAuditEventRepositoryBeanġ + +鿴ϴ̳̣϶ǽauditevents˵㹦ܣδչʾʵ߾෽ԣڸдһ + +漰Ȩ֤Ҫspring-boot-starter-security + +```` + + org.springframework.boot + spring-boot-starter-security +` +```` + +DzģҪsecurityãȻAuthorizationAuditListener,AuthenticationAuditListener ʲô¼? ,Ǽ´룺 + + +```` +@Configuration +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + + auth.inMemoryAuthentication() + .withUser("admin") + .password(bcryptPasswordEncoder().encode("admin")) + .roles("admin"); + } +```` + +```` + @Bean + public PasswordEncoder bcryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } +} +```` + +securityĬϵĵ¼Ȩ޿ƣҲ˵еķʶҪе¼¼ûΪadmin + +⣬ǰᵽҪõAuditEventRepositoryBeanʼһӦBean + + + + +```` +@Configuration +public class AuditEventConfig { + + @Bean + public InMemoryAuditEventRepository repository(){ + return new InMemoryAuditEventRepository(); + } +} +```` + +InMemoryAuditEventRepositoryAuditEventRepositoryӿڵΨһʵࡣ + +Ŀauditevents˵ˡ[http://localhost:8080/actuator](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator "http://localhost:8080/actuator") ,ʱתSecurityṩĵ¼ҳ棺 + +![image-20230530233604253](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233604253.png) + +ָû룬¼ɹת/actuatorҳ棺 + +![image-20230530233625068](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233625068.png) + +Կauditevents˵Ѿɹʾˡ¿ҳ[http://localhost:8080/actuator/auditevents](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fauditevents "http://localhost:8080/actuator/auditevents") չʾ£ + +![image-20230530233716752](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233716752.png) + +ԿѾ¼Ȩص¼еһ¼ֱӷactuator˵ʱ֮ǰΪȨ棬¼Ϊ"AUTHORIZATION_FAILURE"Ҳ֤ʧܡʱת¼ҳ棬Ȼڵ¼ҳû룬¼ɹӦ¼Ϊ"AUTHENTICATION_SUCCESS" + +Ҳ˵auditevents¼û֤¼ϵͳص¼Ϣʱ֤û¼͡ʵַsessionIdȡ + +ʾԴַ[github.com/secbr/sprin](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fsecbr%2Fspringboot-all%2Ftree%2Fmaster%2Fspringboot-actuator-auditevents "https://github.com/secbr/springboot-all/tree/master/springboot-actuator-auditevents") + +## beans˵ + +/beans˵᷵Springbeanı͡ǷϢ + +·Ϊ[http://localhost:8080/actuator/beans](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fbeans "http://localhost:8080/actuator/beans") Χ£ + +![image-20230530233748286](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233748286.png) + +˵չʾĿǰSpringгʼBeanһ£һBeanȷǷɹʵDzǾͿͨ˿ڲѯһأ + +ĿжһTestControllerעһUserService + + +```` +@Controller +public class TestController { + + @Resource + private UserService userService; +} +```` + +ʸö˵㣬ῴϢ + +![image-20230530233805161](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233805161.png) + +ԿTestControllerʵˣUserService + +## caches˵ + +caches˵Ҫڱ¶ӦóеĻ塣Spring BootṩCacheչʾһʵ + +Ŀмspring-boot-starter-cache + + +```` + + org.springframework.boot + spring-boot-starter-cache + +```` + +Ȼ@EnableCaching湦ܡ + +һCacheController䷽queryAllʹûƣ + + +```` +@RestController +public class CacheController { + + @RequestMapping("/queryAll") + @Cacheable(value = "queryAll") + public Map queryAll() { + Map map = new HashMap<>(); + map.put("1", "Tom"); + map.put("2", "Steven"); + return map; + } +} +```` + +ʹ@Cacheableעʵֻ湦ܣkeyΪqueryAllʱ[http://localhost:8080/actuator/caches](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fcaches "http://localhost:8080/actuator/caches") չʾĸݣ沢ûл档 + +һ[http://localhost:8080/queryAll](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2FqueryAll "http://localhost:8080/queryAll") ҲǴһ»ݵɡʱٷӣԿӦóеĻϢˣ + +![image-20230530233852486](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233852486.png) + +ԿصݲչʾӦóĻͬʱҲչʾ˻Keyͻݴ洢Ϣ + +## caches-cache˵ + +caches-cache˵Ƕcaches˵չcaches˵չʾеĻϢֱӿһϢʹcaches-cache˵㡣 + +ʵURLΪ[http://localhost:8080/actuator/caches/{cache}](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fcaches%2F%257Bcache%257D "http://localhost:8080/actuator/caches/%7Bcache%7D") дڵֵ滻Ϊkey + + + + +`http://localhost:8080/actuator/caches/queryAll` + +ռλqueryAllkeyִн£ + +![image-20230530233906164](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233906164.png) + +ԿֻѯָĻϢƣkeyĴ洢͡ + +## health˵ + +health˵Ӧõ״̬Ƶʹõһ˵㡣Ӧʵ״̬ԼӦòԭ򣬱ݿӡ̿ռ䲻ȡ + +ʵַ[http://localhost:8080/actuator/health](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fhealth "http://localhost:8080/actuator/health") + +չʾ + +`{ +"status": "UP" +}` + +ʵڼ򵥣Ŀаݿɽȥ + +` + +```` + + org.springframework.boot + spring-boot-starter-jdbc + + + mysql + mysql-connector-java +` +```` + +Ȼapplicationļнã + + + +``` +spring: + datasource: + url: jdbc:mysql://xxx:3333/xxx?characterEncoding=utf8&serverTimezone=Asia/Shanghai + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver +``` + +ͬʱҪapplicationļһmanagement.endpoint.health.show-detailsֵѡ + +* never չʾϸϢup down ״̬Ĭã +* when-authorizedϸϢչʾ֤ͨûȨĽɫͨmanagement.endpoint.health.roles ã +* alwaysû¶ϸϢ + +ĬֵneverֱӷʿֻUPDOWNڼݿ⣬ͬʱѸֵΪalwaysһ飺 + +![image-20230530233934501](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233934501.png) + +Կ״̬ΪUPΪUPݿMYSQLݿΪSELECT 1ͬʱչʾ˴Ϣping״̬ + +ǰݿûĴʿɵã + +![image-20230530233951145](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233951145.png) + +״̬ΪDOWNdb⣬״̬ΪDOWNerrorչʾԿǽʱˡʵУǿͨhealth˿ڼݿ⡢RedisMongoDB̵ȽActuatorԤĴΪDataSourceHealthIndicator, DiskSpaceHealthIndicator, MongoHealthIndicator, RedisHealthIndicatorȡ + +ÿָ궼ԵĽп͹رգݿΪ + + +```` +management: + health: + db: + enabled: true` +```` + +## info˵ + +/info ˵鿴ļ applicationinfoͷϢĬ applicationвû info ڵãĬΪա + +applicationã + + + +```` +info: + user: + type: ں + name: ӽ + wechat: zhuan2quan +```` + +[http://localhost:8080/actuator/info](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Finfo "http://localhost:8080/actuator/info") չʾ£ + +![image-20230530234019487](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234019487.png) + +## conditions˵ + +Spring BootṩԶùܣʹdz㡣ЩԶʲôЧģǷЧDZȽŲġʱʹ conditions Ӧʱ鿴ijʲôЧΪʲôûЧ + +URL[http://localhost:8080/actuator/conditions](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fconditions "http://localhost:8080/actuator/conditions") ַϢ£ + +![image-20230530234053134](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234053134.png) + +ԿijԶӦЧʾϢ + +## shutdown˵ + +shutdown˵ڲ˵㣬Źر Spring Boot ӦáҪļп + + +```` +management: + endpoint: + shutdown: + enabled: true +```` + +ö˵ֻ֧POSTִؽ£ + + +``` +curl -X POST "http://localhost:8080/actuator/shutdown" +{ + "message": "Shutting down, bye..." +} +``` + +ִ֮󣬻ᷢӦóѾرˡڸö˵رӦóʹҪСġ + +## configprops˵ + +Spring BootĿУǾõ@ConfigurationPropertiesעעһЩԣconfigprops˵ʾЩעעࡣ + +ǰinfoãǾͿԶһInfoProperties + + +```` +@Component +@ConfigurationProperties(prefix = "info") +public class InfoProperties { + + private String type; + + private String name; + + private String wechat; + + // ʡgetter/setter +} +```` + +URL[http://localhost:8080/actuator/configprops](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fconfigprops "http://localhost:8080/actuator/configprops") Ϣ£ + +![image-20230530234110515](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234110515.png) + +ԿϵͳĬϼɵϢԿԶϢҪעǶӦҪʵ@Componentܹ + +ԶзBeanơǰ׺ProjectInfoPropertiesϢ + +## env˵ + +env˵ڻȡȫԣapplicationļеݡϵͳȡ + +URL[http://localhost:8080/actuator/envزϢ](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fenv%25EF%25BC%258C%25E8%25BF%2594%25E5%259B%259E%25E9%2583%25A8%25E5%2588%2586%25E4%25BF%25A1%25E6%2581%25AF%25EF%25BC%259A "http://localhost:8080/actuator/env%EF%BC%8C%E8%BF%94%E5%9B%9E%E9%83%A8%E5%88%86%E4%BF%A1%E6%81%AF%EF%BC%9A") + +![image-20230530234200949](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234200949.png) + +## env-toMatch˵ + +env-toMatch˵cachescaches-cacheƣһǻȡеģһǻȡָġenv-toMatch˵ǻȡָkeyĻԡ + +ʽΪ[http://localhost:8080/actuator/env/{toMatch}](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fenv%2F%257BtoMatch%257D%25E3%2580%2582 "http://localhost:8080/actuator/env/%7BtoMatch%7D%E3%80%82") ʵURL[http://localhost:8080/actuator/env/info.user.name](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fenv%2Finfo.user.name "http://localhost:8080/actuator/env/info.user.name") ؽͼ + +![image-20230530234238073](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234238073.png) + +ϢԵԴvalueֵϢ + +## loggers˵ + +/loggers ˵㱩¶˳ڲõ logger Ϣͬpackageͬ־Ϣ + +URL[http://localhost:8080/actuator/loggers](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Floggers "http://localhost:8080/actuator/loggers") ַؽ + +![image-20230530234301625](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234301625.png) + +## loggers-name˵ + +loggers-name˵Ҳlogger˵ϸ֣ͨnameijһlogger + +ʽ[http://localhost:8080/actuator/loggers/{name}](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Floggers%2F%257Bname%257D "http://localhost:8080/actuator/loggers/%7Bname%7D") ʾURL[http://localhost:8080/actuator/loggers/com.secbro2.SpringbootActuatorApplication](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Floggers%2Fcom.secbro2.SpringbootActuatorApplication "http://localhost:8080/actuator/loggers/com.secbro2.SpringbootActuatorApplication") ؽ£ + + + +`{ +"configuredLevel": null, +"effectiveLevel": "INFO" +}` + +Կ־ΪINFO + +## heapdump˵ + +heapdump˵᷵һJVM dumpͨJVMԴļعVisualVMɴ򿪴ļ鿴ڴաڴŻڶջŲ + +URL[http://localhost:8080/actuator/heapdump](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fheapdump "http://localhost:8080/actuator/heapdump") MacϵͳʻһΪheapdumpļ޺׺30M + +ִjvisualvmVisualVMεļװ롱ǵļҪѡ񡰶Dump(_.hprof,_.*)Ȼѡheapdump + +![image-20230530234346098](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234346098.png) + +ʱͨжջϢķˡķṩ˼Ϊķʽ + +## threaddump˵ + +/threaddump ˵ɵǰ̻߳Ŀաճλʱ鿴̵߳dzãҪչʾ߳߳ID̵߳״̬ǷȴԴϢ + +URL[http://localhost:8080/actuator/threaddump](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fthreaddump "http://localhost:8080/actuator/threaddump") ַؽ + +![image-20230530234405331](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234405331.png) + +ǿͨ߳̿Ų⡣ + +## metrics˵ + +/metrics ˵¶ǰӦõĸҪָ꣬磺ڴϢ߳ϢϢtomcatݿӳصȡ2.x汾ֻʾһָб + +URL[http://localhost:8080/actuator/metrics](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fmetrics "http://localhost:8080/actuator/metrics") + +```` +{ + "names": [ + "jvm.memory.max", + "jvm.threads.states", + "jvm.gc.pause", + "http.server.requests", + "process.files.max", + "jvm.gc.memory.promoted", + "system.load.average.1m", + "jvm.memory.used", + "jvm.gc.max.data.size", + "jvm.memory.committed", + "system.cpu.count", + "logback.events", + "jvm.buffer.memory.used", + "tomcat.sessions.created", + "jvm.threads.daemon", + "system.cpu.usage", + "jvm.gc.memory.allocated", + "tomcat.sessions.expired", + "jvm.threads.live", + "jvm.threads.peak", + "process.uptime", + "tomcat.sessions.rejected", + "process.cpu.usage", + "jvm.classes.loaded", + "jvm.classes.unloaded", + "tomcat.sessions.active.current", + "tomcat.sessions.alive.max", + "jvm.gc.live.data.size", + "process.files.open", + "jvm.buffer.count", + "jvm.buffer.total.capacity", + "tomcat.sessions.active.max", + "process.start.time" + ] +} +```` + +/metrics˵ṩӦ״ָ̬걨棬ܷdzʵãǶڼϵͳеĸعܣǵļݡռƵʶͬÿζͨȫȡķʽռԴֱٷҲǿǴڴ˷ĿǣSpring Boot 2.x֮/metrics˵ֻʾָб + +Ҫ鿴ijָ꣬ͨ/metrics-requiredMetricName˵ʵ֡ + +## metrics-requiredMetricName˵ + +metrics-requiredMetricName˵㣬ڷָָı棬һ/metrics˵ȲָбȻٲѯijָꡣ + +ʽ[http://localhost:8080/actuator/metrics/{requiredMetricName}](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fmetrics%2F%257BrequiredMetricName%257D%25E3%2580%2582 "http://localhost:8080/actuator/metrics/%7BrequiredMetricName%7D%E3%80%82") ʵURL[http://localhost:8080/actuator/metrics/jvm.memory.max](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fmetrics%2Fjvm.memory.max "http://localhost:8080/actuator/metrics/jvm.memory.max") ؽ£ + + +```` +{ + "name": "jvm.memory.max", + "description": "The maximum amount of memory in bytes that can be used for memory management", + "baseUnit": "bytes", + "measurements": [ + { + "statistic": "VALUE", + "value": 5606211583 + } + ], + "availableTags": [ + { + "tag": "area", + "values": [ + "heap", + "nonheap" + ] + }, + { + "tag": "id", + "values": [ + "Compressed Class Space", + "PS Survivor Space", + "PS Old Gen", + "Metaspace", + "PS Eden Space", + "Code Cache" + ] + } + ] +} +```` + +չʾڴָչʾ滻Ӧֽв鿴ɡ + +## scheduledtasks˵ + +/scheduledtasks˵չʾӦеĶʱϢ + +Ŀйʱ@EnableSchedulingʱܡȻ󴴽ʱࣺ + +```` +@Component +public class MyTask { + + @Scheduled(cron = "0/10 * * * * *") + public void work() { + System.out.println("I am a cron job."); + } + + @Scheduled(fixedDelay = 10000) + public void work1() { + System.out.println("I am a fixedDelay job."); + } +} +```` + +ж͵ĶʱworkǻcronʵֵĶʱwork1ǻfixedDelayʵֵĶʱ + +URL[http://localhost:8080/actuator/scheduledtasks](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fscheduledtasks "http://localhost:8080/actuator/scheduledtasks") ؽϢ£ + +``` +{ + "cron": [ + { + "runnable": { + "target": "com.secbro2.job.MyTask.work" + }, + "expression": "0/10 * * * * *" + } + ], + "fixedDelay": [ + { + "runnable": { + "target": "com.secbro2.job.MyTask.work1" + }, + "initialDelay": 0, + "interval": 10000 + } + ], + "fixedRate": [], + "custom": [] +} +``` + +Կͨö˵ȷ֪ǰӦжĶʱԼִģʽƵΡ + +## mappings˵ + +/mappings˵ȫ URI ·ԼͿӳϵDZȽϳõˣϵͳIJ鿴URLӦControllerʹô˶˵㡣 + +URL[http://localhost:8080/actuator/mappings](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fmappings "http://localhost:8080/actuator/mappings") ַؽ£ + +![image-20230530234501440](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234501440.png) + +˹Spring Boot Actuatorṩж˵ϡ + +## С + +ͨSpring Boot Actuatorṩж˵㹹ʵʾĴݺʵȫһϡÿܶŲ⣬ŻȶмİдĵĹҲԽԽ̾ActuatorĹ֮ǿǿƼ + +## ο + +ߣӽ +ӣhttps://juejin.cn/post/6984550846876876814 +Դϡ +ȨСҵתϵ߻Ȩҵתע + diff --git a/docs/monitor/SpringBoot Admin.md b/docs/monitor/SpringBoot Admin.md new file mode 100644 index 0000000..7b0c8ab --- /dev/null +++ b/docs/monitor/SpringBoot Admin.md @@ -0,0 +1,495 @@ +## ժҪ + +Spring Boot Admin ԶSpringBootӦõĸָмأΪ΢ܹеļʹãĽ÷ϸܡ + +## Spring Boot Admin + +SpringBootӦÿͨActuator¶Ӧйеĸָ꣬Spring Boot AdminͨЩָSpringBootӦãȻͨͼλֳSpring Boot AdminԼصӦãԺSpring Cloudע΢Ӧá + +Spring Boot Admin ṩӦõ¼Ϣ + +* ӦйеĸϢ +* ָϢJVMTomcatϢ +* ϢϵͳԡϵͳԼӦϢ +* 鿴дBeanϢ +* 鿴ӦеϢ +* 鿴Ӧ־Ϣ +* 鿴JVMϢ +* 鿴ԷʵWeb˵㣻 +* 鿴HTTPϢ + +## admin-serverģ + +> Ǵһadmin-serverģΪʾ书ܡ + +* pom.xml + + + + + +```` + + org.springframework.boot + spring-boot-starter-web + + + de.codecentric + spring-boot-admin-starter-server + + +```` + +* application.ymlнã + + + + + +````spring: + application: + name: admin-server +server: + port: 9301 +```` + +* @EnableAdminServeradmin-serverܣ + + + + +```` +@EnableAdminServer +@SpringBootApplication +public class AdminServerApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminServerApplication.class, args); + } + +} +```` + +## admin-clientģ + +> Ǵһadmin-clientģΪͻעᵽadmin-server + +* pom.xml + + + +```` + + org.springframework.boot + spring-boot-starter-web + + + de.codecentric + spring-boot-admin-starter-client + +```` + +* application.ymlнã + + + + + + + + +``` +spring: + application: + name: admin-client + boot: + admin: + client: + url: http://localhost:9301 #admin-serverַ +server: + port: 9305 +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: always +logging: + file: admin-client.log #ӿadmin־ +``` + +* admin-serveradmin-client + +## Ϣʾ + +* µַSpring Boot Adminҳ[http://localhost:9301](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A9301 "http://localhost:9301") + +![image-20230531001002163](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001002163.png) + + + + + +* wallboardťѡadmin-client鿴Ϣ + +* Ϣ + +![image-20230531001023644](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001023644.png) + + + + + +* ָϢJVMTomcatϢ + +![image-20230531001053279](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001053279.png) + + + + + +* ϢϵͳԡϵͳԼӦϢ + +![image-20230531001103093](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001103093.png) + + + + + +* 鿴дBeanϢ + +![image-20230531001111221](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001111221.png) + + + + + +* 鿴ӦеϢ + +![image-20230531001124678](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001124678.png) + + + + + +* 鿴־ϢҪòܿ + + + +`logging: +file: admin-client.log #ӿadmin־` + +![image-20230531001136184](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001136184.png) + + + + +* 鿴JVMϢ + +![image-20230531001144614](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001144614.png) + + + + + +* 鿴ԷʵWeb˵㣻 + +![image-20230531001156191](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001156191.png) + + + + + +* 鿴HTTPϢ + +![image-20230531001206364](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001206364.png) + + + + + +## עʹ + +> Spring Boot AdminSpring Cloud עʹãֻ轫admin-serverעϼɣadmin-server ԶעĻȡбȻ󰤸ȡϢEurekaעΪ¸ùܡ + +### ޸admin-server + +* pom.xml + + + + +```` + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + +```` + +* application-eureka.ymlнãֻעüɣ + + + + +```` +spring: + application: + name: admin-server +server: + port: 9301 +eureka: + client: + register-with-eureka: true + fetch-registry: true + service-url: + defaultZone: http://localhost:8001/eureka/ +```` + +* @EnableDiscoveryClient÷עṦܣ + + + + + +```` +`@EnableDiscoveryClient +@EnableAdminServer +@SpringBootApplication +public class AdminServerApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminServerApplication.class, args); + } + +} +```` + +### ޸admin-client + +* pom.xml + + + +```` + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + +```` + +* application-eureka.ymlнãɾԭadmin-serverַãעüɣ + + + + + +``` +spring: + application: + name: admin-client +server: + port: 9305 +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: always +logging: + file: admin-client.log #ӿadmin־ +eureka: + client: + register-with-eureka: true + fetch-registry: true + service-url: + defaultZone: http://localhost:8001/eureka/ +``` + +* @EnableDiscoveryClient÷עṦܣ + + + + + +``` +@EnableDiscoveryClient +@SpringBootApplication +public class AdminClientApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminClientApplication.class, args); + } + +} +``` + +### ʾ + +* eureka-serverʹapplication-eureka.ymladmin-serveradmin-client + +* 鿴עķַע᣺[http://localhost:8001/](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8001%2F "http://localhost:8001/") + +![image-20230531001221519](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001221519.png) + + +* 鿴Spring Boot Admin ҳֿԿϢ[http://localhost:9301](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A9301 "http://localhost:9301") + +![image-20230531001232048](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001232048.png) + + +## ӵ¼֤ + +> ǿͨadmin-serverSpring Security֧õ¼֤ܡ + +### admin-security-serverģ + +* pom.xml + + + +``` + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + de.codecentric + spring-boot-admin-starter-server + 2.1.5 + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + +``` + +* application.ymlнãõ¼û룬admin-security-serverļϢ + + + + + +``` +spring: + application: + name: admin-security-server + security: # õ¼û + user: + name: macro + password: 123456 + boot: # ʾadmin-security-serverļϢ + admin: + discovery: + ignored-services: ${spring.application.name} +server: + port: 9301 +eureka: + client: + register-with-eureka: true + fetch-registry: true + service-url: + defaultZone: http://localhost:8001/eureka/ +``` + +* SpringSecurityãԱadmin-clientע᣺ + + + + + +scss + +ƴ + + + + + +``` +/** + * Created by macro on 2019/9/30. + */ +@Configuration +public class SecuritySecureConfig extends WebSecurityConfigurerAdapter { + private final String adminContextPath; + + public SecuritySecureConfig(AdminServerProperties adminServerProperties) { + this.adminContextPath = adminServerProperties.getContextPath(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler(); + successHandler.setTargetUrlParameter("redirectTo"); + successHandler.setDefaultTargetUrl(adminContextPath + "/"); + + http.authorizeRequests() + //1.о̬Դ͵¼ҳԹ + .antMatchers(adminContextPath + "/assets/**").permitAll() + .antMatchers(adminContextPath + "/login").permitAll() + .anyRequest().authenticated() + .and() + //2.õ¼͵dz· + .formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and() + .logout().logoutUrl(adminContextPath + "/logout").and() + //3.http basic֧֣admin-clientעʱҪʹ + .httpBasic().and() + .csrf() + //4.cookiecsrf + .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) + //5.Щ·csrfԱadmin-clientע + .ignoringAntMatchers( + adminContextPath + "/instances", + adminContextPath + "/actuator/**" + ); + } +} +``` + +* ޸࣬AdminServerעֹᷢܣ + + + + + +``` +@EnableDiscoveryClient +@EnableAdminServer +@SpringBootApplication +public class AdminSecurityServerApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminSecurityServerApplication.class, args); + } +} +``` + +* eureka-serveradmin-security-serverSpring Boot Admin ҳҪ¼ܷʣ[http://localhost:9301](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A9301 "http://localhost:9301") + +![image-20230531001242361](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001242361.png) + + + + + +## ʹõģ + + + + + +``` +springcloud-learning + eureka-server -- eurekaע + admin-server -- adminķ + admin-client -- adminļصӦ÷ + admin-security-server -- ¼֤adminķ` +``` \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\344\272\213\345\212\241\346\266\210\346\201\257\357\274\210\346\234\200\347\273\210\344\270\200\350\207\264\346\200\247\357\274\211.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\344\272\213\345\212\241\346\266\210\346\201\257\357\274\210\346\234\200\347\273\210\344\270\200\350\207\264\346\200\247\357\274\211.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\344\272\213\345\212\241\346\266\210\346\201\257\357\274\210\346\234\200\347\273\210\344\270\200\350\207\264\346\200\247\357\274\211.md" @@ -0,0 +1,62 @@ +# [RocketMQϵУһ](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQǰƷһԴϢмľϢĹܡҵУϢмѡʹRocketMQĻͦģһϵе¶RocketMQģȴRocketMQһЩͻĴʼ + +RocketMQ4ɣֱǣƷName ServerϢУBrokersߣproducerߣconsumer4ֶԽˮƽչӶⵥϣͼ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +RocketMQϵһͼdzг4֣ҶǼȺģʽǾͷֱ˵һ˵4֡ + +## ƷNameServer + +Name ServerݵĽɫһעģZookeeperòࡣҪ£ + +* brokerĹbrokerȺԼϢעᵽNameServerNameServerṩƼÿһbrokerǷ +* ·ɹÿһNameServerbrokerȺͶеϢԱͻˣߺߣѯ + +NameServerЭŷֲʽϵͳеÿһҹÿһTopic·Ϣ + +## Broker + +BrokerҪǴ洢ϢṩTopicĻơṩƺģʽһЩֵĴʩϢǿһBrokcerӻơ + +BrokerĽɫΪ첽ͬ͡ӡɫ㲻ϢĶʧһͬ͡ӡBrokerϢʧҲνֻҪпþokĻá첽͡ӡbrokerֻ򵥵Ĵֻһ첽áӡҲǿԵġ + +ᵽbroker֮ıݣbrokerϢҲǿԱ浽̵ģ浽̵ķʽҲ֣Ƽķʽ첽̣ͬǷdzܵġ + +## + +ּ֧ȺbrokerȺϢֶָ֧ؾķʽ + +brokerϢʱõͽͽһ״̬ǵУϢ`isWaitStoreMsgOK = true`ĬҲ`true`Ϊ`false`ڷϢĹУֻҪ쳣ͽ`SEND_OK``isWaitStoreMsgOK = true`ͽ¼֣ + +* `FLUSH_DISK_TIMEOUT`̳ʱ̵ķʽΪSYNC_FLUSHͬsyncFlushTimeoutõʱڣĬ5sûɱ̵Ķõ״̬ +* `FLUSH_SLAVE_TIMEOUT`ͬӡʱbrokerĽɫΪͬʱõͬʱڣĬΪ5sû֮ͬͻõ״̬ +* `SLAVE_NOT_AVAILABLE`ӡãáͬûáӡbrokerʱ᷵״̬ +* `SEND_OK`Ϣͳɹ + +ϢظϢʧ㷢ϢʧʱͨѡһǶͶɣϢĶˣһѡϢ·ͣпϢظͨ£Ƽ·͵ģϢʱҪȥظϢ + +messageĴСһ㲻512kĬϵķϢķʽͬģͷһֱֱȵصӦȽܣҲ`send(msg, callback)`첽ķʽϢ + +## + +߿**飨consumer group**ͬ****ԶͬTopicҲԶTopicÿһ鶼Լƫ + +Ϣѷʽһ֣˳ѺͲѡ + +* ˳ѣ߽סϢУȷϢ˳һһıѵ˳ѻһʧϢʱ쳣ֱ׳Ӧ÷`SUSPEND_CURRENT_QUEUE_A_MOMENT`״̬߹һʱ󣬻Ϣ +* ѣ߽ϢַʽܷdzãҲƼѷʽѵĹУ쳣ֱ׳Ƿ`RECONSUME_LATER`״̬ڲȷһʱ󣬻ٴ + +ڲʹ`ThreadPoolExecutor`Ϊ̳߳صģǿͨ`setConsumeThreadMin``setConsumeThreadMax`С̡̺߳߳ + +һµ齨ԺҪǷ֮ǰʷϢ`CONSUME_FROM_LAST_OFFSET`ʷϢµϢ`CONSUME_FROM_FIRST_OFFSET`ѶеÿһϢ֮ǰʷϢҲһ顣`CONSUME_FROM_TIMESTAMP`ָϢʱ䣬ָʱԺϢᱻѡ + +ӦòظѣôϢĹУҪϢУ顣 + +ˣ͵ɣһƪǽRocketMQĻ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\237\272\346\234\254\346\246\202\345\277\265.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\237\272\346\234\254\346\246\202\345\277\265.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\237\272\346\234\254\346\246\202\345\277\265.md" @@ -0,0 +1,62 @@ +# [RocketMQϵУһ](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQǰƷһԴϢмľϢĹܡҵУϢмѡʹRocketMQĻͦģһϵе¶RocketMQģȴRocketMQһЩͻĴʼ + +RocketMQ4ɣֱǣƷName ServerϢУBrokersߣproducerߣconsumer4ֶԽˮƽչӶⵥϣͼ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +RocketMQϵһͼdzг4֣ҶǼȺģʽǾͷֱ˵һ˵4֡ + +## ƷNameServer + +Name ServerݵĽɫһעģZookeeperòࡣҪ£ + +* brokerĹbrokerȺԼϢעᵽNameServerNameServerṩƼÿһbrokerǷ +* ·ɹÿһNameServerbrokerȺͶеϢԱͻˣߺߣѯ + +NameServerЭŷֲʽϵͳеÿһҹÿһTopic·Ϣ + +## Broker + +BrokerҪǴ洢ϢṩTopicĻơṩƺģʽһЩֵĴʩϢǿһBrokcerӻơ + +BrokerĽɫΪ첽ͬ͡ӡɫ㲻ϢĶʧһͬ͡ӡBrokerϢʧҲνֻҪпþokĻá첽͡ӡbrokerֻ򵥵Ĵֻһ첽áӡҲǿԵġ + +ᵽbroker֮ıݣbrokerϢҲǿԱ浽̵ģ浽̵ķʽҲ֣Ƽķʽ첽̣ͬǷdzܵġ + +## + +ּ֧ȺbrokerȺϢֶָ֧ؾķʽ + +brokerϢʱõͽͽһ״̬ǵУϢ`isWaitStoreMsgOK = true`ĬҲ`true`Ϊ`false`ڷϢĹУֻҪ쳣ͽ`SEND_OK``isWaitStoreMsgOK = true`ͽ¼֣ + +* `FLUSH_DISK_TIMEOUT`̳ʱ̵ķʽΪSYNC_FLUSHͬsyncFlushTimeoutõʱڣĬ5sûɱ̵Ķõ״̬ +* `FLUSH_SLAVE_TIMEOUT`ͬӡʱbrokerĽɫΪͬʱõͬʱڣĬΪ5sû֮ͬͻõ״̬ +* `SLAVE_NOT_AVAILABLE`ӡãáͬûáӡbrokerʱ᷵״̬ +* `SEND_OK`Ϣͳɹ + +ϢظϢʧ㷢ϢʧʱͨѡһǶͶɣϢĶˣһѡϢ·ͣпϢظͨ£Ƽ·͵ģϢʱҪȥظϢ + +messageĴСһ㲻512kĬϵķϢķʽͬģͷһֱֱȵصӦȽܣҲ`send(msg, callback)`첽ķʽϢ + +## + +߿**飨consumer group**ͬ****ԶͬTopicҲԶTopicÿһ鶼Լƫ + +Ϣѷʽһ֣˳ѺͲѡ + +* ˳ѣ߽סϢУȷϢ˳һһıѵ˳ѻһʧϢʱ쳣ֱ׳Ӧ÷`SUSPEND_CURRENT_QUEUE_A_MOMENT`״̬߹һʱ󣬻Ϣ +* ѣ߽ϢַʽܷdzãҲƼѷʽѵĹУ쳣ֱ׳Ƿ`RECONSUME_LATER`״̬ڲȷһʱ󣬻ٴ + +ڲʹ`ThreadPoolExecutor`Ϊ̳߳صģǿͨ`setConsumeThreadMin``setConsumeThreadMax`С̡̺߳߳ + +һµ齨ԺҪǷ֮ǰʷϢ`CONSUME_FROM_LAST_OFFSET`ʷϢµϢ`CONSUME_FROM_FIRST_OFFSET`ѶеÿһϢ֮ǰʷϢҲһ顣`CONSUME_FROM_TIMESTAMP`ָϢʱ䣬ָʱԺϢᱻѡ + +ӦòظѣôϢĹУҪϢУ顣 + +ˣ͵ɣһƪǽRocketMQĻ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\271\277\346\222\255\344\270\216\345\273\266\350\277\237\346\266\210\346\201\257.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\271\277\346\222\255\344\270\216\345\273\266\350\277\237\346\266\210\346\201\257.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\271\277\346\222\255\344\270\216\345\273\266\350\277\237\346\266\210\346\201\257.md" @@ -0,0 +1,62 @@ +# [RocketMQϵУһ](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQǰƷһԴϢмľϢĹܡҵУϢмѡʹRocketMQĻͦģһϵе¶RocketMQģȴRocketMQһЩͻĴʼ + +RocketMQ4ɣֱǣƷName ServerϢУBrokersߣproducerߣconsumer4ֶԽˮƽչӶⵥϣͼ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +RocketMQϵһͼdzг4֣ҶǼȺģʽǾͷֱ˵һ˵4֡ + +## ƷNameServer + +Name ServerݵĽɫһעģZookeeperòࡣҪ£ + +* brokerĹbrokerȺԼϢעᵽNameServerNameServerṩƼÿһbrokerǷ +* ·ɹÿһNameServerbrokerȺͶеϢԱͻˣߺߣѯ + +NameServerЭŷֲʽϵͳеÿһҹÿһTopic·Ϣ + +## Broker + +BrokerҪǴ洢ϢṩTopicĻơṩƺģʽһЩֵĴʩϢǿһBrokcerӻơ + +BrokerĽɫΪ첽ͬ͡ӡɫ㲻ϢĶʧһͬ͡ӡBrokerϢʧҲνֻҪпþokĻá첽͡ӡbrokerֻ򵥵Ĵֻһ첽áӡҲǿԵġ + +ᵽbroker֮ıݣbrokerϢҲǿԱ浽̵ģ浽̵ķʽҲ֣Ƽķʽ첽̣ͬǷdzܵġ + +## + +ּ֧ȺbrokerȺϢֶָ֧ؾķʽ + +brokerϢʱõͽͽһ״̬ǵУϢ`isWaitStoreMsgOK = true`ĬҲ`true`Ϊ`false`ڷϢĹУֻҪ쳣ͽ`SEND_OK``isWaitStoreMsgOK = true`ͽ¼֣ + +* `FLUSH_DISK_TIMEOUT`̳ʱ̵ķʽΪSYNC_FLUSHͬsyncFlushTimeoutõʱڣĬ5sûɱ̵Ķõ״̬ +* `FLUSH_SLAVE_TIMEOUT`ͬӡʱbrokerĽɫΪͬʱõͬʱڣĬΪ5sû֮ͬͻõ״̬ +* `SLAVE_NOT_AVAILABLE`ӡãáͬûáӡbrokerʱ᷵״̬ +* `SEND_OK`Ϣͳɹ + +ϢظϢʧ㷢ϢʧʱͨѡһǶͶɣϢĶˣһѡϢ·ͣпϢظͨ£Ƽ·͵ģϢʱҪȥظϢ + +messageĴСһ㲻512kĬϵķϢķʽͬģͷһֱֱȵصӦȽܣҲ`send(msg, callback)`첽ķʽϢ + +## + +߿**飨consumer group**ͬ****ԶͬTopicҲԶTopicÿһ鶼Լƫ + +Ϣѷʽһ֣˳ѺͲѡ + +* ˳ѣ߽סϢУȷϢ˳һһıѵ˳ѻһʧϢʱ쳣ֱ׳Ӧ÷`SUSPEND_CURRENT_QUEUE_A_MOMENT`״̬߹һʱ󣬻Ϣ +* ѣ߽ϢַʽܷdzãҲƼѷʽѵĹУ쳣ֱ׳Ƿ`RECONSUME_LATER`״̬ڲȷһʱ󣬻ٴ + +ڲʹ`ThreadPoolExecutor`Ϊ̳߳صģǿͨ`setConsumeThreadMin``setConsumeThreadMax`С̡̺߳߳ + +һµ齨ԺҪǷ֮ǰʷϢ`CONSUME_FROM_LAST_OFFSET`ʷϢµϢ`CONSUME_FROM_FIRST_OFFSET`ѶеÿһϢ֮ǰʷϢҲһ顣`CONSUME_FROM_TIMESTAMP`ָϢʱ䣬ָʱԺϢᱻѡ + +ӦòظѣôϢĹУҪϢУ顣 + +ˣ͵ɣһƪǽRocketMQĻ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\211\271\351\207\217\345\217\221\351\200\201\344\270\216\350\277\207\346\273\244.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\211\271\351\207\217\345\217\221\351\200\201\344\270\216\350\277\207\346\273\244.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\211\271\351\207\217\345\217\221\351\200\201\344\270\216\350\277\207\346\273\244.md" @@ -0,0 +1,62 @@ +# [RocketMQϵУһ](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQǰƷһԴϢмľϢĹܡҵУϢмѡʹRocketMQĻͦģһϵе¶RocketMQģȴRocketMQһЩͻĴʼ + +RocketMQ4ɣֱǣƷName ServerϢУBrokersߣproducerߣconsumer4ֶԽˮƽչӶⵥϣͼ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +RocketMQϵһͼdzг4֣ҶǼȺģʽǾͷֱ˵һ˵4֡ + +## ƷNameServer + +Name ServerݵĽɫһעģZookeeperòࡣҪ£ + +* brokerĹbrokerȺԼϢעᵽNameServerNameServerṩƼÿһbrokerǷ +* ·ɹÿһNameServerbrokerȺͶеϢԱͻˣߺߣѯ + +NameServerЭŷֲʽϵͳеÿһҹÿһTopic·Ϣ + +## Broker + +BrokerҪǴ洢ϢṩTopicĻơṩƺģʽһЩֵĴʩϢǿһBrokcerӻơ + +BrokerĽɫΪ첽ͬ͡ӡɫ㲻ϢĶʧһͬ͡ӡBrokerϢʧҲνֻҪпþokĻá첽͡ӡbrokerֻ򵥵Ĵֻһ첽áӡҲǿԵġ + +ᵽbroker֮ıݣbrokerϢҲǿԱ浽̵ģ浽̵ķʽҲ֣Ƽķʽ첽̣ͬǷdzܵġ + +## + +ּ֧ȺbrokerȺϢֶָ֧ؾķʽ + +brokerϢʱõͽͽһ״̬ǵУϢ`isWaitStoreMsgOK = true`ĬҲ`true`Ϊ`false`ڷϢĹУֻҪ쳣ͽ`SEND_OK``isWaitStoreMsgOK = true`ͽ¼֣ + +* `FLUSH_DISK_TIMEOUT`̳ʱ̵ķʽΪSYNC_FLUSHͬsyncFlushTimeoutõʱڣĬ5sûɱ̵Ķõ״̬ +* `FLUSH_SLAVE_TIMEOUT`ͬӡʱbrokerĽɫΪͬʱõͬʱڣĬΪ5sû֮ͬͻõ״̬ +* `SLAVE_NOT_AVAILABLE`ӡãáͬûáӡbrokerʱ᷵״̬ +* `SEND_OK`Ϣͳɹ + +ϢظϢʧ㷢ϢʧʱͨѡһǶͶɣϢĶˣһѡϢ·ͣпϢظͨ£Ƽ·͵ģϢʱҪȥظϢ + +messageĴСһ㲻512kĬϵķϢķʽͬģͷһֱֱȵصӦȽܣҲ`send(msg, callback)`첽ķʽϢ + +## + +߿**飨consumer group**ͬ****ԶͬTopicҲԶTopicÿһ鶼Լƫ + +Ϣѷʽһ֣˳ѺͲѡ + +* ˳ѣ߽סϢУȷϢ˳һһıѵ˳ѻһʧϢʱ쳣ֱ׳Ӧ÷`SUSPEND_CURRENT_QUEUE_A_MOMENT`״̬߹һʱ󣬻Ϣ +* ѣ߽ϢַʽܷdzãҲƼѷʽѵĹУ쳣ֱ׳Ƿ`RECONSUME_LATER`״̬ڲȷһʱ󣬻ٴ + +ڲʹ`ThreadPoolExecutor`Ϊ̳߳صģǿͨ`setConsumeThreadMin``setConsumeThreadMax`С̡̺߳߳ + +һµ齨ԺҪǷ֮ǰʷϢ`CONSUME_FROM_LAST_OFFSET`ʷϢµϢ`CONSUME_FROM_FIRST_OFFSET`ѶеÿһϢ֮ǰʷϢҲһ顣`CONSUME_FROM_TIMESTAMP`ָϢʱ䣬ָʱԺϢᱻѡ + +ӦòظѣôϢĹУҪϢУ顣 + +ˣ͵ɣһƪǽRocketMQĻ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\266\210\346\201\257\347\232\204\347\224\237\344\272\247\344\270\216\346\266\210\350\264\271.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\266\210\346\201\257\347\232\204\347\224\237\344\272\247\344\270\216\346\266\210\350\264\271.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\266\210\346\201\257\347\232\204\347\224\237\344\272\247\344\270\216\346\266\210\350\264\271.md" @@ -0,0 +1,62 @@ +# [RocketMQϵУһ](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQǰƷһԴϢмľϢĹܡҵУϢмѡʹRocketMQĻͦģһϵе¶RocketMQģȴRocketMQһЩͻĴʼ + +RocketMQ4ɣֱǣƷName ServerϢУBrokersߣproducerߣconsumer4ֶԽˮƽչӶⵥϣͼ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +RocketMQϵһͼdzг4֣ҶǼȺģʽǾͷֱ˵һ˵4֡ + +## ƷNameServer + +Name ServerݵĽɫһעģZookeeperòࡣҪ£ + +* brokerĹbrokerȺԼϢעᵽNameServerNameServerṩƼÿһbrokerǷ +* ·ɹÿһNameServerbrokerȺͶеϢԱͻˣߺߣѯ + +NameServerЭŷֲʽϵͳеÿһҹÿһTopic·Ϣ + +## Broker + +BrokerҪǴ洢ϢṩTopicĻơṩƺģʽһЩֵĴʩϢǿһBrokcerӻơ + +BrokerĽɫΪ첽ͬ͡ӡɫ㲻ϢĶʧһͬ͡ӡBrokerϢʧҲνֻҪпþokĻá첽͡ӡbrokerֻ򵥵Ĵֻһ첽áӡҲǿԵġ + +ᵽbroker֮ıݣbrokerϢҲǿԱ浽̵ģ浽̵ķʽҲ֣Ƽķʽ첽̣ͬǷdzܵġ + +## + +ּ֧ȺbrokerȺϢֶָ֧ؾķʽ + +brokerϢʱõͽͽһ״̬ǵУϢ`isWaitStoreMsgOK = true`ĬҲ`true`Ϊ`false`ڷϢĹУֻҪ쳣ͽ`SEND_OK``isWaitStoreMsgOK = true`ͽ¼֣ + +* `FLUSH_DISK_TIMEOUT`̳ʱ̵ķʽΪSYNC_FLUSHͬsyncFlushTimeoutõʱڣĬ5sûɱ̵Ķõ״̬ +* `FLUSH_SLAVE_TIMEOUT`ͬӡʱbrokerĽɫΪͬʱõͬʱڣĬΪ5sû֮ͬͻõ״̬ +* `SLAVE_NOT_AVAILABLE`ӡãáͬûáӡbrokerʱ᷵״̬ +* `SEND_OK`Ϣͳɹ + +ϢظϢʧ㷢ϢʧʱͨѡһǶͶɣϢĶˣһѡϢ·ͣпϢظͨ£Ƽ·͵ģϢʱҪȥظϢ + +messageĴСһ㲻512kĬϵķϢķʽͬģͷһֱֱȵصӦȽܣҲ`send(msg, callback)`첽ķʽϢ + +## + +߿**飨consumer group**ͬ****ԶͬTopicҲԶTopicÿһ鶼Լƫ + +Ϣѷʽһ֣˳ѺͲѡ + +* ˳ѣ߽סϢУȷϢ˳һһıѵ˳ѻһʧϢʱ쳣ֱ׳Ӧ÷`SUSPEND_CURRENT_QUEUE_A_MOMENT`״̬߹һʱ󣬻Ϣ +* ѣ߽ϢַʽܷdzãҲƼѷʽѵĹУ쳣ֱ׳Ƿ`RECONSUME_LATER`״̬ڲȷһʱ󣬻ٴ + +ڲʹ`ThreadPoolExecutor`Ϊ̳߳صģǿͨ`setConsumeThreadMin``setConsumeThreadMax`С̡̺߳߳ + +һµ齨ԺҪǷ֮ǰʷϢ`CONSUME_FROM_LAST_OFFSET`ʷϢµϢ`CONSUME_FROM_FIRST_OFFSET`ѶеÿһϢ֮ǰʷϢҲһ顣`CONSUME_FROM_TIMESTAMP`ָϢʱ䣬ָʱԺϢᱻѡ + +ӦòظѣôϢĹУҪϢУ顣 + +ˣ͵ɣһƪǽRocketMQĻ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\347\216\257\345\242\203\346\220\255\345\273\272.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\347\216\257\345\242\203\346\220\255\345\273\272.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\347\216\257\345\242\203\346\220\255\345\273\272.md" @@ -0,0 +1,62 @@ +# [RocketMQϵУһ](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQǰƷһԴϢмľϢĹܡҵУϢмѡʹRocketMQĻͦģһϵе¶RocketMQģȴRocketMQһЩͻĴʼ + +RocketMQ4ɣֱǣƷName ServerϢУBrokersߣproducerߣconsumer4ֶԽˮƽչӶⵥϣͼ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +RocketMQϵһͼdzг4֣ҶǼȺģʽǾͷֱ˵һ˵4֡ + +## ƷNameServer + +Name ServerݵĽɫһעģZookeeperòࡣҪ£ + +* brokerĹbrokerȺԼϢעᵽNameServerNameServerṩƼÿһbrokerǷ +* ·ɹÿһNameServerbrokerȺͶеϢԱͻˣߺߣѯ + +NameServerЭŷֲʽϵͳеÿһҹÿһTopic·Ϣ + +## Broker + +BrokerҪǴ洢ϢṩTopicĻơṩƺģʽһЩֵĴʩϢǿһBrokcerӻơ + +BrokerĽɫΪ첽ͬ͡ӡɫ㲻ϢĶʧһͬ͡ӡBrokerϢʧҲνֻҪпþokĻá첽͡ӡbrokerֻ򵥵Ĵֻһ첽áӡҲǿԵġ + +ᵽbroker֮ıݣbrokerϢҲǿԱ浽̵ģ浽̵ķʽҲ֣Ƽķʽ첽̣ͬǷdzܵġ + +## + +ּ֧ȺbrokerȺϢֶָ֧ؾķʽ + +brokerϢʱõͽͽһ״̬ǵУϢ`isWaitStoreMsgOK = true`ĬҲ`true`Ϊ`false`ڷϢĹУֻҪ쳣ͽ`SEND_OK``isWaitStoreMsgOK = true`ͽ¼֣ + +* `FLUSH_DISK_TIMEOUT`̳ʱ̵ķʽΪSYNC_FLUSHͬsyncFlushTimeoutõʱڣĬ5sûɱ̵Ķõ״̬ +* `FLUSH_SLAVE_TIMEOUT`ͬӡʱbrokerĽɫΪͬʱõͬʱڣĬΪ5sû֮ͬͻõ״̬ +* `SLAVE_NOT_AVAILABLE`ӡãáͬûáӡbrokerʱ᷵״̬ +* `SEND_OK`Ϣͳɹ + +ϢظϢʧ㷢ϢʧʱͨѡһǶͶɣϢĶˣһѡϢ·ͣпϢظͨ£Ƽ·͵ģϢʱҪȥظϢ + +messageĴСһ㲻512kĬϵķϢķʽͬģͷһֱֱȵصӦȽܣҲ`send(msg, callback)`첽ķʽϢ + +## + +߿**飨consumer group**ͬ****ԶͬTopicҲԶTopicÿһ鶼Լƫ + +Ϣѷʽһ֣˳ѺͲѡ + +* ˳ѣ߽סϢУȷϢ˳һһıѵ˳ѻһʧϢʱ쳣ֱ׳Ӧ÷`SUSPEND_CURRENT_QUEUE_A_MOMENT`״̬߹һʱ󣬻Ϣ +* ѣ߽ϢַʽܷdzãҲƼѷʽѵĹУ쳣ֱ׳Ƿ`RECONSUME_LATER`״̬ڲȷһʱ󣬻ٴ + +ڲʹ`ThreadPoolExecutor`Ϊ̳߳صģǿͨ`setConsumeThreadMin``setConsumeThreadMax`С̡̺߳߳ + +һµ齨ԺҪǷ֮ǰʷϢ`CONSUME_FROM_LAST_OFFSET`ʷϢµϢ`CONSUME_FROM_FIRST_OFFSET`ѶеÿһϢ֮ǰʷϢҲһ顣`CONSUME_FROM_TIMESTAMP`ָϢʱ䣬ָʱԺϢᱻѡ + +ӦòظѣôϢĹУҪϢУ顣 + +ˣ͵ɣһƪǽRocketMQĻ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\351\241\272\345\272\217\346\266\210\350\264\271.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\351\241\272\345\272\217\346\266\210\350\264\271.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\351\241\272\345\272\217\346\266\210\350\264\271.md" @@ -0,0 +1,62 @@ +# [RocketMQϵУһ](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQǰƷһԴϢмľϢĹܡҵУϢмѡʹRocketMQĻͦģһϵе¶RocketMQģȴRocketMQһЩͻĴʼ + +RocketMQ4ɣֱǣƷName ServerϢУBrokersߣproducerߣconsumer4ֶԽˮƽչӶⵥϣͼ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +RocketMQϵһͼdzг4֣ҶǼȺģʽǾͷֱ˵һ˵4֡ + +## ƷNameServer + +Name ServerݵĽɫһעģZookeeperòࡣҪ£ + +* brokerĹbrokerȺԼϢעᵽNameServerNameServerṩƼÿһbrokerǷ +* ·ɹÿһNameServerbrokerȺͶеϢԱͻˣߺߣѯ + +NameServerЭŷֲʽϵͳеÿһҹÿһTopic·Ϣ + +## Broker + +BrokerҪǴ洢ϢṩTopicĻơṩƺģʽһЩֵĴʩϢǿһBrokcerӻơ + +BrokerĽɫΪ첽ͬ͡ӡɫ㲻ϢĶʧһͬ͡ӡBrokerϢʧҲνֻҪпþokĻá첽͡ӡbrokerֻ򵥵Ĵֻһ첽áӡҲǿԵġ + +ᵽbroker֮ıݣbrokerϢҲǿԱ浽̵ģ浽̵ķʽҲ֣Ƽķʽ첽̣ͬǷdzܵġ + +## + +ּ֧ȺbrokerȺϢֶָ֧ؾķʽ + +brokerϢʱõͽͽһ״̬ǵУϢ`isWaitStoreMsgOK = true`ĬҲ`true`Ϊ`false`ڷϢĹУֻҪ쳣ͽ`SEND_OK``isWaitStoreMsgOK = true`ͽ¼֣ + +* `FLUSH_DISK_TIMEOUT`̳ʱ̵ķʽΪSYNC_FLUSHͬsyncFlushTimeoutõʱڣĬ5sûɱ̵Ķõ״̬ +* `FLUSH_SLAVE_TIMEOUT`ͬӡʱbrokerĽɫΪͬʱõͬʱڣĬΪ5sû֮ͬͻõ״̬ +* `SLAVE_NOT_AVAILABLE`ӡãáͬûáӡbrokerʱ᷵״̬ +* `SEND_OK`Ϣͳɹ + +ϢظϢʧ㷢ϢʧʱͨѡһǶͶɣϢĶˣһѡϢ·ͣпϢظͨ£Ƽ·͵ģϢʱҪȥظϢ + +messageĴСһ㲻512kĬϵķϢķʽͬģͷһֱֱȵصӦȽܣҲ`send(msg, callback)`첽ķʽϢ + +## + +߿**飨consumer group**ͬ****ԶͬTopicҲԶTopicÿһ鶼Լƫ + +Ϣѷʽһ֣˳ѺͲѡ + +* ˳ѣ߽סϢУȷϢ˳һһıѵ˳ѻһʧϢʱ쳣ֱ׳Ӧ÷`SUSPEND_CURRENT_QUEUE_A_MOMENT`״̬߹һʱ󣬻Ϣ +* ѣ߽ϢַʽܷdzãҲƼѷʽѵĹУ쳣ֱ׳Ƿ`RECONSUME_LATER`״̬ڲȷһʱ󣬻ٴ + +ڲʹ`ThreadPoolExecutor`Ϊ̳߳صģǿͨ`setConsumeThreadMin``setConsumeThreadMax`С̡̺߳߳ + +һµ齨ԺҪǷ֮ǰʷϢ`CONSUME_FROM_LAST_OFFSET`ʷϢµϢ`CONSUME_FROM_FIRST_OFFSET`ѶеÿһϢ֮ǰʷϢҲһ顣`CONSUME_FROM_TIMESTAMP`ָϢʱ䣬ָʱԺϢᱻѡ + +ӦòظѣôϢĹУҪϢУ顣 + +ˣ͵ɣһƪǽRocketMQĻ \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka \345\277\253\351\200\237\344\270\212\346\211\213\357\274\210Java\347\211\210\357\274\211.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka \345\277\253\351\200\237\344\270\212\346\211\213\357\274\210Java\347\211\210\357\274\211.md" new file mode 100644 index 0000000..b4bc0a9 --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka \345\277\253\351\200\237\344\270\212\346\211\213\357\274\210Java\347\211\210\357\274\211.md" @@ -0,0 +1,173 @@ +**前言** + +上篇文章讲解了 Kafka 的基础概念和架构,了解了基本概念之后,必须得实践一波了,所谓“实践才是检验真理的唯一办法”,后续系列关于 Kafka 的文章都以 kafka_2.11-0.9.0.0 为例;另外为了让大家快速入门,本文只提供单机版的安装实战教程,如果有想尝试集群方案的,后面在出一篇集群安装的教程,废话不多说了,直接开干。 + +## **安装** + +### **1\. 下载** + +版本号:kafka_2.11-0.9.0.0 + +下载地址:[http://kafka.apache.org/downloads](https://link.zhihu.com/?target=http%3A//kafka.apache.org/downloads) + +### **2\. 安装** + + + +``` +# 安装目录 +$ pwd +/Users/my/software/study + +# 减压 +$ sudo tar -zxvf kafka_2.11-0.9.0.0.tgz + +# 重命名 +$ sudo mv kafka_2.11-0.9.0.0.tgz kafka-0.9 + +# 查看目录结构 +$ cd kafka-0.9 && ls +LICENSE NOTICE bin config libs site-docs + +# 目录结构介绍: +# bin: 存放kafka 客户端和服务端的执行脚本 +# config: 存放kafka的一些配置文件 +# libs: 存放kafka运行的的jar包 +# site-docs: 存放kafka的配置文档说明 + +# 配置环境变量,方便在任意目录下运行kafka命令 +# 博主使用的Mac,所以配置在了 ~/.bash_profile文件中, +# Linux中则配置在 ~/.bashrc 或者 ~/.zshrc文件中 +$ vim ~/.bash_profile + +export KAFKA_HOME=/Users/haikuan1/software/study/kafka-0.9 +export PATH=$PATH:$JAVA_HOME:$KAFKA_HOME/bin + +# 使得环境变量生效 +$ source ~/.bash_profile + +``` + + + +### **3.运行** + +### **3.1 启动 zookeeper** + + + +``` +# 启动zookeeper,因为kafka的元数据需要保存到zookeeper中 +$ bin/zookeeper-server-start.sh config/zookeeper.properties + +# 若出现如下信息,则证明zookeeper启动成功了 +[2020-04-25 16:23:44,493] INFO Server environment:user.dir=/Users/haikuan1/software/study/kafka-0.10 (org.apache.zookeeper.server.ZooKeeperServer) +[2020-04-25 16:23:44,505] INFO tickTime set to 3000 (org.apache.zookeeper.server.ZooKeeperServer) +[2020-04-25 16:23:44,505] INFO minSessionTimeout set to -1 (org.apache.zookeeper.server.ZooKeeperServer) +[2020-04-25 16:23:44,505] INFO maxSessionTimeout set to -1 (org.apache.zookeeper.server.ZooKeeperServer) +[2020-04-25 16:23:44,548] INFO binding to port 0.0.0.0/0.0.0.0:2181 (org.apache.zookeeper.server.NIOServerCnxnFactory) + +``` + + + +### **3.2 启动 Kafka server** + + + +``` +# 以守护进程的方式启动kafka服务端,去掉 -daemon 参数则关闭当前窗口服务端自动退出 +$ bin/kafka-server-start.sh -daemon config/server.properties + +``` + + + +### **3.3 kafka 基础命令使用** + + + +``` +# 1\. 创建一个topic +# --replication-factor:指定副本个数 +# --partition:指定partition个数 +# --topic:指定topic的名字 +$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partition 1 --topic mytopic + +# 2\. 查看创建成功的topic +$ kafka-topics.sh --list --zookeeper localhost:2181 + +# 3\. 创建生产者和消费者 + +# 3.1 启动kafka消费端 +# --from-beginning:从头开始消费,该特性也表明kafka消息具有持久性 +$ bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic mytopic --from-beginning + +# 3.2 启动kafka生产端 +# --broker-list:当前的Broker列表,即提供服务的列表 +$ bin/kafka-console-producer.sh --broker-list localhost:9092 --topic mytopic + +``` + + + +

+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-a1e0c6db02c2822b2ad88db1c3b0b8a7_720w.webp) + +
+ +### **4.使用 Java 连接 kafka 进行测试** + +### **4.1 创建一个 maven 工程,引入如下 pom 依赖** + + + +``` + + org.apache.kafka + kafka-clients + 0.9.0.0 + + + + org.apache.kafka + kafka_2.11 + 0.9.0.0 + + +``` + + + +### **4.2 消费者端代码** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-5e9876ca0dc733fe8c2df51d2e42d1ce_720w.webp) + +
+ +### **4.3 生产者端代码** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-d1e6bfdf23c2b42e23f30d4430c587e2_720w.webp) + +
+ +### **4.4 消费者端效果图** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-1912f5b2b12ac766d746d88a04b9bd28_720w.webp) + +
+ +### **5.总结** + +本文介绍了 kafka 单机版安装及简单命令使用,然后使用 Java 实现了生产者和消费者的简单功能,虽然内容可能比较简单,但还是**强烈建议大家手动去实践一下**,从而对 kafka 的架构有一个更深入的理解。 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\270\200\346\235\241\346\266\210\346\201\257\345\255\230\345\210\260broker\347\232\204\350\277\207\347\250\213.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\270\200\346\235\241\346\266\210\346\201\257\345\255\230\345\210\260broker\347\232\204\350\277\207\347\250\213.md" new file mode 100644 index 0000000..6af0292 --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\270\200\346\235\241\346\266\210\346\201\257\345\255\230\345\210\260broker\347\232\204\350\277\207\347\250\213.md" @@ -0,0 +1,112 @@ +**前言** + +经过上篇文章的简单实战之后,**今天来聊聊生产者将消息从客户端发送到 Broker 上背后发生了哪些故事**,看不看由你,但是我保证可以本篇文章你一定可以学到应用背后的一些实质东西。 + +本文我们从以下 4 个方面来探讨下一条消息如何被准确的发送到 Broker 的 partition 上。 + +**1\. 客户端组件** + +**2\. 客户端缓存存储模型** + +**3\. 确定消息的 partition 位置** + +**4\. 发送线程的工作原理** + +* * * + +## **客户端组件** + +* **KafkaProducer:** + +KafkaProducer 是一个生产者客户端的进程,通过该对象启动生产者来发送消息。 + +* **RecordAccumulator:** + +RecordAccumulator 是一个记录收集器,用于收集客户端发送的消息,并将收集到的消息暂存到客户端缓存中。 + +* **Sender:** + +Sender 是一个发送线程,负责读取记录收集器中缓存的批量消息,经过一些中间转换操作,将要发送的数据准备好,然后交由 Selector 进行网络传输。 + +* **Selector:** + +Selector 是一个选择器,用于处理网络连接和读写处理,使用网络连接处理客户端上的网络请求。 + +通过使用以上四大组件即可完成客户端消息的发送工作。消息在网络中传输的方式只能通过二级制的方式,所以首先需要将消息序列化为二进制形式缓存在客户端,kafka 使用了双端队列的方式将消息缓存起来,然后使用发送线程(Sender)读取队列中的消息交给 Selector 进行网络传输发送给服务端(Broker) + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-7d57acd1d7dc5942e999e6ffebb28679_720w.webp) + +
+ +以上为发送消息的主流程,附上部分源码供大家参考,接下来分析下几个非常重要流程的具体实现原理。 + +* * * + +## **客户端缓存存储模型** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-5da65c5f9f8c0c9082e07c6431e78cd2_720w.webp) + +
+ +从上图可以看出,一条消息首先需要确定要被存储到那个 partition 对应的双端队列上;其次,存储消息的双端队列是以批的维度存储的,即 N 条消息组成一批,一批消息最多存储 N 条,超过后则新建一个组来存储新消息;其次,新来的消息总是从左侧写入,即越靠左侧的消息产生的时间越晚;最后,只有当一批消息凑够 N 条后才会发送给 Broker,否则不会发送到 Broker 上。 + +了解了客户端存储模型后,来探讨下确定消息的 partition(分区)位置? + +* * * + +## **确定消息的 partition 位置** + +消息可分为两种,一种是指定了 key 的消息,一种是没有指定 key 的消息。 + +对于指定了 key 的消息,partition 位置的计算方式为:**`Utils.murmur2(key) % numPartitions`**,即先对 key 进行哈希计算,然后在于 partition 个数求余,从而得到该条消息应该被存储在哪个 partition 上。 + +对于没有指定 key 的消息,partition 位置的计算方式为:**采用 round-robin 方式确定 partition 位置**,即采用轮询的方式,平均的将消息分布到不同的 partition 上,从而避免某些 partition 数据量过大影响 Broker 和消费端性能。 + +### **注意** + +由于 partition 有主副的区分,此处参与计算的 partition 数量是当前有主 partition 的数量,即如果某个 partition 无主的时候,则此 partition 是不能够进行数据写入的。 + +稍微解释一下,主副 partition 的机制是为了提高 kafka 系统的容错性的,即当某个 Broker 意外宕机时,在此 Broker 上的主 partition 状态为不可读写时(只有主 partition 可对外提供读写服务,副 partition 只有数据备份的功能),kafka 会从主 partition 对应的 N 个副 partition 中挑选一个,并将其状态改为主 partition,从而继续对外提供读写操作。 + +消息被确定分配到某个 partition 对应记录收集器(即双端队列)后,接下来,发送线程(Sender)从记录收集器中收集满足条件的批数据发送给 Broker,那么发送线程是如何收集满足条件的批数据的?批数据是按照 partition 维度发送的还是按照 Broker 维度发送数据的? + +* * * + +## **发送线程的工作原理** + +Sender 线程的主要工作是收集满足条件的批数据,何为满足条件的批数据?缓存数据是以批维度存储的,当一批数据量达到指定的 N 条时,就满足发送给 Broker 的条件了。 + +partition 维度和 Broker 维度发送消息模型对比。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-36b7c2761f17fb2d6481747523999011_720w.webp) + +
+ +从图中可以看出,左侧按照 partition 维度发送消息,每个 partition 都需要和 Broker 建连,总共发生了四次网络连接。而右侧将分布在同一个 Broker 的 partition 按组聚合后在与 Broker 建连,只需要两次网络连接即可。所以 Kafka 选择右侧的方式。 + +### **Sender 的主要工作** + +第一步:扫描记录收集器中满足条件的批数据,然后将 partition -> 批数据映射转换成 BrokerId -> N 批数据的映射。第二步:Sender 线程会为每个 BrokerId 创建一个客户端请求,然后将请求交给 NetWorkClient,由 NetWrokClient 去真正发送网络请求到 Broker。 + +### **NetWorkClient 的工作内容** + +Sender 线程准备好要发送的数据后,交由 NetWorkClient 来进行网络相关操作。主要包括客户端与服务端的建连、发送客户端请求、接受服务端响应。完成如上一系列的工作主要由如下方法完成。 + +1. reday()方法。从记录收集器获取准备完毕的节点,并连接所有准备好的节点。 +2. send()方法。为每个节点创建一个客户端请求,然后将请求暂时存到节点对应的 Channel(通道)中。 +3. poll()方法。该方法会真正轮询网络请求,发送请求给服务端节点和接受服务端的响应。 + +* * * + +## **总结** + +以上,即为生产者客户端的一条消息从生产到发送到 Broker 上的全过程。现在是不是就很清晰了呢?也许有些朋友会比较疑惑它的**网络请求模型是什么样的**,作者就猜你会你会问,下一篇我们就来扒开它的神秘面纱看看其究竟是怎么实现的,敬请期待。 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\273\213\347\273\215.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\273\213\347\273\215.md" new file mode 100644 index 0000000..bb4fd3b --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\273\213\347\273\215.md" @@ -0,0 +1,122 @@ +## 一.官网Kafka介绍 + +## 1.1 什么是事件流? + +事件流是相当于人体中枢神经系统的数字系统。它是“永远在线”世界的技术基础,在这个世界里,企业越来越多地由软件定义和自动化,软件的用户也更多地是软件。 + +从技术上讲,事件流是指以事件流的形式从数据库、传感器、移动设备、云服务和软件应用等事件源实时捕获数据的实践;持久地存储这些事件流以备以后检索;实时和回顾性地操作、处理和响应事件流;并根据需要将事件流路由到不同的目的地技术。因此,事件流确保了数据的连续流动和解释,从而使正确的信息在正确的地点、正确的时间出现。 + +## 1.2 我可以使用事件流做什么? + +事件流应用于众多行业和组织的各种用例。它的许多例子包括: 1. 实时处理支付和金融交易,如股票交易所、银行和保险。 2. 实时跟踪和监控汽车、卡车、车队和运输,如物流和汽车行业。 3. 持续捕获和分析来自物联网设备或其他设备的传感器数据,如工厂和风电场。 4. 收集并立即响应客户的互动和订单,如零售、酒店和旅游行业,以及移动应用程序。 5. 监测住院病人,预测病情变化,确保在紧急情况下及时治疗。 6. 连接、存储公司不同部门产生的数据并使其可用。 7. 作为数据平台、事件驱动架构和微服务的基础。 + +## 1.3 Apache Kafka®是一个事件流平台。这是什么意思? + +Kafka结合了三个关键的功能,所以你可以用一个单一的战斗测试解决方案来实现端到端事件流的用例: 1. 发布(写)和订阅(读)事件流,包括从其他系统连续导入/导出数据。 2. 持久性和可靠地存储事件流,只要你想。 3. 在事件发生或回顾时处理事件流。 + +所有这些功能都是以分布式、高度可伸缩、弹性、容错和安全的方式提供的。Kafka可以部署在裸金属硬件、虚拟机和容器上,也可以部署在云上。您可以选择自管理您的Kafka环境和使用由各种供应商提供的完全管理的服务。 + +## 1.4 简而言之,Kafka是如何工作的? + +Kafka是一个分布式系统,由服务器和客户端组成,通过高性能的TCP网络协议进行通信。它可以部署在裸金属硬件、虚拟机和内部环境中的容器上,也可以部署在云环境中。 + +**服务器:**Kafka作为一个集群运行一个或多个服务器,可以跨越多个数据中心或云区域。其中一些服务器构成存储层,称为代理。其他服务器运行Kafka Connect来持续导入和导出数据作为事件流,将Kafka与您现有的系统集成,如关系数据库以及其他Kafka集群。为了让你实现关键任务的用例,Kafka集群具有高度的可扩展性和容错性:如果它的任何一个服务器发生故障,其他服务器将接管它们的工作,以确保持续的操作而不丢失任何数据。 + +**客户机:**它们允许您编写分布式应用程序和微服务,这些应用程序和微服务可以并行地、大规模地读取、写入和处理事件流,甚至在出现网络问题或机器故障的情况下也可以容错。Kafka附带了一些这样的客户端,这些客户端被Kafka社区提供的几十个客户端增强了:客户端可以用于Java和Scala,包括更高级别的Kafka Streams库,用于Go、Python、C/ c++和许多其他编程语言以及REST api。 + +## 1.5 主要概念和术语 + +事件记录了在世界上或你的企业中“发生了某事”的事实。在文档中也称为记录或消息。当你读或写数据到Kafka时,你以事件的形式做这件事。从概念上讲,事件具有键、值、时间戳和可选的元数据头。下面是一个例子: 1. 活动重点:“爱丽丝” 2. 事件值:“向Bob支付200美元” 3. 事件时间戳:“2020年6月25日下午2:06。” + +生产者是那些向Kafka发布(写)事件的客户端应用程序,而消费者是那些订阅(读和处理)这些事件的应用程序。在Kafka中,生产者和消费者是完全解耦的,彼此是不可知的,这是实现Kafka闻名的高可扩展性的一个关键设计元素。例如,生产者从不需要等待消费者。Kafka提供了各种各样的保证,比如精确处理一次事件的能力。 + +事件被组织并持久地存储在主题中。很简单,一个主题类似于文件系统中的一个文件夹,事件就是该文件夹中的文件。一个示例主题名称可以是“payments”。Kafka中的主题总是多生产者和多订阅者:一个主题可以有0个、1个或多个生产者向它写入事件,也可以有0个、1个或多个消费者订阅这些事件。主题中的事件可以根据需要经常读取——与传统消息传递系统不同,事件在使用后不会删除。相反,你可以通过每个主题的配置设置来定义Kafka应该保留你的事件多长时间,之后旧的事件将被丢弃。Kafka的性能相对于数据大小来说是不变的,所以长时间存储数据是完全可以的。 + +主题是分区的,这意味着一个主题分散在位于不同Kafka broker上的多个“桶”上。这种数据的分布式位置对于可伸缩性非常重要,因为它允许客户机应用程序同时从/向多个代理读取和写入数据。当一个新事件被发布到一个主题时,它实际上被附加到主题的一个分区中。具有相同事件键(例如,客户或车辆ID)的事件被写入同一个分区,Kafka保证任何给定主题分区的消费者都将始终以写入的完全相同的顺序读取该分区的事件。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-39059344afb24ff7436bf7fb06bddde4_720w.webp) + +
+ +让你的数据容错和可用性,每一个主题可以被复制,甚至跨geo-regions或数据中心,这样总有多个经纪人有一份数据以防出错,你想做代理维护,等等。一个常见的生产设置是复制因子3,也就是说,您的数据总是有三个副本。这个复制是在主题分区级别执行的。 + +## 二.Kafka简介 + +Kafka是一种消息队列,主要用来处理大量数据状态下的消息队列,一般用来做日志的处理。既然是消息队列,那么Kafka也就拥有消息队列的相应的特性了。 + +**消息队列的好处** 1. 解耦合 耦合的状态表示当你实现某个功能的时候,是直接接入当前接口,而利用消息队列,可以将相应的消息发送到消息队列,这样的话,如果接口出了问题,将不会影响到当前的功能。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-e37a18ea7eddc69582d634cc881eb257_720w.webp) + +
+ +1. 异步处理 异步处理替代了之前的同步处理,异步处理不需要让流程走完就返回结果,可以将消息发送到消息队列中,然后返回结果,剩下让其他业务处理接口从消息队列中拉取消费处理即可。 + +2. 流量削峰 高流量的时候,使用消息队列作为中间件可以将流量的高峰保存在消息队列中,从而防止了系统的高请求,减轻服务器的请求处理压力。 + +## 2.1 Kafka消费模式 + +Kafka的消费模式主要有两种:一种是一对一的消费,也即点对点的通信,即一个发送一个接收。第二种为一对多的消费,即一个消息发送到消息队列,消费者根据消息队列的订阅拉取消息消费。 + +**一对一** + +
+ + +![image-20230525200024084](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230525200024084.png) + +
+ +消息生产者发布消息到Queue队列中,通知消费者从队列中拉取消息进行消费。消息被消费之后则删除,Queue支持多个消费者,但对于一条消息而言,只有一个消费者可以消费,即一条消息只能被一个消费者消费。 + +**一对多** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-d97a2898f3bdc417262bc88be616281c_720w.webp) + +
+ +这种模式也称为发布/订阅模式,即利用Topic存储消息,消息生产者将消息发布到Topic中,同时有多个消费者订阅此topic,消费者可以从中消费消息,注意发布到Topic中的消息会被多个消费者消费,消费者消费数据之后,数据不会被清除,Kafka会默认保留一段时间,然后再删除。 + +## 2.2 Kafka的基础架构 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-ef94691300117c049301d88c6337c9c2_720w.webp) + +
+ +Kafka像其他Mq一样,也有自己的基础架构,主要存在生产者Producer、Kafka集群Broker、消费者Consumer、注册中心Zookeeper. + +1. Producer:消息生产者,向Kafka中发布消息的角色。 + +2. Consumer:消息消费者,即从Kafka中拉取消息消费的客户端。 + +3. Consumer Group:消费者组,消费者组则是一组中存在多个消费者,消费者消费Broker中当前Topic的不同分区中的消息,消费者组之间互不影响,所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。某一个分区中的消息只能够一个消费者组中的一个消费者所消费 + +4. Broker:经纪人,一台Kafka服务器就是一个Broker,一个集群由多个Broker组成,一个Broker可以容纳多个Topic。 + +5. Topic:主题,可以理解为一个队列,生产者和消费者都是面向一个Topic + +6. Partition:分区,为了实现扩展性,一个非常大的Topic可以分布到多个Broker上,一个Topic可以分为多个Partition,每个Partition是一个有序的队列(分区有序,不能保证全局有序) + +7. Replica:副本Replication,为保证集群中某个节点发生故障,节点上的Partition数据不丢失,Kafka可以正常的工作,Kafka提供了副本机制,一个Topic的每个分区有若干个副本,一个Leader和多个Follower + +8. Leader:每个分区多个副本的主角色,生产者发送数据的对象,以及消费者消费数据的对象都是Leader。 + +9. Follower:每个分区多个副本的从角色,实时的从Leader中同步数据,保持和Leader数据的同步,Leader发生故障的时候,某个Follower会成为新的Leader。 + +上述一个Topic会产生多个分区Partition,分区中分为Leader和Follower,消息一般发送到Leader,Follower通过数据的同步与Leader保持同步,消费的话也是在Leader中发生消费,如果多个消费者,则分别消费Leader和各个Follower中的消息,当Leader发生故障的时候,某个Follower会成为主节点,此时会对齐消息的偏移量。 + +## 参考文章 +https://blog.csdn.net/cao131502 +https://zhuanlan.zhihu.com/p/137811719 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\216\237\347\220\206\345\210\206\346\236\220\346\200\273\347\273\223\347\257\207.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\216\237\347\220\206\345\210\206\346\236\220\346\200\273\347\273\223\347\257\207.md" new file mode 100644 index 0000000..58632b3 --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\216\237\347\220\206\345\210\206\346\236\220\346\200\273\347\273\223\347\257\207.md" @@ -0,0 +1,636 @@ +## 一、概述 + +Kakfa起初是由LinkedIn公司开发的一个分布式的消息系统,后成为Apache的一部分,它使用Scala编写,以可水平扩展和高吞吐率而被广泛使用。目前越来越多的开源分布式处理系统如Cloudera、Apache Storm、Spark等都支持与Kafka集成。 + +Kafka凭借着自身的优势,越来越受到互联网企业的青睐,唯品会也采用Kafka作为其内部核心消息引擎之一。Kafka作为一个商业级消息中间件,消息可靠性的重要性可想而知。如何确保消息的精确传输?如何确保消息的准确存储?如何确保消息的正确消费?这些都是需要考虑的问题。本文首先从Kafka的架构着手,先了解下Kafka的基本原理,然后通过对kakfa的存储机制、复制原理、同步原理、可靠性和持久性保证等等一步步对其可靠性进行分析,最后通过benchmark来增强对Kafka高可靠性的认知。 + + + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 二、Kafka的使用场景 + +(1)日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如Hadoop、Hbase、Solr等; + +(2)消息系统:解耦和生产者和消费者、缓存消息等; + +(3)用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到Hadoop、数据仓库中做离线分析和挖掘; + +(4)运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告; + +(5)流式处理:比如spark streaming和storm; + +(6)事件源; + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 三、Kafka基本架构 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181201215942487-1393117307.png) + +如上图所示,一个典型的Kafka体系架构包括: + +* 若干Producer(可以是服务器日志,业务数据,页面前端产生的page view等等), +* 若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高), +* 若干Consumer (Group),以及一个Zookeeper集群。 + +Kafka通过Zookeeper管理集群配置,选举leader,以及在consumer group发生变化时进行rebalance。Producer使用push(推)模式将消息发布到broker,Consumer使用pull(拉)模式从broker订阅并消费消息。 + + + +### 1、Topic & Partition + +一个topic可以认为一个一类消息,每个topic将被分成多个partition,每个partition在存储层面是append log文件。任何发布到此partition的消息都会被追加到log文件的尾部,每条消息在文件中的位置称为offset(偏移量),offset为一个long型的数字,它唯一标记一条消息。每条消息都被append到partition中,是顺序写磁盘,因此效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证)。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181201220820060-2075971944.png) + +每一条消息被发送到broker中,会根据partition规则选择被存储到哪一个partition。partition机制可以通过指定producer的partition.class这一参数来指定,该class必须实现kafka.producer.Partitioner接口。如果partition规则设置的合理,所有消息可以均匀分布到不同的partition里,这样就实现了水平扩展。(如果一个topic对应一个文件,那这个文件所在的机器I/O将会成为这个topic的性能瓶颈,而partition解决了这个问题)。在创建topic时可以在$KAFKA_HOME/config/server.properties中指定这个partition的数量(如下所示),当然可以在topic创建之后去修改partition的数量。 + + + +
# The default number of log partitions per topic. More partitions allow greater
+# parallelism for consumption, but this will also result in more files across
+# the brokers.
+#默认partitions数量
+num.partitions=1
+ + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 四、高可靠性存储分析概述 + +Kafka的高可靠性的保障来源于其健壮的副本(replication)策略。通过调节其副本相关参数,可以使得Kafka在性能和可靠性之间运转的游刃有余。Kafka从0.8.x版本开始提供partition级别的复制,replication的数量可以在$KAFKA_HOME/config/server.properties中配置(default.replication.refactor)。 + +这里先从Kafka文件存储机制入手,从最底层了解Kafka的存储细节,进而对其的存储有个微观的认知。之后通过Kafka复制原理和同步方式来阐述宏观层面的概念。最后从ISR,HW,leader选举以及数据可靠性和持久性保证等等各个维度来丰富对Kafka相关知识点的认知。 + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 五、Kafka文件存储机制 + +Kafka中消息是以topic进行分类的,生产者通过topic向Kafka broker发送消息,消费者通过topic读取数据。然而topic在物理层面又能以partition为分组,一个topic可以分成若干个partition,那么topic以及partition又是怎么存储的呢?partition还可以细分为segment,一个partition物理上由多个segment组成,那么这些segment又是什么呢?下面我们来一一揭晓。 + +为了便于说明问题,假设这里只有一个Kafka集群,且这个集群只有一个Kafka broker,即只有一台物理机。在这个Kafka broker中配置($KAFKA_HOME/config/server.properties中)log.dirs=/tmp/kafka-logs,以此来设置Kafka消息文件存储目录,与此同时创建一个topic:topic_zzh_test,partition的数量为4($KAFKA_HOME/bin/kafka-topics.sh –create –zookeeper localhost:2181 –partitions 4 –topic topic_vms_test –replication-factor 4)。那么我们此时可以在/tmp/kafka-logs目录中可以看到生成了4个目录: + + + + + +
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-0 
+drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-1 
+drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-2 
+drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-3  
+ + + + +在Kafka文件存储中,同一个topic下有多个不同的partition,每个partiton为一个目录,partition的名称规则为:topic名称+有序序号,第一个序号从0开始计,最大的序号为partition数量减1,partition是实际物理上的概念,而topic是逻辑上的概念。 + +上面提到partition还可以细分为segment,这个segment又是什么?如果就以partition为最小存储单位,我们可以想象当Kafka producer不断发送消息,必然会引起partition文件的无限扩张,这样对于消息文件的维护以及已经被消费的消息的清理带来严重的影响,所以这里以segment为单位又将partition细分。每个partition(目录)相当于一个巨型文件被平均分配到多个大小相等的segment(段)数据文件中(每个segment 文件中消息数量不一定相等)这种特性也方便old segment的删除,即方便已被消费的消息的清理,提高磁盘的利用率。每个partition只需要支持顺序读写就行,segment的文件生命周期由服务端配置参数(log.segment.bytes,log.roll.{ms,hours}等若干参数)决定。 + + + + + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码") + +
 #在强制刷新数据到磁盘允许接收消息的数量
+#log.flush.interval.messages=10000 # 在强制刷新之前,消息可以在日志中停留的最长时间
+#log.flush.interval.ms=1000 #一个日志的最小存活时间,可以被删除
+log.retention.hours=168 #  一个基于大小的日志保留策略。段将被从日志中删除只要剩下的部分段不低于log.retention.bytes。
+#log.retention.bytes=1073741824 #  每一个日志段大小的最大值。当到达这个大小时,会生成一个新的片段。
+log.segment.bytes=1073741824 # 检查日志段的时间间隔,看是否可以根据保留策略删除它们
+log.retention.check.interval.ms=300000
+ + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码") + + + +segment文件由两部分组成,分别为“.index”文件和“.log”文件,分别表示为segment索引文件和数据文件。这两个文件的命令规则为:partition全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值,数值大小为64位,20位数字字符长度,没有数字用0填充,如下: + + + + + + + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码") + +
00000000000000000000.index 00000000000000000000.log 00000000000000170410.index 00000000000000170410.log 00000000000000239430.index 00000000000000239430.log  
+ +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码") + + + +以上面的segment文件为例,展示出segment:00000000000000170410的“.index”文件和“.log”文件的对应的关系,如下图: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181201224133022-2085407889.png) + + + + + +如上图,“.index”索引文件存储大量的元数据,“.log”数据文件存储大量的消息,索引文件中的元数据指向对应数据文件中message的物理偏移地址。其中以“.index”索引文件中的元数据[3, 348]为例,在“.log”数据文件表示第3个消息,即在全局partition中表示170410+3=170413个消息,该消息的物理偏移地址为348。 + +那么如何从partition中通过offset查找message呢? + +以上图为例,读取offset=170418的消息,首先查找segment文件,其中00000000000000000000.index为最开始的文件,第二个文件为00000000000000170410.index(起始偏移为170410+1=170411),而第三个文件为00000000000000239430.index(起始偏移为239430+1=239431),所以这个offset=170418就落到了第二个文件之中。其他后续文件可以依次类推,以其实偏移量命名并排列这些文件,然后根据二分查找法就可以快速定位到具体文件位置。其次根据00000000000000170410.index文件中的[8,1325]定位到00000000000000170410.log文件中的1325的位置进行读取。 + +要是读取offset=170418的消息,从00000000000000170410.log文件中的1325的位置进行读取,那么怎么知道何时读完本条消息,否则就读到下一条消息的内容了? + +这个就需要联系到消息的物理结构了,消息都具有固定的物理结构,包括:offset(8 Bytes)、消息体的大小(4 Bytes)、crc32(4 Bytes)、magic(1 Byte)、attributes(1 Byte)、key length(4 Bytes)、key(K Bytes)、payload(N Bytes)等等字段,可以确定一条消息的大小,即读取到哪里截止。 + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 六、复制原理和同步方式 + +Kafka中topic的每个partition有一个预写式的日志文件,虽然partition可以继续细分为若干个segment文件,但是对于上层应用来说可以将partition看成最小的存储单元(一个有多个segment文件拼接的“巨型”文件),每个partition都由一些列有序的、不可变的消息组成,这些消息被连续的追加到partition中。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181201231403669-958736996.png) + +上图中有两个新名词:HW和LEO。这里先介绍下LEO,LogEndOffset的缩写,表示每个partition的log最后一条Message的位置。HW是HighWatermark的缩写,是指consumer能够看到的此partition的位置,这个涉及到多副本的概念,这里先提及一下,下节再详表。 + +言归正传,为了提高消息的可靠性,Kafka每个topic的partition有N个副本(replicas),其中N(大于等于1)是topic的复制因子(replica fator)的个数。Kafka通过多副本机制实现故障自动转移,当Kafka集群中一个broker失效情况下仍然保证服务可用。在Kafka中发生复制时确保partition的日志能有序地写到其他节点上,N个replicas中,其中一个replica为leader,其他都为follower, leader处理partition的所有读写请求,与此同时,follower会被动定期地去复制leader上的数据。 + +如下图所示,Kafka集群中有4个broker, 某topic有3个partition,且复制因子即副本个数也为3: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181201231724531-2038730622.png) + +Kafka提供了数据复制算法保证,如果leader发生故障或挂掉,一个新leader被选举并被接受客户端的消息成功写入。Kafka确保从同步副本列表中选举一个副本为leader,或者说follower追赶leader数据。leader负责维护和跟踪ISR(In-Sync Replicas的缩写,表示副本同步队列,具体可参考下节)中所有follower滞后的状态。当producer发送一条消息到broker后,leader写入消息并复制到所有follower。消息提交之后才被成功复制到所有的同步副本。消息复制延迟受最慢的follower限制,重要的是快速检测慢副本,如果follower“落后”太多或者失效,leader将会把它从ISR中删除。 + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 七、零拷贝 + +Kafka中存在大量的网络数据持久化到磁盘(Producer到Broker)和磁盘文件通过网络发送(Broker到Consumer)的过程。这一过程的性能直接影响Kafka的整体吞吐量。 + + + +### 1、传统模式下的四次拷贝与四次上下文切换 + +以将磁盘文件通过网络发送为例。传统模式下,一般使用如下伪代码所示的方法先将文件数据读入内存,然后通过Socket将内存中的数据发送出去。 + + + +
buffer = File.read
+Socket.send(buffer)
+ + + + +这一过程实际上发生了四次数据拷贝。首先通过系统调用将文件数据读入到内核态Buffer(DMA拷贝),然后应用程序将内存态Buffer数据读入到用户态Buffer(CPU拷贝),接着用户程序通过Socket发送数据时将用户态Buffer数据拷贝到内核态Buffer(CPU拷贝),最后通过DMA拷贝将数据拷贝到NIC Buffer。同时,还伴随着四次上下文切换,如下图所示。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181227142339448-1209004133.png) + +### 2、sendfile和transferTo实现零拷贝 + +Linux 2.4+内核通过`sendfile`系统调用,提供了零拷贝。数据通过DMA拷贝到内核态Buffer后,直接通过DMA拷贝到NIC Buffer,无需CPU拷贝。这也是零拷贝这一说法的来源。除了减少数据拷贝外,因为整个读文件-网络发送由一个`sendfile`调用完成,整个过程只有两次上下文切换,因此大大提高了性能。零拷贝过程如下图所示。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181227142423000-1025665055.png) + +从具体实现来看,Kafka的数据传输通过TransportLayer来完成,其子类`PlaintextTransportLayer`通过[Java NIO](http://www.jasongj.com/java/nio_reactor/)的FileChannel的`transferTo`和`transferFrom`方法实现零拷贝,如下所示。 + + + +
@Override public long transferFrom(FileChannel fileChannel, long position, long count) throws IOException { return fileChannel.transferTo(position, count, socketChannel);
+}
+ + + + +**注:** `transferTo`和`transferFrom`并不保证一定能使用零拷贝。实际上是否能使用零拷贝与操作系统相关,如果操作系统提供`sendfile`这样的零拷贝系统调用,则这两个方法会通过这样的系统调用充分利用零拷贝的优势,否则并不能通过这两个方法本身实现零拷贝。 + + + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 八、 ISR(副本同步队列) + +上节我们涉及到ISR (In-Sync Replicas),这个是指副本同步队列。副本数对Kafka的吞吐率是有一定的影响,但极大的增强了可用性。默认情况下Kafka的replica数量为1,即每个partition都有一个唯一的leader,为了确保消息的可靠性,通常应用中将其值(由broker的参数offsets.topic.replication.factor指定)大小设置为大于1,比如3。 所有的副本(replicas)统称为Assigned Replicas,即AR。ISR是AR中的一个子集,由leader维护ISR列表,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟条数replica.lag.max.messages两个维度, 当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度),任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR + +Kafka 0.10.x版本后移除了replica.lag.max.messages参数,只保留了replica.lag.time.max.ms作为ISR中副本管理的参数。为什么这样做呢?replica.lag.max.messages表示当前某个副本落后leaeder的消息数量超过了这个参数的值,那么leader就会把follower从ISR中删除。假设设置replica.lag.max.messages=4,那么如果producer一次传送至broker的消息数量都小于4条时,因为在leader接受到producer发送的消息之后而follower副本开始拉取这些消息之前,follower落后leader的消息数不会超过4条消息,故此没有follower移出ISR,所以这时候replica.lag.max.message的设置似乎是合理的。但是producer发起瞬时高峰流量,producer一次发送的消息超过4条时,也就是超过replica.lag.max.messages,此时follower都会被认为是与leader副本不同步了,从而被踢出了ISR。但实际上这些follower都是存活状态的且没有性能问题。那么在之后追上leader,并被重新加入了ISR。于是就会出现它们不断地剔出ISR然后重新回归ISR,这无疑增加了无谓的性能损耗。而且这个参数是broker全局的。设置太大了,影响真正“落后”follower的移除;设置的太小了,导致follower的频繁进出。无法给定一个合适的replica.lag.max.messages的值,故此,新版本的Kafka移除了这个参数。注:ISR中包括:leader和follower。 + + + +上面一节还涉及到一个概念,即HW。HW俗称高水位,HighWatermark的缩写,取一个partition对应的ISR中最小的LEO作为HW,consumer最多只能消费到HW所在的位置。另外每个replica都有HW,leader和follower各自负责更新自己的HW的状态。对于leader新写入的消息,consumer不能立刻消费,leader会等待该消息被所有ISR中的replicas同步后更新HW,此时消息才能被consumer消费。这样就保证了如果leader所在的broker失效,该消息仍然可以从新选举的leader中获取。对于来自内部broKer的读取请求,没有HW的限制。 + +下图详细的说明了当producer生产消息至broker后,ISR以及HW和LEO的流转过程: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181202175002622-1830127657.png) + +由此可见,Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。事实上,同步复制要求所有能工作的follower都复制完,这条消息才会被commit,这种复制方式极大的影响了吞吐率。而异步复制方式下,follower异步的从leader复制数据,数据只要被leader写入log就被认为已经commit,这种情况下如果follower都还没有复制完,落后于leader时,突然leader宕机,则会丢失数据。而Kafka的这种使用ISR的方式则很好的均衡了确保数据不丢失以及吞吐率。 + +Kafka的ISR的管理最终都会反馈到Zookeeper节点上。具体位置为:/brokers/topics/[topic]/partitions/[partition]/state。 + +目前有两个地方会对这个Zookeeper的节点进行维护: + +Controller来维护:Kafka集群中的其中一个Broker会被选举为Controller,主要负责Partition管理和副本状态管理,也会执行类似于重分配partition之类的管理任务。在符合某些特定条件下,Controller下的LeaderSelector会选举新的leader,ISR和新的leader_epoch及controller_epoch写入Zookeeper的相关节点中。同时发起LeaderAndIsrRequest通知所有的replicas。 + +leader来维护:leader有单独的线程定期检测ISR中follower是否脱离ISR, 如果发现ISR变化,则会将新的ISR的信息返回到Zookeeper的相关节点中。 + + + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 九、数据可靠性和持久性保证 + +当producer向leader发送数据时,可以通过request.required.acks参数来设置数据可靠性的级别: + + + +* 1(默认):这意味着producer在ISR中的leader已成功收到的数据并得到确认后发送下一条message。如果leader宕机了,则会丢失数据。 + +* 0:这意味着producer无需等待来自broker的确认而继续发送下一批消息。这种情况下数据传输效率最高,但是数据可靠性确是最低的。 + +* -1:producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。但是这样也不能保证数据不丢失,比如当ISR中只有leader时(前面ISR那一节讲到,ISR中的成员由于某些情况会增加也会减少,最少就只剩一个leader),这样就变成了acks=1的情况。 + + [官网配置说明](http://kafka.apache.org/documentation/#configuration) + +如果要提高数据的可靠性,在设置request.required.acks=-1的同时,也要min.insync.replicas这个参数(可以在broker或者topic层面进行设置)的配合,这样才能发挥最大的功效。min.insync.replicas这个参数设定ISR中的最小副本数是多少,默认值为1,当且仅当request.required.acks参数设置为-1时,此参数才生效。如果ISR中的副本数少于min.insync.replicas配置的数量时,客户端会返回异常:org.apache.kafka.common.errors.NotEnoughReplicasExceptoin: Messages are rejected since there are fewer in-sync replicas than required。 + + + +接下来对acks=1和-1的两种情况进行详细分析: + + + + + +### 9.1、request.required.acks=1 + +producer发送数据到leader,leader写本地日志成功,返回客户端成功;此时ISR中的副本还没有来得及拉取该消息,leader就宕机了,那么此次发送的消息就会丢失。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181202181329621-1088596676.png) + + + +### 9.2、request.required.acks=-1 + +同步(Kafka默认为同步,即producer.type=sync)的发送模式,replication.factor>=2且min.insync.replicas>=2的情况下,不会丢失数据。 + +有两种典型情况。acks=-1的情况下(如无特殊说明,以下acks都表示为参数request.required.acks),数据发送到leader, ISR的follower全部完成数据同步后,leader此时挂掉,那么会选举出新的leader,数据不会丢失。 + +![](https://img2018.cnblogs.com/blog/843808/201812/843808-20181202212242480-242555451.png) + +acks=-1的情况下,数据发送到leader后 ,部分ISR的副本同步,leader此时挂掉。比如follower1h和follower2都有可能变成新的leader, producer端会得到返回异常,producer端会重新发送数据,数据可能会重复 +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181202212407453-662912091.png) + +当然上图中如果在leader crash的时候,follower2还没有同步到任何数据,而且follower2被选举为新的leader的话,这样消息就不会重复。 + +注:Kafka只处理fail/recover问题,不处理Byzantine问题。 + + + + + +### 9.3、关于HW的进一步探讨 + +考虑上图(即acks=-1,部分ISR副本同步)中的另一种情况,如果在Leader挂掉的时候,follower1同步了消息4,5,follower2同步了消息4,与此同时follower2被选举为leader,那么此时follower1中的多出的消息5该做如何处理呢? + +这里就需要HW的协同配合了。如前所述,一个partition中的ISR列表中,leader的HW是所有ISR列表里副本中最小的那个的LEO。类似于木桶原理,水位取决于最低那块短板。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203204010599-1107873190.png) + + + +如上图,某个topic的某partition有三个副本,分别为A、B、C。A作为leader肯定是LEO最高,B紧随其后,C机器由于配置比较低,网络比较差,故而同步最慢。这个时候A机器宕机,这时候如果B成为leader,假如没有HW,在A重新恢复之后会做同步(makeFollower)操作,在宕机时log文件之后直接做追加操作,而假如B的LEO已经达到了A的LEO,会产生数据不一致的情况,所以使用HW来避免这种情况。 + +A在做同步操作的时候,先将log文件截断到之前自己的HW的位置,即3,之后再从B中拉取消息进行同步。 + +如果失败的follower恢复过来,它首先将自己的log文件截断到上次checkpointed时刻的HW的位置,之后再从leader中同步消息。leader挂掉会重新选举,新的leader会发送“指令”让其余的follower截断至自身的HW的位置然后再拉取新的消息。 + +当ISR中的个副本的LEO不一致时,如果此时leader挂掉,选举新的leader时并不是按照LEO的高低进行选举,而是按照ISR中的顺序选举。 + + + + + +### 9.4、Leader选举 + +一条消息只有被ISR中的所有follower都从leader复制过去才会被认为已提交。这样就避免了部分数据被写进了leader,还没来得及被任何follower复制就宕机了,而造成数据丢失。而对于producer而言,它可以选择是否等待消息commit,这可以通过request.required.acks来设置。这种机制确保了只要ISR中有一个或者以上的follower,一条被commit的消息就不会丢失。 + +有一个很重要的问题是当leader宕机了,怎样在follower中选举出新的leader,因为follower可能落后很多或者直接crash了,所以必须确保选择“最新”的follower作为新的leader。一个基本的原则就是,如果leader不在了,新的leader必须拥有原来的leader commit的所有消息。这就需要做一个折中,如果leader在表名一个消息被commit前等待更多的follower确认,那么在它挂掉之后就有更多的follower可以成为新的leader,但这也会造成吞吐率的下降。 + +一种非常常用的选举leader的方式是“少数服从多数”,Kafka并不是采用这种方式。这种模式下,如果我们有2f+1个副本,那么在commit之前必须保证有f+1个replica复制完消息,同时为了保证能正确选举出新的leader,失败的副本数不能超过f个。这种方式有个很大的优势,系统的延迟取决于最快的几台机器,也就是说比如副本数为3,那么延迟就取决于最快的那个follower而不是最慢的那个。“少数服从多数”的方式也有一些劣势,为了保证leader选举的正常进行,它所能容忍的失败的follower数比较少,如果要容忍1个follower挂掉,那么至少要3个以上的副本,如果要容忍2个follower挂掉,必须要有5个以上的副本。也就是说,在生产环境下为了保证较高的容错率,必须要有大量的副本,而大量的副本又会在大数据量下导致性能的急剧下降。这种算法更多用在Zookeeper这种共享集群配置的系统中而很少在需要大量数据的系统中使用的原因。HDFS的HA功能也是基于“少数服从多数”的方式,但是其数据存储并不是采用这样的方式。 + +实际上,leader选举的算法非常多,比如Zookeeper的Zab、Raft以及Viewstamped Replication。而Kafka所使用的leader选举算法更像是微软的PacificA算法。 + +Kafka在Zookeeper中为每一个partition动态的维护了一个ISR,这个ISR里的所有replication都跟上了leader,只有ISR里的成员才能有被选为leader的可能(unclean.leader.election.enable=false)。在这种模式下,对于f+1个副本,一个Kafka topic能在保证不丢失已经commit消息的前提下容忍f个副本的失败,在大多数使用场景下,这种模式是十分有利的。事实上,为了容忍f个副本的失败,“少数服从多数”的方式和ISR在commit前需要等待的副本的数量是一样的,但是ISR需要的总的副本的个数几乎是“少数服从多数”的方式的一半。 + +上文提到,在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失,但如果某一个partition的所有replica都挂了,就无法保证数据不丢失了。这种情况下有两种可行的方案: + +等待ISR中任意一个replica“活”过来,并且选它作为leader + +选择第一个“活”过来的replica(并不一定是在ISR中)作为leader + +这就需要在可用性和一致性当中作出一个简单的抉择。如果一定要等待ISR中的replica“活”过来,那不可用的时间就可能会相对较长。而且如果ISR中所有的replica都无法“活”过来了,或者数据丢失了,这个partition将永远不可用。选择第一个“活”过来的replica作为leader,而这个replica不是ISR中的replica,那即使它并不保障已经包含了所有已commit的消息,它也会成为leader而作为consumer的数据源。默认情况下,Kafka采用第二种策略,即   + + + +* unclean.leader.election.enable=true,也可以将此参数设置为false来启用第一种策略。 +* unclean.leader.election.enable这个参数对于leader的选举、系统的可用性以及数据的可靠性都有至关重要的影响。 + +下面我们来分析下几种典型的场景。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203213455180-1212737615.png) + +如果上图所示,假设某个partition中的副本数为3,replica-0, replica-1, replica-2分别存放在broker0, broker1和broker2中。AR=(0,1,2),ISR=(0,1)。 + +设置request.required.acks=-1, min.insync.replicas=2,unclean.leader.election.enable=false。这里讲broker0中的副本也称之为broker0起初broker0为leader,broker1为follower。 + +当ISR中的replica-0出现crash的情况时,broker1选举为新的leader[ISR=(1)],因为受min.insync.replicas=2影响,write不能服务,但是read能继续正常服务。此种情况恢复方案: + +尝试恢复(重启)replica-0,如果能起来,系统正常; +如果replica-0不能恢复,需要将min.insync.replicas设置为1,恢复write功能。 + + +当ISR中的replica-0出现crash,紧接着replica-1也出现了crash, 此时[ISR=(1),leader=-1],不能对外提供服务,此种情况恢复方案: + +尝试恢复replica-0和replica-1,如果都能起来,则系统恢复正常; +如果replica-0起来,而replica-1不能起来,这时候仍然不能选出leader,因为当设置unclean.leader.election.enable=false时,leader只能从ISR中选举,当ISR中所有副本都失效之后,需要ISR中最后失效的那个副本能恢复之后才能选举leader, 即replica-0先失效,replica-1后失效,需要replica-1恢复后才能选举leader。保守的方案建议把unclean.leader.election.enable设置为true,但是这样会有丢失数据的情况发生,这样可以恢复read服务。同样需要将min.insync.replicas设置为1,恢复write功能;replica-1恢复,replica-0不能恢复,这个情况上面遇到过,read服务可用,需要将min.insync.replicas设置为1,恢复write功能; +replica-0和replica-1都不能恢复,这种情况可以参考情形2. + +当ISR中的replica-0, replica-1同时宕机,此时[ISR=(0,1)],不能对外提供服务,此种情况恢复方案:尝试恢复replica-0和replica-1,当其中任意一个副本恢复正常时,对外可以提供read服务。直到2个副本恢复正常,write功能才能恢复,或者将将min.insync.replicas设置为1。 + + + + + +### 9.5、Kafka的发送模式 + +Kafka的发送模式由producer端的配置参数producer.type来设置,这个参数指定了在后台线程中消息的发送方式是同步的还是异步的,默认是同步的方式,即producer.type=sync。如果设置成异步的模式,即producer.type=async,可以是producer以batch的形式push数据,这样会极大的提高broker的性能,但是这样会增加丢失数据的风险。如果需要确保消息的可靠性,必须要将producer.type设置为sync。 + +对于异步模式,还有4个配套的参数,如下: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203213857717-291133501.png) + +以batch的方式推送数据可以极大的提高处理效率,kafka producer可以将消息在内存中累计到一定数量后作为一个batch发送请求。batch的数量大小可以通过producer的参数(batch.num.messages)控制。通过增加batch的大小,可以减少网络请求和磁盘IO的次数,当然具体参数设置需要在效率和时效性方面做一个权衡。在比较新的版本中还有batch.size这个参数。 + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 十、高可靠性使用分析 + + + +### 10.1、消息传输保障 + +前面已经介绍了Kafka如何进行有效的存储,以及了解了producer和consumer如何工作。接下来讨论的是Kafka如何确保消息在producer和consumer之间传输。有以下三种可能的传输保障(delivery guarantee): + + + +* At most once: 消息可能会丢,但绝不会重复传输 +* At least once:消息绝不会丢,但可能会重复传输 +* Exactly once:每条消息肯定会被传输一次且仅传输一次 + +Kafka的消息传输保障机制非常直观。当producer向broker发送消息时,一旦这条消息被commit,由于副本机制(replication)的存在,它就不会丢失。但是如果producer发送数据给broker后,遇到的网络问题而造成通信中断,那producer就无法判断该条消息是否已经提交(commit)。虽然Kafka无法确定网络故障期间发生了什么,但是producer可以retry多次,确保消息已经正确传输到broker中,所以目前Kafka实现的是at least once。 + +consumer从broker中读取消息后,可以选择commit,该操作会在Zookeeper中存下该consumer在该partition下读取的消息的offset。该consumer下一次再读该partition时会从下一条开始读取。如未commit,下一次读取的开始位置会跟上一次commit之后的开始位置相同。当然也可以将consumer设置为autocommit,即consumer一旦读取到数据立即自动commit。如果只讨论这一读取消息的过程,那Kafka是确保了exactly once, 但是如果由于前面producer与broker之间的某种原因导致消息的重复,那么这里就是at least once。 + +考虑这样一种情况,当consumer读完消息之后先commit再处理消息,在这种模式下,如果consumer在commit后还没来得及处理消息就crash了,下次重新开始工作后就无法读到刚刚已提交而未处理的消息,这就对应于at most once了。 + +读完消息先处理再commit。这种模式下,如果处理完了消息在commit之前consumer crash了,下次重新开始工作时还会处理刚刚未commit的消息,实际上该消息已经被处理过了,这就对应于at least once。 + +要做到exactly once就需要引入消息去重机制。 + + + +### 10.2、消息去重 + + + +如上一节所述,Kafka在producer端和consumer端都会出现消息的重复,这就需要去重处理。 + +Kafka文档中提及GUID(Globally Unique Identifier)的概念,通过客户端生成算法得到每个消息的unique id,同时可映射至broker上存储的地址,即通过GUID便可查询提取消息内容,也便于发送方的幂等性保证,需要在broker上提供此去重处理模块,最新版本已经支持。 + +针对GUID, 如果从客户端的角度去重,那么需要引入集中式缓存,必然会增加依赖复杂度,另外缓存的大小难以界定。 + +不只是Kafka, 类似RabbitMQ以及RocketMQ这类商业级中间件也只保障at least once, 且也无法从自身去进行消息去重。所以我们建议业务方根据自身的业务特点进行去重,比如业务消息本身具备幂等性,或者借助Redis等其他产品进行去重处理。 + + + + + +### 10.3、高可靠性配置 + +Kafka提供了很高的数据冗余弹性,对于需要数据高可靠性的场景,我们可以增加数据冗余备份数(replication.factor),调高最小写入副本数的个数(min.insync.replicas)等等,但是这样会影响性能。反之,性能提高而可靠性则降低,用户需要自身业务特性在彼此之间做一些权衡性选择。 + +要保证数据写入到Kafka是安全的,高可靠的,需要如下的配置: + + + +1. topic的配置:replication.factor>=3,即副本数至少是3个;2<=min.insync.replicas<=replication.factor +2. broker的配置:leader的选举条件unclean.leader.election.enable=false +3. producer的配置:request.required.acks=-1(all),producer.type=sync + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 十一、内部网络框架 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181212101055804-450926848.png) + +Broker的内部处理流水线化,分为多个阶段来进行(SEDA),以提高吞吐量和性能,尽量避免Thead盲等待,以下为过程说明。 + +* Accept Thread负责与客户端建立连接链路,然后把Socket轮转交给Process Thread +* Process Thread负责接收请求和响应数据,Process Thread每次基于Selector事件循环,首先从Response Queue读取响应数据,向客户端回复响应,然后接收到客户端请求后,读取数据放入Request Queue。 +* Work Thread负责业务逻辑、IO磁盘处理等,负责从Request Queue读取请求,并把处理结果放入Response Queue中,待Process Thread发送出去。 + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 十二、rebalance机制 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181212101229091-1187958161.png) + +Kafka保证同一consumer group中只有一个consumer会消费某条消息,实际上,Kafka保证的是稳定状态下每一个consumer实例只会消费某一个或多个特定的数据,而某个partition的数据只会被某一个特定的consumer实例所消费。这样设计的劣势是无法让同一个consumer group里的consumer均匀消费数据,优势是每个consumer不用都跟大量的broker通信,减少通信开销,同时也降低了分配难度,实现也更简单。另外,因为同一个partition里的数据是有序的,这种设计可以保证每个partition里的数据也是有序被消费。 + +如果某consumer group中consumer数量少于partition数量,则至少有一个consumer会消费多个partition的数据,如果consumer的数量与partition数量相同,则正好一个consumer消费一个partition的数据,而如果consumer的数量多于partition的数量时,会有部分consumer无法消费该topic下任何一条消息。 + +**Consumer Rebalance算法如下 :** + + + +
1. 将目标 topic 下的所有 partirtion 排序,存于PT 2. 对某 consumer group 下所有 consumer 排序,存于 CG,第 i 个consumer 记为 Ci 3. N=size(PT)/size(CG),向上取整 4. 解除 Ci 对原来分配的 partition 的消费权(i从0开始) 5. 将第i*N到(i+1)*N-1个 partition 分配给 Ci 
+ + + +目前consumer rebalance的控制策略是由每一个consumer通过Zookeeper完成的。具体的控制方式如下: + + + +
在/consumers/[consumer-group]/下注册id
+设置对/consumers/[consumer-group] 的watcher
+设置对/brokers/ids的watcher
+zk下设置watcher的路径节点更改,触发consumer rebalance
+ + + + +在这种策略下,**每一个consumer或者broker的增加或者减少都会触发consumer rebalance**。因为每个consumer只负责调整自己所消费的partition,为了保证整个consumer group的一致性,所以当一个consumer触发了rebalance时,该consumer group内的其它所有consumer也应该同时触发rebalance。 + +* Herd effect + +任何broker或者consumer的增减都会触发所有的consumer的rebalance + +* Split Brain + +每个consumer分别单独通过Zookeeper判断哪些partition down了,那么不同consumer从Zookeeper“看”到的view就可能不一样,这就会造成错误的reblance尝试。而且有可能所有的consumer都认为rebalance已经完成了,但实际上可能并非如此。 + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 十三、BenchMark + +Kafka在唯品会有着很深的历史渊源,根据唯品会消息中间件团队(VMS团队)所掌握的资料显示,在VMS团队运转的Kafka集群中所支撑的topic数已接近2000,每天的请求量也已达千亿级。这里就以Kafka的高可靠性为基准点来探究几种不同场景下的行为表现,以此来加深对Kafka的认知,为大家在以后高效的使用Kafka时提供一份依据。 + + + +### 13.1、测试环境 + +Kafka broker用到了4台机器,分别为broker[0/1/2/3]配置如下: + +CPU: 24core/2.6GHZ +Memory: 62G +Network: 4000Mb +OS/kernel: CentOs release 6.6 (Final) +Disk: 1089G +Kafka版本:0.10.1.0 +broker端JVM参数设置: + + + + + +
-Xmx8G -Xms8G -server -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark -XX:+DisableExplicitGC -Djava.awt.headless=true -Xloggc:/apps/service/kafka/bin/../logs/kafkaServer-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=9999 
+ + + +客户端机器配置: + +* CPU: 24core/2.6GHZ +* Memory: 3G +* Network: 1000Mb +* OS/kernel: CentOs release 6.3 (Final) +* Disk: 240G + +### 13.2、不同场景测试 + +#### 场景1: + +测试不同的副本数、min.insync.replicas策略以及request.required.acks策略(以下简称acks策略)对于发送速度(TPS)的影响。 + +具体配置:一个producer;发送方式为sync;消息体大小为1kB;partition数为12。副本数为:1/2/4;min.insync.replicas分别为1/2/4;acks分别为-1(all)/1/0。 + +具体测试数据如下表(min.insync.replicas只在acks=-1时有效): + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203221444373-736905445.png) + +测试结果分析: + + + +1. 客户端的acks策略对发送的TPS有较大的影响,TPS:acks_0 > acks_1 > ack_-1; +2. 副本数越高,TPS越低;副本数一致时,min.insync.replicas不影响TPS; +3. acks=0/1时,TPS与min.insync.replicas参数以及副本数无关,仅受acks策略的影响。 + +下面将partition的个数设置为1,来进一步确认下不同的acks策略、不同的min.insync.replicas策略以及不同的副本数对于发送速度的影响,详细请看情景2和情景3。 + +#### 场景2: + +在partition个数固定为1,测试不同的副本数和min.insync.replicas策略对发送速度的影响。 + +具体配置:一个producer;发送方式为sync;消息体大小为1kB;producer端acks=-1(all)。变换副本数:2/3/4; min.insync.replicas设置为:1/2/4。 + +测试结果如下: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203221832812-2007554062.png) + +测试结果分析: + +副本数越高,TPS越低(这点与场景1的测试结论吻合),但是当partition数为1时差距甚微。min.insync.replicas不影响TPS。 + +#### 场景3: + +在partition个数固定为1,测试不同的acks策略和副本数对发送速度的影响。 + +具体配置:一个producer;发送方式为sync;消息体大小为1kB;min.insync.replicas=1。topic副本数为:1/2/4;acks: 0/1/-1。 + +测试结果如下: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203222003811-323209661.png) + + + + + + + + + + + + + +测试结果分析(与情景1一致): + +* 副本数越多,TPS越低; +* 客户端的acks策略对发送的TPS有较大的影响,TPS:acks_0 > acks_1 > ack_-1。 + +#### 场景4: + +测试不同partition数对发送速率的影响 + +具体配置:一个producer;消息体大小为1KB;发送方式为sync;topic副本数为2;min.insync.replicas=2;acks=-1。partition数量设置为1/2/4/8/12。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203222113272-1167910484.png) + +测试结果分析: + +partition的不同会影响TPS,随着partition的个数的增长TPS会有所增长,但并不是一直成正比关系,到达一定临界值时,partition数量的增加反而会使TPS略微降低。 + +#### 场景5: + +通过将集群中部分broker设置成不可服务状态,测试对客户端以及消息落盘的影响。 + +具体配置:一个producer;消息体大小1KB;发送方式为sync;topic副本数为4;min.insync.replicas设置为2;acks=-1;retries=0/100000000;partition数为12。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203222220687-1891442968.png) + +测试结果分析: + + + +1. kill两台broker后,客户端可以继续发送。broker减少后,partition的leader分布在剩余的两台broker上,造成了TPS的减小; +2. kill三台broker后,客户端无法继续发送。Kafka的自动重试功能开始起作用,当大于等于min.insync.replicas数量的broker恢复后,可以继续发送; +3. 当retries不为0时,消息有重复落盘;客户端成功返回的消息都成功落盘,异常时部分消息可以落盘。 + +#### 场景6: + +测试单个producer的发送延迟,以及端到端的延迟。 + +具体配置:一个producer;消息体大小1KB;发送方式为sync;topic副本数为4;min.insync.replicas设置为2;acks=-1;partition数为12。 + +测试数据及结果(单位为ms): + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203222407063-2086989349.png) + +**各场景测试总结:** + + + +1. 当acks=-1时,Kafka发送端的TPS受限于topic的副本数量(ISR中),副本越多TPS越低; +2. acks=0时,TPS最高,其次为1,最差为-1,即TPS:acks_0 > acks_1 > ack_-1 +3. min.insync.replicas参数不影响TPS; +4. partition的不同会影响TPS,随着partition的个数的增长TPS会有所增长,但并不是一直成正比关系,到达一定临界值时,partition数量的增加反而会使TPS略微降低; +5. Kafka在acks=-1,min.insync.replicas>=1时,具有高可靠性,所有成功返回的消息都可以落盘。 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\270\270\350\247\201\345\221\275\344\273\244\345\217\212\351\205\215\347\275\256\346\200\273\347\273\223.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\270\270\350\247\201\345\221\275\344\273\244\345\217\212\351\205\215\347\275\256\346\200\273\347\273\223.md" new file mode 100644 index 0000000..a3eb8fd --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\270\270\350\247\201\345\221\275\344\273\244\345\217\212\351\205\215\347\275\256\346\200\273\347\273\223.md" @@ -0,0 +1,517 @@ +## **启动zookeeper** + +bin/zookeeper-server-start.sh config/zookeeper.properties & + +## **启动kafka:** + +bin/kafka-server-start.sh config/server.properties + +这样启动又一个坏处,就是kafka启动完毕之后,不能关闭终端,为此,我们可以运行这条命令: + +nohup bin/kafka-server-start.sh config/server.properties > ./dev/null 2>&1 & + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220812161146385-332776455.png) + +多个kafka的话,在各个虚拟机上运行kafka启动命令多次即可。 + +当然这个是单机的命令,集群的命令后面再讲。 + +## **查看是否启动** + +jps -lm + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220812161210221-836644701.png) + +说明没有启动kfka + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220812161224734-562363764.png) + +说明启动kafka了 + +## 查看kafka版本 + +find ./libs/ -name \*kafka_\* | head -1 | grep -o '\kafka[^\n]*' + +kafka_2.12-2.4.1.jar + +结果: + +就可以看到kafka的具体版本了。 + +其中,2.12为scala版本,2.4.1为kafka版本。 + +## **停止kafka** + +bin/kafka-server-stop.sh + +## **停止zookeeper** + +bin/zookeeper-server-stop.sh + +## **创建topic** + +bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test + +多集群创建,执行这个需要搭建多机器的kafka集群环境,zkq1/zkq2/zkq3分别代表了3台zookeeper集群的三台机器 + +/bin/kafka-topics.sh —create —zookeeper zkq1:2181,zkq2:2181,zkq3:2181 -replication-factor 6 —partition 6 —topic test + +解释: + +--topic后面的test0是topic的名称 + +--zookeeper应该和server.properties文件中的zookeeper.connect一样 + +--config指定当前topic上有效的参数值 + +--partitions指定topic的partition数量,如果不指定该数量,默认是server.properties文件中的num.partitions配置值 + +--replication-factor指定每个partition的副本个数,默认是1个 + +也可以向没有的topic发送消息的时候创建topic + +需要 + +开启自动创建配置:auto.create.topics.enable=true + +使用程序直接往kafka中相应的topic发送数据,如果topic不存在就会按默认配置进行创建。 + +## **展示topic** + +bin/kafka-topics.sh --list --zookeeper localhost:2181 + +## **描述topic** + +bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220812161250801-1389051022.png) + +解释: + +要查看多个topic用逗号分割 + +**leader**: + +是该partitons所在的所有broker中担任leader的broker id,每个broker都有可能成为leader,负责处理消息的读和写,leader是从所有节点中随机选择的. + +-1表示此broker移除了 + +**Replicas**: + +显示该partiton所有副本所在的broker列表,包括leader,不管该broker是否是存活,不管是否和leader保持了同步。列出了所有的副本节点,不管节点是否在服务中. + +**Isr**: + +in-sync replicas的简写,表示存活且副本都已同步的的broker集合,是replicas的子集,是正在服务中的节点. + +举例: + +比如上面结果的第一行:Topic: test0 .Partition:0 ...Leader: 0 ......Replicas: 0,2,1 Isr: 1,0,2 + +Partition: 0[该partition编号是0] + +Replicas: 0,2,1[代表partition0 在broker0,broker1,broker2上保存了副本] + +Isr: 1,0,2 [代表broker0,broker1,broker2都存活而且目前都和leader保持同步] + +Leader: 0 + +代表保存在broker0,broker1,broker2上的这三个副本中,leader是broker0 + +leader负责读写,broker1、broker2负责从broker0同步信息,平时没他俩什么事 + +## **查看topic的partition及增加partition** + +/kafka-topics.sh –zookeeper 10.2.1.1:2181 –topic mcc-logs –describe. + +## **删除Topic** + +/bin/kafka-topics.sh --zookeeper localhost:2181 --delete --topic test + +如果你的server.properties内没有配置相关的配置的话,会出现如下错误: + +Topic test is marked for deletion. + +Note: This will have no impact if delete.topic.enable is not set to true. + +这边是说,你的Topic已经被标记为待删除的Topic,但是呢,你配置文件的开关没有打开,所以只是给它添加了一个标记,实际上,这个Topic并没有被删除。只有,你打开开关之后,会自动删除被标记删除的Topic。 + +解决办法: + +设置server.properties文件内的“delete.topic.enable=true”,并且重启Kafka就可以了。 + +如果不想修改配置也可以完全删除 + +1、删除kafka存储目录(server.propertiewenjian log.dirs配置,默认为“/tmp/kafka-logs”)下对应的topic。(不同broker下存储的topic不一定相同,所有broker都要看一下) + +2、进入zookeeper客户端删掉对应topic + +zkCli.sh .-server 127.0.0.1:42182 + +找到topic目录: + +ls ../brokers/topics + +删掉对应topic + +rmr ./brokers/topic/topic-name + +找到目录: + +ls .../config/topics + +删掉对应topic + +rmr ./config/topics/topic-name . + +这样就完全删除了 + +## **删除topic中存储的内容** + +在config/server.properties中找到如下的位置 + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220812161312458-550425542.png) + +删除log.dirs指定的文件目录, + +登录zookeeper client。 + +命令: + +/home/ZooKeeper/bin/zkCli.sh + +删除zookeeper中该topic相关的目录 + +命令: + +rm -r /kafka/config/topics/test0 + +rm -r /kafka/brokers/topics/test0 + +rm -r /kafka/admin/delete_topics/test0 (topic被标记为marked for deletion时需要这个命令) + +重启zookeeper和broker + +## **生产者发送消息:** + +bin/kafka-console-producer.sh --broker-list 130.51.23.95:9092 --topic my-replicated-topic + +这里的ip和端口是broker的ip及端口,根据自己kafka机器的ip和端口写就可以 + +## **消费者消费消息:** + +bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --new-consumer --from-beginning --consumer.config config/consumer.properties + +## **查看topic某分区偏移量最大(小)值** + +bin/kafka-run-class.sh kafka.tools.GetOffsetShell --topic hive-mdatabase-hostsltable .--time -1 --broker-list node86:9092 --partitions 0 + +注: time为-1时表示最大值,time为-2时表示最小值 + +不指定--partitions 就是指这个topic整体的情况 + +## 查看指定group的消费情况 + +kafka-consumer-groups.sh --bootstrap-server 172.20.72.93:9092 --describe --group mygroup + +运行结果: + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220816164455794-344440282.png) + +* GROUP:消费者组 +* TOPIC:topic名字 +* PARTITION :partition id +* CURRENT-OFFSET: .当前消费到的offset . . . . . . . . +* LOG-END-OFFSETSIZE :最新的offset +* LAG:未消费的条数 +* CONSUMER-ID:消费者组中消费者的id,为—代表没有active的消费者 +* HOST:消费者的机器ip,为—代表没有active的消费者 +* CLIENT-ID:消费者clientID,为—代表没有active的消费者 + +## .查看所有group的消费情况 + +kafka-consumer-groups.sh --bootstrap-server 172.20.72.93:9092 --all-groups --all-topics --describe + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220816172442100-1560497638.png) + +## 修改group消费的offset + +kafka-consumer-groups.sh --bootstrap-server 172.20.72.93:9092 --group mygroup --reset-offsets --topic mytopic --to-offset 61 --execute + +上面就是把mygroup在mytopic的消费offset修改到了61 + +重设位移有几种选项: + +--to-earliest:. .设置到最早位移处,也就是0 + +--to-latest:. . .设置到最新处,也就是主题分区HW的位置 + +--to-offset NUM: 指定具体的位移位置 + +--shift-by NUM:. 基于当前位移向前回退多少 + +--by-duration:. .回退到多长时间 + +## 查看指定group中活跃的消费者 + +kafka-consumer-groups.sh --bootstrap-server 172.20.72.93:9092 --describe --group mygroup --members + +## **增加topic分区数** + +(只能增加不能减少) + +为topic t_cdr 增加10个分区 + +bin/kafka-topics.sh --zookeeper node01:2181 .--alter --topic t_cdr --partitions 10 + +## **常用配置及说明** + +kafka 常见重要配置说明,分为四部分 + +* Broker Config:kafka 服务端的配置 +* Producer Config:生产端的配置 +* Consumer Config:消费端的配置 +* Kafka Connect Config:kafka 连接相关的配置 + +### **Broker Config** + +1. **zookeeper.connect** + +连接 zookeeper 集群的地址,用于将 kafka 集群相关的元数据信息存储到指定的 zookeeper 集群中 + +**2\. advertised.port** + +注册到 zookeeper 中的地址端口信息,在 IaaS 环境中,默认注册到 zookeeper 中的是内网地址,通过该配置指定外网访问的地址及端口,advertised.host.name 和 advertised.port 作用和 advertised.port 差不多,在 0.10.x 之后,直接配置 advertised.port 即可,前两个参数被废弃掉了。 + +**3\. auto.create.topics.enable** + +自动创建 topic,默认为 true。其作用是当向一个还没有创建的 topic 发送消息时,此时会自动创建一个 topic,并同时设置 -num.partition 1 (partition 个数) 和 default.replication.factor (副本个数,默认为 1) 参数。 + +一般该参数需要手动关闭,因为自动创建会影响 topic 的管理,我们可以通过 kafka-topic.sh 脚本手动创建 topic,通常也是建议使用这种方式创建 topic。在 0.10.x 之后提供了 kafka-admin 包,可以使用其来创建 topic。 + +**4\. auto.leader.rebalance.enable** + +自动 rebalance,默认为 true。其作用是通过后台线程定期扫描检查,在一定条件下触发重新 leader 选举;在生产环境中,不建议开启,因为替换 leader 在性能上没有什么提升。 + +**5\. background.threads** + +后台线程数,默认为 10。用于后台操作的线程,可以不用改动。 + +**6\. broker.id** + +Broker 的唯一标识,用于区分不同的 Broker。kafka 的检查就是基于此 id 是否在 zookeeper 中/brokers/ids 目录下是否有相应的 id 目录来判定 Broker 是否健康。 + +**7\. compression.type** + +压缩类型。此配置可以接受的压缩类型有 gzip、snappy、lz4。另外可以不设置,即保持和生产端相同的压缩格式。 + +**8\. delete.topic.enable** + +启用删除 topic。如果关闭,则无法使用 admin 工具进行 topic 的删除操作。 + +**9\. leader.imbalance.check.interval.seconds** + +partition 检查重新 rebalance 的周期时间 + +**10\. leader.imbalance.per.broker.percentage** + +标识每个 Broker 失去平衡的比率,如果超过改比率,则执行重新选举 Broker 的 leader + +**11\. log.dir / log.dirs** + +保存 kafka 日志数据的位置。如果 log.dirs 没有设置,则使用 log.dir 指定的目录进行日志数据存储。 + +**12\. log.flush.interval.messages** + +partition 分区的数据量达到指定大小时,对数据进行一次刷盘操作。比如设置了 1024k 大小,当 partition 积累的数据量达到这个数值时则将数据写入到磁盘上。 + +**13\. log.flush.interval.ms** + +数据写入磁盘时间间隔,即内存中的数据保留多久就持久化一次,如果没有设置,则使用 log.flush.scheduler.interval.ms 参数指定的值。 + +**14\. log.retention.bytes** + +表示 topic 的容量达到指定大小时,则对其数据进行清除操作,默认为-1,永远不删除。 + +**15\. log.retention.hours** + +标示 topic 的数据最长保留多久,单位是小时 + +**16\. log.retention.minutes** + +表示 topic 的数据最长保留多久,单位是分钟,如果没有设置该参数,则使用 log.retention.hours 参数 + +**17\. log.retention.ms** + +表示 topic 的数据最长保留多久,单位是毫秒,如果没有设置该参数,则使用 log.retention.minutes 参数 + +**18\. log.roll.hours** + +新的 segment 创建周期,单位小时。kafka 数据是以 segment 存储的,当周期时间到达时,就创建一个新的 segment 来存储数据。 + +**19\. log.segment.bytes** + +segment 的大小。当 segment 大小达到指定值时,就新创建一个 segment。 + +**20\. message.max.bytes** + +topic 能够接收的最大文件大小。需要注意的是 producer 和 consumer 端设置的大小需要一致。 + +**21\. min.insync.replicas** + +最小副本同步个数。当 producer 设置了 request.required.acks 为-1 时,则 topic 的副本数要同步至该参数指定的个数,如果达不到,则 producer 端会产生异常。 + +**22\. num.io.threads** + +指定 io 操作的线程数 + +**23\. num.network.threads** + +执行网络操作的线程数 + +**24\. num.recovery.threads.per.data.dir** + +每个数据目录用于恢复数据的线程数 + +**25\. num.replica.fetchers** + +从 leader 备份数据的线程数 + +**26\. offset.metadata.max.bytes** + +允许消费者端保存 offset 的最大个数 + +**27\. offsets.commit.timeout.ms** + +offset 提交的延迟时间 + +**28\. offsets.topic.replication.factor** + +topic 的 offset 的备份数量。该参数建议设置更高保证系统更高的可用性 + +**29\. port** + +端口号,Broker 对外提供访问的端口号。 + +**30\. request.timeout.ms** + +Broker 接收到请求后的最长等待时间,如果超过设定时间,则会给客户端发送错误信息 + +**31\. zookeeper.connection.timeout.ms** + +客户端和 zookeeper 建立连接的超时时间,如果没有设置该参数,则使用 zookeeper.session.timeout.ms 值 + +**32\. connections.max.idle.ms** + +空连接的超时时间。即空闲连接超过该时间时则自动销毁连接。 + +### **Producer Config** + +1. **bootstrap.servers** + +服务端列表。即接收生产消息的服务端列表 + +**2\. key.serializer** + +消息键的序列化方式。指定 key 的序列化类型 + +3..**value.serializer** + +消息内容的序列化方式。指定 value 的序列化类型 + +4..**acks** + +消息写入 Partition 的个数。通常可以设置为 0,1,all;当设置为 0 时,只需要将消息发送完成后就完成消息发送功能;当设置为 1 时,即 Leader Partition 接收到数据并完成落盘;当设置为 all 时,即主从 Partition 都接收到数据并落盘。 + +5..**buffer.memory** + +客户端缓存大小。即 Producer 生产的数据量缓存达到指定值时,将缓存数据一次发送的 Broker 上。 + +6..**compression.type** + +压缩类型。指定消息发送前的压缩类型,通常有 none, gzip, snappy, or, lz4 四种。不指定时消息默认不压缩。 + +7..**retries** + +消息发送失败时重试次数。当该值设置大于 0 时,消息因为网络异常等因素导致消息发送失败进行重新发送的次数。 + +### **Consumer Config** + +1. **bootstrap.servers** + +服务端列表。即消费端从指定的服务端列表中拉取消息进行消费。 + +2..**key.deserializer** + +消息键的反序列化方式。指定 key 的反序列化类型,与序列化时指定的类型相对应。 + +3..**value.deserializer** + +消息内容的反序列化方式。指定 value 的反序列化类型,与序列化时指定的类型相对应。 + +4..**fetch.min.bytes** + +抓取消息的最小内容。指定每次向服务端拉取的最小消息量。 + +5..**group.id** + +消费组中每个消费者的唯一表示。 + +6..**heartbeat.interval.ms** + +心跳检查周期。即在周期性的向 group coordinator 发送心跳,当服务端发生 rebalance 时,会将消息发送给各个消费者。该参数值需要小于 session.timeout.ms,通常为后者的 1/3。 + +7..**max.partition.fetch.bytes** + +Partition 每次返回的最大数据量大小。 + +**8\. session.timeout.ms** + +consumer 失效的时间。即 consumer 在指定的时间后,还没有响应则认为该 consumer 已经发生故障了。 + +**9\. auto.offset.reset** + +当 kafka 中没有初始偏移量或服务器上不存在偏移量时,指定从哪个位置开始消息消息。earliest:指定从头开始;latest:从最新的数据开始消费。 + +### **Kafka Connect Config** + +1. **group.id** + +消费者在消费组中的唯一标识 + +**2\. internal.key.converter** + +内部 key 的转换类型。 + +**3\. internal.value.converter** + +内部 value 的转换类型。 + +**4\. key.converter** + +服务端接收到 key 时指定的转换类型。 + +5..**value.converter** + +服务端接收到 value 时指定的转换类型。 + +**6\. bootstrap.servers** + +服务端列表。 + +**7\. heartbeat.interval.ms** + +心跳检测,与 consumer 中指定的配置含义相同。 + +**8\. session.timeout.ms** + +session 有效时间,与 consumer 中指定的配置含义相同。 + +## **总结** + +本文总结了平时经常用到的一些 Kafka 配置及命令说明,方便随时查看;喜欢的朋友可以收藏以备不时之需。 + + +## 参考文章 +https://blog.csdn.net/cao131502 +https://zhuanlan.zhihu.com/p/137811719 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\346\236\266\346\236\204\344\273\213\347\273\215.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\346\236\266\346\236\204\344\273\213\347\273\215.md" new file mode 100644 index 0000000..2d35efe --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\346\236\266\346\236\204\344\273\213\347\273\215.md" @@ -0,0 +1,298 @@ +## 一. 工作流程 + +Kafka中消息是以topic进行分类的,Producer生产消息,Consumer消费消息,都是面向topic的。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-b9d626794f6625526598db6627b780e7_720w.webp) + +
+ +Topic是逻辑上的改变,Partition是物理上的概念,每个Partition对应着一个log文件,该log文件中存储的就是producer生产的数据,topic=N*partition;partition=log + +Producer生产的数据会被不断的追加到该log文件的末端,且每条数据都有自己的offset,consumer组中的每个consumer,都会实时记录自己消费到了哪个offset,以便出错恢复的时候,可以从上次的位置继续消费。流程:Producer => Topic(Log with offset)=> Consumer. + +## 二. 文件存储 + +Kafka文件存储也是通过本地落盘的方式存储的,主要是通过相应的log与index等文件保存具体的消息文件。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-116ebd7dffd85595d69f080e5b5f6948_720w.webp) + +
+ +生产者不断的向log文件追加消息文件,为了防止log文件过大导致定位效率低下,Kafka的log文件以1G为一个分界点,当.log文件大小超过1G的时候,此时会创建一个新的.log文件,同时为了快速定位大文件中消息位置,Kafka采取了分片和索引的机制来加速定位。 + +在kafka的存储log的地方,即文件的地方,会存在消费的偏移量以及具体的分区信息,分区信息的话主要包括.index和.log文件组成 + +
+ + +![](https://pic3.zhimg.com/80/v2-c6de61f43ecbe58d4f3e7aa29541220e_720w.webp) + +
+ +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-8345e4966d8c5274a1e74e29151bd9c6_720w.webp) + +
+ +副本目的是为了备份,所以同一个分区存储在不同的broker上,即当third-2存在当前机器kafka01上,实际上再kafka03中也有这个分区的文件(副本),分区中包含副本,即一个分区可以设置多个副本,副本中有一个是leader,其余为follower。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-6e8de9e7dcbdac0b7bd424eaaf4f8568_720w.webp) + +
+ +如果.log文件超出大小,则会产生新的.log文件。如下所示: + + + +``` +00000000000000000000.index +00000000000000000000.log +00000000000000170410.index +00000000000000170410.log +00000000000000239430.index +00000000000000239430.log + +``` + + + +**此时如何快速定位数据,步骤:** + + + +``` +.index文件存储的消息的offset+真实的起始偏移量。.log中存放的是真实的数据。 + +``` + + + +首先通过二分查找.index文件到查找到当前消息具体的偏移,如上图所示,查找为2,发现第二个文件为6,则定位到一个文件中。 然后通过第一个.index文件通过seek定位元素的位置3,定位到之后获取起始偏移量+当前文件大小=总的偏移量。 获取到总的偏移量之后,直接定位到.log文件即可快速获得当前消息大小。 + +## 三. 生产者分区策略 + +**分区的原因** 1\. 方便在集群中扩展:每个partition通过调整以适应它所在的机器,而一个Topic又可以有多个partition组成,因此整个集群可以适应适合的数据。 2\. 可以提高并发:以Partition为单位进行读写。类似于多路。 + +**分区的原则** 1\. 指明partition(这里的指明是指第几个分区)的情况下,直接将指明的值作为partition的值 2\. 没有指明partition的情况下,但是存在值key,此时将key的hash值与topic的partition总数进行取余得到partition值 3\. 值与partition均无的情况下,第一次调用时随机生成一个整数,后面每次调用在这个整数上自增,将这个值与topic可用的partition总数取余得到partition值,即round-robin算法。 + +## 四. 生产者ISR + +为保证producer发送的数据能够可靠的发送到指定的topic中,topic的每个partition收到producer发送的数据后,都需要向producer发送ackacknowledgement,如果producer收到ack就会进行下一轮的发送,否则重新发送数据。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-409ea1af4f66bd2f44398850cc2ba9e2_720w.webp) + +
+ +**发送ack的时机** 确保有follower与leader同步完成,leader再发送ack,这样可以保证在leader挂掉之后,follower中可以选出新的leader(主要是确保follower中数据不丢失) + +**follower同步完成多少才发送ack** 1\. 半数以上的follower同步完成,即可发送ack 2\. 全部的follower同步完成,才可以发送ack + +## 4.1 副本数据同步策略 + +### 4.1.1 半数follower同步完成即发送ack + +优点是延迟低 + +缺点是选举新的leader的时候,容忍n台节点的故障,需要2n+1个副本(因为需要半数同意,所以故障的时候,能够选举的前提是剩下的副本超过半数),容错率为1/2 + +### 4.1.2 全部follower同步完成完成发送ack + +优点是容错率搞,选举新的leader的时候,容忍n台节点的故障只需要n+1个副本即可,因为只需要剩下的一个人同意即可发送ack了 + +缺点是延迟高,因为需要全部副本同步完成才可 + +### 4.1.3 kafka的选择 + +kafka选择的是第二种,因为在容器率上面更加有优势,同时对于分区的数据而言,每个分区都有大量的数据,第一种方案会造成大量数据的冗余。虽然第二种网络延迟较高,但是网络延迟对于Kafka的影响较小。 + +## 4.2 ISR(同步副本集) + +**猜想** 采用了第二种方案进行同步ack之后,如果leader收到数据,所有的follower开始同步数据,但有一个follower因为某种故障,迟迟不能够与leader进行同步,那么leader就要一直等待下去,直到它同步完成,才可以发送ack,此时需要如何解决这个问题呢? + +**解决** leader中维护了一个动态的ISR(in-sync replica set),即与leader保持同步的follower集合,当ISR中的follower完成数据的同步之后,给leader发送ack,如果follower长时间没有向leader同步数据,则该follower将从ISR中被踢出,该之间阈值由replica.lag.time.max.ms参数设定。当leader发生故障之后,会从ISR中选举出新的leader。 + +## 五. 生产者ack机制 + +对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没有必要等到ISR中所有的follower全部接受成功。 + +Kafka为用户提供了三种可靠性级别,用户根据可靠性和延迟的要求进行权衡选择不同的配置。 + +**ack参数配置** 0:producer不等待broker的ack,这一操作提供了最低的延迟,broker接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据 + +1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将丢失数据。(只是leader落盘) + +
+ + +![](https://pic1.zhimg.com/80/v2-a219d261edd97432347f4edf5794e170_720w.webp) + +
+ +-1(all):producer等待broker的ack,partition的leader和ISR的follower全部落盘成功才返回ack,但是如果在follower同步完成后,broker发送ack之前,如果leader发生故障,会造成数据重复。(这里的数据重复是因为没有收到,所以继续重发导致的数据重复) + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-c9741a10f418f7ea4eed929f0f266bbb_720w.webp) + +
+ +producer返ack,0无落盘直接返,1只leader落盘然后返,-1全部落盘然后返 + +## 六. 数据一致性问题 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-031d84a2012f64b122dd64ab67a4e52a_720w.webp) + +
+ +LEO(Log End Offset):每个副本最后的一个offset HW(High Watermark):高水位,指代消费者能见到的最大的offset,ISR队列中最小的LEO。 + +**follower故障和leader故障** 1\. follower故障:follower发生故障后会被临时踢出ISR,等待该follower恢复后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向leader进行同步,等待该follower的LEO大于等于该partition的HW,即follower追上leader之后,就可以重新加入ISR了。 2\. leader故障:leader发生故障之后,会从ISR中选出一个新的leader,为了保证多个副本之间的数据的一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader中同步数据。 + +**这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复** + +## 七. ExactlyOnce + +将服务器的ACK级别设置为-1(all),可以保证producer到Server之间不会丢失数据,即At Least Once至少一次语义。将服务器ACK级别设置为0,可以保证生产者每条消息只会被发送一次,即At Most Once至多一次。 + +At Least Once可以保证数据不丢失,但是不能保证数据不重复,而At Most Once可以保证数据不重复,但是不能保证数据不丢失,对于重要的数据,则要求数据不重复也不丢失,即Exactly Once即精确的一次。 + +在0.11版本的Kafka之前,只能保证数据不丢失,在下游对数据的重复进行去重操作,多余多个下游应用的情况,则分别进行全局去重,对性能有很大影响。 + +0.11版本的kafka,引入了一项重大特性:幂等性,幂等性指代Producer不论向Server发送了多少次重复数据,Server端都只会持久化一条数据。幂等性结合At Least Once语义就构成了Kafka的Exactly Once语义。 + +启用幂等性,即在Producer的参数中设置enable.idempotence=true即可,Kafka的幂等性实现实际是将之前的去重操作放在了数据上游来做,开启幂等性的Producer在初始化的时候会被分配一个PID,发往同一个Partition的消息会附带Sequence Number,而Broker端会对做缓存,当具有相同主键的消息的时候,Broker只会持久化一条。 + +但PID在重启之后会发生变化,同时不同的Partition也具有不同的主键,所以幂等性无法保证跨分区跨会话的Exactly Once。 + +要解决跨分区跨会话的Exactly Once,就引入了生产者事务的概念。 + +## 八. Kafka消费者分区分配策略 + +**消费方式:** consumer采用pull拉的方式来从broker中读取数据。 + +push推的模式很难适应消费速率不同的消费者,因为消息发送率是由broker决定的,它的目标是尽可能以最快的速度传递消息,但是这样容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而pull方式则可以让consumer根据自己的消费处理能力以适当的速度消费消息。 + +pull模式不足在于如果Kafka中没有数据,消费者可能会陷入循环之中 (因为消费者类似监听状态获取数据消费的),一直返回空数据,针对这一点,Kafka的消费者在消费数据时会传入一个时长参数timeout,如果当前没有数据可供消费,consumer会等待一段时间之后再返回,时长为timeout。 + +## 8.1\. 分区分配策略 + +一个consumer group中有多个consumer,一个topic有多个partition,所以必然会涉及到partition的分配问题,即确定那个partition由那个consumer消费的问题。 + +**Kafka的两种分配策略:** 1\. round-robin循环 2\. range + +**Round-Robin** 主要采用轮询的方式分配所有的分区,该策略主要实现的步骤: 假设存在三个topic:t0/t1/t2,分别拥有1/2/3个分区,共有6个分区,分别为t0-0/t1-0/t1-1/t2-0/t2-1/t2-2,这里假设我们有三个Consumer,C0、C1、C2,订阅情况为C0:t0,C1:t0、t1,C2:t0/t1/t2。 + +此时round-robin采取的分配方式,则是按照分区的字典对分区和消费者进行排序,然后对分区进行循环遍历,遇到自己订阅的则消费,否则向下轮询下一个消费者。即按照分区轮询消费者,继而消息被消费。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-21eed325191d7d72c9d4c39455c4cae5_720w.webp) + +
+ +分区在循环遍历消费者,自己被当前消费者订阅,则消息与消费者共同向下(消息被消费),否则消费者向下消息继续遍历(消息没有被消费)。轮询的方式会导致每个Consumer所承载的分区数量不一致,从而导致各个Consumer压力不均。上面的C2因为订阅的比较多,导致承受的压力也相对较大。 + +**Range** Range的重分配策略,首先计算各个Consumer将会承载的分区数量,然后将指定数量的分区分配给该Consumer。假设存在两个Consumer,C0和C1,两个Topic,t0和t1,这两个Topic分别都有三个分区,那么总共的分区有6个,t0-0,t0-1,t0-2,t1-0,t1-1,t1-2。分配方式如下: + +range按照topic一次进行分配,即消费者遍历topic,t0,含有三个分区,同时有两个订阅了该topic的消费者,将这些分区和消费者按照字典序排列。 按照平均分配的方式计算每个Consumer会得到多少个分区,如果没有除尽,多出来的分区则按照字典序挨个分配给消费者。按照此方式以此分配每一个topic给订阅的消费者,最后完成topic分区的分配。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-d642ed5512a4abdca9a8f35f2d27c277_720w.webp) + +
+ +## 8.2\. 消费者offset的存储 + +由于Consumer在消费过程中可能会出现断电宕机等故障,Consumer恢复以后,需要从故障前的位置继续消费,所以Consumer需要实时记录自己消费到了那个offset,以便故障恢复后继续消费。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-f2a50fd7f054821e36a80b1f6d99ecb0_720w.webp) + +
+ +Kafka0.9版本之前,consumer默认将offset保存在zookeeper中,从0.9版本之后,consumer默认将offset保存在kafka一个内置的topic中,该topic为__consumer_offsets + +## 九. 高效读写&Zookeeper作用 + +## 9.1 高效读写 + +**顺序写磁盘** Kafka的producer生产数据,需要写入到log文件中,写的过程是追加到文件末端,顺序写的方式,官网有数据表明,同样的磁盘,顺序写能够到600M/s,而随机写只有200K/s,这与磁盘的机械结构有关,顺序写之所以快,是因为其省去了大量磁头寻址的时间。 + +**零复制技术** + +NIC:Network Interface Controller网络接口控制器 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-2807d010381d304949bf8cea16ba1744_720w.webp) + +
+ +这是常规的读取操作: 1\. 操作系统将数据从磁盘文件中读取到内核空间的页面缓存 2\. 应用程序将数据从内核空间读入到用户空间缓冲区 3\. 应用程序将读到的数据写回内核空间并放入到socket缓冲区 4\. 操作系统将数据从socket缓冲区复制到网卡接口,此时数据通过网络发送给消费者 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-390cbabdeaba6f9e79a8ba6f4d08d75f_720w.webp) + +
+ +零拷贝技术只用将磁盘文件的数据复制到页面缓存中一次,然后将数据从页面缓存直接发送到网络中(发送给不同的订阅者时,都可以使用同一个页面缓存),从而避免了重复复制的操作。 + +如果有10个消费者,传统方式下,数据复制次数为4*10=40次,而使用“零拷贝技术”只需要1+10=11次,一次为从磁盘复制到页面缓存,10次表示10个消费者各自读取一次页面缓存。 + +## 9.2 zookeeper作用 + +Kafka集群中有一个broker会被选举为Controller,负责管理集群broker的上下线、所有topic的分区副本分配和leader的选举等工作。Controller的工作管理是依赖于zookeeper的。 + +**Partition的Leader的选举过程** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-fc64ea72cba32e702b15344767bdace9_720w.webp) + +
+ +## 十. 事务 + +kafka从0.11版本开始引入了事务支持,事务可以保证Kafka在Exactly Once语义的基础上,生产和消费可以跨分区的会话,要么全部成功,要么全部失败。 + +## 10.1 Producer事务 + +为了按跨分区跨会话的事务,需要引入一个全局唯一的Transaction ID,并将Producer获得的PID(可以理解为Producer ID)和Transaction ID进行绑定,这样当Producer重启之后就可以通过正在进行的Transaction ID获得原来的PID。 + +为了管理Transaction,Kafka引入了一个新的组件Transaction Coordinator,Producer就是通过有和Transaction Coordinator交互获得Transaction ID对应的任务状态,Transaction Coordinator还负责将事务信息写入内部的一个Topic中,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以恢复,从而继续进行。 + +## 10.2 Consumer事务 + +对于Consumer而言,事务的保证相比Producer相对较弱,尤其是无法保证Commit的信息被精确消费,这是由于Consumer可以通过offset访问任意信息,而且不同的Segment File声明周期不同,同一事务的消息可能会出现重启后被删除的情况。 + +## 参考文章 + +https://blog.csdn.net/cao131502 +https://zhuanlan.zhihu.com/p/137811719 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\347\232\204\351\233\206\347\276\244\345\267\245\344\275\234\345\216\237\347\220\206.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\347\232\204\351\233\206\347\276\244\345\267\245\344\275\234\345\216\237\347\220\206.md" new file mode 100644 index 0000000..cd6122e --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\347\232\204\351\233\206\347\276\244\345\267\245\344\275\234\345\216\237\347\220\206.md" @@ -0,0 +1,127 @@ +**前言** + +上篇文章讲到了消息在 Partition 上的存储形式,本来准备接着来聊聊生产中的一些使用方式,想了想还有些很重要的工作组件原理没有讲清楚,比如一个 Topic 由 N 个 Partition 组成,那么这些 Partition 是如何均匀的分布在不同的 Broker 上?再比如当一个 Broker 宕机后,其上负责读写请求的主 Partition 无法正常访问,如何让从 Partition 转变成主 Partition 来继续提供正常的读写服务?想要解决这些问题,就必须先要了解一下 Kafka 集群内部的管理机制,其中一个非常重要的控制器就是 KafkaController。本文我们就来讲讲 KafkaController 是如何来解决上面提到的那些问题的。 + +### **文章概览** + +1. KafkaController 是什么及其选举策略。 +2. KafkaController 监控 ZK 的目录分布。 +3. Partition 分布算法。 +4. Partition 的状态转移。 +5. Kafka 集群的负载均衡处理流程解析。 + +## **KafkaController 是什么及其选举策略** + +Kafka 集群由多台 Broker 构成,每台 Broker 都有一个 KafkaController 用于管理当前的 Broker。试想一下,如果一个集群没有一个“领导者”,那么谁去和“外界”(比如 ZK)沟通呢?谁去协调 Partition 应该如何分布在集群中的不同 Broker 上呢?谁去处理 Broker 宕机后,在其 Broker 上的主 Partition 无法正常提供读写服务后,将对应的从 Partition 转变成主 Partition 继续正常对外提供服务呢?那么由哪个 Broker 的 KafkaController 来担当“领导者”这个角色呢? + +Kafka 的设计者很聪明,Zookeeper 既然是分布式应用协调服务,那么干脆就让它来帮 Kafka 集群选举一个“领导者”出来,这个“领导者”对应的 KafkaController 称为 Leader,其他的 KafkaController 被称为 Follower,在同一时刻,一个 Kafka 集群只能有一个 Leader 和 N 个 Follower。 + +### **Zookeeper 是怎么实现 KafkaController 的选主工作呢?** + +稍微熟悉 Zookeeper 的小伙伴应该都比较清楚,Zookeeper 是通过监控目录(zNode)的变化,从而做出一些相应的动作。 + +Zookeeper 的目录分为四种,第一种是永久的,被称作为 `Persistent`; + +第二种是顺序且永久的,被称作为 `Persistent_Sequential`; + +第三种是临时的,被称为 `Ephemeral`; + +第四种是顺序且临时的,被称作为 `Ephemeral_Sequential`。 + +KafkaController 正是利用了临时的这一特性来完成选主的,在 Broker 启动时,每个 Broker 的 KafkaController 都会向 ZK 的 `/controller` 目录写入 BrokerId,谁先写入谁就是 Leader,剩余的 KafkaController 是 Follower,当 Leader 在周期内没有向 ZK 发送报告的话,则认为 Leader 挂了,此时 ZK 删除临时的 `/controller` 目录,Kafka 集群中的其他 KafkaController 开始新的一轮争主操作,流程和上面一样。下面是选 Leader 的流程图。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-af1f22f109f85fe6b169c6e4a271016f_720w.webp) + +
Leader选举流程图
+ +
+ +从上图可以看出,第一次,Broker1 成功抢先在 Zookeeper 的 /controller 目录上写入信息,所以 Broker1 的 KafkaController 为 Leader,其他两个为 Follower。第二次,Broker1 宕机或者下线,此时 Broker2 和 Broker3 检测到,于是开始新一轮的争抢将信息写入 Zookeeper,从图中可以看出,Broker2 争到了 Leader,所以 Broker3 是 Follower 状态。 + +正常情况下,上面这个流程没有问题,但是如果在 Broker1 离线的情况下,Zookeeper 准备删除 /controller 的临时 node 时,系统 hang 住没办法删除,改怎么办呢?这里留个小疑问供大家思考。后面会用一篇文章专门来解答 Kafka 相关的问题(包括面试题哦,敬请期待)。 + +## **KafkaController 监控的 ZK 目录分布** + +KafkaController 在初始化的时候,会针对不同的 zNode 注册各种各样的监听器,以便处理不同的用户请求或者系统内部变化请求。监控 ZK 的目录大概可以分为两部分,分别是 `/admin` 目录和 `/brokers` 目录。各目录及其对应的功能如下表所示,需要的朋友自提。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-2a603adc2e06f3663e693259e8bf16d4_720w.webp) + +
+ +## **Partition 分布算法** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-36d40cb264f6432a81ad83c9365d7997_720w.webp) + +
Partition分布算法图
+ +
+ +图解:假设集群有 3 个 Broker,Partition 因子为 2。 + +1. 随机选取 Broker 集群中的一个 Broker 节点,然后以轮询的方式将主 Partition 均匀的分布到不同的 Broker 上。 +2. 主 Partition 分布完成后,将从 Partition 按照 AR 组内顺序以轮询的方式将 Partition 均匀的分布到不同的 Broker 上。 + +## **Partition 的状态转移** + +用户针对特定的 Topic 创建了相应的 Partition ,但是这些 Partition 不一定时刻都能够正常工作,所有 Partition 在同一时刻会对应 4 个状态中的某一个;其整个生命周期会经历如下状态的转移,分别是 NonExistentPartition、NewPartition、OnlinePartition、OfflinePartition,其对应的状态转移情况如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-58a8609aa2698130679d9fb80541d19b_720w.webp) + +
Partition状态转移图
+ +
+ +从上图可以看出,Partition 的状态会由前置状态才能够转移到目标状态的,而不是胡乱转移状态的。 + +`NonExistentPartition:`代表没有创建 Partition 时的状态,也有可能是离线的 Partition 被删除后的状态。 + +`NewPartition:`当 Partition 被创建时,此时只有 AR(Assigned Replica),还没有 ISR(In-Synic Replica),此时还不能接受数据的写入和读取。 + +`OnlinePartition:`由 NewPartition 状态转移为 OnlinePartition 状态,此时 Partition 的 Leader 已经被选举出来了,并且也有对应的 ISR 列表等。此时已经可以对外提供读写请求了。 + +`OfflinePartition:`当 Partition 对应的 Broker 宕机或者网络异常等问题,由 OnlinePartition 转移到 OfflinePartition,此时的 Partition 已经不能在对外提供读写服务。当 Partition 被彻底删除后状态就转移成 NonExistentPartition,当网络恢复或者 Broker 恢复后,其状态又可以转移到 OnlinePartition,从而继续对外提供读写服务。 + +## **Kafka 集群的负载均衡处理流程解析** + +前面的文章讲到过,Partition 有 Leader Replica 和 Preferred Replica 两种角色,Leader Replica 负责对外提供读写服务 Preferred Replica 负责同步 Leader Replica 上的数据。现在集群中假设有 3 个 Broker,3 个 Partition,每个 Partition 有 3 个 Replica,当集群运行一段时候后,集群中某些 Broker 宕机,Leader Replica 进行转移,其过程如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-dc0bcd6f072f7e6cef8289882259d59e_720w.webp) + +
Partition的Leader转移图
+ +
+ +从上图可以看出,集群运行一段时间后,Broker1 挂掉了,在其上运行的 Partition0 对应的 Leader Replica 转移到了 Broker2 上。假设一段时间后 Broker3 也挂了,则 Broker3 上的 Partition3 对应的 Leader Replica 也转移到了 Broker2 上,集群中只有 Broker2 上的 Partition 在对外提供读写服务,从而造成 Broker2 上的服务压力比较大,之后 Broker1 和 Broker3 恢复后,其上只有 Preferred Replica 做备份操作。 + +针对以上这种随着时间的推移,集群不在像刚开始时那样平衡,需要通过后台线程将 Leader Replica 重新分配到不同 Broker 上,从而使得读写服务尽量均匀的分布在不同的节点上。 + +重平衡操作是由 partition-rebalance-thread 后台线程操作的,由于其优先级很低,所以只会在集群空闲的时候才会执行。集群的不平衡的评判标准是由`leader.imbalance.per.broker.percentage`配置决定的,当集群的不平衡度达到 10%(默认)时,会触发后台线程启动重平衡操作,其具体执行步骤如下: + +1. 对 Partition 的 AR 列表根据 Preferred Replica 进行分组操作。 +2. 遍历 Broker,对其上的 Partition 进行处理。 +3. 统计 Broker 上的 Leader Replica 和 Preferred Replica 不相等的 Partition 个数。 +4. 统计 Broker 上的 Partition 个数。 +5. Partition 个数 / 不相等的 Partition 个数,如果大于 10%,则触发重平衡操作;反之,则不做任何处理。 + +## **总结** + +本文主要介绍了 Kafka 集群服务内部的一些工作机制,相信小伙伴们掌握了这部分内容后,对 Broker 服务端的工作流程有了进一步的理解,从而更好的把控整体集群服务。下篇文章我们来正式介绍一下**Kafka 常用的命令行操作**,敬请期待。 + +# 参考文章 +https://blog.csdn.net/cao131502 +https://zhuanlan.zhihu.com/p/137811719 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\351\207\215\350\246\201\347\237\245\350\257\206\347\202\271+\351\235\242\350\257\225\351\242\230\345\244\247\345\205\250.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\351\207\215\350\246\201\347\237\245\350\257\206\347\202\271+\351\235\242\350\257\225\351\242\230\345\244\247\345\205\250.md" new file mode 100644 index 0000000..77ac95f --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\351\207\215\350\246\201\347\237\245\350\257\206\347\202\271+\351\235\242\350\257\225\351\242\230\345\244\247\345\205\250.md" @@ -0,0 +1,518 @@ +## 重要面试知识点 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-75cd70ad3052ba44bf706a3ab39e59d5_720w.webp) + + +Kafka 消费端确保一个 Partition 在一个消费者组内只能被一个消费者消费。这句话改怎么理解呢? + +1. 在同一个消费者组内,一个 Partition 只能被一个消费者消费。 +2. 在同一个消费者组内,所有消费者组合起来必定可以消费一个 Topic 下的所有 Partition。 +3. 在同一个消费组内,一个消费者可以消费多个 Partition 的信息。 +4. 在不同消费者组内,同一个分区可以被多个消费者消费。 +5. 每个消费者组一定会完整消费一个 Topic 下的所有 Partition。 + +### **消费组存在的意义** + +了解了消费者与消费组的关系后,有朋友会比较疑惑消费者组有啥实际存在的意义呢?或者说消费组的作用是什么? + +作者对消费组的作用归结了如下两点。 + +1. 在实际生产中,对于同一个 Topic,可能有 A、B、C 等 N 个消费方想要消费。比如一份用户点击日志,A 消费方想用来做一个用户近 N 天点击过哪些商品;B 消费方想用来做一个用户近 N 天点击过前 TopN 个相似的商品;C 消费方想用来做一个根据用户点击过的商品推荐相关周边的商品需求。对于多应用场景,就可以使用消费组来隔离不同的业务使用场景,从而达到一个 Topic 可以被多个消费组重复消费的目的。 +2. 消费组与 Partition 的消费进度绑定。当有新的消费者加入或者有消费者从消费组退出时,会触发消费组的 Repartition 操作(后面会详细介绍 Repartition);在 Repartition 前,Partition1 被消费组的消费者 A 进行消费,Repartition 后,Partition1 消费组的消费者 B 进行消费,为了避免消息被重复消费,需要从消费组记录的 Partition 消费进度读取当前消费到的位置(即 OffSet 位置),然后在继续消费,从而达到消费者的平滑迁移,同时也提高了系统的可用性。 + +## **Repartition 触发时机** + +使用过 Kafka 消费者客户端的同学肯定知道,消费者组内偶尔会触发 Repartition 操作,所谓 Repartition 即 Partition 在某些情况下重新被分配给参与消费的消费者。基本可以分为如下几种情况。 + +1. 消费组内某消费者宕机,触发 Repartition 操作,如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-a9ef6a29cb9ba3456a05ad75cb91cb03_720w.webp) + +
消费者宕机情况
+ +
+ +2\. 消费组内新增消费者,触发 Repartition 操作,如下图所示。一般这种情况是为了提高消费端的消费能力,从而加快消费进度。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-8803223d712fdde035b8e7b9170dd3fb_720w.webp) + +
新增消费者情况
+ +
+ +3.Topic 下的 Partition 增多,触发 Repartition 操作,如下图所示。一般这种调整 Partition 个数的情况也是为了提高消费端消费速度的,因为当消费者个数大于等于 Partition 个数时,在增加消费者个数是没有用的(原因是:在一个消费组内,消费者:Partition = 1:N,当 N 小于 1 时,相当于消费者过剩了),所以一方面增加 Partition 个数同时增加消费者个数可以提高消费端的消费速度。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-8f1a427c6842d9babf139454ce23cfa3_720w.webp) + +
新增Partition个数情况
+ +
+ +## **消费者与 ZK 的关系** + +众所周知,ZK 不仅保存了消费者消费 partition 的进度,同时也保存了消费组的成员列表、partition 的所有者。消费者想要消费 Partition,需要从 ZK 中获取该消费者对应的分区信息及当前分区对应的消费进度,即 OffSert 信息。那么 Partition 应该由那个消费者进行消费,决定因素有哪些呢?从之前的图中不难得出,两个重要因素分别是:消费组中存活的消费者列表和 Topic 对应的 Partition 列表。通过这两个因素结合 Partition 分配算法,即可得出消费者与 Partition 的对应关系,然后将信息存储到 ZK 中。Kafka 有高级 API 和低级 API,如果不需要操作 OffSet 偏移量的提交,可通过高级 API 直接使用,从而降低使用者的难度。对于一些比较特殊的使用场景,比如想要消费特定 Partition 的信息,Kafka 也提供了低级 API 可进行手动操作。 + +## **消费端工作流程** + +在介绍消费端工作流程前,先来熟悉一下用到的一些组件。 + +* `KakfaConsumer`:消费端,用于启动消费者进程来消费消息。 +* `ConsumerConfig`:消费端配置管理,用于给消费端配置相关参数,比如指定 Kafka 集群,设置自动提交和自动提交时间间隔等等参数,都由其来管理。 +* `ConsumerConnector`:消费者连接器,通过消费者连接器可以获得 Kafka 消息流,然后通过消息流就能获得消息从而使得客户端开始消费消息。 + +以上三者之间的关系可以概括为:消费端使用消费者配置管理创建出了消费者连接器,通过消费者连接器创建队列(这个队列的作用也是为了缓存数据),其中队列中的消息由专门的拉取线程从服务端拉取然后写入,最后由消费者客户端轮询队列中的消息进行消费。具体操作流程如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-122b4a706de39655d257928005a83ff1_720w.webp) + +
消费端工作流程
+ +
+ +我们在从消费者与 ZK 的角度来看看其工作流程是什么样的? + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-4ed25ebb9236986b2084ce8a042f65b9_720w.webp) + +
消费端与ZK之间的工作流程
+ +
+ +从上图可以看出,首先拉取线程每拉取一次消息,同步更新一次拉取状态,其作用是为了下一次拉取消息时能够拉取到最新产生的消息;拉取线程将拉取到的消息写入到队列中等待消费消费线程去真正读取处理。消费线程以轮询的方式持续读取队列中的消息,只要发现队列中有消息就开始消费,消费完消息后更新消费进度,此处需要注意的是,消费线程不是每次都和 ZK 同步消费进度,而是将消费进度暂时写入本地。这样做的目的是为了减少消费者与 ZK 的频繁同步消息,从而降低 ZK 的压力。 + +## **消费者的三种消费情况** + +消费者从服务端的 Partition 上拉取到消息,消费消息有三种情况,分别如下: + +1. 至少一次。即一条消息至少被消费一次,消息不可能丢失,但是可能会被重复消费。 +2. 至多一次。即一条消息最多可以被消费一次,消息不可能被重复消费,但是消息有可能丢失。 +3. 正好一次。即一条消息正好被消费一次,消息不可能丢失也不可能被重复消费。 + +### **1.至少一次** + +消费者读取消息,先处理消息,在保存消费进度。消费者拉取到消息,先消费消息,然后在保存偏移量,当消费者消费消息后还没来得及保存偏移量,则会造成消息被重复消费。如下图所示: + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-1a047ed616ba44daebdb4b6ce786a61a_720w.webp) + +
先消费后保存消费进度
+ +
+ +### **2.至多一次** + +消费者读取消息,先保存消费进度,在处理消息。消费者拉取到消息,先保存了偏移量,当保存了偏移量后还没消费完消息,消费者挂了,则会造成未消费的消息丢失。如下图所示: + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-1f9f91ae54396c5e5d93ae89251eb1ed_720w.webp) + +
先保存消费进度后消费消息
+ +
+ +### **3.正好一次** + +正好消费一次的办法可以通过将消费者的消费进度和消息处理结果保存在一起。只要能保证两个操作是一个原子操作,就能达到正好消费一次的目的。通常可以将两个操作保存在一起,比如 HDFS 中。正好消费一次流程如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-a0bbb114e2ad551227f81c1f26d4bd5d_720w.webp) + +
正好消费一次
+ +
+ +## Partition、Replica、Log 和 LogSegment 的关系 + +假设有一个 Kafka 集群,Broker 个数为 3,Topic 个数为 1,Partition 个数为 3,Replica 个数为 2。Partition 的物理分布如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-f8f21631b138321f25c8821c677c5579_720w.webp) + +
Partition分布图
+ +
+ +从上图可以看出,该 Topic 由三个 Partition 构成,并且每个 Partition 由主从两个副本构成。每个 Partition 的主从副本分布在不同的 Broker 上,通过这点也可以看出,当某个 Broker 宕机时,可以将分布在其他 Broker 上的从副本设置为主副本,因为只有主副本对外提供读写请求,当然在最新的 2.x 版本中从副本也可以对外读请求了。将主从副本分布在不同的 Broker 上从而提高系统的可用性。 + +Partition 的实际物理存储是以 Log 文件的形式展示的,而每个 Log 文件又以多个 LogSegment 组成。Kafka 为什么要这么设计呢?其实原因比较简单,随着消息的不断写入,Log 文件肯定是越来越大,Kafka 为了方便管理,将一个大文件切割成一个一个的 LogSegment 来进行管理;每个 LogSegment 由数据文件和索引文件构成,数据文件是用来存储实际的消息内容,而索引文件是为了加快消息内容的读取。 + +可能又有朋友会问,Kafka 本身消费是以 Partition 维度顺序消费消息的,磁盘在顺序读的时候效率很高完全没有必要使用索引啊。其实 Kafka 为了满足一些特殊业务需求,比如要随机消费 Partition 中的消息,此时可以先通过索引文件快速定位到消息的实际存储位置,然后进行处理。 + +总结一下 Partition、Replica、Log 和 LogSegment 之间的关系。消息是以 Partition 维度进行管理的,为了提高系统的可用性,每个 Partition 都可以设置相应的 Replica 副本数,一般在创建 Topic 的时候同时指定 Replica 的个数;Partition 和 Replica 的实际物理存储形式是通过 Log 文件展现的,为了防止消息不断写入,导致 Log 文件大小持续增长,所以将 Log 切割成一个一个的 LogSegment 文件。 + +**注意:** 在同一时刻,每个主 Partition 中有且只有一个 LogSegment 被标识为可写入状态,当一个 LogSegment 文件大小超过一定大小后(比如当文件大小超过 1G,这个就类似于 HDFS 存储的数据文件,HDFS 中数据文件达到 128M 的时候就会被分出一个新的文件来存储数据),就会新创建一个 LogSegment 来继续接收新写入的消息。 + +## 写入消息流程分析 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-eb66e4ecf7cf07fcb6b12029bfdd9b71_720w.webp) + +
消息写入及落盘流程
+ +
+ +流程解析 + +在第 3 篇文章讲过,生产者客户端对于每个 Partition 一次会发送一批消息到服务端,服务端收到一批消息后写入相应的 Partition 上。上图流程主要分为如下几步: + +1. **客户端消息收集器收集属于同一个分区的消息,并对每条消息设置一个偏移量,且每一批消息总是从 0 开始单调递增。比如第一次发送 3 条消息,则对三条消息依次编号 [0,1,2],第二次发送 4 条消息,则消息依次编号为 [0,1,2,3]。注意此处设置的消息偏移量是相对偏移量。** +2. **客户端将消息发送给服务端,服务端拿到下一条消息的绝对偏移量,将传到服务端的这批消息的相对偏移量修改成绝对偏移量。** +3. **将修改后的消息以追加的方式追加到当前活跃的 LogSegment 后面,然后更新绝对偏移量。** +4. **将消息集写入到文件通道。** +5. **文件通道将消息集 flush 到磁盘,完成消息的写入操作。** + +了解以上过程后,我们在来看看消息的具体构成情况。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-6e993c95decd5d274b032cd423936504_720w.webp) + +
消息构成细节图
+ +
+ +一条消息由如下三部分构成: + +* **OffSet:偏移量,消息在客户端发送前将相对偏移量存储到该位置,当消息存储到 LogSegment 前,先将其修改为绝对偏移量在写入磁盘。** +* **Size:本条 Message 的内容大小** +* **Message:消息的具体内容,其具体又由 7 部分组成,crc 用于校验消息,Attribute 代表了属性,key-length 和 value-length 分别代表 key 和 value 的长度,key 和 value 分别代表了其对应的内容。** + +### 消息偏移量的计算过程 + +通过以上流程可以看出,每条消息在被实际存储到磁盘时都会被分配一个绝对偏移量后才能被写入磁盘。在同一个分区内,消息的绝对偏移量都是从 0 开始,且单调递增;在不同分区内,消息的绝对偏移量是没有任何关系的。接下来讨论下消息的绝对偏移量的计算规则。 + +确定消息偏移量有两种方式,一种是顺序读取每一条消息来确定,此种方式代价比较大,实际上我们并不想知道消息的内容,而只是想知道消息的偏移量;第二种是读取每条消息的 Size 属性,然后计算出下一条消息的起始偏移量。比如第一条消息内容为 “abc”,写入磁盘后的偏移量为:8(OffSet)+ 4(Message 大小)+ 3(Message 内容的长度)= 15。第二条写入的消息内容为“defg”,其起始偏移量为 15,下一条消息的起始偏移量应该是:15+8+4+4=31,以此类推。 + +## 消费消息及副本同步流程分析 + +和写入消息流程不同,读取消息流程分为两种情况,分别是消费端消费消息和从副本(备份副本)同步主副本的消息。在开始分析读取流程之前,需要先明白几个用到的变量,不然流程分析可能会看的比较糊涂。 + +* **BaseOffSet**:基准偏移量,每个 Partition 由 N 个 LogSegment 组成,每个 LogSegment 都有基准偏移量,大概由如下构成,数组中每个数代表一个 LogSegment 的基准偏移量:[0,200,400,600, ...]。 +* **StartOffSet**:起始偏移量,由消费端发起读取消息请求时,指定从哪个位置开始消费消息。 +* **MaxLength**:拉取大小,由消费端发起读取消息请求时,指定本次最大拉取消息内容的数据大小。该参数可以通过[max.partition.fetch.bytes](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/draft/3020%23)来指定,默认大小为 1M。 +* **MaxOffSet**:最大偏移量,消费端拉取消息时,最高可拉取消息的位置,即俗称的“高水位”。该参数由服务端指定,其作用是为了防止生产端还未写入的消息就被消费端进行消费。此参数对于从副本同步主副本不会用到。 +* **MaxPosition**:LogSegment 的最大位置,确定了起始偏移量在某个 LogSegment 上开始,读取 MaxLength 后,不能超过 MaxPosition。MaxPosition 是一个实际的物理位置,而非偏移量。 + +假设消费端从 000000621 位置开始消费消息,关于几个变量的关系如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-cd9c62a71cddccd7bc8a5d810d5af216_720w.webp) + +
位置关系图
+ +
+ +消费端和从副本拉取流程如下: + +1. **客户端确定拉取的位置,即 StartOffSet 的值,找到主副本对应的 LogSegment。** +2. **LogSegment 由索引文件和数据文件构成,由于索引文件是从小到大排列的,首先从索引文件确定一个小于等于 StartOffSet 最近的索引位置。** +3. **根据索引位置找到对应的数据文件位置,由于数据文件也是从小到大排列的,从找到的数据文件位置顺序向后遍历,直到找到和 StartOffSet 相等的位置,即为消费或拉取消息的位置。** +4. **从 StartOffSet 开始向后拉取 MaxLength 大小的数据,返回给消费端或者从副本进行消费或备份操作。** + +假设拉取消息起始位置为 00000313,消息拉取流程图如下: + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-9417ca60a0c5e9474ec49a77fff18b1b_720w.webp) + +
消息拉取流程图
+ +
+ +## kafka 如何保证系统的高可用、数据的可靠性和数据的一致性的? + +### kafka 的高可用性: + +1. **Kafka 本身是一个分布式系统,同时采用了 Zookeeper 存储元数据信息,提高了系统的高可用性。** +2. **Kafka 使用多副本机制,当状态为 Leader 的 Partition 对应的 Broker 宕机或者网络异常时,Kafka 会通过选举机制从对应的 Replica 列表中重新选举出一个 Replica 当做 Leader,从而继续对外提供读写服务(当然,需要注意的一点是,在新版本的 Kafka 中,Replica 也可以对外提供读请求了),利用多副本机制在一定程度上提高了系统的容错性,从而提升了系统的高可用。** + +### Kafka 的可靠性: + +1. **从 Producer 端来看,可靠性是指生产的消息能够正常的被存储到 Partition 上且消息不会丢失。Kafka 通过 [request.required.acks](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/edit/49a133ad2b2f2671aa60706b0%23)和[min.insync.replicas](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/edit/49a133ad2b2f2671aa60706b0%23) 两个参数配合,在一定程度上保证消息不会丢失。** +2. **[request.required.acks](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/edit/49a133ad2b2f2671aa60706b0%23) 可设置为 1、0、-1 三种情况。** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-7946f258c85fb8ca3d4aa423269c483a_720w.webp) + +
request.required.acks=1
+ +
+ +设置为 1 时代表当 Leader 状态的 Partition 接收到消息并持久化时就认为消息发送成功,如果 ISR 列表的 Replica 还没来得及同步消息,Leader 状态的 Partition 对应的 Broker 宕机,则消息有可能丢失。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-382c9f37f644feb37dd975c67bc1038f_720w.webp) + +
request.required.acks=0
+ +
+ +设置为 0 时代表 Producer 发送消息后就认为成功,消息有可能丢失。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-592996f264baadc64967d6f4b28f4d23_720w.webp) + +
request.required.acks=-1
+ +
+ +设置为-1 时,代表 ISR 列表中的所有 Replica 将消息同步完成后才认为消息发送成功;但是如果只存在主 Partition 的时候,Broker 异常时同样会导致消息丢失。所以此时就需要[min.insync.replicas](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/edit/49a133ad2b2f2671aa60706b0%23)参数的配合,该参数需要设定值大于等于 2,当 Partition 的个数小于设定的值时,Producer 发送消息会直接报错。 + +上面这个过程看似已经很完美了,但是假设如果消息在同步到部分从 Partition 上时,主 Partition 宕机,此时消息会重传,虽然消息不会丢失,但是会造成同一条消息会存储多次。在新版本中 Kafka 提出了幂等性的概念,通过给每条消息设置一个唯一 ID,并且该 ID 可以唯一映射到 Partition 的一个固定位置,从而避免消息重复存储的问题(作者到目前还没有使用过该特性,感兴趣的朋友可以自行在深入研究一下)。 + +### Kafka 的一致性: + +1. **从 Consumer 端来看,同一条消息在多个 Partition 上读取到的消息是一直的,Kafka 通过引入 HW(High Water)来实现这一特性。** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-9975539d98bf1a4e1a3038f2eceb2bb9_720w.webp) + +
消息同步图
+ +
+ +从上图可以看出,假设 Consumer 从主 Partition1 上消费消息,由于 Kafka 规定只允许消费 HW 之前的消息,所以最多消费到 Message2。假设当 Partition1 异常后,Partition2 被选举为 Leader,此时依旧可以从 Partition2 上读取到 Message2。其实 HW 的意思利用了木桶效应,始终保持最短板的那个位置。 + +从上面我们也可以看出,使用 HW 特性后会使得消息只有被所有副本同步后才能被消费,所以在一定程度上降低了消费端的性能,可以通过设置[replica.lag.time.max.ms](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/edit/49a133ad2b2f2671aa60706b0%23)参数来保证消息同步的最大时间。 + +## kafka 为什么那么快? + +kafka 使用了顺序写入和“零拷贝”技术,来达到每秒钟 200w(Apache 官方给出的数据) 的磁盘数据写入量,另外 Kafka 通过压缩数据,降低 I/O 的负担。 + +1. **顺序写入** + +大家都知道,对于磁盘而已,如果是随机写入数据的话,每次数据在写入时要先进行寻址操作,该操作是通过移动磁头完成的,极其耗费时间,而顺序读写就能够避免该操作。 + +1. **“零拷贝”技术** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-6930901956f341f1ab4a6e5650a0680b_720w.webp) + +
普通数据拷贝流程图
+ +
+ +普通的数据拷贝流程如上图所示,数据由磁盘 copy 到内核态,然后在拷贝到用户态,然后再由用户态拷贝到 socket,然后由 socket 协议引擎,最后由协议引擎将数据发送到网络中。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-9e44873a63d8addca917e658667f0b61_720w.webp) + +
&quot;零拷贝&quot;流程图
+ +
+ +采用了“零拷贝”技术后可以看出,数据不在经过用户态传输,而是直接在内核态完成操作,减少了两次 copy 操作。从而大大提高了数据传输速度。 + +1. **压缩** + +Kafka 官方提供了多种压缩协议,包括 gzip、snappy、lz4 等等,从而降低了数据传输的成本。 + +## Kafka 中的消息是否会丢失和重复消费? + +1. **Kafka 是否会丢消息,答案相信仔细看过前面两个问题的同学都比较清楚了,这里就不在赘述了。** +2. **在低版本中,比如作者公司在使用的 Kafka0.8 版本中,还没有幂等性的特性的时候,消息有可能会重复被存储到 Kafka 上(原因见上一个问题的),在这种情况下消息肯定是会被重复消费的。** + +**这里给大家一个解决重复消费的思路,作者公司使用了 Redis 记录了被消费的 key,并设置了过期时间,在 key 还没有过期内,对于同一个 key 的消息全部当做重复消息直接抛弃掉。** 在网上看到过另外一种解决方案,使用 HDFS 存储被消费过的消息,是否具有可行性存疑(需要读者朋友自行探索),读者朋友们可以根据自己的实际情况选择相应的策略,如果朋友们还有其他比较好的方案,欢迎留言交流。 + +## 为什么要使用 kafka,为什么要使用消息队列? + +### 先来说说为什么要使用消息队列? + +这道题比较主观一些(自认为没有网上其他文章写得话,轻喷),但是都相信大家使用消息队列无非就是为了 **解耦**、**异步**、**消峰**。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-f7c1bb87ab46ddd03255c58109ce360f_720w.webp) + +
系统调用图
+ +
+ +随着业务的发展,相信有不少朋友公司遇到过如上图所示的情况,系统 A 处理的结构被 B、C、D 系统所依赖,当新增系统 E 时,也需要系统 A 配合进行联调和上线等操作;还有当系统 A 发生变更时同样需要告知 B、C、D、E 系统需要同步升级改造。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-0f0c8f9531a38f6d79b2cbb2973bfbfc_720w.webp) + +
引入消息队列图
+ +
+ +引入消息队列后有两个好处: + +1. **各个系统进行了解耦,从上图也可以看出,当系统 A 突然发生热点事件时,同一时间产生大量结果,MQ 充当了消息暂存的效果,防止 B、C、D、E 系统也跟着崩溃。** +2. **当新系统 E 需要接入系统 A 的数据,只需要和 MQ 对接就可以了,从而避免了与系统 A 的调试上线等操作。** + +引入消息队列的坏处: + +万事皆具备两面性,看似引入消息队列这件事情很美好,但是同时也增加了系统的复杂度、系统的维护成本提高(如果 MQ 挂了怎么办)、引入了一致性等等问题需要去解决。 + +## 为什么要使用 Kafka? + +作者认为采用 Kafka 的原因有如下几点: + +1. **Kafka 目前在业界被广泛使用,社区活跃度高,版本更新迭代速度也快。** +2. **Kafka 的生产者和消费者都用 Java 语言进行了重写,在一定程度降低了系统的维护成本(作者的主观意见,因为当下 Java 的使用群体相当庞大)。** +3. **Kafka 系统的吞吐量高,达到了每秒 10w 级别的处理速度。** +4. **Kafka 可以和很多当下优秀的大数据组件进行集成,包括 Spark、Flink、Flume、Storm 等等。** + +## 为什么 Kafka 不支持读写分离? + +这个问题有个先决条件,我们只讨论 Kafka0.9 版本的情况。对于高版本,从 Partition 也可以承担读请求了,这里不多赘述。 + +Kafka 如果支持读写分离的话,有如下几个问题。 + +1. **系统设计的复杂度会比较大,当然这个比较牵强,毕竟高版本的 Kafka 已经实现了。** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-98093ad82970feb7a0c52954c6942aa1_720w.webp) + +
+ +**2\. 从上图可以看出,从从 Partition 上读取数据会有两个问题。一、数据从主 Partition 上同步到从 Partition 有数据延迟问题,因为数据从生产到消费会经历 3 次网络传输才能够被消费,对于时效性要求比较高的场景本身就不适合了。二、数据一致性问题,假设主 Partition 将数据第一次修改成了 A,然后又将该数据修改成了 B,由于从主 Partition 同步到从 Partition 会有延迟问题,所以也就会产生数据一致性问题。** + +分析得出,通过解决以上问题来换取从 Partition 承担读请求,成本可想而知,而且对于写入压力大,读取压力小的场景,本身也就没有什么意义了。 + +## 总结 + +本文介绍了几个常见的 Kafka 的面试题 + +### 常见面试题一览 + +#### 1.1 Kafka 中的 ISR(InSyncRepli)、 OSR(OutSyncRepli)、 AR(AllRepli)代表什么? + +ISR:速率和leader相差低于10s的follower的集合 + +OSR:速率和leader相差大于10s的follwer + +AR:所有分区的follower + +#### 1.2 Kafka 中的 HW、 LEO 等分别代表什么? + +HW:High Water高水位,根据同一分区中最低的LEO决定(Log End Offset) + +LEO:每个分区最大的Offset + +#### 1.3 Kafka 中是怎么体现消息顺序性的? + +在每个分区内,每条消息都有offset,所以消息在同一分区内有序,无法做到全局有序性 + +#### 1.4 Kafka 中的分区器、序列化器、拦截器是否了解?它们之间的处理顺序是什么? + +分区器Partitioner用来对分区进行处理的,即消息发送到哪一个分区的问题。序列化器,这个是对数据进行序列化和反序列化的工具。拦截器,即对于消息发送进行一个提前处理和收尾处理的类Interceptor,处理顺利首先通过拦截器=>序列化器=>分区器 + +#### 1.5 Kafka 生产者客户端的整体结构是什么样子的?使用了几个线程来处理?分别是什么? + +使用两个线程:main和sender 线程,main线程会一次经过拦截器、序列化器、分区器将数据发送到RecoreAccumulator线程共享变量,再由sender线程从共享变量中拉取数据发送到kafka broker + +batch.size达到此规模消息才发送,linger.ms未达到规模,等待当前时长就发送数据。 + +#### 1.6 消费组中的消费者个数如果超过 topic 的分区,那么就会有消费者消费不到数据”这句 话是否正确? + +这句话是对的,超过分区个数的消费者不会在接收数据,主要原因是一个分区的消息只能够被一个消费者组中的一个消费者消费。 + +#### 1.7 消费者提交消费位移时提交的是当前消费到的最新消息的 offset 还是 offset+1? + +生产者发送数据的offset是从0开始的,消费者消费的数据的offset是从1开始,故最新消息是offset+1 + +#### 1.8 有哪些情形会造成重复消费? + +先消费后提交offset,如果消费完宕机了,则会造成重复消费 + +#### 1.9 那些情景会造成消息漏消费? + +先提交offset,还没消费就宕机了,则会造成漏消费 + +#### 1.10 当你使用 kafka-topics.sh 创建(删除)了一个 topic 之后, Kafka 背后会执行什么逻辑? + +会在 zookeeper 中的/brokers/topics 节点下创建一个新的 topic 节点,如:/brokers/topics/first 触发 Controller 的监听程序 kafka Controller 负责 topic 的创建工作,并更新 metadata cache + +#### 1.11 topic 的分区数可不可以增加?如果可以怎么增加?如果不可以,那又是为什么? + +可以增加,修改分区个数--alter可以修改分区个数 + +#### 1.12 topic 的分区数可不可以减少?如果可以怎么减少?如果不可以,那又是为什么? + +不可以减少,减少了分区之后,之前的分区中的数据不好处理 + +#### 1.13 Kafka 有内部的 topic 吗?如果有是什么?有什么所用? + +有,__consumer_offsets主要用来在0.9版本以后保存消费者消费的offset + +#### 1.14 Kafka 分区分配的概念? + +Kafka分区对于Kafka集群来说,分区可以做到负载均衡,对于消费者来说分区可以提高并发度,提高读取效率 + +#### 1.15 简述 Kafka 的日志目录结构? + +每一个分区对应着一个文件夹,命名为topic-0/topic-1…,每个文件夹内有.index和.log文件。 + +#### 1.16 如果我指定了一个 offset, Kafka Controller 怎么查找到对应的消息? + +offset表示当前消息的编号,首先可以通过二分法定位当前消息属于哪个.index文件中,随后采用seek定位的方法查找到当前offset在.index中的位置,此时可以拿到初始的偏移量。通过初始的偏移量再通过seek定位到.log中的消息即可找到。 + +#### 1.17 聊一聊 Kafka Controller 的作用? + +Kafka集群中有一个broker会被选举为Controller,负责管理集群broker的上下线、所有topic的分区副本分配和leader的选举等工作。Controller的工作管理是依赖于zookeeper的。 + +#### 1.18 Kafka 中有那些地方需要选举?这些地方的选举策略又有哪些? + +在ISR中需要选举出Leader,选择策略为先到先得。在分区中需要选举,需要选举出Leader和follower。 + +#### 1.19 失效副本是指什么?有那些应对措施? + +失效副本为速率比leader相差大于10s的follower,ISR会将这些失效的follower踢出,等速率接近leader的10s内,会重新加入ISR + +#### 1.20 Kafka 的哪些设计让它有如此高的性能? + +1. Kafka天生的分布式架构 +2. 对log文件进行了分segment,并对segment建立了索引 +3. 对于单节点使用了顺序读写,顺序读写是指的文件的顺序追加,减少了磁盘寻址的开销,相比随机写速度提升很多 +4. 使用了零拷贝技术,不需要切换到用户态,在内核态即可完成读写操作,且数据的拷贝次数也更少。 + +## 参考文章 +https://blog.csdn.net/cao131502 +https://zhuanlan.zhihu.com/p/137811719 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\345\273\266\350\277\237\351\230\237\345\210\227.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\345\273\266\350\277\237\351\230\237\345\210\227.md" new file mode 100644 index 0000000..584cb61 --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\345\273\266\350\277\237\351\230\237\345\210\227.md" @@ -0,0 +1,173 @@ +KafkaһֲʽϢϵͳ㷺ӦڻʹʵӦУҪʵӳٶеĹܣԱһʱִijЩ߷ijЩϢKafkaṩ˶ַʽʵӳٶУĽһֳʵַ + + + + + +### һӳٶи + + + + + +ӳٶһһʱִϢĻơӦóʱϢ͡ʱȵȡӳٶеʵַʽж֣һֱȽϳʵַǻϢеӳϢơ + + + + + +KafkaУӳϢָڷϢʱָһӳʱ䣬Ϣӳʱ䵽űѡKafkaṩһЩTopicڴ洢ӳϢ"delayed-messages"߽̿ԶڴЩTopicϢϢ·͵ĿTopicУӶʵӳٶеĹܡ + + + + + +### KafkaеӳϢʵԭ + + + + + +KafkaӳϢʵԭȽϼ򵥣Ҫ漰ϢkeyʱϢkeyУһʱʾϢӳʱ䡣߷ϢʱϢ͵"delayed-messages" TopicУϢkeyеʱ߽̻ᶨڴ"delayed-messages" TopicϢϢkeyеʱǷѾڡʱѾڣϢ·͵ĿTopicУ"target-messages"ʱδڣϢ·͵"delayed-messages" TopicУһµӳʱͿʵӳٶеĹܡ + + + +![image-20230526211424767](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526211424767.png) + + + + + +### Kafkaӳٶеʵֲ + + + + + +ʵKafkaеӳٶУ԰²У + + + + + +1.һרŵTopicڴ洢ӳϢ"delayed-messages"ʹKafkaй߻Kafka APIд + + + + + +2.ϢkeyӳʱʹõǰʱӳʱΪkey磺"key":"message_body"ʹKafka APIϢ"delayed-messages" TopicС + + + + + +3.һ߽̣"delayed-messages" TopicеϢʹKafka APIʵ̡߽ + + + + + +4.߽УϢkeyеʱǷѾڡʹõǰʱϢkeyеʱбȽϡʱѾڣϢ·͵ĿTopicУ"target-messages"ʹKafka APIʵϢ·͡ + + + + + +5.ʱδڣϢ·͵"delayed-messages" TopicУһµӳʱʹKafka APIʵϢ·ͣϢkeyµӳʱ + + + + + +6.ȴһʱظִе4͵5ֱϢkeyеʱѾڡ + + + + + +ͨϲ裬ͿʵKafkaеӳٶйܡҪעǣ߽Ҫڴ"delayed-messages" TopicϢϢkeyеʱǷѾڡԸݾӦóòͬӳʱ䡣 + + + + + +![image-20230526211452328](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526211452328.png) + + + + + +### ġӳϢʵֵȱ + + + + + +![image-20230526211506828](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526211506828.png) + + + + + +Kafkaʵӳٶеķŵ㣺 + + + + + +1.ڷֲʽϵͳʵӳٶйܣнϸߵĿչԺͿɿԡ + + + + + +2.ʵּ򵥣ֻҪʹKafka APIɣ򹤾ߡ + + + + + +3.֧Ϣͺѣܺ + + + + + +4.صӳʱĿTopicڲͬӦó + + + + + +ǣKafkaʵӳٶҲһЩȱ㣺 + + + + + +1.Ҫ̶߽ڴ"delayed-messages" TopicϢ߽崻ֹͣӰӳٶеĹܡ + + + + + +2.߽ҪϢ·ͺͼ飬ҪһԴʱ䡣 + + + + + +3.ӳʱ侫ޣСֻܴﵽ뼶 + + + + + +### ġܽ + + + + + +KafkaһǿķֲʽϢϵͳʵӳٶеĹܡͨϢkeyӳʱ̵߽ĶѺ·ͣʵӳٶеĹܡKafkaʵӳٶеķʵּ򵥡չԸߡܺõŵ㣬ҲһЩȱ㡣ʵӦУҪݾӦóѡʵӳٶʵַ \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\346\255\273\344\277\241\351\230\237\345\210\227.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\346\255\273\344\277\241\351\230\237\345\210\227.md" new file mode 100644 index 0000000..5617b88 --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\346\255\273\344\277\241\351\230\237\345\210\227.md" @@ -0,0 +1,578 @@ +### Apache Kafka ڴŶУ Uber Crowdstrike ʵͰо + + + + + +ʶʹκοɿܵDZزٵġƪ̽** Apache Kafka ܹ****ʹŶʵִʵ**ЩѡԶʵ֡Kafka StreamsKafka ConnectSpring ܺͲߡʵоչʾ UberCrowdStrike ɣ̹Լ˹ģɿʵʱ + + + + + +Apache Kafka ΪҵܹϲļмʹսԣҵҲ Kafka Ϊԭƽ̨ (iPaaS) + + + + + +### Apache Kafka еϢģʽ + + + + + +ҿʼƪ֮ǰ֪**ڡJMSϢк Apache KafkaIJϵ**һ֣ + + + + + +* JMS Ϣ Apache Kafka **10 Ƚϱ׼** +* _**ƪ**_**C ͨApache Kafka еŶ (DQL)**д +* ʹ Apache Kafkaʵ**-ظģʽ** +* __Ƴ**ѡȷϢϵͳľ**JMS Apache Kafka +* _Ƴ_ JMS Ϣ Apache Kafka**ɡǨƺ/滻** + + + + + +### ʲôŶмģʽ Apache Kafka У + + + + + +**Ŷ (DLQ)**Ϣϵͳƽ̨ڵһַʵ֣**洢δɹϢ**ϵͳDZתϢǽƶŶС + + + + + +ҵ**ģʽ (EIP)**ΪģʽͨǿԽͬʡ + + + + + +![image-20230526224702433](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526224702433.png) + + + + + +صƽ̨ Apache Kafka** Kafka нϢ DLQ **ҪԭͨϢʽϢЧ/ȱʧ磬Ԥֵ߷ַᷢӦóڸ̬ĻУⲻڡ쳣޷Ϣһ + + + + + +ˣͨҪʹме֪ʶMessage Queue м JMS IBM MQTIBCO EMS RabbitMQֲʽύ־ KafkaĹʽͬԭϢе DLQ ϢϵͳЩԭһһӳ䵽 Kafka磬MQ ϵͳеϢÿϢ TTLʱ䣩ڡ + + + + + +ˣ** Kafka нϢ DLQ ҪԭϢʽϢЧ/ȱʧ** + + + + + +### Apache Kafka Ŷе + + + + + +Kafka еŶһ Kafka ⣬**պʹ洢ڴ޷һܵдϢ**˸ʹ´ϢϢЧϢĴֹͣ + + + + + +### Kafka Broker ܱܶ˵ṩ + + + + + +**Kafka ܹ֧ broker** r еDLQأKafka ִ΢ͬԭϣʹáƹܵܶ˵㡱ԭΪʲô봫ͳϢȣKafka չ֮á˺ʹڿͻӦóС + + + + + +ƽ̨ʵָɾơ**ÿ΢ӦóͨԼѡļͨŷʽʹʵ߼** + + + + + +ڴͳмϢУṩ߼еĿչԺԽϲΪֻмŶӲʵּ߼ + + + + + +### καԶʵ Kafka Ŷ + + + + + +Kafka еŶжʹõĿܡһЩΪŶṩ˿伴õĹܡǣʹJavaGoC++Python **καΪ Kafka ӦóдŶ߼**Ҳס + + + + + +**Ŷʵ**Դһ try-catch Ԥڻ쳣ûзϢκ쳣뽫Ϣ͵רõ DLQ Kafka ⡣ + + + + + +**ʧԭӦӵ Kafka Ϣıͷ**СӦļֵԱ㽫ʷ¼´͹Ϸ + + + + + +### ŶеĿ伴 Kafka ʵ + + + + + +㲢ҪʵŶС**ͿѾṩǵ DLQ ʵ** + + + + + +ʹԼӦóͨԿƴڳִʱ޸롣ǣ** 3rd Ӧóļɲһܿ缯ϰĴ**ˣDLQ øҪijЩС + + + + + +### Kafka Connect Ŷ + + + + + +**Kafka Connect Kafka ļɿ**ڿԴ Kafka СҪ Connect Ⱥе + + + + + +Ĭ£ʹЧϢʹô JSON תȷ AVRO תʱKafka Connect ֹͣɾЧϢһѡ񡣺̴ + + + + + +Kafka Connect DLQ úܼ򵥡ֻ轫ѡ ' errors.tolerance' ' errors.deadletterqueue.topic.name' ֵΪȷֵ + + + + + +![image-20230526224856544](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526224856544.png) + + + + + +¡ Kafka Connect Deep Dive C ŶСʾʹ DLQ ϸִʾ + + + + + +**Kafka Connect ڴ DLQ еĴϢ**ֻ貿һʹ te DLQ 磬Ӧó Avro ϢҴϢ JSON ʽȻʹ JSON ϢתΪ AVRO ϢԳɹ´ + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/61aa7624a01c5016d3ab9aaa8ee6938d5514.jpeg)ע⣬Kafka Connect **ûԴŶ** + + + + + +### Kafka Streams ӦóеĴ + + + + + +**Kafka Streams Kafka **ʽ Apache FlinkStormBeam ƹߡǣ Kafka ԭġζڵչҿɿĻܹйĶ˵ + + + + + +ֱʹ JavaJVM ̬ϵͳ Kafka Ӧó**鼸ʹ Kafka Streams Kafka ı׼ Java ͻ**Ϊʲô + + + + + +* Kafka StreamsֻǡһΧƳ Java ߺ API İװԼõĸӹܡ +* ߶ֻǶ뵽 Java ӦóеĿ⣨JAR ļ +* ߶ǿԴ Kafka صһ - ûж֤ġ +* Ѿ伴õؽԹܡ״̬Ƕʽ洢ڡʽѯȵȣ + + + + + +Kafka Streams**ù֮һĬϵķл쳣**޷л**¼쳣**𻵵ݡȷл߼δļ¼Ͷܵ´󡣸ùܲΪŶУ伴õؽͬ⡣ + + + + + +### Spring Kafka Spring Cloud Stream Ĵ + + + + + +Spring ܶ Apache Kafka кܺõ֧֡ṩģԱԼд롣**Spring-Kafka Spring Cloud Stream Kafka ָ֧Ժʹѡ**ʱ/ԡŶеȡ + + + + + + Spring ܹܷdzḻеأһѧϰߡˣdzʺ½ĿѾ Spring Ŀ + + + + + +кܶܰIJչʾ˲ͬʾѡŶеĹٷ Spring Cloud Stream ʾSpring ʹü򵥵ע͹߼ DLQ̷ֱһЩԱӰķһЩϲֻ˽ѡΪԼѡʵѡɡ + + + + + +### Apache Kafka ߵĿչʹ + + + + + +ͻԻУʵ֤**ŶеҪԭͨǴӵⲿ Web ݿʧ**ʱ Kafka ޷з͸ᵼijЩӦó̱һܺõĽ + + + + + +Apache Kafka****Apache 2.0 **ĿԴĿ**ṩһпͻ˶еIJ Apache Kafka ͻ˰װһ**ؼԵĸ򵥵/ API**Լ**չķ IO** + + + + + +ÿ**ͨ Kafka Consumer дϢζڲ**Ҫеķ Kafka Consumer жȡ**ͨ Kafka ĸ**ӳ١µ缫˲ԡⲿݷḻŶӡ + + + + + +һؼ**ڵ Kafka Ӧóд/ظ Web ݿ**лһη͵ Web Ҫ + + + + + +![image-20230526224910457](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526224910457.png) + + + + + +**Parallel Consumer ͻ˾ǿ**߼õӳٺͶ̬ҲԷ͵ŶС + + + + + +### ʹŶеϢ + + + + + +**͵ŶкûɣϢҪٱأ** + + + + + +Ŷ**¼д⴦ݴ**ľѷʽζŴ¼ֿݱ䡣 + + + + + +ڴʹŶеĴԡDO DONT ̽ʵ;ѵ + + + + + +### + + + + + +мѡڴ洢ŶеϢ + + + + + +* **´**DLQеһЩϢҪ´ǣȣҪ⡣Զű༭Ϣ˹߷شҪ·ͣģϢ +* **ɾϢһ**ãִܻϢǣɾ֮ǰҵӦüǡ磬DZӦóʹôϢǿӻ +* **߼**һѡǷԻȡʵʱ⣬Ǵ DLQ еÿϢ磬һ򵥵 ksqlDB ӦóӦм㣬ÿСʱϢƽκȷ Kafka ӦóеĴļ⡣ +* **ֹͣ**ٻֻϢֹͣҵ̡öԶģҲ˾ȻֹͣҲ׳ Kafka ӦóɡҪDLQ ;ⲿ +* ****ѡֻŶʲôȻʹijЩҲܺã Kafka ӦóΪסKafka бʱ䣬ڸʱ֮ɾϢֻΪȷķʽɡ DQL ǷΪ̫죩 + + + + + +### Apache Kafka Ŷеʵ + + + + + + Kafka ӦóʹŶндһЩ**ʵ;ѵ** + + + + + +* **ЧϢҵ**Զ˹ + * ʵͨû˴ DLQ Ϣ + * ѡ 1ҪվǻܹŶ + * ѡ 2Ӧ֪ͨ¼ŶϵͳݴǽҪӼ¼ϵͳ·/޸ݡ + * û˹ĻԹ뿼ɺ DLQ ڵıҪԡ෴ЩϢҲڳʼ Kafka Ӧóбԡʡ˴縺ءʩʽ +* **ʵDZ**Ŷӣ磬ͨʼ Slack +* ÿ Kafka **ȼֹͣɾ´** +* **ԵĴϢ͵ DLQ** - ӦóΡ +* **ԭʼϢ**Ǵ洢 DLQ УжıͷϢʱ䡢ӦóƵȣʹ´͹ųøס +* **Ҫ Dead Letter Queue Kafka **ȡᡣǽд洢ڵ DLQ пܶԽһ´û塣 + + + + + +ס**DLQ б֤˳ֹʹκ͵ߴø**ˣKafka DQL ʺÿ + + + + + +### ʱ Kafka ʹŶУ + + + + + +̽һ²ӦýЩ͵Ϣ Kafka ŶУ + + + + + +* **DLQ ڱѹ**ڴϢķֵʹ DLQ нһ⡣Kafka ־Ĵ洢Զѹ԰ԼٶȻȡݵķʽȡݣô󣩡ܵĻԵչߡʹĴ洢ռDLQ Ҳ޼¡⣬Ƿʹ DLQ ޹ء +* **ʧܵDLQ**ʧܶϢ DQL ޼£ʹڶ֮󣩡ϢҲ޷ӵϵͳҪ⡣ϢԸҪ洢ڳУȡڱʱ䣩 + + + + + +### ʹԤģʽע + + + + + +ͬҪǣ̽ijЩ**ŶеĿԡ** + + + + + +****Schema Registry**һȷԷֹڸгķ** Kafka ǿִȷϢṹ + +![image-20230526224926155](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526224926155.png) + + + + + +ģʽעģʽĿͻ˼顣Confluent Server һЩʵڴṩ˶ģʽ飬ԾܾδʹģʽעߵЧϢ + + + + + +### Kafka Ŷеİо + + + + + +ǿ**UberCrowdStrike Santander Bank о Kafka ʩʵʲŶ**סЩǷdzӡÿĿҪôӡ + + + + + +### Uber - ɿٴŶ + + + + + +ڷֲʽϵͳУDzɱġ󵽸⣬ϵжϣģеķ׼þŵʶʹϡ + + + + + + Uber ӪΧٶȣϵͳ**ݴʧʱЭ**Uber Apache Kafka ڸּ˹ģʵһĿꡣ + + + + + +ЩԣUber չŶչ Kafka ¼ܹеãͨʹ n**´Ŷʵֽɹ۲Ĵжʵʱ**òѡļʻԱ˺ƻ 200 пɿУΪעʻԱ۳ÿг̵ÿӢﱣѡ + + + + + + Uber ʾήļֱ½ DLQ + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526225030750.png) + + + + + +йظϢĶ Uber dzϸļ£ʹ Apache Kafka ɿٴŶС + + + + + +### CrowdStrike - ¼Ĵ + + + + + +CrowdStrike һλڵ¿˹ݰ˹͡**簲ȫ˾**ṩ**ƹغͶ˵㰲ȫв鱨繥Ӧ** + + + + + +CrowdStrike Ļʩ** ÿʹ Apache Kafka ڸ¼**ҵġ Apache Kaka 簲ȫϵСУҽκιģʵʱ̬Ƹ֪в鱨 + + + + + +CrowdStrike ʵ ɹʵŶкʹ + + + + + +* **ȷϵͳд洢Ϣ**ʩʹԲͼšCrowdStrike ʹ S3 洢洢DZڵĴϢע⣬Kafka ķֲ洢伴õؽ⣬洢ӿڣ磬 Confluent Cloud е޴洢 +* **ʹԶ**ùʹ޸һʧΪֶɴܷdz׳ +* **¼ҵ̲ƸŶ**׼ͼ¼ȷʹáйʦϤ֯ϢIJԡ + + + + + +** CrowdStrike 簲ȫƽ̨УģʵʱݴҪ**ҪҲڴ**һ繥ǹʵЧݵĶϢ** JavaScript ©ãˣͨŶʵʱ + + + + + +### ɣ̹ - Ժ DLQ ϵ 2.0 + + + + + +ɣ̹**Ӧóдݵͬݴپ޴ս**¼ܹǵĻܹһҿչļܹΪSantander Mailbox 2.0 + + + + + +Santander ĹزתƵ** Apache Kafka ṩֵ֧¼Դ** + + + + + +![image-20230526225045196](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526225045196.png) + + + + + +µĻ첽¼ļܹеһؼսǴ** Santander ʹԺ DQL Kafka ⹹Ĵ**Щ⣺ + + + + + +![image-20230526225057151](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526225057151.png) + + + + + +鿴 Santander ļɺ Consdata Kafka ݽԲԺ Apache Kafka еĿɿ¼ݡеϸϢ + + + + + +### Apache Kafka пɿҿչĴ + + + + + +**ڹɿܵƽ̨Ҫ**ڲͬ⡣ýŶеԶʵֻʹõĿܣ Kafka StreamsKafka ConnectSpring ܻ Kafka IJߡ + + + + + +ŲCrowdStrike ɣ̹еİоǺʵ֡µӦóܹʱҪһʼͿǵһ㡣**ʹ Apache Kafka ʵʱֻܹΪʱܳɹ**Ŷೡľѡ \ No newline at end of file