From ab2b40bf79d413c1cce742a3392ee99f304d8417 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sun, 23 Apr 2023 19:57:13 +0800 Subject: [PATCH 01/20] modify spring catalog --- ReadMe.md | 177 +++++++++++++----- ...@EnableWebMvc \346\263\250\350\247\243.md" | 0 ...emo\345\217\212@EnableAspectJAutoProxy.md" | 0 ...\274\232cglib \344\273\243\347\220\206.md" | 0 ...57\274\232aop \346\200\273\347\273\223.md" | 0 ...50\346\200\201\344\273\243\347\220\206.md" | 0 ...ComponentScan \346\263\250\350\247\243.md" | 0 ...20\206@Import \346\263\250\350\247\243.md" | 0 ...\220\206@Bean \346\263\250\350\247\243.md" | 0 ...6@Conditional \346\263\250\350\247\243.md" | 0 10 files changed, 130 insertions(+), 47 deletions(-) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC demo \344\270\216 @EnableWebMvc \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC demo \344\270\216@EnableWebMvc \346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AOP \347\244\272\344\276\213 demo \345\217\212 @EnableAspectJAutoProxy.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AOP\347\244\272\344\276\213demo\345\217\212@EnableAspectJAutoProxy.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/spring aop\357\274\210\344\272\224\357\274\211\357\274\232cglib \344\273\243\347\220\206.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib \344\273\243\347\220\206.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/spring aop\357\274\210\345\205\255\357\274\211\357\274\232aop \346\200\273\347\273\223.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop \346\200\273\347\273\223.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/spring aop\357\274\210\345\233\233\357\274\211\357\274\232jdk \345\212\250\346\200\201\344\273\243\347\220\206.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk \345\212\250\346\200\201\344\273\243\347\220\206.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206 @ComponentScan \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan \346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206 @Import \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import \346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206 @Bean \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean \346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206 @Conditional \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional \346\263\250\350\247\243.md" (100%) diff --git a/ReadMe.md b/ReadMe.md index c23b543..9b3a2c6 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -30,9 +30,9 @@

-## Java基础 +# Java基础 -### 基础知识 +## 基础知识 * [面向对象基础](docs/Java/basic/面向对象基础.md) * [Java基本数据类型](docs/Java/basic/Java基本数据类型.md) * [string和包装类](docs/Java/basic/string和包装类.md) @@ -56,7 +56,7 @@ * [序列化和反序列化](docs/Java/basic/序列化和反序列化.md) * [继承封装多态的实现原理](docs/Java/basic/继承封装多态的实现原理.md) -### 容器 +## 集合类 * [Java集合类总结](docs/Java/collection/Java集合类总结.md) * [Java集合详解:一文读懂ArrayList,Vector与Stack使用方法和实现原理](docs/Java/collection/Java集合详解:一文读懂ArrayList,Vector与Stack使用方法和实现原理.md) @@ -68,7 +68,7 @@ * [Java集合详解:HashSet,TreeSet与LinkedHashSet](docs/Java/collection/Java集合详解:HashSet,TreeSet与LinkedHashSet.md) * [Java集合详解:Java集合类细节精讲](docs/Java/collection/Java集合详解:Java集合类细节精讲.md) -## JavaWeb +# JavaWeb * [走进JavaWeb技术世界:JavaWeb的由来和基础知识](docs/JavaWeb/走进JavaWeb技术世界:JavaWeb的由来和基础知识.md) * [走进JavaWeb技术世界:JSP与Servlet的曾经与现在](docs/JavaWeb/走进JavaWeb技术世界:JSP与Servlet的曾经与现在.md) @@ -87,9 +87,9 @@ * [走进JavaWeb技术世界:深入浅出Mybatis基本原理](docs/JavaWeb/走进JavaWeb技术世界:深入浅出Mybatis基本原理.md) * [走进JavaWeb技术世界:极简配置的SpringBoot](docs/JavaWeb/走进JavaWeb技术世界:极简配置的SpringBoot.md) -## Java进阶 +# Java进阶 -### 并发编程 +## 并发编程 * [Java并发指南:并发基础与Java多线程](docs/Java/concurrency/Java并发指南:并发基础与Java多线程.md) * [Java并发指南:深入理解Java内存模型JMM](docs/Java/concurrency/Java并发指南:深入理解Java内存模型JMM.md) @@ -108,7 +108,7 @@ * [Java并发指南:ForkJoin并发框架与工作窃取算法剖析](docs/Java/concurrency/Java并发指南:ForkJoin并发框架与工作窃取算法剖析.md) * [Java并发编程学习总结](docs/Java/concurrency/Java并发编程学习总结.md) -### JVM +## JVM * [JVM总结](docs/Java/JVM/JVM总结.md) * [深入理解JVM虚拟机:JVM内存的结构与消失的永久代](docs/Java/JVM/深入理解JVM虚拟机:JVM内存的结构与消失的永久代.md) @@ -126,7 +126,7 @@ * [深入理解JVM虚拟机:再谈四种引用及GC实践](docs/Java/JVM/深入理解JVM虚拟机:再谈四种引用及GC实践.md) * [深入理解JVM虚拟机:GC调优思路与常用工具](docs/Java/JVM/深入理解JVM虚拟机:GC调优思路与常用工具.md) -### Java网络编程 +## Java网络编程 * [Java网络编程和NIO详解:JAVA 中原生的 socket 通信机制](docs/Java/network/Java网络编程与NIO详解:JAVA中原生的socket通信机制.md) * [Java网络编程与NIO详解:JAVA NIO 一步步构建IO多路复用的请求模型](docs/Java/network/Java网络编程与NIO详解:JavaNIO一步步构建IO多路复用的请求模型.md) @@ -139,8 +139,10 @@ * [Java网络编程与NIO详解:基于NIO的网络编程框架Netty](docs/Java/network/Java网络编程与NIO详解:基于NIO的网络编程框架Netty.md) * [Java网络编程与NIO详解:Java网络编程与NIO详解](docs/Java/network/Java网络编程与NIO详解:深度解读Tomcat中的NIO模型.md) * [Java网络编程与NIO详解:Tomcat中的Connector源码分析(NIO)](docs/Java/network/Java网络编程与NIO详解:Tomcat中的Connector源码分析(NIO).md) -## Spring全家桶 -### Spring + +# Spring全家桶 + +## Spring * [SpringAOP的概念与作用](docs/Spring全家桶/Spring/Spring常见注解.md) * [SpringBean的定义与管理(核心)](docs/Spring全家桶/Spring/Spring常见注解.md) @@ -157,8 +159,9 @@ * [Spring概述](docs/Spring全家桶/Spring/Spring常见注解.md) * [第一个Spring应用](docs/Spring全家桶/Spring/Spring常见注解.md) -### Spring源码分析 +## Spring源码分析 +### 综合 * [Spring源码剖析:初探SpringIOC核心流程](docs/Spring全家桶/Spring源码分析/Spring源码剖析:初探SpringIOC核心流程.md) * [Spring源码剖析:SpringIOC容器的加载过程 ](docs/Spring全家桶/Spring源码分析/Spring源码剖析:SpringIOC容器的加载过程.md) * [Spring源码剖析:懒加载的单例Bean获取过程分析](docs/Spring全家桶/Spring源码分析/Spring源码剖析:懒加载的单例Bean获取过程分析.md) @@ -168,7 +171,56 @@ * [Spring源码剖析:Spring事务概述](docs/Spring全家桶/Spring源码分析/Spring源码剖析:Spring事务概述.md) * [Spring源码剖析:Spring事务源码剖析](docs/Spring全家桶/Spring源码分析/Spring源码剖析:Spring事务源码剖析.md) -### SpringMVC +### AOP +* [AnnotationAwareAspectJAutoProxyCreator 分析(上)](docs/Spring全家桶/Spring源码分析/SpringAOP/AnnotationAwareAspectJAutoProxyCreator 分析(上).md) +* [AnnotationAwareAspectJAutoProxyCreator 分析(下)](docs/Spring全家桶/Spring源码分析/SpringAOP/AnnotationAwareAspectJAutoProxyCreator 分析(下).md) +* [AOP示例demo及@EnableAspectJAutoProxy](docs/Spring全家桶/Spring源码分析/SpringAOP/AOP示例demo及@EnableAspectJAutoProxy.md) +* [SpringAop(四):jdk 动态代理](docs/Spring全家桶/Spring源码分析/SpringAOP/SpringAop(四):jdk 动态代理.md) +* [SpringAop(五):cglib 代理](docs/Spring全家桶/Spring源码分析/SpringAOP/SpringAop(五):cglib 代理.md) +* [SpringAop(六):aop 总结](docs/Spring全家桶/Spring源码分析/SpringAOP/SpringAop(六):aop 总结.md) + +### 事务 +* [spring 事务(一):认识事务组件](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(一):认识事务组件.md) +* [spring 事务(二):事务的执行流程](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(二):事务的执行流程.md) +* [spring 事务(三):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(三):事务的隔离级别与传播方式的处理 01.md) +* [spring 事务(四):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(四):事务的隔离级别与传播方式的处理 02.md) +* [spring 事务(五):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(五):事务的隔离级别与传播方式的处理 03.md) +* [spring 事务(六):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(六):事务的隔离级别与传播方式的处理 04.md) + +### 启动流程 +* [spring 启动流程(一):启动流程概览](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(一):启动流程概览.md) +* [spring 启动流程(二):ApplicationContext 的创建](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(二):ApplicationContext 的创建.md) +* [spring 启动流程(三):包的扫描流程](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(三):包的扫描流程.md) +* [spring 启动流程(四):启动前的准备工作](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(四):启动前的准备工作.md) +* [spring 启动流程(五):执行 BeanFactoryPostProcessor](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(五):执行 BeanFactoryPostProcessor.md) +* [spring 启动流程(六):注册 BeanPostProcessor](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(六):注册 BeanPostProcessor.md) +* [spring 启动流程(七):国际化与事件处理](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(七):国际化与事件处理.md) +* [spring 启动流程(八):完成 BeanFactory 的初始化](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(八):完成 BeanFactory 的初始化.md) +* [spring 启动流程(九):单例 bean 的创建](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(九):单例 bean 的创建.md) +* [spring 启动流程(十):启动完成的处理](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(十):启动完成的处理.md) +* [spring 启动流程(十一):启动流程总结](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(十一):启动流程总结.md) + +### 组件分析 +* [spring 组件之 ApplicationContext](docs/Spring全家桶/Spring源码分析/Spring组件分析/spring 组件之 ApplicationContext.md) +* [spring 组件之 BeanDefinition](docs/Spring全家桶/Spring源码分析/Spring组件分析/spring 组件之 BeanDefinition.md) +* [Spring 组件之 BeanFactory](docs/Spring全家桶/Spring源码分析/Spring组件分析/Spring 组件之 BeanFactory.md) +* [spring 组件之 BeanFactoryPostProcessor](docs/Spring全家桶/Spring源码分析/Spring组件分析/spring 组件之 BeanFactoryPostProcessor.md) +* [spring 组件之 BeanPostProcessor](docs/Spring全家桶/Spring源码分析/Spring组件分析/spring 组件之 BeanPostProcessor.md) + +### 重要机制探秘 + +* [ConfigurationClassPostProcessor(一):处理 @ComponentScan 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(一):处理@ComponentScan 注解.md) +* [ConfigurationClassPostProcessor(三):处理 @Import 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(三):处理@Import 注解.md) +* [ConfigurationClassPostProcessor(二):处理 @Bean 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(二):处理@Bean 注解.md) +* [ConfigurationClassPostProcessor(四):处理 @Conditional 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(四):处理@Conditional 注解.md) +* [Spring 探秘之 AOP 的执行顺序](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring 探秘之 AOP 的执行顺序.md) +* [Spring 探秘之 Spring 事件机制](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring 探秘之 Spring 事件机制.md) +* [spring 探秘之循环依赖的解决(一):理论基石](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/spring 探秘之循环依赖的解决(一):理论基石.md) +* [spring 探秘之循环依赖的解决(二):源码分析](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/spring 探秘之循环依赖的解决(二):源码分析.md) +* [spring 探秘之监听器注解 @EventListener](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/spring 探秘之监听器注解@EventListener.md) +* [spring 探秘之组合注解的处理](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/spring 探秘之组合注解的处理.md) + +## SpringMVC * [SpringMVC中的国际化功能](docs/Spring全家桶/SpringMVC/SpringMVC中的国际化功能.md) * [SpringMVC中的异常处理器](docs/Spring全家桶/SpringMVC/SpringMVC中的异常处理器.md) @@ -179,16 +231,22 @@ * [SpringMVC如何实现文件上传](docs/Spring全家桶/SpringMVC/SpringMVC如何实现文件上传.md) * [SpringMVC中的常用功能](docs/Spring全家桶/SpringMVC/SpringMVC中的常用功能.md) -### SpringMVC源码分析 +## SpringMVC源码分析 * [SpringMVC源码分析:SpringMVC概述](docs/Spring全家桶/SpringMVC源码分析/SpringMVC源码分析:SpringMVC概述.md) * [SpringMVC源码分析:SpringMVC设计理念与DispatcherServlet](docs/Spring全家桶/SpringMVC源码分析/SpringMVC源码分析:SpringMVC设计理念与DispatcherServlet.md) * [SpringMVC源码分析:DispatcherServlet的初始化与请求转发 ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC源码分析:DispatcherServlet的初始化与请求转发.md) * [SpringMVC源码分析:DispatcherServlet如何找到正确的Controller ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC源码分析:DispatcherServlet如何找到正确的Controller.md) * [SpringMVC源码剖析:消息转换器HttpMessageConverter与@ResponseBody注解](docs/Spring全家桶/SpringMVC/SpringMVC源码剖析:消息转换器HttpMessageConverter与@ResponseBody注解.md) -* [SpringMVC源码分析:SpringMVC的视图解析原理 ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC源码分析:SpringMVC的视图解析原理.md) +* [DispatcherServlet 初始化流程 ](docs/Spring全家桶/SpringMVC源码分析/DispatcherServlet 初始化流程.md) +* [RequestMapping 初始化流程 ](docs/Spring全家桶/SpringMVC源码分析/RequestMapping 初始化流程.md) +* [Spring 容器启动 Tomcat ](docs/Spring全家桶/SpringMVC源码分析/Spring 容器启动 Tomcat.md) +* [SpringMVC demo 与@EnableWebMvc 注解 ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC demo 与@EnableWebMvc 注解.md) +* [SpringMVC 整体源码结构总结 ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC 整体源码结构总结.md) +* [请求执行流程(一)之获取 Handler ](docs/Spring全家桶/SpringMVC源码分析/请求执行流程(一)之获取 Handler.md) +* [请求执行流程(二)之执行 Handler 方法 ](docs/Spring全家桶/SpringMVC源码分析/请求执行流程(二)之执行 Handler 方法.md) -### SpringBoot +## SpringBoot * [SpringBoot系列:SpringBoot的前世今生](docs/Spring全家桶/SpringBoot/SpringBoot的前世今生.md) * [给你一份SpringBoot知识清单.md](docs/Spring全家桶/SpringBoot/给你一份SpringBoot知识清单.md) @@ -205,15 +263,40 @@ * [SpringBoot中的任务调度与@Async](docs/Spring全家桶/SpringBoot/SpringBoot中的任务调度与@Async.md) * [基于SpringBoot中的开源监控工具SpringBootAdmin](docs/Spring全家桶/SpringBoot/基于SpringBoot中的开源监控工具SpringBootAdmin.md) -### SpringBoot源码分析 - -### SpringCloud - -### SpringCloud源码分析 +## SpringBoot源码分析 +* [@SpringBootApplication 注解](docs/Spring全家桶/SpringBoot源码解析/@SpringBootApplication 注解.md) +* [springboot web应用(一):servlet 组件的注册流程](docs/Spring全家桶/SpringBoot源码解析/springboot web应用(一):servlet 组件的注册流程.md) +* [springboot web应用(二):WebMvc 装配过程](docs/Spring全家桶/SpringBoot源码解析/springboot web应用(二):WebMvc 装配过程.md) + +* [SpringBoot 启动流程(一):准备 SpringApplication](docs/Spring全家桶/SpringBoot源码解析/SpringBoot 启动流程(一):准备 SpringApplication.md) +* [SpringBoot 启动流程(二):准备运行环境](docs/Spring全家桶/SpringBoot源码解析/SpringBoot 启动流程(二):准备运行环境.md) +* [SpringBoot 启动流程(三):准备IOC容器](docs/Spring全家桶/SpringBoot源码解析/SpringBoot 启动流程(三):准备IOC容器.md) +* [springboot 启动流程(四):启动IOC容器](docs/Spring全家桶/SpringBoot源码解析/springboot 启动流程(四):启动IOC容器.md) +* [springboot 启动流程(五):完成启动](docs/Spring全家桶/SpringBoot源码解析/springboot 启动流程(五):完成启动.md) +* [springboot 启动流程(六):启动流程总结](docs/Spring全家桶/SpringBoot源码解析/springboot 启动流程(六):启动流程总结.md) + +* [springboot 自动装配(一):加载自动装配类](docs/Spring全家桶/SpringBoot源码解析/springboot 自动装配(一):加载自动装配类.md) +* [springboot 自动装配(二):条件注解](docs/Spring全家桶/SpringBoot源码解析/springboot 自动装配(二):条件注解.md) +* [springboot 自动装配(三):自动装配顺序](docs/Spring全家桶/SpringBoot源码解析/springboot 自动装配(三):自动装配顺序.md) + +## SpringCloud +* [Spring Cloud Config](docs/Spring全家桶/SpringCloud/Spring Cloud Config.md) +* [Spring Cloud Consul](docs/Spring全家桶/SpringCloud/Spring Cloud Consul.md) +* [Spring Cloud Eureka](docs/Spring全家桶/SpringCloud/Spring Cloud Eureka.md) +* [Spring Cloud Gateway](docs/Spring全家桶/SpringCloud/Spring Cloud Gateway.md) +* [Spring Cloud Hystrix](docs/Spring全家桶/SpringCloud/Spring Cloud Hystrix.md) +* [Spring Cloud LoadBalancer](docs/Spring全家桶/SpringCloud/Spring Cloud LoadBalancer.md) +* [Spring Cloud OpenFeign](docs/Spring全家桶/SpringCloud/Spring Cloud OpenFeign.md) +* [Spring Cloud Ribbon](docs/Spring全家桶/SpringCloud/Spring Cloud Ribbon.md) +* [Spring Cloud Sleuth](docs/Spring全家桶/SpringCloud/Spring Cloud Sleuth.md) +* [Spring Cloud Zuul](docs/Spring全家桶/SpringCloud/Spring Cloud Zuul.md) +* [SpringCloud概述](docs/Spring全家桶/SpringCloud/SpringCloud概述.md) + +## SpringCloud源码分析 todo -## 设计模式 +# 设计模式 * [设计模式学习总结](docs/Java/design-parttern/设计模式学习总结.md) * [初探Java设计模式:创建型模式(工厂,单例等).md](docs/Java/design-parttern/初探Java设计模式:创建型模式(工厂,单例等).md) @@ -223,32 +306,32 @@ todo * [初探Java设计模式:Spring涉及到的种设计模式.md](docs/Java/design-parttern/初探Java设计模式:Spring涉及到的种设计模式.md) -## 计算机基础 +# 计算机基础 -### 计算机网络 +## 计算机网络 todo -### 操作系统 +## 操作系统 todo -#### Linux相关 +## Linux相关 todo -### 数据结构与算法 +## 数据结构与算法 todo -#### 数据结构 +## 数据结构 todo -#### 算法 +## 算法 todo -## 数据库 +# 数据库 todo -### MySQL +## MySQL * [Mysql原理与实践总结](docs/database/Mysql原理与实践总结.md) * [重新学习Mysql数据库:无废话MySQL入门](docs/database/重新学习MySQL数据库:无废话MySQL入门.md) * [重新学习Mysql数据库:『浅入浅出』MySQL和InnoDB](docs/database/重新学习MySQL数据库:『浅入浅出』MySQL和InnoDB.md) @@ -265,9 +348,9 @@ todo * [重新学习Mysql数据库:Mysql主从复制,读写分离,分表分库策略与实践](docs/database/重新学习MySQL数据库:Mysql主从复制,读写分离,分表分库策略与实践.md) -## 缓存 +# 缓存 -### Redis +## Redis * [Redis原理与实践总结](docs/cache/Redis原理与实践总结.md) * [探索Redis设计与实现开篇:什么是Redis](docs/cache/探索Redis设计与实现开篇:什么是Redis.md) * [探索Redis设计与实现:Redis的基础数据结构概览](docs/cache/探索Redis设计与实现:Redis的基础数据结构概览.md) @@ -286,11 +369,11 @@ todo * [探索Redis设计与实现:Redis事务浅析与ACID特性介绍](docs/cache/探索Redis设计与实现:Redis事务浅析与ACID特性介绍.md) * [探索Redis设计与实现:Redis分布式锁进化史 ](docs/cache/探索Redis设计与实现:Redis分布式锁进化史.md ) -## 消息队列 +# 消息队列 -### Kafka +## Kafka -## 大后端 +# 大后端 * [后端技术杂谈开篇:云计算,大数据与AI的故事](docs/backend/后端技术杂谈开篇:云计算,大数据与AI的故事.md) * [后端技术杂谈:搜索引擎基础倒排索引](docs/backend/后端技术杂谈:搜索引擎基础倒排索引.md) * [后端技术杂谈:搜索引擎工作原理](docs/backend/后端技术杂谈:搜索引擎工作原理.md) @@ -305,8 +388,8 @@ todo * [后端技术杂谈:十分钟理解Kubernetes核心概念](docs/backend/后端技术杂谈:十分钟理解Kubernetes核心概念.md) * [后端技术杂谈:捋一捋大数据研发的基本概念](docs/backend/后端技术杂谈:捋一捋大数据研发的基本概念.md) -## 分布式 -### 分布式理论 +# 分布式 +## 分布式理论 * [分布式系统理论基础:一致性PC和PC ](docs/distributed/basic/分布式系统理论基础:一致性PC和PC.md) * [分布式系统理论基础:CAP ](docs/distributed/basic/分布式系统理论基础:CAP.md) * [分布式系统理论基础:时间时钟和事件顺序](docs/distributed/basic/分布式系统理论基础:时间时钟和事件顺序.md) @@ -317,7 +400,7 @@ todo * [分布式系统理论基础:zookeeper分布式协调服务 ](docs/distributed/basic/分布式系统理论基础:zookeeper分布式协调服务.md) * [分布式理论总结](docs/distributed/分布式技术实践总结.md) -### 分布式技术 +## 分布式技术 * [搞懂分布式技术:分布式系统的一些基本概念](docs/distributed/practice/搞懂分布式技术:分布式系统的一些基本概念.md ) * [搞懂分布式技术:分布式一致性协议与Paxos,Raft算法](docs/distributed/practice/搞懂分布式技术:分布式一致性协议与Paxos,Raft算法.md) * [搞懂分布式技术:初探分布式协调服务zookeeper](docs/distributed/practice/搞懂分布式技术:初探分布式协调服务zookeeper.md ) @@ -338,29 +421,29 @@ todo * [搞懂分布式技术:浅谈分布式消息技术Kafka](docs/distributed/practice/搞懂分布式技术:浅谈分布式消息技术Kafka.md ) * [分布式技术实践总结](docs/distributed/分布式理论总结.md) -## 面试指南 +# 面试指南 todo -### 校招指南 +## 校招指南 todo -### 面经 +## 面经 todo -## 工具 +# 工具 todo -## 资料 +# 资料 todo -### 书单 +## 书单 todo -## 待办 +# 待办 springboot和springcloud -## 微信公众号 +# 微信公众号 -### Java技术江湖 +## Java技术江湖 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号【Java技术江湖】 ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/Javatutorial.jpeg) diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC demo \344\270\216 @EnableWebMvc \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC demo \344\270\216@EnableWebMvc \346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC demo \344\270\216 @EnableWebMvc \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC demo \344\270\216@EnableWebMvc \346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AOP \347\244\272\344\276\213 demo \345\217\212 @EnableAspectJAutoProxy.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AOP\347\244\272\344\276\213demo\345\217\212@EnableAspectJAutoProxy.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AOP \347\244\272\344\276\213 demo \345\217\212 @EnableAspectJAutoProxy.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AOP\347\244\272\344\276\213demo\345\217\212@EnableAspectJAutoProxy.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/spring aop\357\274\210\344\272\224\357\274\211\357\274\232cglib \344\273\243\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib \344\273\243\347\220\206.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/spring aop\357\274\210\344\272\224\357\274\211\357\274\232cglib \344\273\243\347\220\206.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib \344\273\243\347\220\206.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/spring aop\357\274\210\345\205\255\357\274\211\357\274\232aop \346\200\273\347\273\223.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop \346\200\273\347\273\223.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/spring aop\357\274\210\345\205\255\357\274\211\357\274\232aop \346\200\273\347\273\223.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop \346\200\273\347\273\223.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/spring aop\357\274\210\345\233\233\357\274\211\357\274\232jdk \345\212\250\346\200\201\344\273\243\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk \345\212\250\346\200\201\344\273\243\347\220\206.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/spring aop\357\274\210\345\233\233\357\274\211\357\274\232jdk \345\212\250\346\200\201\344\273\243\347\220\206.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk \345\212\250\346\200\201\344\273\243\347\220\206.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206 @ComponentScan \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan \346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206 @ComponentScan \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan \346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206 @Import \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import \346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206 @Import \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import \346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206 @Bean \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean \346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206 @Bean \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean \346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206 @Conditional \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional \346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206 @Conditional \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional \346\263\250\350\247\243.md" From efe7aa7460af62adebcd28a599081938ed827cb1 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 29 Apr 2023 12:19:37 +0800 Subject: [PATCH 02/20] SpringCloud sourceCode analysis --- ...20\347\240\201\345\210\206\346\236\220.md" | 997 +++++++++ ...20\347\240\201\345\210\206\346\236\220.md" | 652 ++++++ ...15\345\212\241\345\217\221\347\216\260.md" | 469 +++++ ...15\345\212\241\346\263\250\345\206\214.md" | 349 ++++ ...20\357\274\232\346\246\202\350\247\210.md" | 494 +++++ ...15\347\275\256\344\270\255\345\277\203.md" | 259 +++ ...20\347\240\201\345\210\206\346\236\220.md" | 1041 ++++++++++ ...20\347\240\201\345\210\206\346\236\220.md" | 1776 +++++++++++++++++ ...20\347\240\201\345\210\206\346\236\220.md" | 407 ++++ ...20\347\240\201\345\210\206\346\236\220.md" | 1187 +++++++++++ ...20\347\240\201\345\210\206\346\236\220.md" | 662 ++++++ ...20\347\240\201\345\210\206\346\236\220.md" | 895 +++++++++ ...20\347\240\201\345\210\206\346\236\220.md" | 947 +++++++++ 13 files changed, 10135 insertions(+) create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Seata\346\272\220\347\240\201\345\210\206\346\236\220.md" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Sentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\345\217\221\347\216\260.md" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\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" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\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" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Config\346\272\220\347\240\201\345\210\206\346\236\220.md" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Eureka\346\272\220\347\240\201\345\210\206\346\236\220.md" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Gateway\346\272\220\347\240\201\345\210\206\346\236\220.md" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Hystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud LoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud OpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Ribbon\346\272\220\347\240\201\345\210\206\346\236\220.md" 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/Spring Cloud Seata\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/Spring Cloud Seata\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..1287eef --- /dev/null +++ "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/Spring Cloud Seata\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,997 @@ +���� | һ��ߣJava +��Դ |����ͷ�� + +**ѧϰĿ��** + +* Seata ATģʽԴ������ + **��1�� ATģʽ����** + **1.1 ˼ά�����Ƶ�** + �������Ѿ�����ATģʽ�Ĵ���ԭ������Դ���У�ͨ��READMEҲ�ܿ�����ATģʽ��ʹ�ã��DZ��Ľ��ӵײ�Դ�����ȥ����ATģʽ��ԭ�����ڷ���ԭ��֮ǰ��������������ͼ������һ�����Ĺ���˼·��ģʽ�� + +�ȿ���˼ά�Ƶ�ͼ![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0388859932ccee17ed32246e2e5f4e0a88dee6.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������")1.2 ��ʼ�������Ƶ� +![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f24e8bd05e84b683e3f3227490146155bf5b17.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������")1.3 ִ�������Ƶ� +![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/93dea0538eba8be5778699ea2af8f71c0e396c.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������")**��2�� Դ�����** +**2.1 SeataAutoConfiguration** +����seataԴ����о���Ҫ��seata�������ҵ��SQL����undo_log���ݣ������һ�׶���ɺ��ύȫ�����������һ�׶�ҵ��ʧ�ܺ�ͨ��undo_log�ع����񣬽������񲹳��� + +seataҲ����spring����ʹ�õģ����SpringBoot��seataҲ������һЩ�Զ�����![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/719ef4a775ece0688c929227415e97f68f1c58.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������")seata���Զ������������dz���ֱ�ӣ��ͽ�����SeataAutoConfiguration�����Ǵ������ + + + +``` +@ComponentScan(basePackages = "io.seata.spring.boot.autoconfigure.properties")@ConditionalOnProperty(prefix = StarterConstants.SEATA_PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)@Configuration@EnableConfigurationProperties({SeataProperties.class})public class SeataAutoConfiguration { } +``` + + + + + + + + + +���ȣ�@ComponentScanɨ����һ��properties����������һ�������SeataProperties��Bean���� + +@ConditionalOnProperty����������Ч��������Ϊseata.enabled=true��Ĭ��ֵ��true�����Կ��Կ��طֲ�ʽ�����ܣ���client�˵�file.conf����������ã��� + +@Configuration������SeataAutoConfiguration������Ϊ��spring�������ࡣ + +@EnableConfigurationProperties�����ð�ת����һ��SeataProperties��Bean������ʹ�á� + +�������Ķ�SeataAutoConfiguration���ڲ����� + + + +``` +@Bean@DependsOn({BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER, BEAN_NAME_FAILURE_HANDLER})@ConditionalOnMissingBean(GlobalTransactionScanner.class)public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataProperties, FailureHandler failureHandler) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Automatically configure Seata"); } return new GlobalTransactionScanner(seataProperties.getApplicationId(), seataProperties.getTxServiceGroup(), failureHandler);} +``` + + + + + + + + + +�Զ����õĺ��ĵ������������һ��Bean��GlobalTransactionScanner�� + +���ǿ����������Bean�dz��ļ򵥣����췽��ֻ��Ҫһ��applicationId��txServiceGroup�� + +applicationId: ����spring.application.name=�㶨��ĵ�ǰӦ�õ����֣����磺userService + +txServiceGroup: ������applicationId ���� -seata-service-group�����ģ����磺 +userService-seata-service-group������汾�ϵ͵Ļ�����ʱ����ܻ�����seata����fescar�����Ĭ������������fescarΪ��׺�� + +new��һ��GlobalTransactionScanner����SeataAutoConfiguration����Զ�����������þͽ����ˡ�SeataAutoConfigurationֻ������һ���������������á� + +**2.2 GlobalTransactionScanner** +��Ȼ���ĵ�����GlobalTransactionScanner����࣬���Ǽ�����ע���������������ʵ�Ϳ��Բ²⵽һ���������ã�ɨ��@GlobalTransactional���ע�⣬���Դ�����������������ǿ����Ĺ��ܡ� + +Ҫ�˽�����࣬���ò����Ķ�һ������UMLͼ![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/83cb3ed76073a33cb19242ffe542b615346d16.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������")���Կ�����GlobalTransactionScanner��Ҫ��4����ֵ�ù�ע�� + +1��ApplicationContextAware��ʾ�����õ�spring���� + +2��InitializingBean�ӿڣ������˳�ʼ����ʱ������һЩ���� + +3��AbstractAutoProxyCreator��ʾ�����spring�����е�Bean����������ǿ��Ҳ�����������������������ǿ�IJ²⡣ + +4��Disposable�ӿڣ�������spring�������ٵ�ʱ������һЩ���� + +����������΢��עһ����4����ִ��˳�� + +ApplicationContextAware -> InitializingBean -> AbstractAutoProxyCreator -> DisposableBean + +**2.3 InitializingBean** + + + +``` +@Overridepublic void afterPropertiesSet() { if (disableGlobalTransaction) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Global transaction is disabled."); } return; } initClient();} +``` + + + + + + + + + +��ʼ��Seata��Client�˵Ķ�����Client����Ҫ����TransactionManager��ResourceManager��������Ϊ�˼򻯰ɣ���û�а�initClient����´�GlobalTransactionScanner�����������һ���ࡣ + +����initClient���� + + + +``` +private void initClient() { //init TM TMClient.init(applicationId, txServiceGroup); //init RM RMClient.init(applicationId, txServiceGroup); registerSpringShutdownHook();} +``` + + + + + + + + + +initClient�߼��������ӣ���������TMClient.init��ʼ��TransactionManager��RPC�ͻ��ˣ�RMClient.init��ʼ��ResourceManager��RPC�ͻ��ˡ�seata��RPC����netty��ʵ�֣�seata��װ����һ��ʹ�á���ע����һ��Spring��ShutdownHook���Ӻ��� + +**2.3.1 TMClient��ʼ��** + + + +``` +@Overridepublic void init() { timerExecutor.scheduleAtFixedRate(new Runnable() { @Override public void run() { clientChannelManager.reconnect(getTransactionServiceGroup()); } }, SCHEDULE_DELAY_MILLS, SCHEDULE_INTERVAL_MILLS, TimeUnit.MILLISECONDS);...} +``` + + + + + + + + + +������һ����ʱ�����Ͻ����������������� +clientChannelManager.reconnect������������ + + + +``` +void reconnect(String transactionServiceGroup) { List availList = null; try { availList = getAvailServerList(transactionServiceGroup); } catch (Exception e) { ... } ... for (String serverAddress : availList) { try { acquireChannel(serverAddress); } catch (Exception e) { ... } }} +``` + + + + + + + + + +����transactionServiceGroup��ȡseata-server��ip��ַ�б���Ȼ��������� + + + +``` +private List getAvailServerList(String transactionServiceGroup) throws Exception { List availInetSocketAddressList = RegistryFactory.getInstance() .lookup(transactionServiceGroup); if (CollectionUtils.isEmpty(availInetSocketAddressList)) { return Collections.emptyList(); } return availInetSocketAddressList.stream() .map(NetUtil::toStringAddress) .collect(Collectors.toList());} +``` + + + + + + + + + +RegistryFactory.getInstance().lookup(transactionServiceGroup);�ǶԲ�ͬע��������������ģ�Ĭ�Ͽ���Nacos��ʽ��ʵ��![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/745b802128d80980a6e082f91406451526cbb3.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������")�ȸ�����������ҵ�����������server��Ⱥ���ƣ�������default��Ȼ����ݼ�Ⱥ�����ҵ�server��Ӧip�˿ڵ�ַ + + + +``` +@Overridepublic List lookup(String key) throws Exception { //default String clusterName = getServiceGroup(key); if (clusterName == null) { return null; } if (!LISTENER_SERVICE_MAP.containsKey(clusterName)) { synchronized (LOCK_OBJ) { if (!LISTENER_SERVICE_MAP.containsKey(clusterName)) { List clusters = new ArrayList<>(); clusters.add(clusterName); List firstAllInstances = getNamingInstance().getAllInstances(getServiceName(), getServiceGroup(), clusters); if (null != firstAllInstances) { List newAddressList = firstAllInstances.stream() .filter(instance -> instance.isEnabled() && instance.isHealthy()) .map(instance -> new InetSocketAddress(instance.getIp(), instance.getPort())) .collect(Collectors.toList()); CLUSTER_ADDRESS_MAP.put(clusterName, newAddressList); } subscribe(clusterName, event -> { List instances = ((NamingEvent) event).getInstances(); if (null == instances && null != CLUSTER_ADDRESS_MAP.get(clusterName)) { CLUSTER_ADDRESS_MAP.remove(clusterName); } else if (!CollectionUtils.isEmpty(instances)) { List newAddressList = instances.stream() .filter(instance -> instance.isEnabled() && instance.isHealthy()) .map(instance -> new InetSocketAddress(instance.getIp(), instance.getPort())) .collect(Collectors.toList()); CLUSTER_ADDRESS_MAP.put(clusterName, newAddressList); } }); } } } return CLUSTER_ADDRESS_MAP.get(clusterName);} +``` + + + + + + + + + +Seata-server��IP��ַ�ѻ�ȡ��,Ȼ�����acquireChannel + + + +``` +Channel acquireChannel(String serverAddress) { Channel channelToServer = channels.get(serverAddress); if (channelToServer != null) { channelToServer = getExistAliveChannel(channelToServer, serverAddress); if (channelToServer != null) { return channelToServer; } }... channelLocks.putIfAbsent(serverAddress, new Object()); synchronized (channelLocks.get(serverAddress)) { return doConnect(serverAddress); }} +``` + + + + + + + + + +��󽫻�ȡ����seata-server��IP��ַ�ŵ�Netty�з�װ��TmClient�ͳ�ʼ����� + +TmClient��ʼ���ܽ᣺ + +* ������ʱ�������Խ���һ������seata-server +* ����ʱ���ȴ�nacos�������������ã��и��ݷ�������(service_group)�ҵ���Ⱥ����(cluster_name) +* �ٸ��ݼ�Ⱥ�����ҵ���Ⱥip�˿��б� +* ��ip�б���ѡ��һ����netty�������� + **2.3.2 RMClient��ʼ��** + + + +``` +public static void init(String applicationId, String transactionServiceGroup) { // ��ȡ�������� RmRpcClient rmRpcClient = RmRpcClient.getInstance(applicationId, transactionServiceGroup); // ����ResourceManager�ĵ������� rmRpcClient.setResourceManager(DefaultResourceManager.get()); // ���Ӽ�����������Server�˵���Ϣ���� rmRpcClient.setClientMessageListener(new RmMessageListener(DefaultRMHandler.get())); // ��ʼ��RPC rmRpcClient.init();} +``` + + + + + + + + + +��TMClient��ȣ�RMClient�����һ������Server����Ϣ�������Ļ��ơ�Ҳ����˵TM��ְ��������������Server��ͨ�ţ����磺ȫ�������begin��commit��rollback�ȡ� + +��RM������������������Դ�⣬������Ϊȫ�������commit��rollback�ȵ���Ϣ���ͣ��Ӷ��Ա�����Դ������ز����� + +������Դ������resourceManager��������Ϣ�ص����������ڽ���TC�ڶ��׶η������ύ���߻ع�����Seata�ж�ResourceManager��AbstractRMHandler����SPI���䣬��ResouceManagerΪ���� + + + +``` +public class DefaultResourceManager implements ResourceManager { protected void initResourceManagers() { //init all resource managers List allResourceManagers = EnhancedServiceLoader.loadAll(ResourceManager.class); if (CollectionUtils.isNotEmpty(allResourceManagers)) { for (ResourceManager rm : allResourceManagers) { resourceManagers.put(rm.getBranchType(), rm); } } }} +``` + + + + + + + + + +���Կ�����ʼ��DefaultResouceManagerʱ��ʹ��ClassLoaderȥ���ض�ӦJar�µ�ʵ�֣���Ĭ��ATģʽʹ�õ�ʵ�������ݿ⣬Ҳ����rm-datasource���µ�ʵ�֣���ʵ����·����Ҫ��λ��/resources/META-INF/��չ�ӿ�ȫ·��ȥ�ң��ͻ��ҵ���Ӧ��ʵ���� ![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/84399f558194f3dce18275c72cb9a92219db25.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������ϣ�-��Դ������������")ResourceManager��Ӧʵ����ȫ·�� +io.seata.rm.datasource.DataSourceManager��������ָ�������ύ�ͻع��ķ�����DefaultRMHandler��Ӧʵ����ȫ·��io.seata.rm.RMHandlerAT���Ǹ�����server��Ϣ������Ӧ�ύ���߻ع������Ļص������ࡣ + +RMClinet��init()������TMClient����һ�� + +**2.3.3 �ܽ�** + +* Spring����ʱ����ʼ����2���ͻ���TmClient��RmClient +* TmClient��seata-serverͨ��Netty�������Ӳ�������Ϣ +* RmClient��seata-serverͨ��Netty�������ӣ�������ն��׶��ύ���ع���Ϣ���ڻص���(RmHandler)�������� + +���� | һ��ߣJava +��Դ |����ͷ�� + +**2.4 AbstractAutoProxyCreator** +GlobalTransactionScanner��ʼ������TM��RM�Ժ������ٹ�עһ��AbstractAutoProxyCreator���Զ������� + +�Զ�������������ɶ�����أ�����˵����spring�е�Bean��ǿ��ʲô���ܣ� + +GlobalTransactionScanner��Ҫ��չ��AbstractAutoProxyCreator��wrapIfNecessary + +������ǿ��ǰ���жϴ�������ʾ�Ƿ��Bean��Ҫ��ǿ�������ǿ�Ļ����������� + +**2.4.1 wrapIfNecessary** + + + +``` +@Overrideprotected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (disableGlobalTransaction) { return bean; } try { synchronized (PROXYED_SET) { // ��ͬBean���� if (PROXYED_SET.contains(beanName)) { return bean; } interceptor = null; // �ж��Ƿ���TCCģʽ if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) { // TCCʵ�ֵ������� interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName)); } else { Class serviceInterface = SpringProxyUtils.findTargetClass(bean); Class[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean); // �ж��Ƿ����@GlobalTransactional����@GlobalLockע�� if (!existsAnnotation(new Class[]{serviceInterface}) && !existsAnnotation(interfacesIfJdk)) { return bean; } if (interceptor == null) { // ��TCC�������� if (globalTransactionalInterceptor == null) { globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook); ConfigurationCache.addConfigListener( ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, (ConfigurationChangeListener)globalTransactionalInterceptor); } interceptor = globalTransactionalInterceptor; } } // �жϵ�ǰBean�Ƿ��Ѿ���spring�Ĵ������� if (!AopUtils.isAopProxy(bean)) { // ��������ǣ���ô��һ��spring�Ĵ������̼��� bean = super.wrapIfNecessary(bean, beanName, cacheKey); } else { // �����һ��spring�Ĵ����࣬��ô�����ȡ���������Ѿ����ڵ����������ϣ�Ȼ�����ӵ��ü��ϵ��� AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null)); for (Advisor avr : advisor) { advised.addAdvisor(0, avr); } } PROXYED_SET.add(beanName); return bean; } } catch (Exception exx) {}} +``` + + + + + + + + + +wrapIfNecessary�����ϳ����Ƿֲ��迴�� + +1��isTccAutoProxy�ж��Ƿ���tccģʽ�������Ļ�ѡ����TccActionInterceptor����������tccģʽѡ�� +GlobalTransactionalInterceptor��������Ĭ�ϲ����� + +2��existAnnotation�жϵ�ǰBean�Ƿ�������߽ӿڵķ�������@GlobalTransactional����@GlobalLockע�⣬���û����ֱ�ӷ��� + +3��isAopProxy�������жϵ�ǰ��Bean�Ƿ��Ѿ���spring�Ĵ������ˣ�������JDK��̬��������Cglib��������������ͨ��Bean����ԭ�е����ɴ����߼����ɣ�����Ѿ��Ǵ����࣬��ôҪͨ�������ȡ���������ڵ�����������Ҳ����Advisor��ֱ�����ӵ��ü��ϵ��С� + +wrapIfNecessary�ķ����������ӣ���������Դ������Ǻ���Ϥ������ϸ�ڵ����Щ���� + +**2.4.1.1 ATһ�׶ο���ȫ������** +����Ҫ����ȫ����������Ľӿ��ϣ����@GlobalTransactionalע�⣬���ע�����һ����Ӧ���������������� +GlobalTransactionalInterceptor��invoke�������ط��� + + + +``` +@Overridepublic Object invoke(final MethodInvocation methodInvocation) throws Throwable { Class targetClass = methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null; Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass); if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) { final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod); //��ȡ�����ϵ�ȫ������ע�� final GlobalTransactional globalTransactionalAnnotation = getAnnotation(method, targetClass, GlobalTransactional.class); //��ȡ�����ϵ�ȫ����ע�� final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class); boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes); if (!localDisable) { //�����������ȫ������ע�⣬����handleGlobalTransaction����ȫ������ if (globalTransactionalAnnotation != null) { return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation); //�����������ȫ����ע�⣬����handleGlobalLock����ȫ���� } else if (globalLockAnnotation != null) { return handleGlobalLock(methodInvocation); } } } //���ɶ��û�У�����ͨ����ִ�У��������� return methodInvocation.proceed();} +``` + + + + + + + + + +��handleGlobalTransaction�����е����� +transactionalTemplate.execute���� + + + +``` +// 2\. ����ȫ������beginTransactionbeginTransaction(txInfo, tx); Object rs = null;try { // ִ��ҵ�񷽷�business.execute() rs = business.execute(); } catch (Throwable ex) { // 3.�����쳣ִ��completeTransactionAfterThrowing�ع� completeTransactionAfterThrowing(txInfo, tx, ex); throw ex;} // 4\. û���쳣�ύ����commitTransactioncommitTransaction(tx); +``` + + + + + + + + + +����ȫ���������յ��� +io.seata.tm.api.DefaultGlobalTransaction#begin(int, java.lang.String)���� + + + +``` +@Overridepublic void begin(int timeout, String name) throws TransactionException { //�˴��Ľ�ɫ�ж��йؼ�������//������ǰ��ȫ������ķ����ߣ�Launcher�����Dz����ߣ�Participant��//����ڷֲ�ʽ���������ϵͳ������Ҳ����GlobalTransactionalע��//��ô���Ľ�ɫ����Participant��������Ժ����begin���˳��� //���ж��Ƿ����ߣ�Launcher�����Dz����ߣ�Participant���Ǹ��ݵ�ǰ�������Ƿ��Ѵ���XID���ж� //û��XID�ľ���Launcher���Ѿ�����XID�ľ���Participant if (role != GlobalTransactionRole.Launcher) { assertXIDNotNull(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Ignore Begin(): just involved in global transaction [{}]", xid); } return; } assertXIDNull(); if (RootContext.getXID() != null) { throw new IllegalStateException(); } xid = transactionManager.begin(null, null, name, timeout); status = GlobalStatus.Begin; RootContext.bind(xid); if (LOGGER.isInfoEnabled()) { LOGGER.info("Begin new global transaction [{}]", xid); } } +``` + + + + + + + + + +����seata-server��ȡȫ������XID + + + +``` +@Overridepublic String begin(String applicationId, String transactionServiceGroup, String name, int timeout) throws TransactionException { GlobalBeginRequest request = new GlobalBeginRequest(); request.setTransactionName(name); request.setTimeout(timeout); //���� GlobalBeginResponse response = (GlobalBeginResponse) syncCall(request); if (response.getResultCode() == ResultCode.Failed) { throw new TmTransactionException(TransactionExceptionCode.BeginFailed, response.getMsg()); } return response.getXid();} +private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException { try { //TMClient��װ��Netty���� return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request); } catch (TimeoutException toe) { throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe); }} +``` + + + + + + + + + +��XID����RootContext�У��ɴ˿��Կ���ȫ����������TM����ģ�TM����ȫ�����������seata-server����seata-server������ܵ����������������seata������룩�� + + + +``` +@Overrideprotected void doGlobalBegin(GlobalBeginRequest request, GlobalBeginResponse response, RpcContext rpcContext) throws TransactionException { //����begin response.setXid(core.begin(rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(), request.getTransactionName(), request.getTimeout())); if (LOGGER.isInfoEnabled()) { LOGGER.info("Begin new global transaction applicationId: {},transactionServiceGroup: {}, transactionName: {},timeout:{},xid:{}", rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(), request.getTransactionName(), request.getTimeout(), response.getXid()); }} +``` + + + + + + + + + +io.seata.server.coordinator.DefaultCoordinator#doGlobalBegin�������ܿͻ��˿���ȫ����������󣬵���io.seata.server.coordinator.DefaultCore#begin����ȫ������ + + + +``` +@Overridepublic String begin(String applicationId, String transactionServiceGroup, String name, int timeout) throws TransactionException { GlobalSession session = GlobalSession.createGlobalSession(applicationId, transactionServiceGroup, name, timeout); MDC.put(RootContext.MDC_KEY_XID, session.getXid()); session.addSessionLifecycleListener(SessionHolder.getRootSessionManager());//�����Ự session.begin(); // transaction start event eventBus.post(new GlobalTransactionEvent(session.getTransactionId(), GlobalTransactionEvent.ROLE_TC, session.getTransactionName(), applicationId, transactionServiceGroup, session.getBeginTime(), null, session.getStatus())); return session.getXid();} +``` + + + + + + + + + +ͨ����ǰ�Ự���� + + + +``` +@Overridepublic void begin() throws TransactionException { this.status = GlobalStatus.Begin; this.beginTime = System.currentTimeMillis(); this.active = true; for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { lifecycleListener.onBegin(this); }} +``` + + + + + + + + + +![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e7c784186f75581eba83325a0fc4708602dadf.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������") + +���� +io.seata.server.session.AbstractSessionManager#onBegin�������ֵ���io.seata.server.storage.db.session.DataBaseSessionManager#addGlobalSession���� + + + +``` +@Overridepublic void addGlobalSession(GlobalSession session) throws TransactionException { if (StringUtils.isBlank(taskName)) { //���� boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_ADD, session); if (!ret) { throw new StoreException("addGlobalSession failed."); } } else { boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_UPDATE, session); if (!ret) { throw new StoreException("addGlobalSession failed."); } }} +``` + + + + + + + + + +���������ݿ���д������ + + + +``` +@Overridepublic boolean writeSession(LogOperation logOperation, SessionStorable session) { if (LogOperation.GLOBAL_ADD.equals(logOperation)) { return logStore.insertGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) { return logStore.updateGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) { return logStore.deleteGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.BRANCH_ADD.equals(logOperation)) { return logStore.insertBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) { return logStore.updateBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) { return logStore.deleteBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else { throw new StoreException("Unknown LogOperation:" + logOperation.name()); }} +``` + + + + + + + + + +������seata��global_tab�������ݣ�����ȫ�������ѿ��� + +**2.4.1.2 ATһ�׶�ִ��ҵ��SQL** +ȫ�������ѿ�����������Ҫִ��ҵ��SQL������undo_log���ݣ�ȫ���������سɹ������ջ���ִ����ҵ�񷽷��ģ���������Seata������Դ���˴���������sql������undo_log��������������Դ������ִ�еģ���������Seata��DataSource��Connection��Statement���Ĵ�����װ�� + + + +``` +/*** ����datasource���������滻ԭ���ĵ�datasource*/@Primary@Bean("dataSource")public DataSourceProxy dataSourceProxy(DataSource druidDataSource){ return new DataSourceProxy(druidDataSource);} +``` + + + + + + + + + +��Ŀ��ʹ�õ�����Դ����seata��DataSourceProxy����![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c8a7a189816e282015a950bfd3c13b7d859974.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������")���ն�Sql���н���������������StatementProxy���� + + + +``` +@Overridepublic boolean execute(String sql) throws SQLException { this.targetSQL = sql; return ExecuteTemplate.execute(this, (statement, args) -> statement.execute((String) args[0]), sql);} +``` + + + + + + + + + + + +``` +public static T execute(List sqlRecognizers, StatementProxy statementProxy, StatementCallback statementCallback, Object... args) throws SQLException { if (!RootContext.requireGlobalLock() && !StringUtils.equals(BranchType.AT.name(), RootContext.getBranchType())) { //����ȫ�������ֱ��ִ�У��������� return statementCallback.execute(statementProxy.getTargetStatement(), args); } String dbType = statementProxy.getConnectionProxy().getDbType(); if (CollectionUtils.isEmpty(sqlRecognizers)) { sqlRecognizers = SQLVisitorFactory.get( statementProxy.getTargetSQL(), dbType); } Executor executor; if (CollectionUtils.isEmpty(sqlRecognizers)) { executor = new PlainExecutor<>(statementProxy, statementCallback); } else { if (sqlRecognizers.size() == 1) { SQLRecognizer sqlRecognizer = sqlRecognizers.get(0); //��ͬSQL���ͣ���ͬ���� switch (sqlRecognizer.getSQLType()) { case INSERT: executor = EnhancedServiceLoader.load(InsertExecutor.class, dbType, new Class[]{StatementProxy.class, StatementCallback.class, SQLRecognizer.class}, new Object[]{statementProxy, statementCallback, sqlRecognizer}); break; case UPDATE: executor = new UpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer); break; case DELETE: executor = new DeleteExecutor<>(statementProxy, statementCallback, sqlRecognizer); break; case SELECT_FOR_UPDATE: executor = new SelectForUpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer); break; default: executor = new PlainExecutor<>(statementProxy, statementCallback); break; } } else { executor = new MultiExecutor<>(statementProxy, statementCallback, sqlRecognizers); } } T rs; try { //ִ��SQL rs = executor.execute(args); } catch (Throwable ex) { if (!(ex instanceof SQLException)) { // Turn other exception into SQLException ex = new SQLException(ex); } throw (SQLException) ex; } return rs; } +``` + + + + + + + + + +* ���ж��Ƿ�����ȫ���������û�У����ߴ�����������sql���������� +* ����SQLVisitorFactory��Ŀ��sql���н��� +* ����ض�����sql����(INSERT,UPDATE,DELETE,SELECT_FOR_UPDATE)�Ƚ���������� +* ִ��sql�����ؽ�� + ��ͬ���͵�SQL����������һ����������insertΪ�� + +![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b1eb1b610beb647b230995f19ecbb4bbc98602.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������")insertʹ�õ���InsertExecutor.execute����������ʵ���ջ���ʹ�� +io.seata.rm.datasource.exec.BaseTransactionalExecutor#execute���� + + + +``` +@Overridepublic T execute(Object... args) throws Throwable { if (RootContext.inGlobalTransaction()) { String xid = RootContext.getXID(); statementProxy.getConnectionProxy().bind(xid); } statementProxy.getConnectionProxy().setGlobalLockRequire(RootContext.requireGlobalLock()); return doExecute(args);} +``` + + + + + + + + + +���������е�xid�󶨵���statementProxy�У���������doExecute����������AbstractDMLBaseExecutor�е�doExecute���� + + + +``` +@Overridepublic T doExecute(Object... args) throws Throwable { AbstractConnectionProxy connectionProxy = statementProxy.getConnectionProxy(); if (connectionProxy.getAutoCommit()) { return executeAutoCommitTrue(args); } else { return executeAutoCommitFalse(args); }} +``` + + + + + + + + + +�����е����� +executeAutoCommitTrue/executeAutoCommitFalse + + + +``` +protected T executeAutoCommitTrue(Object[] args) throws Throwable { ConnectionProxy connectionProxy = statementProxy.getConnectionProxy(); try { connectionProxy.setAutoCommit(false); return new LockRetryPolicy(connectionProxy).execute(() -> { T result = executeAutoCommitFalse(args); connectionProxy.commit(); return result; }); } catch (Exception e) { ... } finally { connectionProxy.getContext().reset(); connectionProxy.setAutoCommit(true); }} +``` + + + + + + + + + +����ϸ���֣����ն��ǵ���executeAutoCommitFalse���� + + + +``` +protected T executeAutoCommitFalse(Object[] args) throws Exception { //����getTableMeta���� if (!JdbcConstants.MYSQL.equalsIgnoreCase(getDbType()) && getTableMeta().getPrimaryKeyOnlyName().size() > 1) { throw new NotSupportYetException("multi pk only support mysql!"); } //��ȡbeforeImage TableRecords beforeImage = beforeImage(); //ִ��ҵ��sql T result = statementCallback.execute(statementProxy.getTargetStatement(), args); //��ȡafterImage TableRecords afterImage = afterImage(beforeImage); //����image prepareUndoLog(beforeImage, afterImage); return result;} +``` + + + + + + + + + +��ȡbeforeImage + + + +``` +//tableMeta��������������С�����������protected TableMeta getTableMeta(String tableName) { if (tableMeta != null) { return tableMeta; } ConnectionProxy connectionProxy = statementProxy.getConnectionProxy(); tableMeta = TableMetaCacheFactory.getTableMetaCache(connectionProxy.getDbType()) .getTableMeta(connectionProxy.getTargetConnection(), tableName, connectionProxy.getDataSourceProxy().getResourceId()); return tableMeta;} +``` + + + + + + + + + +ִ��ҵ��sql����ʹ�� +com.alibaba.druid.pool.DruidPooledPreparedStatement#execute����ִ�� + +��ȡafterImage![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/540ff5c254dde029bed208247025e97bd1f497.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������")���ύ����ʱ������undo_log��־ + + + +``` +protected T executeAutoCommitTrue(Object[] args) throws Throwable { ConnectionProxy connectionProxy = statementProxy.getConnectionProxy(); try { connectionProxy.setAutoCommit(false); return new LockRetryPolicy(connectionProxy).execute(() -> { T result = executeAutoCommitFalse(args); //���� connectionProxy.commit(); return result; }); } catch (Exception e) { ... } finally { connectionProxy.getContext().reset(); connectionProxy.setAutoCommit(true); }} +``` + + + + + + + + + + + +``` +public void commit() throws SQLException { try { LOCK_RETRY_POLICY.execute(() -> { //���� doCommit(); return null; }); } catch (SQLException e) { throw e; } catch (Exception e) { throw new SQLException(e); }} +``` + + + + + + + + + + + +``` +private void doCommit() throws SQLException { if (context.inGlobalTransaction()) { //���� processGlobalTransactionCommit(); } else if (context.isGlobalLockRequire()) { processLocalCommitWithGlobalLocks(); } else { targetConnection.commit(); }} +``` + + + + + + + + + + + +``` +private void processGlobalTransactionCommit() throws SQLException { try { //��seata-serverע���֧��Ϣ register(); } catch (TransactionException e) { recognizeLockKeyConflictException(e, context.buildLockKeys()); } try { //�ύ����֮ǰ������undo_log,����flushUndoLogs UndoLogManagerFactory.getUndoLogManager(this.getDbType()).flushUndoLogs(this); targetConnection.commit(); } catch (Throwable ex) { ... } if (IS_REPORT_SUCCESS_ENABLE) { report(true); } context.reset();} +``` + + + + + + + + + + + +``` +public void flushUndoLogs(ConnectionProxy cp) throws SQLException { ConnectionContext connectionContext = cp.getContext(); if (!connectionContext.hasUndoLog()) { return; } String xid = connectionContext.getXid(); long branchId = connectionContext.getBranchId(); ...//�÷�������undo_log insertUndoLogWithNormal(xid, branchId, buildContext(parser.getName()), undoLogContent, cp.getTargetConnection());} +``` + + + + + + + + + +�ڸ÷�����ע���֧����![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/92309a950c73829078a3170c585fc58fb03b0d.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������")�ύ������seata-serverע���֧��Ϣ��seata-server���յ�����seataԴ�룩![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c116df5929a1116e61e683298e14b953450bb7.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������") + +io.seata.server.coordinator.DefaultCoordinator#doBranchRegister���� + + + +``` +public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys) throws TransactionException { GlobalSession globalSession = assertGlobalSessionNotNull(xid, false); return SessionHolder.lockAndExecute(globalSession, () -> { ... try { //����ע�� globalSession.addBranch(branchSession); } catch (RuntimeException ex) { ... } ... return branchSession.getBranchId(); });} +``` + + + + + + + + + + + +``` +@Overridepublic void addBranch(BranchSession branchSession) throws TransactionException { for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { //����onAddBranch��ѡ��AbstractSessionManager lifecycleListener.onAddBranch(this, branchSession); } branchSession.setStatus(BranchStatus.Registered); add(branchSession);} +``` + + + + + + + + + +io.seata.server.storage.db.session.DataBaseSessionManager#addBranchSession���� + + + +``` +@Overridepublic void onAddBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { //���룬ѡ��DataBaseSessionManager addBranchSession(globalSession, branchSession);} +``` + + + + + + + + + + + +``` +@Overridepublic void addBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException { if (StringUtils.isNotBlank(taskName)) { return; } //���� boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_ADD, session); if (!ret) { throw new StoreException("addBranchSession failed."); }} +``` + + + + + + + + + + + +``` +@Overridepublic boolean writeSession(LogOperation logOperation, SessionStorable session) { if (LogOperation.GLOBAL_ADD.equals(logOperation)) { return logStore.insertGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) { return logStore.updateGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) { return logStore.deleteGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.BRANCH_ADD.equals(logOperation)) { return logStore.insertBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) { return logStore.updateBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) { return logStore.deleteBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else { throw new StoreException("Unknown LogOperation:" + logOperation.name()); }} +``` + + + + + + + + + + + +``` +@Overridepublic boolean insertBranchTransactionDO(BranchTransactionDO branchTransactionDO) { String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getInsertBranchTransactionSQL(branchTable); Connection conn = null; PreparedStatement ps = null; try { int index = 1; conn = logStoreDataSource.getConnection(); conn.setAutoCommit(true); ps = conn.prepareStatement(sql); ps.setString(index++, branchTransactionDO.getXid()); ps.setLong(index++, branchTransactionDO.getTransactionId()); ps.setLong(index++, branchTransactionDO.getBranchId()); ps.setString(index++, branchTransactionDO.getResourceGroupId()); ps.setString(index++, branchTransactionDO.getResourceId()); ps.setString(index++, branchTransactionDO.getBranchType()); ps.setInt(index++, branchTransactionDO.getStatus()); ps.setString(index++, branchTransactionDO.getClientId()); ps.setString(index++, branchTransactionDO.getApplicationData()); return ps.executeUpdate() > 0; } catch (SQLException e) { throw new StoreException(e); } finally { IOUtil.close(ps, conn); }} +``` + + + + + + + + + +Seata-server���ӷ�֧��Ϣ��ɣ������һ�׶ν�����ҵ�����ݣ�undo_log����֧��Ϣ���Ѿ�д�����ݿ� + +**2.4.1.3 AT���׶��ύ** +�ص�handleGlobalTransaction�����У������� +transactionalTemplate.execute���� + + + +``` +// 2\. ����ȫ������beginTransactionbeginTransaction(txInfo, tx); Object rs = null;try { // ִ��ҵ�񷽷�business.execute() rs = business.execute(); } catch (Throwable ex) { //������һ�׶� //�����Ƕ��׶� // 3.�����쳣ִ��completeTransactionAfterThrowing�ع� completeTransactionAfterThrowing(txInfo, tx, ex); throw ex;} // 4\. û���쳣�ύ����commitTransactioncommitTransaction(tx); +``` + + + + + + + + + +���׶��ύ + +commitTransaction(tx);���� + + + +``` +private void commitTransaction(GlobalTransaction tx) throws TransactionalExecutor.ExecutionException { try { triggerBeforeCommit(); //���� tx.commit(); triggerAfterCommit(); } catch (TransactionException txe) { // 4.1 Failed to commit throw new TransactionalExecutor.ExecutionException(tx, txe, TransactionalExecutor.Code.CommitFailure); }} +``` + + + + + + + + + +![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/61b35980530bd17bff9025f1112baa9e3e93e5.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������") + + + +``` +@Overridepublic GlobalStatus commit(String xid) throws TransactionException { GlobalCommitRequest globalCommit = new GlobalCommitRequest(); globalCommit.setXid(xid); //����syncCall GlobalCommitResponse response = (GlobalCommitResponse) syncCall(globalCommit); return response.getGlobalStatus();} +``` + + + + + + + + + + + +``` +private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException { try { return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request); } catch (TimeoutException toe) { throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe); }} +``` + + + + + + + + + +����ͨ��TM����seata-server��Seata-server���յ�ȫ���ύ����seataԴ�룩 + +DefaultCoordinator�� + + + +``` +@Overrideprotected void doGlobalCommit(GlobalCommitRequest request, GlobalCommitResponse response, RpcContext rpcContext) throws TransactionException { MDC.put(RootContext.MDC_KEY_XID, request.getXid()); //����commit response.setGlobalStatus(core.commit(request.getXid()));} +``` + + + + + + + + + +![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/185262d74acd3a723a7770d5771c19fa9fa5cf.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������")![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/51249f8944b29b9ffc82897a818cb4c33ed06a.png "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������")![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/14a2f2b58ad3f11be518376d5e6f68cc678af9.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������") +Seata-server���յ��ͻ���ȫ���ύ������Ȼص��ͻ��ˣ�ɾ��undo_log��seata��ɾ����֧��ȫ������ + +֮ǰ˵��RMClient�ڳ�ʼ��ʱ��������Դ������resourceManager��������Ϣ�ص����������ڽ���TC�ڶ��׶η������ύ���߻ع�����![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/884c07085b1230a1aab1883d691532ccf6b79c.png "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������")Seata-serverɾ����֧���ݼ�ȫ���������� + + + +``` +@Overridepublic void removeBranch(BranchSession branchSession) throws TransactionException { // do not unlock if global status in (Committing, CommitRetrying, AsyncCommitting), // because it's already unlocked in 'DefaultCore.commit()' if (status != Committing && status != CommitRetrying && status != AsyncCommitting) { if (!branchSession.unlock()) { throw new TransactionException("Unlock branch lock failed, xid = " + this.xid + ", branchId = " + branchSession.getBranchId()); } } for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { //���� lifecycleListener.onRemoveBranch(this, branchSession); } remove(branchSession);} +``` + + + + + + + + + + + +``` +private void writeSession(LogOperation logOperation, SessionStorable sessionStorable) throws TransactionException { if (!transactionStoreManager.writeSession(logOperation, sessionStorable)) { if (LogOperation.GLOBAL_ADD.equals(logOperation)) { throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to store global session"); } else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) { throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to update global session"); } else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) { throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to remove global session"); } else if (LogOperation.BRANCH_ADD.equals(logOperation)) { throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to store branch session"); } else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) { throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to update branch session"); } else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) { throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to remove branch session"); } else { throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession, "Unknown LogOperation:" + logOperation.name()); } }} +``` + + + + + + + + + + + +``` +public static void endCommitted(GlobalSession globalSession) throws TransactionException { globalSession.changeStatus(GlobalStatus.Committed); //ɾ��ȫ������ globalSession.end();} +``` + + + + + + + + + +�ͻ���ɾ��undo_log���� + +�ڽ����ύ���� + + + +``` +protected void doBranchCommit(BranchCommitRequest request, BranchCommitResponse response) throws TransactionException { String xid = request.getXid(); long branchId = request.getBranchId(); String resourceId = request.getResourceId(); String applicationData = request.getApplicationData(); if (LOGGER.isInfoEnabled()) { LOGGER.info("Branch committing: " + xid + " " + branchId + " " + resourceId + " " + applicationData); } //���� BranchStatus status = getResourceManager().branchCommit(request.getBranchType(), xid, branchId, resourceId, applicationData); response.setXid(xid); response.setBranchId(branchId); response.setBranchStatus(status); if (LOGGER.isInfoEnabled()) { LOGGER.info("Branch commit result: " + status); } } +``` + + + + + + + + + +getResourceManager��ȡ�ľ���RMClient��ʼ��ʱ���õ���Դ������DataSourceManager + + + +``` +public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException { return asyncWorker.branchCommit(branchType, xid, branchId, resourceId, applicationData);} +``` + + + + + + + + + + + +``` +@Overridepublic BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException { if (!ASYNC_COMMIT_BUFFER.offer(new Phase2Context(branchType, xid, branchId, resourceId, applicationData))) { LOGGER.warn("Async commit buffer is FULL. Rejected branch [{}/{}] will be handled by housekeeping later.", branchId, xid); } return BranchStatus.PhaseTwo_Committed;} +``` + + + + + + + + + +���ֻ����һ��ASYNC_COMMIT_BUFFER����List��������һ�����׶��ύ��context���������ύ��AsyncWorker��init()���� + + + +``` +public synchronized void init() { LOGGER.info("Async Commit Buffer Limit: {}", ASYNC_COMMIT_BUFFER_LIMIT); ScheduledExecutorService timerExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("AsyncWorker", 1, true)); timerExecutor.scheduleAtFixedRate(() -> { try {//���� doBranchCommits(); } catch (Throwable e) { LOGGER.info("Failed at async committing ... {}", e.getMessage()); } }, 10, 1000 * 1, TimeUnit.MILLISECONDS);} +``` + + + + + + + + + +![SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/a730eb7781ec4bd3ad2578fb7855aa02d45fbc.jpg "SpringCloud Alibabaϵ�С���17Seata ATģʽԴ��������£�-��Դ������������")ɾ��Undo_log + +**���׶λع�** + +���׶λع�seata-server�˴�������׶��ύ���ƣ�����ʡ�� + + + +``` +protected void doGlobalRollback(GlobalRollbackRequest request, GlobalRollbackResponse response, RpcContext rpcContext) throws TransactionException { MDC.put(RootContext.MDC_KEY_XID, request.getXid()); //ȫ�ֻع�sea���������� response.setGlobalStatus(core.rollback(request.getXid()));} +``` + + + + + + + + + +��Ҫ���ع��ͻ�����ν������񲹳� + + + +``` +@Overridepublic BranchRollbackResponse handle(BranchRollbackRequest request) { BranchRollbackResponse response = new BranchRollbackResponse(); exceptionHandleTemplate(new AbstractCallback() { @Override public void execute(BranchRollbackRequest request, BranchRollbackResponse response) throws TransactionException { //���� doBranchRollback(request, response); } }, request, response); return response;} +``` + + + + + + + + + + + +``` +public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException { DataSourceProxy dataSourceProxy = get(resourceId); if (dataSourceProxy == null) { throw new ShouldNeverHappenException(); } try { UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType()).undo(dataSourceProxy, xid, branchId); } catch (TransactionException te) { StackTraceLogger.info(LOGGER, te, "branchRollback failed. branchType:[{}], xid:[{}], branchId:[{}], resourceId:[{}], applicationData:[{}]. reason:[{}]", new Object[]{branchType, xid, branchId, resourceId, applicationData, te.getMessage()}); if (te.getCode() == TransactionExceptionCode.BranchRollbackFailed_Unretriable) { return BranchStatus.PhaseTwo_RollbackFailed_Unretryable; } else { return BranchStatus.PhaseTwo_RollbackFailed_Retryable; } } return BranchStatus.PhaseTwo_Rollbacked; } +``` + + + + + + + + + +���ջع��������õ���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����һ���ύ������seata��ATģʽԴ�������ϡ� \ 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/Spring Cloud Sentinel\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/Spring Cloud Sentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..0d6c22c --- /dev/null +++ "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/Spring Cloud Sentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,652 @@ +���� | һ��ߣJava +��Դ |����ͷ�� + +**ѧϰĿ��** + +* Sentinel�Ĺ���ԭ�� + **��1�� ����ԭ��** + ��Sentinel�У����е���Դ����Ӧһ����Դ�����Լ�һ��Entry��ÿһ��entry���Ա�ʾһ�����󡣶�Sentinel�У�����Ե�ǰ������ڹ�����ж���ʵ�����صĿ��ƣ�ԭ������ͼ��ʾ�� + +![SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1296c955070646bbc74310e726679bbabfe585.jpg "SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������")��ͼ����Ϊ���˼���չʾ��ͼ�� Slot ��˳���Ѻ����°� Sentinel Slot Chain ˳��һ�� +��һ���ⲿ�������֮�󣬻ᴴ��һ��Entry��������Entry��ͬʱ��Ҳ�ᴴ��һϵ�е�slot ���һ����������ÿ��slot�в�ͬ�Ĺ���ְ�� + +* NodeSelectorSlot �����ռ���Դ��·����������Щ��Դ�ĵ���·��������״�ṹ�洢���������ڸ��ݵ���·�������������� +* ClusterBuilderSlot �����ڴ洢��Դ��ͳ����Ϣ�Լ���������Ϣ���������Դ�� RT, QPS,thread count �ȵȣ���Щ��Ϣ������Ϊ��ά�����������������ݣ� +* StatisticSlot �����ڼ�¼��ͳ�Ʋ�ͬγ�ȵ� runtime ָ������Ϣ�� +* FlowSlot �����ڸ���Ԥ������������Լ�ǰ�� slot ͳ�Ƶ�״̬���������������ƣ� +* AuthoritySlot ��������õĺڰ������͵�����Դ��Ϣ�������ڰ��������ƣ� +* DegradeSlot ��ͨ��ͳ����Ϣ�Լ�Ԥ��Ĺ��������۶Ͻ����� +* SystemSlot ��ͨ��ϵͳ��״̬������ load1 �ȣ��������ܵ���������� +* LogSlot �ڳ����������۶ϡ�ϵͳ����ʱ�����¼��־ +* ... + Sentinel �� ProcessorSlot ��Ϊ SPI �ӿڽ�����չ��1.7.2 �汾��ǰ SlotChainBuilder ��ΪSPI����ʹ�� Slot Chain �߱�����չ�����������������м����Զ���� slot ������ slot ���˳�򣬴Ӷ����Ը� Sentinel �����Զ���Ĺ��ܡ� + +![SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d65c2688084bf5be06d3687ce8663cb1b7167b.jpg "SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������")**Spring Cloud ����Sentinel��ԭ��** + +Spring Cloud �м���Sentinel�������ǻ�����������ʵ�֣������ʵ��·�����¡� + +SentinelWebAutoConfiguration����>addInterceptors����>SentinelWebInterceptor->AbstractSentinelInterceptor + + + +``` +public boolean preHandle(HttpServletRequest request, HttpServletResponseresponse, Object handler) throws Exception { try { String resourceName = this.getResourceName(request); if (StringUtil.isEmpty(resourceName)) { return true; } else if (this.increaseReferece(request,this.baseWebMvcConfig.getRequestRefName(), 1) != 1) { return true; } else { String origin = this.parseOrigin(request); String contextName = this.getContextName(request); ContextUtil.enter(contextName, origin); Entry entry = SphU.entry(resourceName, 1, EntryType.IN); request.setAttribute(this.baseWebMvcConfig.getRequestAttributeName(), entry); return true; } } catch (BlockException var12) { BlockException e = var12; try { this.handleBlockException(request, response, e); } finally { ContextUtil.exit(); } return false; }} +``` + + + + + + + + + +> ��Դ���õ��������ͣ������������ EntryType.IN �����dz��������� EntryType.OUT ����ע��ϵͳ����ֻ�� IN ��Ч + +**��2�� SphU.entry** +�����Ǽ���dubboҲ�ã����Ǽ��ɵ�spring cloud��Ҳ�ã����ն��ǵ���SphU.entry������������������жϵģ����������Ǵ�SphU.entry���������ȥ�˽�����ʵ��ԭ���� + +���������ǿ���Ψһ�ɻ�ģ�Ҳ����ؼ���һ���� SphU.entry(resource) �� ���Ǵ���ȥ��һ����Դ�������Դ�����Ƿ������������ǽӿڣ���ô����������ʲô�أ���������һ�����ҿ�����������ɴ�� + + + +``` +public static Entry entry(String name) throws BlockException { return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0);}public class Env { public static final Sph sph = new CtSph(); ......//ʡ�Բ��ִ���} +``` + + + + + + + + + +�� SphU.entry() ��������ִ�л���뵽 Sph.entry() ��Sph��Ĭ��ʵ������ CtSph,�����ջ����CtSph ��entry ������ + + + +``` +@Overridepublic Entry entry(String name, EntryType type, int count, Object... args) throws BlockException {���� //��װ��һ����Դ���� StringResourceWrapper resource = new StringResourceWrapper(name, type); return entry(resource, count, args);} +``` + + + + + + + + + +�������Ҫ������ͨ�����Ǹ�������Դȥ��װ��һ�� StringResourceWrapper ��Ȼ�����Լ������ط������̶����� entryWithPriority(resourceWrapper, count, false, args)�� + +* ResourceWrapper ��ʾsentinel����Դ�����˷�װ +* count��ʾ���������ռ�õIJ���������Ĭ����1 +* prioritized�����ȼ� + + + +``` +private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count,boolean prioritized, Object... args) throws BlockException { //��ȡ�����Ļ������洢��ThreadLocal�У�context�л�洢���������� Context context = ContextUtil.getContext(); //����� NullContext����ô˵�� context name ������ 2000 �����μ� ContextUtil#trueEnter //���ʱ��Sentinel ���ٽ��ܴ����µ� context ���ã�Ҳ���Dz�����Щ�µĽӿڵ�ͳ�ơ������۶ϵ� if (context instanceof NullContext) { // The {@link NullContext} indicates that the amount of context has exceeded the threshold, // so here init the entry only. No rule checking will be done. return new CtEntry(resourceWrapper, null, context); } if (context == null) {//ʹ��Ĭ��context // ����Context�IJ��� context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME); } // Global switch is close, no rule checking will do. if (!Constants.ON) {//ȫ�����������Ƿ��Ѿ�����������ر��ˣ��Ͳ��������������� return new CtEntry(resourceWrapper, null, context); } //���ģʽ�е�������ģʽ�� //����һ��slot���� ProcessorSlot chain = lookProcessChain(resourceWrapper); //���� lookProcessChain ����������֪������ resource ���� Constants.MAX_SLOT_CHAIN_SIZE�� // Ҳ���� 6000 ��ʱ��Sentinel ��ʼ�������µ�������ô����Ҫ��Ϊ�� Sentinel �����ܿ��� if (chain == null) { return new CtEntry(resourceWrapper, null, context); } //���������������ʼ�����ɸ�entry Entry e = new CtEntry(resourceWrapper, chain, context); try { //��ʼ����������� chain.entry(context, resourceWrapper, null, count, prioritized, args); } catch (BlockException e1) { e.exit(count, args); //���������׳��쳣�� throw e1; } catch (Throwable e1) { // This should not happen, unless there are errors existing in Sentinel internal. RecordLog.info("Sentinel unexpected exception", e1); } return e;//���������Ľ��} +``` + + + + + + + + + +������Ĵ������ǿ���֪�����÷�������Ҫ�ǻ�ȡ���˱���Դ����Ӧ����Դ���������������� lookProcessChain �з��֣�����ȥ��ȡ��һ����������ȥִ����Դ�����ϴ�������Ȼ�����ﴦ�������Ļ����£���ô����������϶��Ƕ��ڵ�ǰ�������������������������صĴ��������Է�Ϊ���¼������֣� + +* �Բ�ȫ������������⣬���������Ҫ���ֱ�ӷ�����һ��CtEntry���󣬲����ٽ��к����������⣬�����������ļ�����̡����ݰ�װ������Դ�����ȡ��Ӧ��SlotChain +* ִ��SlotChain��entry���������SlotChain��entry�����׳���BlockException���򽫸��쳣���������׳������SlotChain��entry��������ִ���ˣ������Ὣ��entry���󷵻� +* ����ϲ㷽��������BlockException����˵�����������ˣ���������������ִ�� + **2.1 ����Context** + InternalContextUtil.internalEnter--->trueEnter + + + +``` +protected static Context trueEnter(String name, String origin) { //��ThreadLocal�л�ȡ����һ�ο϶���null Context context = contextHolder.get(); if (context == null) { //�����Ǹ���Context�����ֻ�ȡNode Map localCacheNameMap = contextNameNodeMap; DefaultNode node = localCacheNameMap.get(name); if (node == null) { if (localCacheNameMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) { setNullContext(); return NULL_CONTEXT; } else { LOCK.lock(); try { node = contextNameNodeMap.get(name); if (node == null) { if (contextNameNodeMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) { setNullContext(); return NULL_CONTEXT; } else { //������EntranceNode node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null); //����ȫ�ֵĽڵ� // Add entrance node. Constants.ROOT.addChild(node);//����map�� Map newMap = new HashMap<>(contextNameNodeMap.size() + 1); newMap.putAll(contextNameNodeMap); newMap.put(name, node); contextNameNodeMap = newMap; } } } finally { LOCK.unlock(); } } } context = new Context(node, name); context.setOrigin(origin); //����ThreadLocal�� contextHolder.set(context); } return context;} +``` + + + + + + + + + +������߼����DZȽϼ򵥵� + +* ������ThreadLocal��ȡ����ȡ�����ʹ�������Ȼ�ͷ��� +* Ȼ����Map�и���ContextName��һ��Node +* û���ҵ�Node�ͼ����ķ�ʽ������һ��EntranceNode��Ȼ�����Map�� +* ����Context������node��name��origin���ٷ���ThreadLocal�� + ����Context�ʹ������ + +ĿǰContext�����״̬����ͼ![SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/a114ee154527b7fcf24169d5291c7bac87ac93.png "SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������")**2.2 ����slot��** +����һ��slot������·�����Ϊ + +> DefaultProcessorSlotChain -> NodeSelectorSlot -> ClusterBuilderSlot -> LogSlot ->StatisticSlot -> AuthoritySlot -> SystemSlot -> ParamFlowSlot -> FlowSlot -> DegradeSlot + + + +``` +ProcessorSlot lookProcessChain(ResourceWrapper resourceWrapper) { //���Կ�����chain��������Դ����Ϊkey����ͬ����Դ�϶��Dz�ͬchain�� ProcessorSlotChain chain = chainMap.get(resourceWrapper); if (chain == null) {////������spring(����bean) dubbo(˫�ؼ����)�����һ�ޣ����û������ synchronized (LOCK) { chain = chainMap.get(resourceWrapper); if (chain == null) { //chainMap��С����һ��ֵ��Ҳ����entry������С�����ˣ�һ��chain��Ӧһ��entry if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) { return null; } //����һ��slot chain chain = SlotChainProvider.newSlotChain(); //������߼��ǣ��½�һ��Map��С��oldMap+1 Map newMap = new HashMap( chainMap.size() + 1); //Ȼ�����������oldMap���ٷ��½���chain newMap.putAll(chainMap); newMap.put(resourceWrapper, chain); //���ӵ�newMap�� ����Ӧ���ǿ��DZ���Ƶ������ chainMap = newMap; } } } return chain;} +``` + + + + + + + + + +����Ĵ�����������Է��֣����ȴӻ����л�ȡ�ô�����������һ�ν����϶���û�еģ������������ SlotChainProvider ȥ���촦������������ɺ�����뻺���Ա��´�ʹ�ã� + + + +``` +public static ProcessorSlotChain newSlotChain() { if (slotChainBuilder != null) { return slotChainBuilder.build(); } // ����ͨ��spi����ȥ������������������Լ�����slot�Ļ�ֻ��Ҫ����SPI����ʵ��SlotChainBuilder�ӿھͺ� //SentinelĬ�ϵ�����sentinel-core���µ�META-INF.services�� slotChainBuilder = SpiLoader.loadFirstInstanceOrDefault(SlotChainBuilder.class, DefaultSlotChainBuilder.class); if (slotChainBuilder == null) { // Should not go through here. RecordLog.warn("[SlotChainProvider] Wrong state when resolving slot chain builder, using default"); slotChainBuilder = new DefaultSlotChainBuilder(); } else { RecordLog.info("[SlotChainProvider] Global slot chain builder resolved: " + slotChainBuilder.getClass().getCanonicalName()); } return slotChainBuilder.build();} +``` + + + + + + + + + +������������˶�ε�У�飬ȷ��builder ��Ϊ�գ�Ȼ��ͨ����ȥ��������������� + + + +``` +public class DefaultSlotChainBuilder implements SlotChainBuilder { @Override public ProcessorSlotChain build() { ProcessorSlotChain chain = new DefaultProcessorSlotChain(); chain.addLast(new NodeSelectorSlot()); chain.addLast(new ClusterBuilderSlot()); chain.addLast(new LogSlot()); chain.addLast(new StatisticSlot()); chain.addLast(new SystemSlot()); chain.addLast(new AuthoritySlot()); chain.addLast(new FlowSlot()); chain.addLast(new DegradeSlot()); return chain; }} +``` + + + + + + + + + +���������������ڷ����������������������������Ҳ�ж������˵�����Ͼ���Sentinel�����������㷨��ʵ�ָ��أ����ǿ�һ�¹����Ľ��ܣ� + +�� Sentinel ���棬���е���Դ����Ӧһ����Դ���ƣ�resourceName����ÿ����Դ���ö��ᴴ��һ�� Entry ����Entry ����ͨ����������ܵ������Զ�������Ҳ����ͨ��ע��ķ�ʽ����� SphU API ��ʽ������Entry ������ʱ��ͬʱҲ�ᴴ��һϵ�й��ܲ�ۣ�slot chain������Щ����в�ͬ��ְ�𡣾���ְ���������Ѿ��ᵽ�ˡ� + +**��������** + +�����ִ���������¡� + +* NodeSelectorSlot����Ҫ���ڹ����������� + +* ClusterBuilderSlot�����ڼ�Ⱥ�������۶ϡ� + +* LogSlot�����ڼ�¼��־�� + +* StatisticSlot������ʵʱ�ռ�ʵʱ��Ϣ�� + +* AuthoritySlot������Ȩ��У��ġ� + +* SystemSlot��������֤ϵͳ����Ĺ��� + +* FlowSlot��ʵ���������ơ� + +* DegradeSlot��ʵ���۶ϻ��ơ� + **2.3 ����Entry** + + > Entry e = new CtEntry(resourceWrapper, chain, context); + + + +``` +CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot chain, Context context) { super(resourceWrapper); this.chain = chain; this.context = context; setUpEntryFor(context);}private void setUpEntryFor(Context context) { // The entry should not be associated to NullContext. if (context instanceof NullContext) { return; } this.parent = context.getCurEntry(); if (parent != null) { ((CtEntry) parent).child = this; } context.setCurEntry(this);} + +``` + + + + + + + + + +����һ��Entry���ɵ�ʱ��context.getCurEntry�ض���NULL����ôֱ��ִ��Context.setCurEntry���� + +Ȼ�����Context��״̬����ͼ ![SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/57273a794d51cfc587c923de97943491e9da0b.jpg "SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������")��ִ��һ���µ�Sphu.entry����ٴ��½�һ��Entry�����ʱ��curEntry����null����ôִ��((CtEntry)parent).child = this; + +�������ͼ![SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c3c36f101ef031102dd270287bf7635e715229.jpg "SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������")���Կ�����ԭ����CtEntry���Ƴ�Context���½���CtEntry�;�CtEntryͨ���ڲ���parent��child�������� + +**2.4 NodeSelectorSlot** +�������Ҫ���ڹ����������������Ҫ����һ�£��ں��������л�ȽϹؼ����������¡� + + + +``` +@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args) throws Throwable { //�����и����棬����context�����ֻ���node DefaultNode node = map.get(context.getName()); //˫�ؼ�⣬�̰߳�ȫ if (node == null) { synchronized (this) { node = map.get(context.getName()); if (node == null) { //�������ɵ���DefaultNode�ڵ� node = new DefaultNode(resourceWrapper, null); //������Щ�߼��Ƿ���map���߼�����Ϊ����map�Ƚϴ������������룬���ܻ��һЩ HashMap cacheMap = new HashMap(map.size()); cacheMap.putAll(map); cacheMap.put(context.getName(), node); map = cacheMap; // �ؼ����⣬�����޸ĵ��������ĵط� ((DefaultNode) context.getLastNode()).addChild(node); } } } //�滻context�е�curEntry�е�curNode context.setCurNode(node); fireEntry(context, resourceWrapper, node, count, prioritized, args);} +``` + + + + + + + + + +��ѯ�������Ƿ������node������߼�Ҳ�ܼ� + +* ����ContextName��ѯ�����Ƿ������Node +* û�о��������DefaultNode�����뻺�棬Ȼ����������� +* Context��curEntry�е�curnode����Ϊ���node + �����м�������Ҫ����˵���� + +1��context����ʾ�����ģ�һ���̶߳�Ӧһ��context�����а���һЩ�������� + +* name������ +* entranceNode����������� +* curEntry����ǰentry +* origin����������Դ +* async���첽 + 2��Node�� ��ʾһ���ڵ㣬����ڵ�ᱣ��ij����Դ�ĸ���ʵʱͳ�����ݣ�ͨ������ij���ڵ㣬�Ϳ��Ի�ö�Ӧ��Դ��ʵʱ״̬�����������Ϣ�����������ͽ��������м��ֽڵ����� +* StatisticNode��ʵ����Node�ӿڣ���װ�˻���������ͳ�ƺͻ�ȡ���� +* DefaultNode��Ĭ�Ͻڵ㣬NodeSelectorSlot�д����ľ�������ڵ㣻����ͬ����Դ�ڲ�ͬ�������и��Ե�������� +* ClusterNode����Ⱥ�ڵ㣬����ͬ����Դ�ڲ�ͬ�������������������� +* EntranceNode���ýڵ��ʾһ�õ�����������ڽڵ㣬ͨ�������Ի�ȡ�������������е��ӽڵ㣻ÿ�������Ķ�����һ����ڽڵ㣬����ͳ�Ƶ�ǰ�����ĵ������������ +* OriginNode����һ��StatisticNode���͵Ľڵ㣬������ͬ����Դ������Դ��������� + **2.5 StatisticSlot** + ������slot��·�У��Ƚ���Ҫ�ģ�������������ͳ���Լ�����������������slot��������������һ��StatisticSlot������� + +StatisticSlot�� Sentinel �ĺ��Ĺ��ܲ��֮һ������ͳ��ʵʱ�ĵ������ݡ� + +* clusterNode����ԴΨһ��ʶ�� ClusterNode �� runtime ͳ�� +* origin���������Բ�ͬ�����ߵ�ͳ����Ϣ +* defaultnode: ������������Ŀ���ƺ���Դ ID �� runtime ͳ�� +* ��ڵ�ͳ�� + + + +``` +public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { try { // �Ƚ��ɺ���������&������processorSlot������Ȼ����ݴ����������ͳ�� // Sentinel�������ľ�������ʹ�� for ѭ���������� ProcessorSlot ��ԭ�� fireEntry(context, resourceWrapper, node, count, prioritized, args); //ִ�е������ʾͨ����飬�������� // Request passed, add thread count and pass count. node.increaseThreadNum(); //��ǰ�ڵ�������߳�����1 node.addPassRequest(count); //��Բ�ͬ���͵�node��¼�߳�����������ͨ��������ͳ�ơ� if (context.getCurEntry().getOriginNode() != null) { // Add count for origin node. context.getCurEntry().getOriginNode().increaseThreadNum(); context.getCurEntry().getOriginNode().addPassRequest(count); } if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseThreadNum(); Constants.ENTRY_NODE.addPassRequest(count); } //�ɵ��� StatisticSlotCallbackRegistry#addEntryCallback ��̬����ע��ProcessorSlotEntryCallback for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onPass(context, resourceWrapper, node, count, args); } //���ȼ��ȴ��쳣�������FlowRule�л����漰���� } catch (PriorityWaitException ex) {//�����߳�ͳ�� node.increaseThreadNum(); if (context.getCurEntry().getOriginNode() != null) { // Add count for origin node. context.getCurEntry().getOriginNode().increaseThreadNum(); } if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseThreadNum(); } // Handle pass event with registered entry callback handlers. for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onPass(context, resourceWrapper, node, count, args); } } catch (BlockException e) { // Blocked, set block exception to current entry. context.getCurEntry().setBlockError(e); //���������쳣����ǰentry�� // Add block count. node.increaseBlockQps(count); //���ӱ����������� //���ݲ�ͬNode�����������������Ĵ��� if (context.getCurEntry().getOriginNode() != null) { context.getCurEntry().getOriginNode().increaseBlockQps(count); } if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseBlockQps(count); } // Handle block event with registered entry callback handlers. for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onBlocked(e, context, resourceWrapper, node, count, args); } throw e; } catch (Throwable e) { // Unexpected internal error, set error to current entry. context.getCurEntry().setError(e); throw e; }} +``` + + + + + + + + + +����ֳ��������֣���һ������entry�������÷������Ȼᴥ������slot��entry��������SystemSlot��FlowSlot��DegradeSlot�ȵĹ����������ͨ�����ͻ��׳�BlockException�������node��ͳ�Ʊ�block����������֮����node��ͳ��ͨ�������������߳�������Ϣ���ڶ���������exit�����У����˳���Entry���ʱ����ͳ��rt��ʱ�䣬�������߳����� + +���ǿ��Կ��� node.addPassRequest() ��δ�������fireEntryִ��֮��ִ�еģ�����ζ�ţ���ǰ����ͨ����sentinel�����صȹ��򣬴�ʱ��Ҫ�����������¼������Ҳ����ִ�� node.addPassRequest()���д��룬���Ǹ���ȥ������ + +**2.5.1 addPassRequest** +@Overridepublic void addPassRequest(int count) { // ���ø��ࣨStatisticNode��������ͳ�� super.addPassRequest(count); // ����clusterNode ����ͳ�ƣ�����Ҳ�ǵ��ø���StatisticNode�� this.clusterNode.addPassRequest(count);} +��������֪�������node��һ�� DefaultNode ʵ�����ڵ�һ��NodeSelectorSlot ��entry�����ж���Դ�����˷�װ����װ����һ��DefaultNode�� + +* DefaultNode��������ij��resource��ij��context�е�ʵʱָ�꣬ÿ��DefaultNode��ָ��һ��ClusterNode +* ClusterNode��������ij��resource�����е�context��ʵʱָ����ܺͣ�ͬ����resource�Ṳ��ͬһ��ClusterNode�����������ĸ�context�� + �ֱ��������ʱ�䴰������������������ + +�ڲ�ʵ�ʵ��õ���ArrayMetric����������������ͳ�� + + + +``` +//��������ͳ�ƣ��ֳ��������ڣ�ÿ������500ms������ͳ��QPSprivate transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL);//���շ���ͳ�ƣ�����60�����ڣ�ÿ������1000msprivate transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000, false);public void addPassRequest(int count) { rollingCounterInSecond.addPass(count); rollingCounterInMinute.addPass(count);} +``` + + + + + + + + + +������õ��ǻ������ڵķ�ʽ����¼����Ĵ�����![SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e4624bb214fd8020d1447098c8cde98a554e59.jpg "SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������")������Ĺ�ϵͼʵ�����DZȽ������ģ�ArrayMetricʵ������һ����װ�࣬�ڲ�ͨ��LeapArray��ʵ�־����ͳ���߼�����LeapArray��ά���˶��WindowWrap���������ڣ�����WindowWrap�в�����MetricBucket������ָ�����ݵ�ͳ�ơ� + +* Metric: ָ���ռ��Ľӿڣ����廬�������гɹ��������쳣����������������TPS����Ӧʱ������� +* ArrayMetric �������ں���ʵ���� +* LeapArray +* WindowWrap ÿһ���������ڵİ�װ�࣬�ڲ������ݽṹ����MetricBucket +* MetricBucket�� ��ʾָ��Ͱ�����������������쳣�������ɹ�������Ӧʱ��� +* MetricEvent ָ�����ͣ�ͨ���������������쳣�����ɹ����� + **2.5.2 ArrayMetric.addPass** + �������Ŵ������¿������뵽ArrayMetric.addPass������ +* ��LeapArray�и��ݵ�ǰʱ���õ���Ӧ�Ĵ��� +* ����MetricBucket�е�addPass���������ӵ�ǰ�����е�ͳ�ƴ��� + +�Ӵ��������ǿ��Կ���������ָ����� addPass ��ͨ��һ���� ArrayMetric ���࣬���������ڽ��� ArrayMetric �п�һ�¡�����Ĵ���������ʾ�� + + + +``` +private final LeapArray data;// SAMPLE_COUNT=2 INTERVAL=1000public ArrayMetric(int sampleCount, int intervalInMs) { //������������ʾ���������ڵĴ�С��2����ÿһ���������ڵ�ʱ�䵥λ��500ms this.data = new OccupiableBucketLeapArray(sampleCount, intervalInMs);}public void addPass(int count) { WindowWrap wrap = data.currentWindow(); wrap.value().addPass(count);} +``` + + + + + + + + + +�������ڳ������뻬����������ô������� window�ˣ�window�����Ǵ��������ͨ�� data ����ȡ��ǰ���ڡ�������Ĵ��ڴ�СΪ sampleCount=2.���ǿ��Կ�����������ͨ�� MetricBucket ���������ָ�꣬����ά����һ��ͳ��������LongAdder[] counters �����棬�� WindowWrap�����ǿ��Կ���ÿһ�� WindowWrap����������������ɣ� + + + +``` +public class WindowWrap {����// ʱ�䴰�ڵij��� private final long windowLengthInMs;����// ʱ�䴰�ڵĿ�ʼʱ�䣬��λ�Ǻ��� private long windowStart;���� //ʱ�䴰�ڵ����ݣ��� WindowWrap �����÷��ͱ�ʾ���ֵ�ģ���ʵ���Ͼ��� MetricBucket �� private T value; //......ʡ�Բ��ִ���} +``` + + + + + + + + + +�ٿ� LeapArray ����ࣺ + + + +``` +public abstract class LeapArray { // ʱ�䴰�ڵij��� protected int windowLength; // �������ڵĸ��� protected int sampleCount; // �Ժ���Ϊ��λ��ʱ���� protected int intervalInMs; // ������ʱ�䴰������ protected AtomicReferenceArray> array; /** * LeapArray���� * @param windowLength ʱ�䴰�ڵij��ȣ���λ������ * @param intervalInSec ͳ�Ƶļ������λ���� */ public LeapArray(int windowLength, int intervalInSec) { this.windowLength = windowLength; // ʱ�䴰�ڵIJ���������Ĭ��Ϊ2���������� this.sampleCount = intervalInSec * 1000 / windowLength; this.intervalInMs = intervalInSec * 1000;//������Ϊ��λ��ʱ�䴰���У����ʼ���������ȵ����飺`AtomicReferenceArray>array`����������ʾ�������ڵĴ�С�����У�ÿ�����ڻ�ռ��500ms��ʱ�䡣 this.array = new AtomicReferenceArray>(sampleCount); }} +``` + + + + + + + + + +���Ժ������Ŀ������� LeapArray �д�����һ�� AtomicReferenceArray ���飬������ʱ�䴰���е�ͳ��ֵ���в�����ͨ��������ͳ��ֵ�ټ����ƽ��ֵ������������Ҫ�����յ�ʵʱָ���ֵ�ˡ����Կ�����������Ĵ�����ͨ��ע�ͣ�������Ĭ�ϲ�����ʱ�䴰�ڵĸ�����2�������ֵ����ô�õ����أ����ǻ���һ�� LeapArray ���󴴽�����ͨ���� StatisticNode �У�new��һ�� ArrayMetric��Ȼ�󽫲���һ·���ϴ��ݺ󴴽��ģ� + + + +``` +private transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT,IntervalProperty.INTERVAL); +``` + + + + + + + + + +**2.5.3 currentWindow** +���Ǹ�����ȡ��ǰ���ڵķ��� data.currentWindow() �У� + + + +``` +@Overridepublic WindowWrap currentWindow(long time) { .....//ʡ�Բ��ִ��� //���㵱ǰʱ���ڻ��������е����������㷽ʽ�Ƚϼ򵥣���ǰʱ����Ե���ʱ�䴰�ڵ�ʱ�䳤�ȣ��ٴ�����ʱ�䴰�ڳ��Ƚ���ȡģ���� int idx = calculateTimeIdx(timeMillis); //���㵱ǰʱ����ʱ�䴰���еĿ�ʼʱ�� long windowStart = calculateWindowStart(timeMillis); // timeÿ����һ��windowLength�ij��ȣ�timeId�ͻ�����1��ʱ�䴰�ھͻ���ǰ����һ�� while (true) { // �Ӳ��������и���������ȡ�����ʱ�䴰�� WindowWrap old = array.get(idx); // array���鳤�Ȳ��˹��󣬷���old�ܶ�����¶����в��ˣ��ͻᴴ���ܶ��WindowWrap���� //���Ϊ�գ�˵���˴���δ��ʼ�� if (old == null) { // ���û�л�ȡ�����򴴽�һ���µ� WindowWrap window = new WindowWrap(windowLength, currentWindowStart, new Window()); // ͨ��CAS���´������õ�������ȥ if (array.compareAndSet(idx, null, window)) { // ��������óɹ����򽫸ô��ڷ��� return window; } else { // ����ǰ�߳��ó�ʱ��Ƭ���ȴ� Thread.yield(); } // �����ǰ���ڵĿ�ʼʱ����old�Ŀ�ʼʱ����ȣ���ֱ�ӷ���old���� } else if (currentWindowStart == old.windowStart()) { return old; // �����ǰʱ�䴰�ڵĿ�ʼʱ���Ѿ�������old���ڵĿ�ʼʱ�䣬�����old���� // ����time����Ϊ�µ�ʱ�䴰�ڵĿ�ʼʱ�䣬��ʱ������ǰ���� } else if (currentWindowStart > old.windowStart()) { if (addLock.tryLock()) { try { // if (old is deprecated) then [LOCK] resetTo currentTime. return resetWindowTo(old, currentWindowStart); } finally { addLock.unlock(); } } else { Thread.yield(); } // ������������ܴ��� } else if (currentWindowStart < old.windowStart()) { // Cannot go through here. return new WindowWrap(windowLength, currentWindowStart, new Window()); } }} +``` + + + + + + + + + +����ܳ��������𲽽���ֽ⣬����ʵ�ʿ��԰����ֳ����¼����� + +1. ���ݵ�ǰʱ�䣬�����ʱ���timeId��������timeId�����ǰ�����ڲ������������е�����idx�� +2. ���ݵ�ǰʱ�������ǰ���ڵ�Ӧ�ö�Ӧ�Ŀ�ʼʱ��time���Ժ���Ϊ��λ�� +3. ��������idx���ڲ�������������ȡ��һ��ʱ�䴰�ڡ� +4. ѭ���ж�ֱ����ȡ��һ����ǰʱ�䴰�� old �� + +* ���oldΪ�գ��򴴽�һ��ʱ�䴰�ڣ����������뵽array�ĵ�idx��λ�ã�array�����Ѿ��������ˣ���һ�� AtomicReferenceArray�� +* �����ǰ���ڵĿ�ʼʱ��time��old�Ŀ�ʼʱ����ȣ���ô˵��old���ǵ�ǰʱ�䴰�ڣ�ֱ�ӷ���old�� +* �����ǰ���ڵĿ�ʼʱ��time����old�Ŀ�ʼʱ�䣬��˵��old�����Ѿ���ʱ�ˣ���old�Ŀ�ʼʱ�����Ϊ����ֵ��time��������һ�ε�ѭ�����жϵ�ǰ���ڵĿ�ʼʱ��time��old�Ŀ�ʼʱ����ȵ�ʱ�򷵻ء� +* �����ǰ���ڵĿ�ʼʱ��timeС��old�Ŀ�ʼʱ�䣬ʵ������������Dz����ܴ��ڵģ���Ϊtime�ǵ�ǰʱ�䣬old�ǹ�ȥ��һ��ʱ�䡣 + ����timeId�ǻ�����ʱ������������ӣ���ǰʱ��ÿ����һ��windowLength�ij��ȣ�timeId�ͼ�1������idx����������ֻ����0��1֮��任����Ϊarray����ij�����2��ֻ����������ʱ�䴰�ڡ�����ΪʲôĬ��ֻ�������������ڣ����˾�����Ϊsentinel�DZȽ������Ŀ�ܡ�ʱ�䴰���б����źܶ�ͳ�����ݣ����ʱ�䴰�ڹ���Ļ���һ�����ռ�ù����ڴ棬��һ����ʱ�䴰�ڹ������ζ��ʱ�䴰�ڵij��Ȼ��С�����ʱ�䴰�ڳ��ȱ�С���ͻᵼ��ʱ�䴰�ڹ���Ƶ���Ļ�����������һ�����еĵ�һ�����ڶ����� + + + +``` +private int calculateTimeIdx(/*@Valid*/ long timeMillis) { // timeÿ����һ��windowLength�ij��ȣ�timeId�ͻ�����1��ʱ�䴰�ھͻ���ǰ����һ�� long timeId = timeMillis / windowLengthInMs; // idx���ֳ�[0,arrayLength-1]�е�ijһ��������Ϊarray�����е����� return (int)(timeId % array.length());}protected long calculateWindowStart(/*@Valid*/ long timeMillis) { return timeMillis - timeMillis % windowLengthInMs;} +``` + + + + + + + + + +���ݵ�ǰʱ����� windowLength �õ�һ�� timeId(���500ms���������ֵ����һ�µ�),����timeId��ȡ�����ڵij��Ƚ���һ��ȡģ����ô��һ�������� 0��1����λ�õ�����һ����Ȼ����ݵ�ǰʱ�������ǰ���ڵ�Ӧ�ö�Ӧ�Ŀ�ʼʱ��time�����ڸոտ�ʼ��ʱ�� array �ǿյģ���ô����ȡ����oldӦ����null����ô���ᴴ��һ���µ�ʵ����������ͼ��һ�³�ʼ���� LeapArray�� + +��Ӧ���� currentWindow ������ 4.1 ����(����idx=0)��![SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/95b0b64926eae4d2753986af9c978da9980da0.jpg "SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������")����ȡ������null,��ô��ʼ��ʱ��arrays������ֻ��һ������(�����ǵ�һ��(idx=0)��Ҳ�����ǵڶ���(idx=1))��ÿ��ʱ�䴰�ڵij�����500ms�������ζ��ֻҪ��ǰʱ����ʱ�䴰�ڵIJ�ֵ��500ms֮�ڣ�ʱ�䴰�ھͲ�����ǰ���������磬���統ǰʱ���ߵ�300����500ʱ����ǰʱ�䴰����Ȼ����ͬ���Ǹ��� + +��Ӧ���� currentWindow ������ 4.2 ���裺![SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/85c047336a2c2186bcc7737a4c28da852cb603.jpg "SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������")ʱ�������ǰ�ߣ�������500msʱ��ʱ�䴰�ھͻ���ǰ��������һ������ʱ�ͻ���µ�ǰ���ڵĿ�ʼʱ��,ʱ�������ǰ�ߣ�ֻҪ������1000ms����ǰ���ڲ��ᷢ���仯�����д���ʵ���� resetWindowTo ������ + + + +``` +protected WindowWrap resetWindowTo(WindowWrap w, long time) { // Update the start time and reset value. // ����windowStart w.resetTo(time); MetricBucket borrowBucket = borrowArray.getWindowValue(time); if (borrowBucket != null) { w.value().reset(); w.value().addPass((int)borrowBucket.pass()); } else { w.value().reset(); } return w;} +``` + + + + + + + + + +��Ӧ���� currentWindow ������ 4.3 ����![SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/03d6e3886f95f5768e05969d7eb8307c26f381.jpg "SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������")��ʱ�������ǰ�ߣ���ǰʱ�䳬��1000msʱ���ͻ��ٴν�����һ��ʱ�䴰�ڣ���ʱarrays�����еĴ��ڽ�����һ��ʧЧ��������һ���µĴ��ڽ����滻��![SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/241926a53c2a5b06938844199046d31e593d62.jpg "SpringCloud Alibabaϵ�С���15Sentinelԭ������-��Դ������������")�Դ���������ʱ������ţ�ʱ�䴰��Ҳ�ڷ����仯���ڵ�ǰʱ����н�������󣬻ᱻͳ�Ƶ���ǰʱ���Ӧ��ʱ�䴰���У��ص�addpass �����У� + + + +``` +public void addPass(int count) { WindowWrap wrap = data.currentWindow(); wrap.value().addPass(count);} +``` + + + + + + + + + +��ȡ�������Ժ����뵽 wrap.value().addPass(count); QPS�����ӡ�������� wrap.value() �õ�����֮ǰ�ᵽ�� MetricBucket ���� Sentinel ��QPS������ݵ�ͳ�ƽ����ά���������� LongAdder[] �У����������ָ����������ʵ�����úõĹ������ƥ�䣬�鿴�Ƿ�������Ҳ���� StatisticSlot��entry �����е� fireEntry(context, resourceWrapper, node, count, prioritized, args); ��Ҫ�Ƚ��뵽 FlowSlot��entry���������������ˣ� + + + +``` +public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { checkFlow(resourceWrapper, context, node, count, prioritized); fireEntry(context, resourceWrapper, node, count, prioritized, args);} +``` + + + + + + + + + +���Կ��������и�����Ҫ�ķ��� checkFlow ����ȥ������ + + + +``` +public void checkFlow(Function> ruleProvider, ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException { if (ruleProvider == null || resource == null) { return; } Collection rules = ruleProvider.apply(resource.getName()); if (rules != null) { for (FlowRule rule : rules) { if (!canPassCheck(rule, context, node, count, prioritized)) { throw new FlowException(rule.getLimitApp(), rule); } } }} +``` + + + + + + + + + +������һ�ж�Ӧ�������ˣ������õ����������õ� FlowRule ѭ��ƥ����Դ�����������ˡ������Sentinel ������������ԭ�� + +**2.6 FlowRuleSlot** +��� slot ��Ҫ����Ԥ�����Դ��ͳ����Ϣ�����չ̶��Ĵ���������Ч�����һ����Դ��Ӧ�������߶������ع������������´������μ��飬ֱ��ȫ��ͨ��������һ��������ЧΪֹ: + +* ָ��Ӧ����Ч�Ĺ��򣬼���Ե��÷������ģ� +* ���÷�Ϊ other �Ĺ��� +* ���÷�Ϊ default �Ĺ��� + + + +``` +@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { checkFlow(resourceWrapper, context, node, count, prioritized); fireEntry(context, resourceWrapper, node, count, prioritized, args);} +``` + + + + + + + + + +**2.6.1 checkFlow** +���뵽FlowRuleChecker.checkFlow�����С� + +* ������Դ���ƣ��ҵ����������б� +* �����������Ϊ�գ���������򣬵���canPassCheck��������У�顣 + + + +``` +public void checkFlow(Function> ruleProvider,ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException { if (ruleProvider == null || resource == null) { return; } Collection rules = ruleProvider.apply(resource.getName()); if (rules != null) { for (FlowRule rule : rules) { if (!canPassCheck(rule, context, node, count, prioritized)) { throw new FlowException(rule.getLimitApp(), rule); } } }} +``` + + + + + + + + + +**2.6.2 canPassCheck** +�ж��Ƿ��Ǽ�Ⱥ����ģʽ������ǣ�����passClusterCheck�����򣬵���passLocalCheck������ + + + +``` +public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context,DefaultNode node, int acquireCount, boolean prioritized) { String limitApp = rule.getLimitApp(); if (limitApp == null) { return true; } if (rule.isClusterMode()) { return passClusterCheck(rule, context, node, acquireCount, prioritized); } return passLocalCheck(rule, context, node, acquireCount, prioritized);} +``` + + + + + + + + + +**2.6.3 passLocalCheck** + +* selectNodeByRequesterAndStrategy����������Ͳ��������Node +* rule.getRater(), ���ݲ�ͬ������������Ϊ��������canPass����У�顣 + + + +``` +private static boolean passLocalCheck(FlowRule rule, Context context,DefaultNode node, int acquireCount, boolean prioritized) { Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node); if (selectedNode == null) { return true; } return rule.getRater().canPass(selectedNode, acquireCount, prioritized);} +``` + + + + + + + + + +**2.6.4 DefaultController.canPass** +ͨ��Ĭ�ϵ�������Ϊ��ֱ�Ӿܾ��������������жϡ� + + + +``` +@Overridepublic boolean canPass(Node node, int acquireCount, boolean prioritized) { //�ȸ���node��ȡ��Դ��ǰ��ʹ����������������qps���߲����������������ص�ֵ int curCount = avgUsedTokens(node); //��ǰ��ʹ�õ����������ϱ�������������Ƿ������ֵ if (curCount + acquireCount > count) {//���Ϊtrue��˵��Ӧ�ñ����� // �����������һ�������ȼ����󣬲�����������Ϊqps���򲻻�����ʧ�ܣ�����ȥռ��δ����ʱ�䴰�ڣ��ȵ���һ��ʱ�䴰��ͨ������ if (prioritized && grade == RuleConstant.FLOW_GRADE_QPS) { // long currentTime; long waitInMs; currentTime = TimeUtil.currentTimeMillis(); waitInMs = node.tryOccupyNext(currentTime, acquireCount, count); if (waitInMs < OccupyTimeoutProperty.getOccupyTimeout()) { node.addWaitingRequest(currentTime + waitInMs, acquireCount); node.addOccupiedPass(acquireCount); sleep(waitInMs); // PriorityWaitException indicates that the request will pass after waiting for {@link @waitInMs}. throw new PriorityWaitException(waitInMs); } } return false; } return true;} +``` + + + + + + + + + +һ�����ܾ������׳� FlowException �쳣�� + +**2.7 PriorityWait** +��DefaultController.canPass�У��������´���ȥ������Ĵ��� + + + +``` +node.addWaitingRequest(currentTime + waitInMs, acquireCount);node.addOccupiedPass(acquireCount); +``` + + + + + + + + + +> addWaitingRequest -> ArrayMetric.addWaiting->OccupiableBucketLeapArray.addWaiting + +borrowArray������һ��FutureBucketLeapArray�������ﶨ�����δ����ʱ�䴰�ڣ�Ȼ����δ��ʱ��Ĵ���ȥ���Ӽ��� + + + +``` +@Overridepublic void addWaiting(long time, int acquireCount) { WindowWrap window = borrowArray.currentWindow(time); window.value().add(MetricEvent.PASS, acquireCount);} +``` + + + + + + + + + +���գ���StatisticSlot.entry�У������쳣 + +����������ȼ��Ƚϸߵ����񣬲��ҵ�ǰ�������Ѿ��ﵽ��ֵ���׳�����쳣��ʵ������ȥռ��δ����һ��ʱ�䴰��ȥ���м������׳�����쳣֮�󣬻���뵽StatisticSlot�н��в���Ȼ��ֱ��ͨ�� + + + +``` +public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { try{ //... } catch (PriorityWaitException ex) { node.increaseThreadNum(); if (context.getCurEntry().getOriginNode() != null) { // Add count for origin node. context.getCurEntry().getOriginNode().increaseThreadNum(); } if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseThreadNum(); } // Handle pass event with registered entry callback handlers. for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onPass(context, resourceWrapper, node, count, args); } }} +``` + + + 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/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\345\217\221\347\216\260.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/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\345\217\221\347\216\260.md" new file mode 100644 index 0000000..af691d6 --- /dev/null +++ "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/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\345\217\221\347\216\260.md" @@ -0,0 +1,469 @@ +# һ��Nacos����������ͼ + +![���������ͼƬ����](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b5b4ef0330dc4882b0fc2f73994face7.png "���������ͼƬ����") + +�������Լ�����һ�����̣�Ҳ���Բο���[Nacos����ע��Դ���������ͼ](https://blog.csdn.net/Saintmm/article/details/121981184) + +# ������Դ����� + +spring-cloud-commons���ж�����һ�׷����ֵĹ淶�������߼���`DiscoveryClient`�ӿ��У� +![���������ͼƬ����](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/75ebfd400023456faaafd95e7d9cdbf7.png "���������ͼƬ����") +����Spring Cloudʵ�ַ����ֵ��������ʵ��`DiscoveryClient`�ӿڣ�nacos-discovery���µ�`NacosDiscoveryClient`��ʵ��`DiscoveryClient`�ӿڡ� +![���������ͼƬ����](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0c330a7ce5c744c4a2b25dc024b2430f.png "���������ͼƬ����") + +# �����ͻ��˷����� + +> 1����nacos�ͻ�����?����֮����ֻ��ȥ������ע�ᡢ���û�ȡ�Ȳ���������������ȥ���������Ϣ�� +> 2������һ������ʱ�򣬲Ż�ȥ��ȡ���񣬼�`�����ػ���`�� + +#### 1���ȴӱ��ػ���serviceInfoMap�л�ȡ����ʵ����Ϣ����ȡ������ͨ��`NamingProxy`����Nacos ����˻�ȡ����ʵ����Ϣ���������ʱ����ÿ���������� ��ȡʵ����Ϣ�б��������±��ػ���serviceInfoMap�� + +``` +// NacosDiscoveryClient#getInstances() +public List getInstances(String serviceId) { + try { + // ͨ��NacosNamingService��ȡ�����Ӧ��ʵ����Ϣ�����ȥ + List instances = discoveryProperties.namingServiceInstance() + .selectInstances(serviceId, true); + return hostToServiceInstanceList(instances, serviceId); + } catch (Exception e) { + throw new RuntimeException( + "Can not get hosts from nacos server. serviceId: " + serviceId, e); + } +} + +// NacosNamingService#selectInstances() +public List selectInstances(String serviceName, boolean healthy) throws NacosException { + return selectInstances(serviceName, new ArrayList(), healthy); +} +public List selectInstances(String serviceName, List clusters, boolean healthy) + throws NacosException { + // Ĭ���߶���ģʽ + return selectInstances(serviceName, clusters, healthy, true); +} +public List selectInstances(String serviceName, List clusters, boolean healthy, + boolean subscribe) throws NacosException { + // Ĭ�ϲ�ѯDEFAULT_GROUP�µķ���ʵ����Ϣ + return selectInstances(serviceName, Constants.DEFAULT_GROUP, clusters, healthy, subscribe); +} +public List selectInstances(String serviceName, String groupName, List clusters, boolean healthy, boolean subscribe) throws NacosException { + + ServiceInfo serviceInfo; + // Ĭ���߶���ģʽ����subscribeΪTRUE + if (subscribe) { + serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")); + } else { + serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")); + } + return selectInstances(serviceInfo, healthy); +} +``` + +`HostReactor#getServiceInfo()`������������ȡ����ʵ����Ϣ�ĵط��� + +``` +public ServiceInfo getServiceInfo(final String serviceName, final String clusters) { + + NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch()); + String key = ServiceInfo.getKey(serviceName, clusters); + if (failoverReactor.isFailoverSwitch()) { + return failoverReactor.getService(key); + } + + // 1���ӱ��ػ���serviceInfoMap�л�ȡʵ����Ϣ + ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters); + + // 2��������ػ�����û�У�����HTTP���ô�Nacos����˻�ȡ + if (null == serviceObj) { + serviceObj = new ServiceInfo(serviceName, clusters); + + serviceInfoMap.put(serviceObj.getKey(), serviceObj); + + updatingMap.put(serviceName, new Object()); + updateServiceNow(serviceName, clusters); + updatingMap.remove(serviceName); + + } else if (updatingMap.containsKey(serviceName)) { + + if (UPDATE_HOLD_INTERVAL > 0) { + // hold a moment waiting for update finish + synchronized (serviceObj) { + try { + serviceObj.wait(UPDATE_HOLD_INTERVAL); + } catch (InterruptedException e) { + NAMING_LOGGER.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e); + } + } + } + } + + // 3������һ����ʱ����ÿ��һ���Nacos����˻�ȡ���µķ���ʵ����Ϣ�����µ����ػ���seriveInfoMap�� + scheduleUpdateIfAbsent(serviceName, clusters); + + // 4�� �ӱ��ػ���serviceInfoMap�л�ȡ����ʵ����Ϣ + return serviceInfoMap.get(serviceObj.getKey()); +} +``` + +1���ӱ��ػ����л�ȡ����ʵ����Ϣ�� + +``` +private ServiceInfo getServiceInfo0(String serviceName, String clusters) { + + String key = ServiceInfo.getKey(serviceName, clusters); + + return serviceInfoMap.get(key); +} +``` + +2������HTTP���ô�Nacos����˻�ȡ����ʵ����Ϣ�� + +``` +public void updateServiceNow(String serviceName, String clusters) { + ServiceInfo oldService = getServiceInfo0(serviceName, clusters); + try { + + // ͨ��NamingProxy��HTTP�ӿڵ��ã���ȡ����ʵ����Ϣ + String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUDPPort(), false); + if (StringUtils.isNotEmpty(result)) { + // ���±��ػ���serviceInfoMap + processServiceJSON(result); + } + } catch (Exception e) { + NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e); + } finally { + if (oldService != null) { + synchronized (oldService) { + oldService.notifyAll(); + } + } + } +} +``` + +3������һ����ʱ����ÿ��һ���Nacos����˻�ȡ���µķ���ʵ����Ϣ�����µ����ػ���seriveInfoMap�У� + +``` +public void scheduleUpdateIfAbsent(String serviceName, String clusters) { + if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) { + return; + } + + synchronized (futureMap) { + if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) { + return; + } + + // ������ʱ���� + ScheduledFuture future = addTask(new UpdateTask(serviceName, clusters)); + futureMap.put(ServiceInfo.getKey(serviceName, clusters), future); + } +} + +// ��ʱ����ִ���߼���UpdateTask#run() +public void run() { + try { + ServiceInfo serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters)); + + if (serviceObj == null) { + updateServiceNow(serviceName, clusters); + executor.schedule(this, DEFAULT_DELAY, TimeUnit.MILLISECONDS); + return; + } + + if (serviceObj.getLastRefTime() <= lastRefTime) { + updateServiceNow(serviceName, clusters); + serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters)); + } else { + // if serviceName already updated by push, we should not override it + // since the push data may be different from pull through force push + refreshOnly(serviceName, clusters); + } + + // ����һ����ʱ����1s֮��ִ�� + executor.schedule(this, serviceObj.getCacheMillis(), TimeUnit.MILLISECONDS); + + lastRefTime = serviceObj.getLastRefTime(); + } catch (Throwable e) { + NAMING_LOGGER.warn("[NA] failed to update serviceName: " + serviceName, e); + } + +} +``` + +��ѯ����ʵ���б��� + +``` +public String queryList(String serviceName, String clusters, int udpPort, boolean healthyOnly) + throws NacosException { + + final Map params = new HashMap(8); + params.put(CommonParams.NAMESPACE_ID, namespaceId); + params.put(CommonParams.SERVICE_NAME, serviceName); + params.put("clusters", clusters); + params.put("udpPort", String.valueOf(udpPort)); + params.put("clientIP", NetUtils.localIP()); + params.put("healthyOnly", String.valueOf(healthyOnly)); + + return reqAPI(UtilAndComs.NACOS_URL_BASE + "/instance/list", params, HttpMethod.GET); +} +``` + +#### 2����HostReactorʵ������ʱ���ʵ����PushReceiver����������һ���߳���ѭ��ͨ��`DatagramSocket#receive()`����Nacos������з���ʵ����Ϣ����������UDP֪ͨ�� + +``` +public class PushReceiver implements Runnable { + private DatagramSocket udpSocket; + + public PushReceiver(HostReactor hostReactor) { + try { + this.hostReactor = hostReactor; + udpSocket = new DatagramSocket(); + // ����һ���߳� + executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("com.alibaba.nacos.naming.push.receiver"); + return thread; + } + }); + + executorService.execute(this); + } catch (Exception e) { + NAMING_LOGGER.error("[NA] init udp socket failed", e); + } + } + + public void run() { + while (true) { + try { + // byte[] is initialized with 0 full filled by default + byte[] buffer = new byte[UDP_MSS]; + DatagramPacket packet = new DatagramPacket(buffer, buffer.length); + + // ����Nacos����˷���ʵ����Ϣ������֪ͨ + udpSocket.receive(packet); + + String json = new String(IoUtils.tryDecompress(packet.getData()), "UTF-8").trim(); + NAMING_LOGGER.info("received push data: " + json + " from " + packet.getAddress().toString()); + + PushPacket pushPacket = JSON.parseObject(json, PushPacket.class); + String ack; + if ("dom".equals(pushPacket.type) || "service".equals(pushPacket.type)) { + hostReactor.processServiceJSON(pushPacket.data); + + // send ack to server + ack = "{\"type\": \"push-ack\"" + + ", \"lastRefTime\":\"" + pushPacket.lastRefTime + + "\", \"data\":" + "\"\"}"; + } else if ("dump".equals(pushPacket.type)) { + // dump data to server + ack = "{\"type\": \"dump-ack\"" + + ", \"lastRefTime\": \"" + pushPacket.lastRefTime + + "\", \"data\":" + "\"" + + StringUtils.escapeJavaScript(JSON.toJSONString(hostReactor.getServiceInfoMap())) + + "\"}"; + } else { + // do nothing send ack only + ack = "{\"type\": \"unknown-ack\"" + + ", \"lastRefTime\":\"" + pushPacket.lastRefTime + + "\", \"data\":" + "\"\"}"; + } + + udpSocket.send(new DatagramPacket(ack.getBytes(Charset.forName("UTF-8")), + ack.getBytes(Charset.forName("UTF-8")).length, packet.getSocketAddress())); + } catch (Exception e) { + NAMING_LOGGER.error("[NA] error while receiving push data", e); + } + } + } + +} +``` + +# �ġ�����˷����� + +Nacos����˵ķ�������Ҫ�������£� + +> 1����ѯ����ʵ���б����ȴӻ���serviceMap���ҵ�service��Ӧ��Cluster���ٴ�Cluster������Set��`persistentInstances`��`ephemeralInstances`��ȡȫ����ʵ����Ϣ�� +> 2�����ͻ��˴�����ip��udp�˿ںż����ӵ�`clientMap`���������������ͣ�clientMap����`NamingSubscriberService`��ʵ����`NamingSubscriberServiceV1Impl`����key��service name��value�Ƕ����˸÷���Ŀͻ����б�(ip+�˿ں�)�� + +��naming��Ŀ�µ� InstanceController���list()������ + +#### 1����ȡ����ʵ���б� + +``` +@GetMapping("/list") +@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ) +public Object list(HttpServletRequest request) throws Exception { + + String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); + String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); + NamingUtils.checkServiceNameFormat(serviceName); + + String agent = WebUtils.getUserAgent(request); + String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY); + String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY); + int udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0")); + boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false")); + + boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request, "isCheck", "false")); + + String app = WebUtils.optional(request, "app", StringUtils.EMPTY); + String env = WebUtils.optional(request, "env", StringUtils.EMPTY); + String tenant = WebUtils.optional(request, "tid", StringUtils.EMPTY); + + Subscriber subscriber = new Subscriber(clientIP + ":" + udpPort, agent, app, clientIP, namespaceId, serviceName, + udpPort, clusters); + // ��ȥInstanceOperatorServiceImpl#listInstance()������ȡ����ʵ���б� + return getInstanceOperator().listInstance(namespaceId, serviceName, subscriber, clusters, healthyOnly); +} + +//InstanceOperatorServiceImpl#listInstance() +public ServiceInfo listInstance(String namespaceId, String serviceName, Subscriber subscriber, String cluster, + boolean healthOnly) throws Exception { + ClientInfo clientInfo = new ClientInfo(subscriber.getAgent()); + String clientIP = subscriber.getIp(); + ServiceInfo result = new ServiceInfo(serviceName, cluster); + Service service = serviceManager.getService(namespaceId, serviceName); + long cacheMillis = switchDomain.getDefaultCacheMillis(); + + // now try to enable the push + try { + // �����������ͷ���UdpPushService��������ʵ����Ϣ�������ʱͨ��UDP�ķ�ʽ֪ͨNacos Client + if (subscriber.getPort() > 0 && pushService.canEnablePush(subscriber.getAgent())) { + subscriberServiceV1.addClient(namespaceId, serviceName, cluster, subscriber.getAgent(), + new InetSocketAddress(clientIP, subscriber.getPort()), pushDataSource, StringUtils.EMPTY, + StringUtils.EMPTY); + cacheMillis = switchDomain.getPushCacheMillis(serviceName); + } + } catch (Exception e) { + Loggers.SRV_LOG.error("[NACOS-API] failed to added push client {}, {}:{}", clientInfo, clientIP, + subscriber.getPort(), e); + cacheMillis = switchDomain.getDefaultCacheMillis(); + } + + if (service == null) { + if (Loggers.SRV_LOG.isDebugEnabled()) { + Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName); + } + result.setCacheMillis(cacheMillis); + return result; + } + + // �������Ƿ���� + checkIfDisabled(service); + + // �����ǻ�ȡ����ע����Ϣ�Ĺؼ����룬��ȡ�������ú���ʱ����ʵ�� + List srvedIps = service + .srvIPs(Arrays.asList(StringUtils.split(cluster, StringUtils.COMMA))); + + // filter ips using selector��ѡ�������˷��� + if (service.getSelector() != null && StringUtils.isNotBlank(clientIP)) { + srvedIps = selectorManager.select(service.getSelector(), clientIP, srvedIps); + } + + // ����Ҳ��������򷵻ص�ǰ���� + if (CollectionUtils.isEmpty(srvedIps)) { + ....... + return result; + } + +// Service#srvIPs() +public List srvIPs(List clusters) { + if (CollectionUtils.isEmpty(clusters)) { + clusters = new ArrayList<>(); + clusters.addAll(clusterMap.keySet()); + } + return allIPs(clusters); +} + +// Service#allIPs() +public List allIPs(List clusters) { + List result = new ArrayList<>(); + for (String cluster : clusters) { + // ����ע���ʱ�򣬻Ὣʵ����Ϣд��clusterMap�У����ڴ�����ȡ + Cluster clusterObj = clusterMap.get(cluster); + if (clusterObj == null) { + continue; + } + + result.addAll(clusterObj.allIPs()); + } + return result; +} + +// Cluster#allIPs() +public List allIPs() { + List allInstances = new ArrayList<>(); + // ��ȡ���������еij־û�ʵ�� + allInstances.addAll(persistentInstances); + // ��ȡ���������е���ʱʵ�� + allInstances.addAll(ephemeralInstances); + return allInstances; +} +``` + +#### 2������UDP��ʽ������ʵ������ + +NamingSubscriberServiceV1Impl#addClient()�� + +``` +public void addClient(String namespaceId, String serviceName, String clusters, String agent, + InetSocketAddress socketAddr, DataSource dataSource, String tenant, String app) { + + // ��ʼ�����Ϳͻ���ʵ��PushClient + PushClient client = new PushClient(namespaceId, serviceName, clusters, agent, socketAddr, dataSource, tenant, + app); + // ��������Ŀ��ͻ��� + addClient(client); +} + +// ���ط���addClient() +public void addClient(PushClient client) { + // client is stored by key 'serviceName' because notify event is driven by serviceName change + // �ͻ����ɼ��� serviceName���洢����Ϊ֪ͨ�¼���serviceName�������� + String serviceKey = UtilsAndCommons.assembleFullServiceName(client.getNamespaceId(), client.getServiceName()); + ConcurrentMap clients = clientMap.get(serviceKey); + // �����ȡ�����ͻ�������õ�ServiceName��Ӧ�����Ϳͻ��ˣ����½����Ϳͻ��ˣ������� + if (clients == null) { + clientMap.putIfAbsent(serviceKey, new ConcurrentHashMap<>(1024)); + clients = clientMap.get(serviceKey); + } + + PushClient oldClient = clients.get(client.toString()); + // �����ϵ�PushClient����ˢ�� + if (oldClient != null) { + oldClient.refresh(); + } else { + // ���򻺴�PushClient + PushClient res = clients.putIfAbsent(client.toString(), client); + if (res != null) { + Loggers.PUSH.warn("client: {} already associated with key {}", res.getAddrStr(), res); + } + Loggers.PUSH.debug("client: {} added for serviceName: {}", client.getAddrStr(), client.getServiceName()); + } +} +``` + +# �塢�ܽ� + +�ͻ��ˣ� + +> 1�����ȴӱ��ػ����л�ȡ����ʵ����Ϣ�� +> 2��ά����ʱ����ʱ��Nacos����˻�ȡ����ʵ����Ϣ�� + +����ˣ� + +> 1������ָ�������ռ����ڴ�ע��������е�����ʵ������ʱʵ�����ͻ��ˣ� +> 2������һ��UDP����ʵ����Ϣ������ͷ��� + + +# �ο����� +https://developer.aliyun.com/article/1058262 +https://ost.51cto.com/posts/14835 +https://developer.aliyun.com/article/1048465 +https://zhuanlan.zhihu.com/p/70478036 +https://juejin.cn/post/6999814668390760484#heading-8 \ 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/SpringCloudAlibaba nacos\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/SpringCloudAlibaba nacos\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" new file mode 100644 index 0000000..3569058 --- /dev/null +++ "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/SpringCloudAlibaba nacos\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" @@ -0,0 +1,349 @@ +# һ�� Nacos����ע��Դ����� + + + +* * * + + + +## 1.1 Դ�뷽ʽ��� + + + +* * * + + + +�ͻ���Դ�������Ӵ����ʽ����Դ�������� + + + + + + + +``` org.apache.maven.plugins maven-source-plugin 3.2.1 true compile jar ``` + + + + + + + +Ȼ������ + + + + + + + +```mvn install -DskipTests ``` + + + + + + + +## 1.2 ��� + + + +* * * + + + +[github.com/alibaba/nac��](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Falibaba%2Fnacos%2Ftree%2F1.4.1) + +�������ǻ��Դ���������������ǻ�ͨ��Դ��ķ�ʽ���������� �����ͨ��debug�ķ�ʽ�����������ж��������й��̡� + +���Ǵ�Դ��ĽǶ�������һ�£����������������ϱ���ע�����ģ� + +NacosNamingService ���Ƿ���ע��ͷ�����ص��࣬�����������ォ��ǰ�����ķ������ע��ʵ���ķ��������ǿ�һ�����������ʲô�ˣ� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/fc004433c7304147905c088dd3227005.png "image.png") + +![image-20211221124947544](E:\BaiduNetdiskWorkspace\springcloud alibaba\img\image-20211221124947544.png) + +������ƴ����һЩ��������http���󣬵������ע�����Ľ��з��֣�����ľ������·���� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/51f5b45b70ef4a7dbe1258e05314fa61.png "image.png") + +������Ƕ�Ӧ��·�������ǻص��ٷ��ĵ���ָ�ϵ��� + +[nacos.io/zh-cn/docs/��](https://link.juejin.cn?target=https%3A%2F%2Fnacos.io%2Fzh-cn%2Fdocs%2Fopen-api.html) + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d3b30ba715ab4363b3ee8b4e21a2f3a2.png "image.png") + +�ã��������ǽ�������Ͳ����������濴�ˣ����ǿ��Ե��ȥ��һ�£� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/9f4bbf69cc1746e79545831252796e55.png "image.png") + +�������ĵ��ø���������� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b06c94937d374ddb9fc2c224e5932205.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d0d55d3616ad4109837d9aee36e5a946.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/450565c4c14f4f3bb2097ab1ea69aae7.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/527d2af9bb0c4206b090d3410238a576.png "image.png") + +��������е��� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3f3f39de5aaa4ad2b3ac7dccc33aa574.png "image.png") + +������ͬѧ������ô֪���ϵ�ʹﵽ��������ǿ�һ�������鿴Դ���������·�������ǿ�һ�����Ƕ���΢�����·��������Ҫ����nacos�ķ����ֹ��ܣ�����Ҫ�������ǵ�discovery�İ�������һ��starter��ǰ������ѧ��springboot����֪���κ�starter����һ���и�spring.factories����Ϊһ����� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/be47c62f16f94507a1af84d0169ebf53.png "image.png") + +�����涯̬���ص���ܶ࣬NacosServiceRegistryAutoConfiguration �������������ܷ�������һ��nacos����ע����Զ������࣬ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/84017587439846a4b21a696f12775b6c.png "image.png") + +������ʵ���������࣬���ǿ�һ�������NacosAUtoServiceRegistration + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/fe7ee87e88944b689835432c833972c2.png "image.png") + +�Զ�ע���࣬���ǿ��Կ�һ�����ļ��ɹ�ϵ����һ��ApplicationListener spring������ɺ󶼻ᷢ��һ����Ϣ��applicaitonListener����ͨ�����������ϢȻ�����ִ�еġ���������֪����һ������Ӧ����ô���� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/2a12ba62dae24aa092f97b0cff5dfaaa.png "image.png") + +���Բ鿴���ij����ࡣ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1f9253dbbbd349ffb6d0e60d0761b3ec.png "image.png") + +�鿴onApplicationEvent�����������ڷ�������ͻᷢ������һ����Ϣ���յ������Ϣ�ͻ�������bind���� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/41a7323a2e2c49d19aaf5768e66af7d8.png "image.png") + +�����и�if return ���Ǿ�ֱ����������һ���Ƿ�֧���룬�������ķ�֧�������ǾͲ�Ҫ������һ ��Ҫ�����ߣ���������ֱ�ӿ������start�����������������û�ж�Ӧ�Ĵ����߼����ǿ��Խ��������֧������ �ã�������start, begin��init��register�������Ǻ���Ҫ�ķ���������һ��Ҫ��ȥ�� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3923ffd589e9488eb1143811a50d28c0.png "image.png") + +��һ��if�Ͳ��ÿ����ڶ���if��Ҫ������Ϊ�����û���߼����㿴���register()��Ӧ�þ��������������Ϊ����Dz鿴ע������̡� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d5b527e0900a4f1b9abcd76db4bfc138.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d3501fdf6479417180e881c38638bdac.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7c0ea798fcd7404d8f350047e947add1.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0ca3ab1a98f248a0aa3854730eba85fb.png "image.png") + +������Ҫ��֪��SpringBoot�Զ�װ��Ļ���֪ʶ�����Ҫ֪��Spring�������ֵ� ����֪ʶ�� + +## 1.3 ����ע�� + + + +* * * + + + +Nacos�߲���֧���첽�������ڴ�������� + +�ղ����ڷ����ṩ�����潲�����ݣ��������Ƿ���ע����������һ�� + +�������instanceʵ���������һ��springmvc��controller���������ǿ���ȫ������ Controller��������instanceʵ���ɣ�������������InstanceController + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4e02f16787a84704add5aa41f9d7662a.png "image.png") + +��֮ǰ�����õ���post�����������Dz鿴post������������delete,update...,��������ʲô��� restFul + +���Ƿ�������û�ж�Ӧ��DefaultGroup���ڷ���ע��ͷ��ֵ���������group�Dz������õġ����õĻ�ֻ���Լ��Ĺ淶�ͷ�������ġ��ڷ���ע��ͷ�����Դ���ж�û���á��������ռ䣬��������Ȼ�����Dz���ת��Ϊʵ����������Ƿ���ģ���е�����ģ�͡� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/09f059ba31a7444a88a24b03399d502d.png "image.png") + +�����ǿ�һ������ע��ʵ����������ʲô�� ����������ע��������ע��instance�����Ǿ�Χ�����������з������Dz��Ǿ�����addInstance�� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4b69913abe6f40288a08dee89ecb0772.png "image.png") + +createEmptyService + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b8a3063b3c924ee195af64d5d8efa647.png "image.png") + +1����ȡservice ���λ�ȡһ��Ϊ�գ����ǿ��Խ�ȥ����һ�� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/fdf453ebadee486c9def86560bc3b8de.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7025fb4edcc247b3aca0f1f851d26203.png "image.png") + +�������ע���������ǰ��˵��nacos��������ģ�͡����Բο�ͼ�������map���Ƕ�Ӧ��ע��� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4f1db03d3fcc4f93aaa17cc485fb59c2.png "image.png") + +���������÷���ͳ�ʼ�� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1af3a9015b2c4e1ab06da283e4dc5332.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/cb795dd33b114f84bc51cdbdca5af202.png "image.png") + +�����ʼ�������� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0d0583abf0184ed49bf5fd93c9effafe.png "image.png") + +�������Ǹ�scheduleNamingHealth��һ����ʱ��������ֻ��Ҫ��һ��task����Ϳ��� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/51ba8120a9954389b9e2f44d49532a14.png "image.png") + +task����������Ҫ��һ��run������ + +���������ǿ��ǻ�ȡ���е�ʵ�������Ե��ȥ��һ�¡� + +��ǰʱ�� - �ϴ�����ʱ�� �������15�� ��ʵ������Ϊ�ǽ����� ������30��û���յ�������ֱ���޳� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f9131eea860e410685d8c4cd5beea69e.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/586fd2fcb489414590295ce2dfafd91d.png "image.png") + +�ã����ǻ�����������������ر�ã�createEmtyService���Ǵ���һ���շ��񣬺������ǵ�ʵ�����Dz��ǻ���ע�ᵽ���棬 + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/9518406856b642298d3ee2f16e36bed0.png "image.png") + +> ���ǿ��Կ�һ�·���ģ�ͣ���������ǰ˵����һ�� +> +> �����ռ� ��cluster ��Ⱥ���� +> +> ![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0323827c8249417b8ab3fd6c4bd3e61e.png "image.png") +> +> ![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/044d8f58dc5b4c7e83dab05c62397566.png "image.png") +> +> ��Ⱥ�ж�Ӧ��ʵ���� +> +> ![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/da6adcbf25f44992b08f919988f81b62.png "image.png") + +���ǿ�һ��addinstance + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/cb9d4cb8fd4d4f0aa75e5d95c4923aae.png "image.png") + +������Ӧ��key: + + + + + + + +```String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral); ``` + + + + + + + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/6ab491f925e3403fa1377c2649d4721d.png "image.png") + + + + + + + +```//��ȡע��ʵ����IP�˿��б� List instanceList = addIpAddresses(service, ephemeral, ips); ``` + + + + + + + +���ǽ���򵥵Ŀ�һ�£����Ƿ������add,remove����������������Ƴ�ʵ�� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/bebd689b54d04e0dab0e64ac552e965e.png "image.png") + +������Ҫ������дע�������ips�������Ǿ͵��ips����������ʾ������Ȼ���������ѭ��instance���������ǿ��Կ�����������Ƴ��ʹ�map�����Ƴ���ȥ���������������instanceMap������һ�£�����ڷ��ء� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4d797c90012b4c22ac6e999bdfad8d63.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/158d309e86084a76be392ac8b67ccb94.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/04b51596154e4d4b933a39f0eb249587.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/86453757688743df89df30752bbbe0e5.png "image.png") + +���࣬���ǿ��Բ²�һ�£�����debug��ȥ����Ȼ����࣬���ǵ��һ�£��������ĵط� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f7a189074e00495da10119f2a5de7bc5.png "image.png") + +��ָ�������ƣ� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c54ca72e12e04d80b189d587a535d834.png "image.png") + +�ã�����ȫ��������һ�£� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/2b24cf84302c42669bed76d19a11e301.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/fcf6ffab779548a694a68c443a90a214.png "image.png") + +����֪��ǰ��˵��ephemeral��true����ѡ���һ���� �����ʣ� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4adb989d26cf46f98c943a114e047679.png "image.png") + +����Ӧ�õ���EphemeralConsistencyService��Ӧ��put����������EphemeralConsistencyServiceֻ��һ���ӿڣ�����Ӧ�õ��ö�Ӧ��ʵ��ʵ���� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/87869280d709441fbb8ceec818d67a97.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3764ecf8f5904315af85482f3c3ee71d.png "image.png") + +���ǿ�һ������onput������ + +���ڷŵ������У� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/02a34386b7444f0e8a6e35e57ceedf84.png "image.png") + +��������ǰѺ��ĵ�����ŵ�blockquene���棬Ҳ����һ������������ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/81b1c141753c492493eb095ead9e34da.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/252863f614a24c2da64713af52669ce5.png "image.png") + +����ע��Ĺ��̾���ô�򵥣�һ��������������ս�����ע������󣬷ŵ����ǵ��������е��У��ŵ���ɫ����֮�������������оͷ����ˡ��Ƿŵ���������֮�������з�������������С� + +���ע�����Notifier��һ���̣߳���ʦ�����һ�����ɣ��������һ���߳̾���Ҫ������run������Ϊrun���������� ����ִ�д���ĵط��� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/659d96fac1d445859a6ea6086ab8277c.png "image.png") + +�����������ѭ���������ݴ��������ϵĴ����ͻ���ע�����Ϣ����������ʵ�ֺ�����첽ע����Ϣ�� + +����̻߳�һֱ��ת��һֱ���У������������˵����������͹ҵ��ˣ��ã��㿴��������쳣Ҳ�Ե��ˣ�����һֱ�����У����û�����������������������ó�cpu + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/487a2e6d15f642549fe0d145447cc5cf.png "image.png") + +ע����ṹ�ķ����� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3eab037db7e2480ba4a9b439b18567da.png "image.png") + +���ȵ����Ǹ�change��ʱ�����Ǿͽ���onChange�����Ǹ������������ǽ�������service����ȥ���� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4adf69d76188430d860f2003b6c508d6.png "image.png") + +������Ͳ��ÿ��ˣ��ȿ�Ȩ�أ�Ȩ�ش��ڶ��ٵ�ʱ���������ֵ��Ȩ��С�ڶ��ٵ�ʱ������һ����Сֵ��Ȼ����Ǻ��ĵķ���updateIP + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/887332bfdcb64c2f9ad871ee55961b59.png "image.png") + +�����updateIPs����ʲô�أ� ���ľ����ұ���������Ҫע���ʵ����Ȼ��ͷŵ����ǵ�clusterMap���� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/87df4508357847f1ab49a785ab464e5b.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b31cf0150533469ebd4fbc462c6a0952.png "image.png") + +�ǵ������ҿ��ܾ��������ˣ���ʲôʱ����������̣߳���ʵʱ����������Ϣ�����������أ� �̴�������һ������������������ڿ��Ÿ����Notifier����Ϊ����������һ���̣߳����ᶪ��һ���̳߳��н������У����ǿ�һ����������������ʵ�����ģ� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/16223a5f9aab4fdaa004d0698e6cb0a6.png "image.png") + +���ǿ���������������ע����ǵ����spring��һ������г�ʼ��֮����е��õģ������ǿ�һ�����init��������������ʲô + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b9d2a25274834d73ab8f939006e9b075.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/64f25704efac43a7ae6ee5afda46cdf7.png "image.png") + +��һ��Scheduled�̳߳أ� + +![image-20211222222903941](E:\BaiduNetdiskWorkspace\springcloud alibaba\img\image-20211222222903941.png) + +Ҳ�����ڶ����ʼ����ʱ��ͽ�������һ���̳߳أ�ȥ����notifier��Ӧ�ķ��������run������������run�ġ�������ͻ�ʵʱ�����첽���С�����д�ĺô������ǽ�д�ʹ�����ȫ�����ˡ�ͨ�����������ܵ��ڴ���У�������������飬�������ĺô���1��������� + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e27c8e72b30845c3ab9346b89932fb42.png "image.png") \ 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/SpringCloudAlibaba nacos\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/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" new file mode 100644 index 0000000..0bc283c --- /dev/null +++ "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/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" @@ -0,0 +1,494 @@ +�ڿ���ƪ����֮ǰ����ö�NACOS��ع��������˽⣬�Ƽ�����[Spring Cloud Alibaba Nacos������ƪ��](https://zhuanlan.zhihu.com/p/68700978)�� + +��Թ��ܣ���Ŀ�ĵ�ȥ�����Ӧ��Դ���룬��һ���˽⹦������α�ʵ�ֳ����ġ� + +���������һ��Դ�����Ķ��������Ⱥ����������̫���ϸ�ڣ�����Ҫ���ߴ�Դ����٣�������ᡣ + +## һ������ + +����GitHub��Ӧ��[ҳ��](https://link.zhihu.com/?target=https%3A//github.com/alibaba/nacos)����NACOS����clone������Ŀ¼���ļ����������߳������Ƕ��ڿ�Դ���������а����IJ��ֲ����ࡣ + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-e9966f158af7cfac39baf5bba456fd17_720w.webp) + +
nacos����Ŀ¼�ṹ
+ +
+ +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-9195cfddde4e94b16f239bc101825a5a_720w.webp) + +
ģ������ͼ
+ +
+ +
+ + +![](https://pic4.zhimg.com/80/v2-6b30d0fc994745002ee7dcc7b04154d3_720w.webp) + +
nacos�����
+ +
+ +����������ͼ������˳���ҵ�ͻ�ƿ��ˣ��������ݾͼ�����nacos-console��nacos-naming��nacos-config��˳�����ϣ����ܿ������������ˡ� + +������Ǹо��޴����ֵĻ����Ǿ��Ʋ�nacos-example����������Ҫҵ��ĵ�����ڣ�һ����֪�� + +## �������÷��� + +���ȴ�һ��������˵��com.alibaba.nacos.api.NacosFactory�� + +����ľ�̬�������ڴ���ConfigService��NamingService���������ƣ��Դ���ConfigServiceΪ���� + + + +``` +public static ConfigService createConfigService(Properties properties) throws NacosException { + try { + Class driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService"); + Constructor constructor = driverImplClass.getConstructor(Properties.class); + ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties); + return vendorImpl; + } catch (Throwable e) { + throw new NacosException(-400, e.getMessage()); + } +} +``` + + + +û��ʲô���ӵ��߼���ʹ�õ��ǻ����ķ���ԭ�����������������properties����Щ���Կ���ͨ��bootstrap.yml��ָ������Ӧ����NacosConfigProperties�� + +��Ҫϸ�����ǹ��캯���ж���namespace��ʼ�����Dz������ݡ� + + + +``` +private void initNamespace(Properties properties) { + String namespaceTmp = null; + + String isUseCloudNamespaceParsing = + properties.getProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, + System.getProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, + String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING))); + + if (Boolean.valueOf(isUseCloudNamespaceParsing)) { + namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable() { + @Override + public String call() { + return TenantUtil.getUserTenantForAcm(); + } + }); + + namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable() { + @Override + public String call() { + String namespace = System.getenv(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_NAMESPACE); + return StringUtils.isNotBlank(namespace) ? namespace : EMPTY; + } + }); + } + + if (StringUtils.isBlank(namespaceTmp)) { + namespaceTmp = properties.getProperty(PropertyKeyConst.NAMESPACE); + } + namespace = StringUtils.isNotBlank(namespaceTmp) ? namespaceTmp.trim() : EMPTY; + properties.put(PropertyKeyConst.NAMESPACE, namespace); +} +``` + + + +�����properties��ָ���Ƿ�����ƻ����е�namespace����������ǵģ�����ȥ��ȡ�����ƻ�����ϵͳ������������ǣ���ô�Ͷ�ȡproperties��ָ����namespace��û��ָ���Ļ������ս����������ǿ��ַ������Ӵ����Ͽ���������ȡ�ƻ�����namespace�������첽������ʽ������Ŀǰ�汾����ʹ�õ�ͬ�����á� + +��������ConfigService�����涨����һϵ�нӿڷ���������������Ҫ���ġ� + +ÿ��ҵ��ʵ�����ն����ΪHttp���󣬾������õ�serverAddr�������ַ��������תʹ�ã���Ȼ����һ����ʱʱ�����������󣬶����󲻳ɹ��ˣ��Ǿͻ��׳��쳣�� + +������nacos-client�����շ����ն����䵽nacos-config�����ϣ����ʹ��JdbcTemplate�������ݳ־û��� + +��һ���ֵĴ���һ�������ף��������ã���ȡ���ú�ɾ�����ö��������֣��Ͳ�չ�������ˡ� + +�ص����һ�����ü������ֵ�Դ���롣 + +�Ƚ�ע��������com.alibaba.nacos.client.config.impl.CacheData������ݽṹ�ϣ��Ǹ����͵ij�Ѫģ�ͣ���Ҫ�dz䵱listener�����ߵĽ�ɫ����������������ȡ�ò�������ô�Ѻ��ˡ� + +ʵ���ϣ����Կ���CacheData��������Ϣ��namespace, content����listener�ۺ���һ���ˣ�������Ϊһ�����ÿ��Ը��Ӷ���listenerʵʩ��������Ϊlistener�ӿڿ����ж���ʵ�֣���ÿ��listenerֻ����һ��ʵ�������������ϡ� + + + +``` +public void addListener(Listener listener) { + if (null == listener) { + throw new IllegalArgumentException("listener is null"); + } + ManagerListenerWrap wrap = new ManagerListenerWrap(listener); + if (listeners.addIfAbsent(wrap)) { + LOGGER.info("[{}] [add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", name, tenant, dataId, group, + listeners.size()); + } +} +``` + + + +ʹ����CopyOnWriteArrayList.addIfAbsent�����������������Ҫ����equals������ManagerListenerWrap�Ƕ�listener������һ����ʽ�İ�������ʵ����equals������ + + + +``` +@Override +public boolean equals(Object obj) { + if (null == obj || obj.getClass() != getClass()) { + return false; + } + if (obj == this) { + return true; + } + ManagerListenerWrap other = (ManagerListenerWrap) obj; + return listener.equals(other.listener); +} +``` + + + +�����ϲ㷭�������ҵ�����listener���߲�Ĺ���API��com.alibaba.nacos.client.config.impl.ClientWorker�� + +ͬ���Ƕ�listener�Ĺ����������������ظ�У�飬����cacheMap�ǹؼ������¶��壺 + + + +``` +private final AtomicReference> cacheMap = new AtomicReference>() +``` + + + +ʹ���˾���ԭ���Բ������Ե�AtomicReference�����Ա��Ⲣ�����������ݲ�һ�µ����⣬�����������һ��HashMap��value��CacheData���󣬶�key����һ�����ɹ���ģ���GroupKey������п����ҵ��� + + + +``` +static public String getKeyTenant(String dataId, String group, String tenant) { + StringBuilder sb = new StringBuilder(); + urlEncode(dataId, sb); + sb.append('+'); + urlEncode(group, sb); + if (StringUtils.isNotEmpty(tenant)) { + sb.append('+'); + urlEncode(tenant, sb); + } + return sb.toString(); +} +``` + + + +ʵ�����ǽ�������Ϣ�á�+���Ž���ƴ�ӣ����������Ϣ�б��������ˡ�+���͡�%������ʹ��urlEncode�������б���ת�塣��Ȼ��Ҳ�����׵Ľ�������������Ͳ���չ�������ˡ� + +���������޷Ǿ��Ǿ�cacheMap��һϵ��get��set����������ά��listener���ر�ע����ǣ�ÿ�θ��²�������������һ��copy���󣬲����˶���֮��������set�����ǣ���cacheMap�С� + +���˵һ��listener��������������ġ� + +��Ȼ����ClientWorker���п����ҵ�����ע����ת�Ƶ����캯���С����У�����ע�⵽����ʼ���������̳߳أ� + + + +``` + executor = Executors.newScheduledThreadPool(1, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("com.alibaba.nacos.client.Worker." + agent.getName()); + t.setDaemon(true); + return t; + } + }); + + executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("com.alibaba.nacos.client.Worker.longPolling." + agent.getName()); + t.setDaemon(true); + return t; + } + }); + + executor.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + try { + checkConfigInfo(); + } catch (Throwable e) { + LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e); + } + } + }, 1L, 10L, TimeUnit.MILLISECONDS); +``` + + + +��������ִ�ж�ʱ�����scheduledThreadPool�����������̳߳صķֹ�Ҳ��Ƕ�׵ģ�executor���ڷ������ü������񣬶�executorService��������Ľ����ߣ�������ִ������Ľ�ɫ�� + +���Է���������̳߳�ֻ������1�������߳�������ִ��������̳߳صĺ����߳���CPU������ + +��Ϊ���ü����һ������ѯ�Ĺ��̣�һ������ִ�����ܼ�������������Ҫ�õ����ƣ�����NACOSĿǰʹ����һ���Ƚϼ򵥵ķ�������� + + + +``` +public void checkConfigInfo() { + // ������ + int listenerSize = cacheMap.get().size(); + // ����ȡ��Ϊ���� + int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize()); + if (longingTaskCount > currentLongingTaskCount) { + for (int i = (int) currentLongingTaskCount; i < longingTaskCount; i++) { + // Ҫ�ж������Ƿ���ִ�� �����Ҫ�ú����롣 �����б�����������ġ��仯���̿��������� + executorService.execute(new LongPollingRunnable(i)); + } + currentLongingTaskCount = longingTaskCount; + } +} +``` + + + +��ParamUtil.getPerTaskConfigSize()�з��ص���ÿ�������ܼ��������������ޣ�Ĭ����3000��������ͨ��ϵͳ����PER_TASK_CONFIG_SIZE����������ޡ� + +�Ӵ����Ͽ��Կ����������ǰlistener������û�г���3000�������ü����̳߳ػ���ת�����������ϸ��������ֵĴ��룬���ǻᷢ��һЩ����ģ���Ҫ��Χ���������������������һϵ�����⡣ + +����ѯ������Ҫ���������߼��� + +* ��鱾�����ã���CacheData�洢����Ϣ����һ�£� +* ���server�����ã�����CacheData�洢����Ϣ�� + +## ��������ע���뷢�� + +���������Ļ������ⲿ�ִ��뿴������Ƚ������ˣ��ṹ�ϻ������ơ� + +ֱ�ӽ���com.alibaba.nacos.api.naming.NamingService�������ж��registerInstance�ع����������ڷ���ע�ᡣ + +�ȿ���Instanceʵ������������ݣ�id��ip��port��serviceName��clusterName�����ڼ�Ⱥ����weight��Ȩ�أ���healthy���Ƿ���������enabled���Ƿ����ã���ephemeral���Ƿ�����ʱ�ģ�����9������ȫ����������Console���������֡� + +Ȼ��ֱ�ӿ�ע�����ķ����� + + + +``` + @Override + public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { + + if (instance.isEphemeral()) { + BeatInfo beatInfo = new BeatInfo(); + beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName)); + beatInfo.setIp(instance.getIp()); + beatInfo.setPort(instance.getPort()); + beatInfo.setCluster(instance.getClusterName()); + beatInfo.setWeight(instance.getWeight()); + beatInfo.setMetadata(instance.getMetadata()); + beatInfo.setScheduled(false); + beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo); + } + serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance); + } +``` + + + +ǰ��һ��δ����Ƕ���ʱ����ʵ���Ĵ����������ڹ���һ�����������͸�NACOS���� + +registerService�������Ƿ�װ��HTTP����������InstanceController�д������� + +�����Ŀ������spring-cloud-starter-alibaba-nacos-discovery������������Ĭ�����Զ�ע��ġ�����뿴�Զ�ע��Ĺ��̣����Դ�AbstractAutoServiceRegistration��ʼ���֣�������һ�δ��룺 + + + +``` + @EventListener(WebServerInitializedEvent.class) + public void bind(WebServerInitializedEvent event) { + ApplicationContext context = event.getApplicationContext(); + if (context instanceof ConfigurableWebServerApplicationContext) { + if ("management".equals( + ((ConfigurableWebServerApplicationContext) context).getServerNamespace())) { + return; + } + } + this.port.compareAndSet(0, event.getWebServer().getPort()); + this.start(); + } +``` + + + +������Web�����ʼ����ɵ��¼������ջ�ִ��start������ + + + +``` + public void start() { + if (!isEnabled()) { + if (logger.isDebugEnabled()) { + logger.debug("Discovery Lifecycle disabled. Not starting"); + } + return; + } + // only initialize if nonSecurePort is greater than 0 and it isn't already running + // because of containerPortInitializer below + if (!this.running.get()) { + register(); + if (shouldRegisterManagement()) { + registerManagement(); + } + this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration())); + this.running.compareAndSet(false, true); + } + + } +``` + + + +���У�register������������ĵIJ����ˣ���Դ��NacosServiceRegistry��ʵ�֣� + + + +``` + @Override + public void register(NacosRegistration registration) { + + if (!registration.isRegisterEnabled()) { + logger.info("Nacos Registration is disabled..."); + return; + } + if (StringUtils.isEmpty(registration.getServiceId())) { + logger.info("No service to register for nacos client..."); + return; + } + NamingService namingService = registration.getNacosNamingService(); + String serviceId = registration.getServiceId(); + + Instance instance = new Instance(); + instance.setIp(registration.getHost()); + instance.setPort(registration.getPort()); + instance.setWeight(registration.getRegisterWeight()); + instance.setClusterName(registration.getCluster()); + instance.setMetadata(registration.getMetadata()); + try { + namingService.registerInstance(serviceId, instance); + logger.info("nacos registry, {} {}:{} register finished", serviceId, instance.getIp(), instance.getPort()); + }catch (Exception e) { + logger.error("nacos registry, {} register failed...{},", serviceId, registration.toString(), e); + } + } +``` + + + +��δ���ͷdz���Ϥ�ˣ����վͻص���������namingService.registerInstance������ + + + +``` + /** + * Map> + */ + private Map> serviceMap = new ConcurrentHashMap<>(); +``` + + + +���ϳ���������һ��ʵ���ࣺcom.alibaba.nacos.naming.core.Service��Service�ǰ�����Instance��һ��Service���ж��Instance���������һ��Cluster�� + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-21ca2c51d56d6401dee0db0444df8ddf_720w.webp) + +
��ʵ����Ⱥ
+ +
+ +�ڵ���registerInstanceע��ʵ����ʱ��������ֶ�Ӧ��Serviceû�б�ע�ᣬ��ô��registerService�����һ��ʼ����Ӧ��Cluster�������������Ķ�ʱ���� + +��registerInstance�෴����deregisterInstance����Ϊȡ��ע�ᣬҲ������Ϊ�Ƿ���ʵ�����ߡ� + +���������NACOS���ʵ�ַ����ֹ��ܡ� + +�������ߣ����÷����ĽǶ����������ɵ�starter��Ŀ���и��ࣺNacosServerList������Ҫ���Ǽ̳���AbstractServerList��ʵ���������ؼ��Ľӿڷ������൱����NACOS��Ribbon�ĶԽӵ㡣 + + + +``` +public interface ServerList { + + public List getInitialListOfServers(); + + /** + * Return updated list of servers. This is called say every 30 secs + * (configurable) by the Loadbalancer's Ping cycle + * + */ + public List getUpdatedListOfServers(); + +} +``` + + + +NACOS������������ӿڵ�ʵ�֣���ʹ����getServers�����������뵽getServers���������棬��ʵ����������������˵��NacosNamingService.selectInstances������ͨ��serviceId��ȡ��ServiceInfo����Ȼ���ȡ��Service�����������Ч��Instance�� + +���ṩ�ߣ������÷����ĽǶȿ���NACOS��ͨ����ʱ����ʵʱ����ServiceInfo����Ҫҵ���߼�����HostReactor��ʵ�ֵġ���ǰ����serviceMap��һ����HostReactor��ά������serviceInfoMap�� + + + +``` +private Map serviceInfoMap; +``` + + + +HostReactor������FailoverReactor��ServiceInfo���˴��̻��棬��Ȼ�������˶�ʱ������ָ����Ŀ¼�����л�ServiceInfo���Դ�ʵ����Failover���ơ�������failover-modeҲ���п��صģ���ʵ����һ���ض��ļ���һ�������ݣ���Щ���õļ��Ҳ��ͨ����ʱ������ʵ�ֵġ� + + + +``` +File switchFile = new File(failoverDir + UtilAndComs.FAILOVER_SWITCH); +``` + + + +������������ͼ��ʾ�� + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-d16112ec6ff6dda0b029b019c313177c_720w.webp) + +
�����ָ���ͼ
+ +
+ +## �ġ���������̨(Console) + +��һ�����ǹ�������̨��ʵ�֣���ʵ��һ���dz����͵�WEB��Ŀ�� + +ʹ����Spring Security + JWT���а�ȫ���ƣ�ǰ�˼�����ReactJs������JdbcTemplate�������ݿ�־û��� + +��Ҫע����ǣ�����̨�ṩ�Ĺ��ܲ������Ǵ�nacos-console��������л�ȡ�����ݣ����Ƿ�ɢ���˸��������С� + +nacos-console�ṩ�˿���̨��¼��namespace����������̨����״̬�������������������ù����ͷ�������ֱ��������nacos-config��nacos-naming���ṩ��API������ЩAPI���ǹ������ᵽ��Open-API�� + +## �塢�ܽ� + +NACOS���Դ��ͨ���׶���û��ʲô��������Ҳû�н��в���װ�Ͱ�������һ����̾���ij���Ա���ڰ�Сʱ֮�ڰ���������Ŀ�����硣 + +��Ȼ��Ҳ�����һЩ���ɺ��ӵ�ȱ�㣬���磬ע�͹��٣����뻹�кܴ���ع��ռ䣬tenant��namespace�����������ʹ�á� + +����Spring Cloud Alibaba Nacos�Ľ��ܵ��˾ͽ����ˣ�ϣ���������������� \ 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/SpringCloudAlibaba nacos\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/SpringCloudAlibaba nacos\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" new file mode 100644 index 0000000..2ed31c8 --- /dev/null +++ "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/SpringCloudAlibaba nacos\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" @@ -0,0 +1,259 @@ +# Nacos�������� + +## Nacos�������ĵ�ʹ�� + +�ο��ٷ���[github.com/alibaba/spr��](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Falibaba%2Fspring-cloud-alibaba%2Fwiki%2FNacos-config "https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config") + +## Config������� + + Nacos ����ģ�� Key ����Ԫ��Ψһȷ��, NamespaceĬ���ǿմ������������ռ䣨public��������Ĭ���� DEFAULT_GROUP + +![image-20230429084711954](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084711954.png) + +* **֧�����õĶ�̬����** + + ����̬����ˢ��ʱ������µ� Enviroment�У��������ÿ��һ���д�Enviroment�л�ȡ���� + +``` +@SpringBootApplication +public class NacosConfigApplication { + + public static void main(String[] args) throws InterruptedException { + ConfigurableApplicationContext applicationContext = SpringApplication.run(NacosConfigApplication.class, args); + + while(true) { + //����̬����ˢ��ʱ������µ� Enviroment�У��������ÿ��һ���д�Enviroment�л�ȡ���� + String userName = applicationContext.getEnvironment().getProperty("common.name"); + String userAge = applicationContext.getEnvironment().getProperty("common.age"); + System.err.println("common name :" + userName + "; age: " + userAge); + TimeUnit.SECONDS.sleep(1); + } + } +} +���ƴ��� +``` + +* **֧��profile���ȵ�����** + + spring-cloud-starter-alibaba-nacos-config �ڼ������õ�ʱ�򣬲������������� dataid Ϊ spring.application.name.{spring.application.name}.spring.application.name.{file-extension:properties} Ϊǰ׺�Ļ������ã���������dataidΪ spring.application.name?{spring.application.name}-spring.application.name?{profile}.file?extension:properties�Ļ������á� + + ���ճ�����������������׻����µIJ�ͬ���ã�����ͨ��Spring�ṩ��{file-extension:properties} �Ļ������á����ճ�����������������׻����µIJ�ͬ���ã�����ͨ��Spring �ṩ�� file?extension:properties�Ļ������á����ճ�����������������׻����µIJ�ͬ���ã�����ͨ��Spring�ṩ��{spring.profiles.active} ��������������á� + +``` +spring.profiles.active=dev +���ƴ��� +``` + +* **֧���Զ��� namespace ������** + + ���ڽ����⻧���ȵ����ø��롣��ͬ�������ռ��£����Դ�����ͬ�� Group �� Data ID �����á�Namespace �ij��ó���֮һ�Dz�ͬ���������õ����ָ��룬���翪�����Ի����������������ʣ������á����񣩸���ȡ� + + ��û����ȷָ�� ${spring.cloud.nacos.config.namespace} ���õ�����£� Ĭ��ʹ�õ��� Nacos �� Public ���namespace�������Ҫʹ���Զ���������ռ䣬����ͨ������������ʵ�֣� + +``` +spring.cloud.nacos.config.namespace=71bb9785-231f-4eca-b4dc-6be446e12ff8 +���ƴ��� +``` + +* **֧���Զ��� Group ������** + + Group����֯���õ�ά��֮һ��ͨ��һ����������ַ������� Buy �� Trade �������ü����з��飬�Ӷ����� Data ID ��ͬ�����ü��������� Nacos �ϴ���һ������ʱ�����δ��д���÷�������ƣ������÷��������Ĭ�ϲ��� DEFAULT_GROUP �����÷���ij�����������ͬ��Ӧ�û����ʹ������ͬ���������ͣ��� database_url ���ú� MQ_topic ���á� + +��û����ȷָ�� ${spring.cloud.nacos.config.group} ���õ�����£�Ĭ����DEFAULT_GROUP �������Ҫ�Զ����Լ��� Group������ͨ������������ʵ�֣� + +``` +spring.cloud.nacos.config.group=DEVELOP_GROUP +���ƴ��� +``` + +* **֧���Զ�����չ�� Data Id ����** + + Data ID ����֯�������õ�ά��֮һ��Data ID ͨ��������֯����ϵͳ�����ü���һ��ϵͳ����Ӧ�ÿ��԰���������ü���ÿ�����ü������Ա�һ������������Ʊ�ʶ��Data ID ͨ�������� Java ������ com.taobao.tc.refund.log.level������������֤ȫ��Ψһ�ԡ������������ǿ�ơ� + +ͨ���Զ�����չ�� Data Id ���ã��ȿ��Խ�����Ӧ�ü����ù��������⣬�ֿ���֧��һ��Ӧ���ж�������ļ��� + +``` +# �Զ��� Data Id ������ +#��ͬ���̵�ͨ������ ֧�ֹ����� DataId +spring.cloud.nacos.config.sharedConfigs[0].data-id= common.yaml +spring.cloud.nacos.config.sharedConfigs[0].group=REFRESH_GROUP +spring.cloud.nacos.config.sharedConfigs[0].refresh=true + +# config external configuration +# ֧��һ��Ӧ�ö�� DataId ������ +spring.cloud.nacos.config.extensionConfigs[0].data-id=ext-config-common01.properties +spring.cloud.nacos.config.extensionConfigs[0].group=REFRESH_GROUP +spring.cloud.nacos.config.extensionConfigs[0].refresh=true + +spring.cloud.nacos.config.extensionConfigs[1].data-id=ext-config-common02.properties +spring.cloud.nacos.config.extensionConfigs[1].group=REFRESH_GROUP +���ƴ��� +``` + +## ���õ����ȼ� + +Spring Cloud Alibaba Nacos Config Ŀǰ�ṩ���������������� Nacos ��ȡ��ص����á� + +* A: ͨ�� spring.cloud.nacos.config.shared-configs ֧�ֶ������ Data Id ������ + +* B: ͨ�� spring.cloud.nacos.config.ext-config[n].data-id �ķ�ʽ֧�ֶ����չ Data Id ������ + +* C: ͨ���ڲ���ع���(Ӧ������Ӧ����+ Profile )�Զ�������ص� Data Id ���� + +�����ַ�ʽ��ͬʹ��ʱ�����ǵ�һ�����ȼ���ϵ��:A < B < C + +���ȼ��Ӹߵ��ͣ� + +1. nacos-config-product.yaml ��׼���� + +2. nacos-config.yaml ͬ���̲�ͬ������ͨ������ + +3. ext-config: ��ͬ���� ��չ���� + +4. shared-dataids ��ͬ����ͨ������ + +## @RefreshScope + +@Valueע����Ի�ȡ���������ĵ�ֵ�������޷���̬��֪�޸ĺ��ֵ����Ҫ����@RefreshScopeע�� + +``` +@RestController +@RefreshScope +public class TestController { + + @Value("${common.age}") + private String age; + + @GetMapping("/common") + public String hello() { + return age; + } +} +���ƴ��� +``` + +## Nacos��������Դ����� + +**��ϸԴ������ͼ��** + +[www.processon.com/view/link/6��](https://link.juejin.cn?target=https%3A%2F%2Fwww.processon.com%2Fview%2Flink%2F60f78ddbf346fb761bbac19d "https://www.processon.com/view/link/60f78ddbf346fb761bbac19d") + +### �������ļܹ� + +![image-20230429084840636](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084840636.png) + +��������ʹ��demo + +``` +public class ConfigServerDemo { + + public static void main(String[] args) throws NacosException, InterruptedException { + String serverAddr = "localhost"; + String dataId = "nacos-config-demo.yaml"; + String group = "DEFAULT_GROUP"; + Properties properties = new Properties(); + properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr); + //��ȡ���÷��� + ConfigService configService = NacosFactory.createConfigService(properties); + //��ȡ���� + String content = configService.getConfig(dataId, group, 5000); + System.out.println(content); + //ע������� + configService.addListener(dataId, group, new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + System.out.println("===recieve:" + configInfo); + } + + @Override + public Executor getExecutor() { + return null; + } + }); + + //�������� + //boolean isPublishOk = configService.publishConfig(dataId, group, "content"); + //System.out.println(isPublishOk); + //����properties��ʽ + configService.publishConfig(dataId,group,"common.age=30", ConfigType.PROPERTIES.getType()); + + Thread.sleep(3000); + content = configService.getConfig(dataId, group, 5000); + System.out.println(content); + +// boolean isRemoveOk = configService.removeConfig(dataId, group); +// System.out.println(isRemoveOk); +// Thread.sleep(3000); + +// content = configService.getConfig(dataId, group, 5000); +// System.out.println(content); +// Thread.sleep(300000); + } +} +���ƴ��� +``` + +## nacos config clientԴ�� + +�������ĺ��Ľӿ�ConfigService + +![image-20230429084850542](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084850542.png) + +### ��ȡ���� + + ��ȡ���õ���Ҫ������ NacosConfigService ��� getConfig ������ͨ������¸÷���ֱ�Ӵӱ����ļ���ȡ�����õ�ֵ����������ļ������ڻ�������Ϊ�գ�����ͨ�� HTTP GET ������Զ����ȡ���ã������浽���ؿ����С���ͨ�� HTTP ��ȡԶ������ʱ��Nacos �ṩ�������۶ϲ��ԣ�һ�dz�ʱʱ�䣬����������Դ�����Ĭ���������Ρ� + +![image-20230429084858759](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084858759.png) + +### ע������� + +�������Ŀͻ��˻�ͨ����������ע��������ﵽ������������ʱ��ִ�лص��Ĺ��� + +* NacosConfigService#getConfigAndSignListener + +* ConfigService#addListener + + Nacos ����ͨ�����Ϸ�ʽע��������������ڲ���ʵ�־��ǵ��� ClientWorker ��� addCacheDataIfAbsent������ CacheData ��һ��ά�������������ע������м�������ʵ�������е� CacheData �������� ClientWorker ���е�ԭ�� cacheMap �У����ڲ��ĺ��ij�Ա�У� + +![image-20230429084908207](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084908207.png) + +![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f14749bd9b614b21a55a261187cd5521~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp) + +### ���ó���ѯ + + ClientWorker ͨ�����µ������̳߳�������ó���ѯ�Ĺ�����һ���ǵ��̵߳� executor��ÿ�� 10ms ����ÿ 3000 ��������Ϊһ������ȡ����ѯ�� cacheData ʵ���������װ��Ϊһ�� LongPollingTask �ύ����ڶ����̳߳� executorService ������ + +![image-20230429084917535](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084917535.png) + +## nacos config serverԴ����� + +### ����dump + +![image-20230429084926987](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084926987.png) + + ���������ʱ�ͻ����� DumpService �� init �����������ݿ��� load ���ô洢�ڱ��ش����ϣ�����һЩ��Ҫ��Ԫ��Ϣ���� MD5 ֵ�������ڴ��С�����˻���������ļ��б�������һ������ʱ�䣬���жϵ����Ǵ����ݿ� dump ȫ���������ݻ��Dz��������������ݣ���������ϴ���������� 6h ���ڵĻ����� + +ȫ�� dump ��Ȼ����մ��̻��棬Ȼ��������� ID ÿ����ȡһǧ������ˢ�����̺��ڴ档���� dump ������ȡ�����Сʱ���������ã��������µĺ�ɾ���ģ����Ȱ�����������ˢ��һ���ڴ���ļ����ٸ����ڴ������е�����ȫ��ȥ�ȶ�һ�����ݿ⣬����иı����ͬ��һ�Σ������ȫ�� dump �Ļ������һ�������ݿ� IO �ʹ��� IO ������ + +### ���÷��� + + �������õĴ���λ�� ConfigController#publishConfig�С���Ⱥ��������һ��ʼҲֻ���һ̨��������̨���������ò���Mysql�н��г־û�������˲��������ÿ�����ò�ѯ��ȥ���� MySQL �����ǻ����� dump �����ڱ����ļ��н����û�����������˵���̨���������������֮����Ҫ֪ͨ��������ˢ���ڴ�ͱ��ش����е��ļ����ݣ�������ᷢ��һ����Ϊ ConfigDataChangeEvent ���¼�������¼���ͨ�� HTTP ����֪ͨ���м�Ⱥ�ڵ㣨���������������������ļ����ڴ��ˢ�¡� + +### ��������ѯ + + �ͻ��˻���һ������ѯ������ȡ����˵����ñ��������˴����߼���LongPollingService���У�������һ�� Runnable ������ΪClientLongPolling������˻Ὣ�ܵ�����ѯ�����װ��һ�� ClientLongPolling ���񣬸��������һ�� AsyncContext ��Ӧ����ͨ����ʱ�̳߳��Ӻ� 29.5s ִ�С��ȿͻ��� 30s �ij�ʱʱ����ǰ 500ms ������Ϊ�����̶��ϱ�֤�ͻ��˲�����Ϊ������ʱ��ɳ�ʱ. + +![image-20230429084934117](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084934117.png) + + + + + + + + + +���ߣ������Ƶ��������Ŷ� +���ӣ�https://juejin.cn/post/6999814668390760484 +��Դ��ϡ����� +����Ȩ���������С���ҵת������ϵ���߻����Ȩ������ҵת����ע�������� \ 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/Spring Cloud Config\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/Spring Cloud Config\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..60d1f61 --- /dev/null +++ "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/Spring Cloud Config\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,1041 @@ +Ҫ���˽�spring cloud config�����Ǿͱ����˽�springboot��environment�����ļ��ع��̡� + + + +``` +SpringApplication.run(AppApiApplication.class, args); + +public static ConfigurableApplicationContext run(Class[] primarySources, + String[] args) { + return new SpringApplication(primarySources).run(args); +} + +``` + + + +��`SpringApplication�Ĺ��췽����`������ôһ�δ��� + + + +``` +setInitializers((Collection) getSpringFactoriesInstances( + ApplicationContextInitializer.class)); + setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); + +``` + + + + + +``` +Set names = new LinkedHashSet<>( + SpringFactoriesLoader.loadFactoryNames(type, classLoader)); + +``` + + + +��δ����о��Ǵ�META-INFĿ¼�����ȡ`ApplicationListener��ApplicationContextInitializer`���͵������ࡣ +��spring-boot.jar����spring.factories��������`ConfigFileApplicationListener`����࣬���û���Ҳ����application�ļ��ļ��ض���ͨ������� + + + +``` +# Application Listeners +org.springframework.context.ApplicationListener=\ +org.springframework.boot.context.config.ConfigFileApplicationListener,\ + +# Run Listeners +org.springframework.boot.SpringApplicationRunListener=\ +org.springframework.boot.context.event.EventPublishingRunListener + +``` + + + +��������springboot�����׶� + + + +``` + public ConfigurableApplicationContext run(String... args) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + ConfigurableApplicationContext context = null; + Collection exceptionReporters = new ArrayList<>(); + configureHeadlessProperty(); + SpringApplicationRunListeners listeners = getRunListeners(args); + listeners.starting(); + try { + ApplicationArguments applicationArguments = new DefaultApplicationArguments( + args); + ConfigurableEnvironment environment = prepareEnvironment(listeners, + applicationArguments); + configureIgnoreBeanInfo(environment); + Banner printedBanner = printBanner(environment); + context = createApplicationContext(); + exceptionReporters = getSpringFactoriesInstances( + SpringBootExceptionReporter.class, + new Class[] { ConfigurableApplicationContext.class }, context); + prepareContext(context, environment, listeners, applicationArguments, + printedBanner); + refreshContext(context); + afterRefresh(context, applicationArguments); + stopWatch.stop(); + if (this.logStartupInfo) { + new StartupInfoLogger(this.mainApplicationClass) + .logStarted(getApplicationLog(), stopWatch); + } + listeners.started(context); + callRunners(context, applicationArguments); + } + catch (Throwable ex) { + handleRunFailure(context, ex, exceptionReporters, listeners); + throw new IllegalStateException(ex); + } + + try { + listeners.running(context); + } + catch (Throwable ex) { + handleRunFailure(context, ex, exceptionReporters, null); + throw new IllegalStateException(ex); + } + return context; + } + +``` + + + + + +``` +ConfigurableEnvironment environment = prepareEnvironment(listeners, + applicationArguments); +prepareContext(context, environment, listeners, applicationArguments, + printedBanner); + +``` + + + +���������δ������װ�ػ����Ĵ��� +��`prepareEnvironment`�����г�ʼ����`environment`�� + + + +``` + private ConfigurableEnvironment getOrCreateEnvironment() { + if (this.environment != null) { + return this.environment; + } + switch (this.webApplicationType) { + case SERVLET: + return new StandardServletEnvironment(); + case REACTIVE: + return new StandardReactiveWebEnvironment(); + default: + return new StandardEnvironment(); + } + } + +``` + + + +��Ϊ����servlet��������`Environment`Ϊ`StandardServletEnvironment`�������ڸ���ĵĹ�������ִ����`customizePropertySources`���� + + + +``` + public AbstractEnvironment() { + customizePropertySources(this.propertySources); + if (logger.isDebugEnabled()) { + logger.debug("Initialized " + getClass().getSimpleName() + " with PropertySources " + this.propertySources); + } + } + +``` + + + + + +``` + protected void customizePropertySources(MutablePropertySources propertySources) { + propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); + propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); + if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { + propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME)); + } + super.customizePropertySources(propertySources); + } + +``` + + + + + +``` + @Override + protected void customizePropertySources(MutablePropertySources propertySources) { + propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); + propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); + } + +``` + + + +������,���ǵõ���`StandardServletEnvironment`���Ҽ��ص�����˳���� + +1. servletConfigInitParams +2. servletContextInitParams +3. jndiProperties +4. systemProperties +5. systemEnvironment + + + +``` + protected void configureEnvironment(ConfigurableEnvironment environment, + String[] args) { + configurePropertySources(environment, args); + configureProfiles(environment, args); + } + + protected void configurePropertySources(ConfigurableEnvironment environment, + String[] args) { + MutablePropertySources sources = environment.getPropertySources(); + if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { + sources.addLast( + new MapPropertySource("defaultProperties", this.defaultProperties)); + } + if (this.addCommandLineProperties && args.length > 0) { + String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; + if (sources.contains(name)) { + PropertySource source = sources.get(name); + CompositePropertySource composite = new CompositePropertySource(name); + composite.addPropertySource(new SimpleCommandLinePropertySource( + "springApplicationCommandLineArgs", args)); + composite.addPropertySource(source); + sources.replace(name, composite); + } + else { + sources.addFirst(new SimpleCommandLinePropertySource(args)); + } + } + } + +``` + + + +���������ǿ��Կ����ּ�����`defaultProperties`������������Լ�`SimpleCommandLinePropertySource`�����в�������ǰ�档��ˣ����ڵ�˳���� + +1. SimpleCommandLinePropertySourcem���������� +2. servletConfigInitParams +3. servletContextInitParams +4. jndiProperties +5. systemProperties +6. systemEnvironment +7. defaultProperties + + + +``` + public void environmentPrepared(ConfigurableEnvironment environment) { + for (SpringApplicationRunListener listener : this.listeners) { + listener.environmentPrepared(environment); + } + } + + @Override + public void environmentPrepared(ConfigurableEnvironment environment) { + this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( + this.application, this.args, environment)); + } + +``` + + + +������`SpringApplicationRunListener`Ϊ`EventPublishingRunListener`,������ǰ���`getRunListeners` + + + +``` + private SpringApplicationRunListeners getRunListeners(String[] args) { + Class[] types = new Class[] { SpringApplication.class, String[].class }; + return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( + SpringApplicationRunListener.class, types, this, args)); + } + +``` + + + +��ͨ�����췽����ʼ����ʱ������this����������`initialMulticaster`�е�listenerΪapplication�е�listener + + + +``` + public EventPublishingRunListener(SpringApplication application, String[] args) { + this.application = application; + this.args = args; + this.initialMulticaster = new SimpleApplicationEventMulticaster(); + for (ApplicationListener listener : application.getListeners()) { + this.initialMulticaster.addApplicationListener(listener); + } + } + +``` + + + +��������ᷢ�͸��¼�֪ͨ��������`ConfigFileApplicationListener` + + + +``` + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationEnvironmentPreparedEvent) { + onApplicationEnvironmentPreparedEvent( + (ApplicationEnvironmentPreparedEvent) event); + } + if (event instanceof ApplicationPreparedEvent) { + onApplicationPreparedEvent(event); + } + } + +``` + + + + + +``` + private void onApplicationEnvironmentPreparedEvent( + ApplicationEnvironmentPreparedEvent event) { + List postProcessors = loadPostProcessors(); + postProcessors.add(this); + AnnotationAwareOrderComparator.sort(postProcessors); + for (EnvironmentPostProcessor postProcessor : postProcessors) { + postProcessor.postProcessEnvironment(event.getEnvironment(), + event.getSpringApplication()); + } + } + + List loadPostProcessors() { + return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, + getClass().getClassLoader()); + } + +``` + + + +���������и���չ�㣬ͨ����ȡ`spring.factories`�ļ������õ�`EnvironmentPostProcessor`�����`postProcessEnvironment`�������Լ�����Ҳ���뵽��`postProcessors`�����У����������ֻ��ߵ�`postProcessors`���� + + + +``` + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, + SpringApplication application) { + addPropertySources(environment, application.getResourceLoader()); + } + +``` + + + + + +``` + protected void addPropertySources(ConfigurableEnvironment environment, + ResourceLoader resourceLoader) { + RandomValuePropertySource.addToEnvironment(environment); + new Loader(environment, resourceLoader).load(); + } + + public static void addToEnvironment(ConfigurableEnvironment environment) { + environment.getPropertySources().addAfter( + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, + new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME)); + logger.trace("RandomValuePropertySource add to Environment"); + } + + public void load() { + this.profiles = new LinkedList<>(); + this.processedProfiles = new LinkedList<>(); + this.activatedProfiles = false; + this.loaded = new LinkedHashMap<>(); + initializeProfiles(); + while (!this.profiles.isEmpty()) { + Profile profile = this.profiles.poll(); + if (profile != null && !profile.isDefaultProfile()) { + addProfileToEnvironment(profile.getName()); + } + load(profile, this::getPositiveProfileFilter, + addToLoaded(MutablePropertySources::addLast, false)); + this.processedProfiles.add(profile); + } + resetEnvironmentProfiles(this.processedProfiles); + load(null, this::getNegativeProfileFilter, + addToLoaded(MutablePropertySources::addFirst, true)); + addLoadedPropertySources(); + } + +``` + + + +������ֽ�random���ü��뵽systemEnvironment֮���������ǵ����ü���˳����� +defaultProperties + +1. SimpleCommandLinePropertySourcem���������� +2. servletConfigInitParams +3. servletContextInitParams +4. jndiProperties +5. systemProperties +6. systemEnvironment +7. RandomValuePropertySource +8. defaultProperties + +·�� + +> classpath:/,classpath:/config/,file:./,file:./config/ +> spring.config.location= +> spring.config.additional-location= + +�ļ���Ĭ��Ϊapplication + +> ���������spring.config.name= +> �ļ�����Ϊspring.config.name���õ����� + +����ͨ��`load`����������·�����ļ������ļ����뵽`environment��`�����������`spring.profiles.active`�����Ҽ���prefix + "-" + profile + fileExtension�ļ���Ҳ����`application-.properties`��`y`�ļ� + + + +``` + private void load(Profile profile, DocumentFilterFactory filterFactory, + DocumentConsumer consumer) { + getSearchLocations().forEach((location) -> { + boolean isFolder = location.endsWith("/"); + Set names = isFolder ? getSearchNames() : NO_SEARCH_NAMES; + names.forEach( + (name) -> load(location, name, profile, filterFactory, consumer)); + }); + } + + private void load(String location, String name, Profile profile, + DocumentFilterFactory filterFactory, DocumentConsumer consumer) { + if (!StringUtils.hasText(name)) { + for (PropertySourceLoader loader : this.propertySourceLoaders) { + if (canLoadFileExtension(loader, location)) { + load(loader, location, profile, + filterFactory.getDocumentFilter(profile), consumer); + return; + } + } + } + Set processed = new HashSet<>(); + for (PropertySourceLoader loader : this.propertySourceLoaders) { + for (String fileExtension : loader.getFileExtensions()) { + if (processed.add(fileExtension)) { + loadForFileExtension(loader, location + name, "." + fileExtension, + profile, filterFactory, consumer); + } + } + } + } + +``` + + + +������ͨ��`this.propertySourceLoaders`�����ж��Ƿ���ϸ�SourceLoader�ĺ�׺��������ϣ��ͽ���Դ���ؽ���,��`propertySourceLoaders`������`Loader`���췽�� + + + +``` + Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { + this.environment = environment; + this.resourceLoader = (resourceLoader != null) ? resourceLoader + : new DefaultResourceLoader(); + this.propertySourceLoaders = SpringFactoriesLoader.loadFactories( + PropertySourceLoader.class, getClass().getClassLoader()); + } + +``` + + + + + +``` +# PropertySource Loaders +org.springframework.boot.env.PropertySourceLoader=\ +org.springframework.boot.env.PropertiesPropertySourceLoader,\ +org.springframework.boot.env.YamlPropertySourceLoader + +``` + + + +��˿��Լ���`yml`�ļ���`properties`�ļ� + + + +``` + private void addLoadedPropertySources() { + MutablePropertySources destination = this.environment.getPropertySources(); + List loaded = new ArrayList<>(this.loaded.values()); + Collections.reverse(loaded); + String lastAdded = null; + Set added = new HashSet<>(); + for (MutablePropertySources sources : loaded) { + for (PropertySource source : sources) { + if (added.add(source.getName())) { + addLoadedPropertySource(destination, lastAdded, source); + lastAdded = source.getName(); + } + } + } + } + +``` + + + +���շ��뵽��`environment` + +���������Ǿ�������Spring cloud config + +��`spring-cloud-context`��jar�У�`spring.factories`�������˸�`BootstrapApplicationListener` +���Ҹ�����������`ConfigFileApplicationListener`������������������springcloud�������ô +�¼�������������`BootstrapApplicationListener` + + + +``` +# Application Listeners +org.springframework.context.ApplicationListener=\ +org.springframework.cloud.bootstrap.BootstrapApplicationListener,\ + +``` + + + + + +``` + @Override + public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { + ConfigurableEnvironment environment = event.getEnvironment(); + if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, + true)) { + return; + } + // don't listen to events in a bootstrap context + if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { + return; + } + ConfigurableApplicationContext context = null; + String configName = environment + .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); + for (ApplicationContextInitializer initializer : event.getSpringApplication() + .getInitializers()) { + if (initializer instanceof ParentContextApplicationContextInitializer) { + context = findBootstrapContext( + (ParentContextApplicationContextInitializer) initializer, + configName); + } + } + if (context == null) { + context = bootstrapServiceContext(environment, event.getSpringApplication(), + configName); + } + apply(context, event.getSpringApplication(), environment); + } + +``` + + + + + +``` + private ConfigurableApplicationContext bootstrapServiceContext( + ConfigurableEnvironment environment, final SpringApplication application, + String configName) { + StandardEnvironment bootstrapEnvironment = new StandardEnvironment(); + MutablePropertySources bootstrapProperties = bootstrapEnvironment + .getPropertySources(); + for (PropertySource source : bootstrapProperties) { + bootstrapProperties.remove(source.getName()); + } + String configLocation = environment + .resolvePlaceholders("${spring.cloud.bootstrap.location:}"); + Map bootstrapMap = new HashMap<>(); + bootstrapMap.put("spring.config.name", configName); + // if an app (or test) uses spring.main.web-application-type=reactive, bootstrap will fail + // force the environment to use none, because if though it is set below in the builder + // the environment overrides it + bootstrapMap.put("spring.main.web-application-type", "none"); + if (StringUtils.hasText(configLocation)) { + bootstrapMap.put("spring.config.location", configLocation); + } + bootstrapProperties.addFirst( + new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap)); + for (PropertySource source : environment.getPropertySources()) { + if (source instanceof StubPropertySource) { + continue; + } + bootstrapProperties.addLast(source); + } + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + // Use names and ensure unique to protect against duplicates + List names = new ArrayList<>(SpringFactoriesLoader + .loadFactoryNames(BootstrapConfiguration.class, classLoader)); + for (String name : StringUtils.commaDelimitedListToStringArray( + environment.getProperty("spring.cloud.bootstrap.sources", ""))) { + names.add(name); + } + // TODO: is it possible or sensible to share a ResourceLoader? + SpringApplicationBuilder builder = new SpringApplicationBuilder() + .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF) + .environment(bootstrapEnvironment) + // Don't use the default properties in this builder + .registerShutdownHook(false).logStartupInfo(false) + .web(WebApplicationType.NONE); + final SpringApplication builderApplication = builder.application(); + if(builderApplication.getMainApplicationClass() == null){ + // gh_425: + // SpringApplication cannot deduce the MainApplicationClass here + // if it is booted from SpringBootServletInitializer due to the + // absense of the "main" method in stackTraces. + // But luckily this method's second parameter "application" here + // carries the real MainApplicationClass which has been explicitly + // set by SpringBootServletInitializer itself already. + builder.main(application.getMainApplicationClass()); + } + if (environment.getPropertySources().contains("refreshArgs")) { + // If we are doing a context refresh, really we only want to refresh the + // Environment, and there are some toxic listeners (like the + // LoggingApplicationListener) that affect global static state, so we need a + // way to switch those off. + builderApplication + .setListeners(filterListeners(builderApplication.getListeners())); + } + List> sources = new ArrayList<>(); + for (String name : names) { + Class cls = ClassUtils.resolveClassName(name, null); + try { + cls.getDeclaredAnnotations(); + } + catch (Exception e) { + continue; + } + sources.add(cls); + } + AnnotationAwareOrderComparator.sort(sources); + builder.sources(sources.toArray(new Class[sources.size()])); + final ConfigurableApplicationContext context = builder.run(); + // gh-214 using spring.application.name=bootstrap to set the context id via + // `ContextIdApplicationContextInitializer` prevents apps from getting the actual + // spring.application.name + // during the bootstrap phase. + context.setId("bootstrap"); + // Make the bootstrap context a parent of the app context + addAncestorInitializer(application, context); + // It only has properties in it now that we don't want in the parent so remove + // it (and it will be added back later) + bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); + mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties); + return context; + } + +``` + + + +������Ҫ�������������� + +1. ����Environment��������spring.config.name=bootstrap +2. ����һ���µ�SpringApplication����������������sourcesΪ��չ���µ�BootstrapConfiguration������ + + + +``` +List names = new ArrayList<>(SpringFactoriesLoader + .loadFactoryNames(BootstrapConfiguration.class, classLoader)); + +``` + + + +�򵥵ľ��ǿ�������Ϊ������һ���µ���������������ΪBootstrapConfiguration���õ��ࡣ����ͨ��run�����õ��˳�ʼ�����BeanFactory�������ҽ�contenxt��װ��AncestorInitializer���뵽�������Լ���SpringApplication�� + + + +``` +application.addInitializers(new AncestorInitializer(context)); + +``` + + + +����`apply`������,��ȡ��`ApplicationContextInitializer`���͵����ж�����뵽�����ǵ�ǰ��`SpringApplication`�� + + + +``` + private void apply(ConfigurableApplicationContext context, + SpringApplication application, ConfigurableEnvironment environment) { + @SuppressWarnings("rawtypes") + List initializers = getOrderedBeansOfType(context, + ApplicationContextInitializer.class); + application.addInitializers(initializers + .toArray(new ApplicationContextInitializer[initializers.size()])); + addBootstrapDecryptInitializer(application); + } + +``` + + + +��`spring-cloud-context`���У�`spring.factories`������������� + + + +``` +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\ + +``` + + + +����������������`SpringApplication`���Ѿ�����`AncestorInitializer,PropertySourceBootstrapConfiguration`����ApplicationContextInitializer + +���ص����������� + + + +``` + private void prepareContext(ConfigurableApplicationContext context, + ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, + ApplicationArguments applicationArguments, Banner printedBanner) { + context.setEnvironment(environment); + postProcessApplicationContext(context); + applyInitializers(context); + listeners.contextPrepared(context); + if (this.logStartupInfo) { + logStartupInfo(context.getParent() == null); + logStartupProfileInfo(context); + } + + // Add boot specific singleton beans + context.getBeanFactory().registerSingleton("springApplicationArguments", + applicationArguments); + if (printedBanner != null) { + context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); + } + + // Load the sources + Set sources = getAllSources(); + Assert.notEmpty(sources, "Sources must not be empty"); + load(context, sources.toArray(new Object[0])); + listeners.contextLoaded(context); + } + +``` + + + + + +``` + protected void applyInitializers(ConfigurableApplicationContext context) { + for (ApplicationContextInitializer initializer : getInitializers()) { + Class requiredType = GenericTypeResolver.resolveTypeArgument( + initializer.getClass(), ApplicationContextInitializer.class); + Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); + initializer.initialize(context); + } + } + +``` + + + +������ͻ���õ�`ApplicationContextInitializer.initialize`���� +�ͻ�������`AncestorInitializer` + + + +``` + @Override + public void initialize(ConfigurableApplicationContext context) { + while (context.getParent() != null && context.getParent() != context) { + context = (ConfigurableApplicationContext) context.getParent(); + } + reorderSources(context.getEnvironment()); + new ParentContextApplicationContextInitializer(this.parent) + .initialize(context); + } + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + if (applicationContext != this.parent) { + applicationContext.setParent(this.parent); + applicationContext.addApplicationListener(EventPublisher.INSTANCE); + } + } + +``` + + + +���Կ������ォBootStrap������������Ϊ���ǵ�ǰ�����ĸ����������Ҹ������еĶ��󶼳�ʼ�����ˣ�`PropertySourceBootstrapConfiguration`��Ҳ��ʼ������,���Ҹ�`ApplicationContextInitializer`���뵽�������Լ������������棬��˻���ó�ʼ�����˵�`PropertySourceBootstrapConfiguration.initialize`,��`PropertySourceLocator`��ע������� + + + +``` +# Bootstrap components +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration,\ + +``` + + + + + +``` + @Bean + @ConditionalOnMissingBean(ConfigServicePropertySourceLocator.class) + @ConditionalOnProperty(value = "spring.cloud.config.enabled", matchIfMissing = true) + public ConfigServicePropertySourceLocator configServicePropertySource(ConfigClientProperties properties) { + ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator( + properties); + return locator; + } + +``` + + + + + +``` + @Autowired(required = false) + private List propertySourceLocators = new ArrayList<>(); + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + CompositePropertySource composite = new CompositePropertySource( + BOOTSTRAP_PROPERTY_SOURCE_NAME); + AnnotationAwareOrderComparator.sort(this.propertySourceLocators); + boolean empty = true; + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + for (PropertySourceLocator locator : this.propertySourceLocators) { + PropertySource source = null; + source = locator.locate(environment); + if (source == null) { + continue; + } + logger.info("Located property source: " + source); + composite.addPropertySource(source); + empty = false; + } + if (!empty) { + MutablePropertySources propertySources = environment.getPropertySources(); + String logConfig = environment.resolvePlaceholders("${logging.config:}"); + LogFile logFile = LogFile.get(environment); + if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { + propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); + } + insertPropertySources(propertySources, composite); + reinitializeLoggingSystem(environment, logConfig, logFile); + setLogLevels(applicationContext, environment); + handleIncludedProfiles(environment); + } + } + +``` + + + +������ջ�����`ConfigServicePropertySourceLocator`�� + + + +``` + private Environment getRemoteEnvironment(RestTemplate restTemplate, + ConfigClientProperties properties, String label, String state) { + String path = "/{name}/{profile}"; + String name = properties.getName(); + String profile = properties.getProfile(); + String token = properties.getToken(); + int noOfUrls = properties.getUri().length; + if (noOfUrls > 1) { + logger.info("Multiple Config Server Urls found listed."); + } + + Object[] args = new String[] { name, profile }; + if (StringUtils.hasText(label)) { + if (label.contains("/")) { + label = label.replace("/", "(_)"); + } + args = new String[] { name, profile, label }; + path = path + "/{label}"; + } + ResponseEntity response = null; + + for (int i = 0; i < noOfUrls; i++) { + Credentials credentials = properties.getCredentials(i); + String uri = credentials.getUri(); + String username = credentials.getUsername(); + String password = credentials.getPassword(); + + logger.info("Fetching config from server at : " + uri); + + try { + HttpHeaders headers = new HttpHeaders(); + addAuthorizationToken(properties, headers, username, password); + if (StringUtils.hasText(token)) { + headers.add(TOKEN_HEADER, token); + } + if (StringUtils.hasText(state) && properties.isSendState()) { + headers.add(STATE_HEADER, state); + } + + final HttpEntity entity = new HttpEntity<>((Void) null, headers); + response = restTemplate.exchange(uri + path, HttpMethod.GET, entity, + Environment.class, args); + } + catch (HttpClientErrorException e) { + if (e.getStatusCode() != HttpStatus.NOT_FOUND) { + throw e; + } + } + catch (ResourceAccessException e) { + logger.info("Connect Timeout Exception on Url - " + uri + + ". Will be trying the next url if available"); + if (i == noOfUrls - 1) + throw e; + else + continue; + } + + if (response == null || response.getStatusCode() != HttpStatus.OK) { + return null; + } + + Environment result = response.getBody(); + return result; + } + + return null; + } + +``` + + + +���Կ��������ǵ��÷���˵Ľӿڻ�ȡ���µ����á� +������ⲿ��������ŵ�systemEnvironment֮ǰ����˾ͻḲ�DZ������ã����ǿ���ͨ���������� + + + +``` +@ConfigurationProperties("spring.cloud.config") +public class PropertySourceBootstrapProperties { + + /** + * Flag to indicate that the external properties should override system properties. + * Default true. + */ + private boolean overrideSystemProperties = true; + + /** + * Flag to indicate that {@link #isOverrideSystemProperties() + * systemPropertiesOverride} can be used. Set to false to prevent users from changing + * the default accidentally. Default true. + */ + private boolean allowOverride = true; + + /** + * Flag to indicate that when {@link #setAllowOverride(boolean) allowOverride} is + * true, external properties should take lowest priority, and not override any + * existing property sources (including local config files). Default false. + */ + private boolean overrideNone = false; + +``` + + + + + +``` +if (!remoteProperties.isAllowOverride() || (!remoteProperties.isOverrideNone() + && remoteProperties.isOverrideSystemProperties())) { + propertySources.addFirst(composite); + return; + } + if (remoteProperties.isOverrideNone()) { + propertySources.addLast(composite); + return; + } + if (propertySources + .contains(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)) { + if (!remoteProperties.isOverrideSystemProperties()) { + propertySources.addAfter( + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, + composite); + } + else { + propertySources.addBefore( + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, + composite); + } + } + else { + propertySources.addLast(composite); + } + +``` + + + +**�ܽ�** + +1. ��չ�� + 1.1 `EnvironmentPostProcessor` + �Ի��������� + 1.2 `PropertySourceLoader` + ������ͬ�ĸ�ʽ���ļ� + 1.2 `ApplicationListener` + spring-cloud����ͨ��������չBootstrapApplicationListener + 1.3 `BootstrapConfiguration` + ͨ����չ����������� + +2. �ļ����ع��� + ͨ��ApplicationListener�¼�����һ���µ�SpringApliction�����ཫBootstrapConfiguration��չ����Ϊ���������࣬Ȼ������һ����ʼ���˵�BootStrap����������������װ��AncestorInitializer����뵽�����Լ����������У����þ��ǽ������Լ������ĸ���������ΪBootStrap������ͨ��BootStrap������ó�ʼ���ú��ApplicationContextInitializer���Ͷ��󣬶�����������PropertySourceBootstrapConfiguration���������࣬��������ע����PropertySourceLocator�࣬����ConfigServiceBootstrapConfiguration����������������bean���� + ConfigServicePropertySourceLocator���������ջὫBootStrap�г�ʼ�õ�PropertySourceBootstrapConfiguration���뵽�����Լ����������е��ã����յ���initialize����Ȼ�����ConfigServicePropertySourceLocator.locate����ȥConfig server�����ȡ���á� + + + +���ߣ�ӵ���¶�_to +���ӣ�https://www.jianshu.com/p/60c6ab0e79d5 +��Դ������ +����Ȩ���������С���ҵת������ϵ���߻����Ȩ������ҵת����ע�������� \ 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/Spring Cloud Eureka\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/Spring Cloud Eureka\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..8ad8c31 --- /dev/null +++ "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/Spring Cloud Eureka\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,1776 @@ +EurekaԴ����� + +**��1�� ��������** +**1.1 Eureka����ʲô��** + +�������Ƕ����ף�Eureka������������ע��ģ���ע������Ҫʵ��ʲô�����أ����������ȷ�ˡ� + +1. ��Ȼ��ע�����ģ�������Ҫ�ܱ�������ip��port����Ϣ�ɣ�����Eureka-server�����ṩ�Ļ������ܡ� +2. ����ע������֮�󣬻�Ҫ�ṩһЩ��̬��֪���������ߵĹ��ܰɣ����һ�����������ߣ�Eureka-server����֪����������������һ�IJ�ֵ�ˡ� +3. ��Eureka-server�˸�֪������ı仯֮���ܵ�֪ͨ���Ѷ˰ɣ������ǣ����ʱserver������֪ͨclient���ػ���client���Լ�ȥ��ȡ��Ϣ�أ�������ڲ�֪�����Ȼ�ȥ��Դ����֤�� +4. OK������Ĺ��ܶ�ʵ���ˣ�Eureka�����ϸ��ˣ��ǻ���һ������Ȼ�����ip�Ͷ˿ڶ���Eureka-server���棬�������Ѷ˵��÷���˵�ʱ��ͨ�����õ�OpenFeign����OpenFeign����ô֪�������ĸ�����ģ�֮ǰ����д����application.properties�����.ribbon.listOfServers�У�����Eureka��ô�Զ�д��ȥ���أ� +5. ����4�����ܻ����������ע�����ĸ��еĹ��ܣ������ʱ����������˼��һ�£�ע����������΢������Ŀ�У���ע������Ҳ��Ϊһ��������Ҳ��Ҫ����Ⱥ�ģ����ʱ�����Ǿ�Ҫ��һ�£�����Ⱥ��ô��֤����һ���ԣ��û���ʲô���ۣ� + ������EurekaҪʵ�ֵ�����ĵĹ��ܣ�����Щ�����ṩ�����ˣ�����Ŀ��ôȥ�����أ�����ֱ��ȥ��API�ɣ�����鷳������ȥѧһ��Eureka��api���궿���ˡ� + +���ʱ�����Ǿͻ����뵽SpringBoot���Զ�װ���Starter��������������������������˺���bean���Զ�ע�룬�ײ����ֱ���õ�bean��Ȼ����Starter�����Ӧ���Զ������ǵ�����API�ģ�OK���ǻع�ͷ���ҷ��֣��ҵ�Eureka-client����һ��starter������ٺ٣��е㶫���ˡ����client�˺����߼��϶��ǰ����Ƿ�װ�˸���bean��Ȼ������ǵ����˺���api�ˡ� + +�������Eureka�ĺ��Ĺ�����������һ���������ܽ᣺ + +1. ʵ��ע�ᣬ�������ڴ��� +2. ��̬��֪����Ľ���״̬ +3. ����ķ��֣�����̬��֪����ı仯 + **1.2 ���������Ƶ�** + ��ȷ�˺��Ĺ��ܣ��Լ���ε��õģ��������������󵨵��Ƶ�һ�º�������ͼ�� + +![SpringCloudϵ�С�Spring Cloud Դ�����֮Eureka-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f15564313c2406546821713bcaf3eb0e9ac73d.jpg "SpringCloudϵ�С�Spring Cloud Դ�����֮Eureka-��Դ������������")���������Ƶ�����֮����������ͨ��Դ����һ����֤�� + +**��2�� Դ�����** +**2.1 ����ע������** +����ע������spring bootӦ��������ʱ����ġ������ִ��·���������Ҳ������Ȼع�һ��ǰ�����ǽ�����֪ʶ�� + +����˵spring cloud��һ����̬�����ṩ��һ�ױ�׼�����ױ�׼����ͨ����ͬ�������ʵ�֣����оͰ�������ע��/���֡��۶ϡ����ؾ���ȣ���spring-cloud-common������У� +org.springframework.cloud.client.serviceregistry ·���£����Կ���һ������ע��Ľӿڶ� �� ServiceRegistry �������Ƕ�����spring cloud�з���ע���һ���ӿڡ� + +���ǿ�һ���������ϵͼ������ӿ���һ��Ψһ��ʵ�� EurekaServiceRegistry ����ʾ���õ���Eureka Server��Ϊ����ע�����ġ� + +![SpringCloudϵ�С�Spring Cloud Դ�����֮Eureka-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/49d085328febfffcbae4030dac151db2d0bfcb.jpg "SpringCloudϵ�С�Spring Cloud Դ�����֮Eureka-��Դ������������")**2.1.1 ע���ʱ��** +����ע��ķ������ǿ��Բ²�һ��Ӧ����ʲôʱ����ɣ������Ҫ������ʵӦ�ò��Ѳ²⵽�������ע��ȡ���ڷ����Ƿ��Ѿ��������ˡ�����spring boot�У���ȵ�spring ���������������е����ö����֮��������ע�ᡣ�����������spring boot�����������е�refreshContext����ɡ� + +���ǹ۲�һ��finishRefresh����������������Ͽ��Կ������������������ˢ�µIJ�����Ҳ����ˢ�����֮��Ҫ���ĺ��õIJ���������Ҫ���������� + +* ��ջ��� + +* ��ʼ��һ��LifecycleProcessor����Spring������ʱ������bean����spring������ʱ������bean + +* ����LifecycleProcessor��onRefresh����������ʵ����Lifecycle�ӿڵ�bean + +* ����ContextRefreshedEvent + +* ע��Bean��ͨ��JMX���м�غ͹��� + + + + ``` + protected void finishRefresh() { + // Clear context-level resource caches (such as ASM metadata from scanning). + clearResourceCaches(); + // Initialize lifecycle processor for this context. + initLifecycleProcessor(); + // Propagate refresh to lifecycle processor first. + getLifecycleProcessor().onRefresh(); + // Publish the final event. + publishEvent(new ContextRefreshedEvent(this)); + // Participate in LiveBeansView MBean, if active. + LiveBeansView.registerApplicationContext(this); + } + ``` + + + + + + + + + +����������У������ص��ע getLifecycleProcessor().onRefresh() �����ǵ����������ڴ�������onrefresh�������ҵ�SmartLifecycle�ӿڵ�����ʵ���ಢ����start������ + +**2.1.2 SmartLifeCycle** +����չһ��SmartLifeCycle����֪ʶ�� SmartLifeCycle��һ���ӿڣ���Spring�������������е�Bean���ҳ�ʼ��֮�󣬻�����ص�ʵ����SmartLifeCycle�ӿڵ����ж�Ӧ�ķ��������磨start���� + +ʵ���������Լ�Ҳ������չ��������springboot���̵�main����ͬ��Ŀ¼�£�дһ�������࣬ʵ��SmartLifeCycle�ӿڣ�����ͨ�� @Service ����Ϊһ��bean����ΪҪ��springȥ���أ����ȵ���bean�� + + + +``` +@Service +public class TestSmartLifeCycle implements SmartLifecycle { + /** + * ����������ִ��.������ʾ����start����. + * ��������isAutoStartup()����ֵ,ֻ��isAutoStartup()����true��ʱ��,start()�Żᱻִ�� + */ + @Override + public void start() { + System.out.println("----------start-----------"); + } + /** + * ����ֹͣǰִ�з��� + * ǰ������: isRunning()����true�Żᱻִ�� + */ + @Override + public void stop() { + System.out.println("----------stop-----------"); + } + /** + * ���ط�������״̬,Ӱ�쵽�����Ƿ����stop���� + * @return + */ + @Override + public boolean isRunning() { + return false; + } + /** + * �Ƿ����start����,��Ҫע�� + * ��ǰ��������false�Dz���ִ��start() + * @return + */ + @Override + public boolean isAutoStartup() { + return true; + } + @Override + public void stop(Runnable runnable) { + stop(); + runnable.run(); + } + /** + * ָ��ִ��˳�� + * ��ǰ��������ж����ʵ����SmartLifecycle,�򰴴˷���������ֵ����ִ�� + * @return + */ + @Override + public int getPhase() { + return 0; + } +} +``` + + + + + + + + + +���ţ���������spring bootӦ�ú󣬿��Կ�������̨����� start �ַ����� + +������DefaultLifecycleProcessor.startBeans�����ϼ�һ��debug�����Ժ����ԵĿ��������Լ������TestSmartLifeCycle��ɨ�赽�ˣ�����������ø�bean��start������![SpringCloudϵ�С�Spring Cloud Դ�����֮Eureka-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/75b18e528ba1879148c106d9bd3fb61ab9b5bb.jpg "SpringCloudϵ�С�Spring Cloud Դ�����֮Eureka-��Դ������������")��startBeans�����У����ǿ��Կ��������Ȼ�������ʵ����SmartLifeCycle��Bean��Ȼ���ѭ������ʵ����SmartLifeCycle��bean��start�������������¡� + + + +``` +private void startBeans(boolean autoStartupOnly) { + Map lifecycleBeans = this.getLifecycleBeans(); + Map phases = new HashMap(); + lifecycleBeans.forEach((beanName, bean) -> { + if (!autoStartupOnly || bean instanceof SmartLifecycle && + ((SmartLifecycle)bean).isAutoStartup()) { + int phase = this.getPhase(bean); + DefaultLifecycleProcessor.LifecycleGroup group = + (DefaultLifecycleProcessor.LifecycleGroup)phases.get(phase); + if (group == null) { + group = new DefaultLifecycleProcessor.LifecycleGroup(phase,this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly); + phases.put(phase, group); + } + group.add(beanName, bean); + } + }); + if (!phases.isEmpty()) { + List keys = new ArrayList(phases.keySet()); + Collections.sort(keys); + Iterator var5 = keys.iterator(); + while(var5.hasNext()) { + Integer key = (Integer)var5.next(); + ((DefaultLifecycleProcessor.LifecycleGroup)phases.get(key)).start(); + } + } +} +``` + + + + + + + + + +**2.1.3 doStart** + + + +``` +private void doStart(Map lifecycleBeans, String beanName, boolean autoStartupOnly) { + Lifecycle bean = (Lifecycle)lifecycleBeans.remove(beanName); + if (bean != null && bean != this) { + String[] dependenciesForBean = this.getBeanFactory().getDependenciesForBean(beanName); + String[] var6 = dependenciesForBean; + int var7 = dependenciesForBean.length; + for(int var8 = 0; var8 < var7; ++var8) { + String dependency = var6[var8]; + this.doStart(lifecycleBeans, dependency, autoStartupOnly); + } + if (!bean.isRunning() && (!autoStartupOnly || !(bean instanceof SmartLifecycle) || ((SmartLifecycle)bean).isAutoStartup())) { + if (this.logger.isTraceEnabled()) { + this.logger.trace("Starting bean '" + beanName + "' of type [" + bean.getClass().getName() + "]"); + } + try { + bean.start(); //��ʱ Bean��ʵ��Ӧ����EurekaAutoServiceRegistration + } catch (Throwable var10) { + throw new ApplicationContextException("Failed to start bean '" + beanName + "'", var10); + } + if (this.logger.isDebugEnabled()) { + this.logger.debug("Successfully started bean '" + beanName + "'"); + } + } + } +} +``` + + + + + + + + + +��ʱ��bean.start()�����õĿ����� +EurekaAutoServiceRegistration�е�start��������Ϊ����Ȼ����ʵ����SmartLifeCycle�ӿڡ� + + + +``` +public class EurekaAutoServiceRegistration implements AutoServiceRegistration,SmartLifecycle, Ordered, SmartApplicationListener { + @Override + public void start() { + // only set the port if the nonSecurePort or securePort is 0 and this.port != 0 + if (this.port.get() != 0) { + if (this.registration.getNonSecurePort() == 0) { + this.registration.setNonSecurePort(this.port.get()); + } + if (this.registration.getSecurePort() == 0 && + this.registration.isSecure()) { + this.registration.setSecurePort(this.port.get()); + } + } + // only initialize if nonSecurePort is greater than 0 and it isn't already running + // because of containerPortInitializer below + if (!this.running.get() && this.registration.getNonSecurePort() > 0) { + this.serviceRegistry.register(this.registration); + this.context.publishEvent(new InstanceRegisteredEvent<>(this, + this.registration.getInstanceConfig())); + this.running.set(true); + } + } +} +``` + + + + + + + + + +��start�����У����ǿ��Կ��� +this.serviceRegistry.register �����������ʵ���Ͼ��Ƿ������ע��Ļ��ơ� + +��ʱthis.serviceRegistry��ʵ����Ӧ���� EurekaServiceRegistry �� ԭ���� +EurekaAutoServiceRegistration�Ĺ��췽���У�����һ����ֵ��������������췽������EurekaClientAutoConfiguration ����Զ�װ�����б�װ��ͳ�ʼ���ģ��������¡� + + + +``` +@Bean +@ConditionalOnBean(AutoServiceRegistrationProperties.class) +@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) +public EurekaAutoServiceRegistration eurekaAutoServiceRegistration( + ApplicationContext context, EurekaServiceRegistry registry, + EurekaRegistration registration) { + return new EurekaAutoServiceRegistration(context, registry, registration); +} +``` + + + + + + + + + +**2.2 �����ע������** +���������Ƿ�������ע������� + + + +``` +public class EurekaAutoServiceRegistration implements AutoServiceRegistration, +SmartLifecycle, Ordered, SmartApplicationListener { + @Override + public void start() { + //ʡ��... + this.serviceRegistry.register(this.registration); + this.context.publishEvent(new InstanceRegisteredEvent<> this,this.registration.getInstanceConfig())); + } +} +``` + + + + + + + + + +this.serviceRegistry.register(this.registration); �������ջ���� + +EurekaServiceRegistry ���е� register ������ʵ�ַ���ע�� + +**2.2.1 register** + + + +``` +@Override +public void register(EurekaRegistration reg) { + maybeInitializeClient(reg); + if (log.isInfoEnabled()) { + log.info("Registering application " + + reg.getApplicationInfoManager().getInfo().getAppName() + + " with eureka with status " + + reg.getInstanceConfig().getInitialStatus()); + } + //���õ�ǰʵ����״̬��һ�����ʵ����״̬�����仯��ֻҪ״̬����DOWN����ô�ͻᱻ��������������ִ�з���ע�ᡣ + reg.getApplicationInfoManager().setInstanceStatus(reg.getInstanceConfig().getInitialStatus()); + //���ý������Ĵ��� + reg.getHealthCheckHandler().ifAvailable(healthCheckHandler -> reg.getEurekaClient().registerHealthCheck(healthCheckHandler)); +} +``` + + + + + + + + + +����������������ע�᷽���в�û����������Eureka�ķ���ȥִ��ע�ᣬ���ǽ���������һ��״̬�Լ����ý�����鴦���������Ǽ�����һ�� +reg.getApplicationInfoManager().setInstanceStatus������ + + + +``` +public synchronized void setInstanceStatus(InstanceStatus status) { + InstanceStatus next = instanceStatusMapper.map(status); + if (next == null) { + return; + } + InstanceStatus prev = instanceInfo.setStatus(next); + if (prev != null) { + for (StatusChangeListener listener : listeners.values()) { + try { + listener.notify(new StatusChangeEvent(prev, next)); + } catch (Exception e) { + logger.warn("failed to notify listener: {}", listener.getId(),e); + } + } + } +} +``` + + + + + + + + + +����������У�����ͨ��������������һ��״̬����¼���ok����ʱlistener��ʵ����StatusChangeListener ��Ҳ���ǵ��� StatusChangeListener ��notify����������¼��Ǵ���һ������״̬�����Ӧ�����еط����������¼���Ȼ���������¼�����ע�ᡣ + +���ʱ��������Ϊ�ҵ��˷���Ȼ������ȥһ������������һ���ӿڡ��������Ƿ������Ǿ�̬���ڲ��ӿڣ����޷�ֱ�ӿ�������ʵ���ࡣ + +���Ҷ���Դ���Ķ����飬�����������ң���Ϊ�һ������ܲ²⵽һ������ij���ط����˳�ʼ���Ĺ��������ǣ������ҵ� +EurekaServiceRegistry.register�����е� reg.getApplicationInfoManager ���ʵ����ʲô���������Ƿ���ApplicationInfoManager��������EurekaRegistration������е����ԡ���EurekaRegistration������EurekaAutoServiceRegistration�������ʵ�����ġ��������룬�Dz������Զ�װ��������ʲô�����������ҵ�EurekaClientAutoConfiguration����࣬��Ȼ������Bean��һЩ�Զ�װ�䣬���а��� EurekaClient �� ApplicationInfoMangager �� EurekaRegistration �ȡ� + +**2.2.2 EurekaClientConfiguration** + + + +``` +@Configuration(proxyBeanMethods = false) +@ConditionalOnMissingRefreshScope +protected static class EurekaClientConfiguration { + @Autowired + private ApplicationContext context; + @Autowired + private AbstractDiscoveryClientOptionalArgs optionalArgs; + @Bean(destroyMethod = "shutdown") + @ConditionalOnMissingBean(value = EurekaClient.class,search = SearchStrategy.CURRENT) + public EurekaClient eurekaClient(ApplicationInfoManager manager,EurekaClientConfig config) { + return new CloudEurekaClient(manager, config, this.optionalArgs,this.context); + } + @Bean + @ConditionalOnMissingBean(value = ApplicationInfoManager.class,search = SearchStrategy.CURRENT) + public ApplicationInfoManager eurekaApplicationInfoManager( + EurekaInstanceConfig config) { + InstanceInfo instanceInfo = new InstanceInfoFactory().create(config); + return new ApplicationInfoManager(config, instanceInfo); + } + @Bean + @ConditionalOnBean(AutoServiceRegistrationProperties.class) + @ConditionalOnProperty( + value = "spring.cloud.service-registry.auto-registration.enabled", + matchIfMissing = true) + public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient,CloudEurekaInstanceConfig + instanceConfig,ApplicationInfoManager applicationInfoManager, @Autowired(required = false) + ObjectProvider healthCheckHandler) { + return EurekaRegistration.builder(instanceConfig).with(applicationInfoManager).with(eurekaClient).with(healthCheckHandler).build(); + } +} +``` + + + + + + + + + +���ѷ��֣������ƺ�������һ������Ҫ��Bean��������ʱ�������Զ�װ�䣬Ҳ����CloudEurekaClient �����������������ҿ��Ժ����׵�ʶ�𲢲²������Eureka�ͻ��˵�һ�������࣬����ʵ�ֺͷ���˵�ͨ���Լ�����������Ǻܶ�Դ��һ�����·��Ҫô�ڹ��췽������ȥ���ܶ�ij�ʼ����һЩ��ִ̨�еij��������Ҫô����ͨ���첽�¼��ķ�ʽ�����������ţ����ǿ�һ��CloudEurekaClient�ij�ʼ�����̣����Ĺ��췽���л�ͨ�� super ���ø���Ĺ��췽����Ҳ����DiscoveryClient�Ĺ��졣 + +**2.2.3 CloudEurekaClient** +super(applicationInfoManager, config, args);���ø���Ĺ��췽������CloudEurekaClient�ĸ�����DiscoveryClient. + + + +``` +public CloudEurekaClient(ApplicationInfoManager applicationInfoManager,EurekaClientConfig config,AbstractDiscoveryClientOptionalArgs args,ApplicationEventPublisher publisher) { + super(applicationInfoManager, config, args); + this.applicationInfoManager = applicationInfoManager; + this.publisher = publisher; + this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class,"eurekaTransport"); + ReflectionUtils.makeAccessible(this.eurekaTransportField); +} +``` + + + + + + + + + +**2.2.4 DiscoveryClient����** +���ǿ��Կ��������յ�DiscoveryClient���췽���У��зdz����Ĵ��롣��ʵ�ܶ������Բ���Ҫ���ģ��󲿷ֶ���һЩ��ʼ�������������ʼ���˼�����ʱ���� + +* scheduler + +* heartbeatExecutor ������ʱ���� + +* cacheRefreshExecutor ��ʱȥͬ������˵�ʵ���б� + + + + ``` + DiscoveryClient(ApplicationInfoManager applicationInfoManager,EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,Provider backupRegistryProvider,EndpointRandomizer endpointRandomizer) { + //ʡ�Բ��ִ���... + //�Ƿ�Ҫ��eureka server�ϻ�ȡ�����ַ��Ϣ + if (config.shouldFetchRegistry()) { + this.registryStalenessMonitor = new ThresholdLevelsMetric(this,METRIC_REGISTRY_PREFIX + "lastUpdateSec_", new long[]{15L, 30L, 60L, 120L, 240L,480L}); + } else { + this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC; + } + //�Ƿ�Ҫע�ᵽeureka server�� + if (config.shouldRegisterWithEureka()) { + this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this,METRIC_REGISTRATION_PREFIX + "lastHeartbeatSec_", new long[]{15L, 30L, 60L,120L, 240L, 480L}); + } else { + this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC; + } + //�������Ҫע�Ტ�Ҳ���Ҫ���·����ַ + if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) { + + return; // no need to setup up an network tasks and we are done + } + try { + // default size of 2 - 1 each for heartbeat and cacheRefresh + scheduler = Executors.newScheduledThreadPool(2,new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-%d") + .setDaemon(true) + .build()); + heartbeatExecutor = new ThreadPoolExecutor(1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, + TimeUnit.SECONDS, + new SynchronousQueue(), + new ThreadFactoryBuilder() + .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d") + .setDaemon(true) + .build() + ); // use direct handoff + cacheRefreshExecutor = new ThreadPoolExecutor( + 1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, + TimeUnit.SECONDS, + new SynchronousQueue(), + new ThreadFactoryBuilder() + .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d") + .setDaemon(true) + .build() + ); // use direct handoff + eurekaTransport = new EurekaTransport(); + scheduleServerEndpointTask(eurekaTransport, args); + AzToRegionMapper azToRegionMapper; + if (clientConfig.shouldUseDnsForFetchingServiceUrls()) { + azToRegionMapper = new DNSBasedAzToRegionMapper(clientConfig); + } else { + azToRegionMapper = new PropertyBasedAzToRegionMapper(clientConfig); + } + if (null != remoteRegionsToFetch.get()) { + + azToRegionMapper.setRegionsToFetch(remoteRegionsToFetch.get().split(",")); + } + instanceRegionChecker = new InstanceRegionChecker(azToRegionMapper, + clientConfig.getRegion()); + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize DiscoveryClient!", e); + } + //�����Ҫע�ᵽEureka server�����ǿ����˳�ʼ����ʱ��ǿ��ע�ᣬ�����register()�������ע�� + if (clientConfig.shouldRegisterWithEureka() && + clientConfig.shouldEnforceRegistrationAtInit()) { + try { + if (!register() ) { + throw new IllegalStateException("Registration error at startup.Invalid server response."); + } + } catch (Throwable th) { + logger.error("Registration error at startup: {}", th.getMessage()); + throw new IllegalStateException(th); + } + } + // finally, init the schedule tasks (e.g. cluster resolvers, heartbeat,instanceInfo replicator, fetch + initScheduledTasks(); + } + ``` + + + + + + + + + +**2.2.5 initScheduledTasks** +initScheduledTasks ȥ����һ����ʱ���� + +* ��������˿�����ע������ˢ�·����б�����Ὺ��cacheRefreshExecutor�����ʱ���� +* ��������˷���ע�ᵽEureka����ͨ����Ҫ����������. + +1. �������������� + +ͨ���ڲ�����ʵ����StatusChangeListener ʵ��״̬��ؽӿڣ��������ǰ�������ڷ��������������������ģ�����notify�ķ�����ʵ���ϻ����������֡� + + + +``` +private void initScheduledTasks() { + //��������˿�����ע������ˢ�·����б�����Ὺ��cacheRefreshExecutor�����ʱ���� + if (clientConfig.shouldFetchRegistry()) { + // registry cache refresh timer + int registryFetchIntervalSeconds = + clientConfig.getRegistryFetchIntervalSeconds(); + int expBackOffBound = + clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); + scheduler.schedule( + new TimedSupervisorTask( + "cacheRefresh", + scheduler, + cacheRefreshExecutor, + registryFetchIntervalSeconds, + TimeUnit.SECONDS, + expBackOffBound, + new CacheRefreshThread() + ), + registryFetchIntervalSeconds, TimeUnit.SECONDS); + } + //��������˷���ע�ᵽEureka����ͨ����Ҫ���������� + if (clientConfig.shouldRegisterWithEureka()) { + int renewalIntervalInSecs = + instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); + int expBackOffBound = + clientConfig.getHeartbeatExecutorExponentialBackOffBound(); + logger.info("Starting heartbeat executor: " + "renew interval is: {}", + renewalIntervalInSecs); + // Heartbeat timer + scheduler.schedule( + new TimedSupervisorTask( + "heartbeat", + scheduler, + heartbeatExecutor, + renewalIntervalInSecs, + TimeUnit.SECONDS, + expBackOffBound, + new HeartbeatThread() + ), + renewalIntervalInSecs, TimeUnit.SECONDS); + // InstanceInfo replicator ��ʼ��һ��:instanceInfoReplicator + instanceInfoReplicator = new InstanceInfoReplicator( + this, + instanceInfo, + clientConfig.getInstanceInfoReplicationIntervalSeconds(), + 2); // burstSize + statusChangeListener = new ApplicationInfoManager.StatusChangeListener() + { + @Override + public String getId() { + return "statusChangeListener"; + } + @Override + public void notify(StatusChangeEvent statusChangeEvent) { + if (InstanceStatus.DOWN == statusChangeEvent.getStatus() || + InstanceStatus.DOWN == + statusChangeEvent.getPreviousStatus()) { + // log at warn level if DOWN was involved + logger.warn("Saw local status change event {}", + statusChangeEvent); + } else { + logger.info("Saw local status change event {}", + statusChangeEvent); + } + instanceInfoReplicator.onDemandUpdate(); + } + }; + //ע��ʵ��״̬�仯�ļ��� + if (clientConfig.shouldOnDemandUpdateStatusChange()) { + applicationInfoManager.registerStatusChangeListener(statusChangeListener); + } + //����һ��ʵ����Ϣ����������Ҫ����Ϊ�˿���һ����ʱ�̣߳�ÿ40���ж�ʵ����Ϣ�Ƿ�������������������ע�� + instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationInte + rvalSeconds()); + } else { + logger.info("Not registering with Eureka server per configuration"); + } +} +``` + + + + + + + + + +**2.2.6 onDemandUpdate** +�����������Ҫ�����Ǹ���ʵ�������Ƿ����仯������������ע�����ĵ����ݡ� + + + +``` +public boolean onDemandUpdate() { + //�����ж� + if (rateLimiter.acquire(burstSize, allowedRatePerMinute)) { + if (!scheduler.isShutdown()) { + //�ύһ������ + scheduler.submit(new Runnable() { + @Override + public void run() { + logger.debug("Executing on-demand update of local InstanceInfo"); + //ȡ��֮ǰ�Ѿ��ύ������Ҳ������start�������ύ�ĸ��������������û��ִ����ɣ���ȡ��֮ǰ������ + Future latestPeriodic = scheduledPeriodicRef.get(); + if (latestPeriodic != null && !latestPeriodic.isDone()) { + logger.debug("Canceling the latest scheduled update, it will be rescheduled at the end of on demand update"); + latestPeriodic.cancel(false);//���������δ��ɣ�������ȡ�� + } + //ͨ������run����������������ʱ��ִ�У��൱�������������е�һ�� + InstanceInfoReplicator.this.run(); + } + }); + return true; + } else { + logger.warn("Ignoring onDemand update due to stopped scheduler"); + return false; + } + } else { + logger.warn("Ignoring onDemand update due to rate limiter"); + return false; + } +} +``` + + + + + + + + + +**2.2.7 run** +run����ʵ���Ϻ�ǰ���Զ�װ����ִ�еķ���ע�᷽����һ���ģ�Ҳ���ǵ��� register �������з���ע�ᣬ������finally�У�ÿ30s�ᶨʱִ��һ�µ�ǰ��run �������м�顣 + + + +``` +public void run() { + try { + discoveryClient.refreshInstanceInfo(); + Long dirtyTimestamp = instanceInfo.isDirtyWithTime(); + if (dirtyTimestamp != null) { + discoveryClient.register(); + instanceInfo.unsetIsDirty(dirtyTimestamp); + } + } catch (Throwable t) { + logger.warn("There was a problem with the instance info replicator", t); + } finally { + Future next = scheduler.schedule(this, replicationIntervalSeconds, + TimeUnit.SECONDS); + scheduledPeriodicRef.set(next); + } +} +``` + + + + + + + + + +**2.2.8 register** +���գ����������ҵ�����ע�������ˣ� +eurekaTransport.registrationClient.register ���յ��õ��� AbstractJerseyEurekaHttpClient#register(...)`�� ��Ȼ�������Լ�ȥ�����룬�ͻᷢ��ȥ����֮ǰ�кܶ�������ȥ�Ĵ��룬���繤��ģʽ��װ����ģʽ�ȡ� + + + +``` +boolean register() throws Throwable { + logger.info(PREFIX + "{}: registering service...", appPathIdentifier); + EurekaHttpResponse httpResponse; + try { + httpResponse = eurekaTransport.registrationClient.register(instanceInfo); + } catch (Exception e) { + logger.warn(PREFIX + "{} - registration failed {}", appPathIdentifier,e.getMessage(), e); + throw e; + } + if (logger.isInfoEnabled()) { + logger.info(PREFIX + "{} - registration status: {}", appPathIdentifier,httpResponse.getStatusCode()); + } + return httpResponse.getStatusCode() == Status.NO_CONTENT.getStatusCode(); +} +``` + + + + + + + + + +����Ȼ�������Ƿ�����һ��http���󣬷���Eureka-Server��apps/${APP_NAME}�ӿڣ�����ǰ����ʵ������Ϣ���͵�Eureka Server���б��档 + +���ˣ����ǻ������Ѿ�֪��Spring Cloud Eureka �������������ʱ��ѷ�����Ϣע�ᵽEureka Server�ϵ��ˡ� + + + +``` +public EurekaHttpResponse register(InstanceInfo info) { + String urlPath = "apps/" + info.getAppName(); + ClientResponse response = null; + try { + Builder resourceBuilder = + jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder(); + addExtraHeaders(resourceBuilder); + response = resourceBuilder + .header("Accept-Encoding", "gzip") + .type(MediaType.APPLICATION_JSON_TYPE) + .accept(MediaType.APPLICATION_JSON) + .post(ClientResponse.class, info); + return + anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build(); + } finally { + if (logger.isDebugEnabled()) { + logger.debug("Jersey HTTP POST {}/{} with instance {}; statusCode={}", serviceUrl, urlPath, info.getId(), + response == null ? "N/A" : response.getStatus()); + } + if (response != null) { + response.close(); + } + } +} +``` + + + + + + + + + +���ǣ��ƺ��ʼ�����⻹û�н����Ҳ����Spring BootӦ��������ʱ�������start���������յ��� +StatusChangeListener.notify ȥ���·����һ��״̬����û��ֱ�ӵ���register����ע�ᡣ�������Ǽ���ȥ��һ�� statusChangeListener.notify ������ + +**2.2.9 �����ܽ�** +���ˣ�����֪��Eureka Client�������ע��ʱ���������ط���ִ�з���ע������� + +1. ��Spring Boot����ʱ�������Զ�װ����ƽ�CloudEurekaClientע�뵽������������ִ���˹��췽�������ڹ��췽������һ����ʱ����ÿ40s��ִ��һ���жϣ��ж�ʵ����Ϣ�Ƿ����˱仯���������ᷢ�����ע������� +2. ��Spring Boot����ʱ��ͨ��refresh���������յ���StatusChangeListener.notify���з���״̬����ļ���������������ķ����ܵ��¼�֮���ȥִ�з���ע�ᡣ + **2.3 Server���߼�** + ��û����Դ��ʵ��֮ǰ������һ��֪�����϶�����������ķ���ʵ�����ݽ����˴洢����ô����ȥEureka Server�˿�һ�´������̡� + +��������ڣ� +com.netflix.eureka.resources.ApplicationResource.addInstance() �� + +��ҿ��Է��֣��������ṩ��REST���񣬲��õ���jersey��ʵ�ֵġ�Jersey�ǻ���JAX-RS��׼���ṩREST��ʵ�ֵ�֧�֣�����Ͳ�չ�������ˡ� + +**2.3.1 addInstance()** +��EurekaClient����register��������ע��ʱ������� +ApplicationResource.addInstance������ + +����ע����Ƿ���һ��POST������ϵ�ǰʵ����Ϣ���� ApplicationResource �� addInstance�������з���ע�ᡣ + + + +``` +@POST +@Consumes({"application/json", "application/xml"}) +public Response addInstance(InstanceInfo info, @HeaderParam("x-netflix-discovery-replication") String isReplication) { + logger.debug("Registering instance {} (replication={})", info.getId(), + isReplication); + DataCenterInfo dataCenterInfo = info.getDataCenterInfo(); + if (dataCenterInfo instanceof UniqueIdentifier) { + String dataCenterInfoId = + ((UniqueIdentifier)dataCenterInfo).getId(); + if (this.isBlank(dataCenterInfoId)) { + boolean experimental = "true".equalsIgnoreCase(this.serverConfig.getExperimental("registration.validation.dataCenterInfoId")); + if (experimental) { + String entity = "DataCenterInfo of type " + + dataCenterInfo.getClass() + " must contain a valid id"; + return Response.status(400).entity(entity).build(); + } + if (dataCenterInfo instanceof AmazonInfo) { + AmazonInfo amazonInfo = (AmazonInfo)dataCenterInfo; + String effectiveId = amazonInfo.get(MetaDataKey.instanceId); + if (effectiveId == null) { + amazonInfo.getMetadata().put(MetaDataKey.instanceId.getName(), info.getId()); + } + } else { + logger.warn("Registering DataCenterInfo of type {} without an appropriate id", dataCenterInfo.getClass()); + } + } + } + this.registry.register(info, "true".equals(isReplication)); + return Response.status(204).build(); +} + +``` + + + + + + + + + +**2.3.2 register** +���������� +PeerAwareInstanceRegistryImpl�����ϵͼ�������ϵͼ���Կ�����PeerAwareInstanceRegistry�����ӿ�ΪLeaseManager��LookupService, + +* ����LookupService������������ķ���ʾ������Ϊ +* LeaseManager�����˴����ͻ���ע�ᣬ��Լ��ע���Ȳ��� + +![SpringCloudϵ�С�Spring Cloud Դ�����֮Eureka-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/897d97d444664479bda0430417ec683f1f079a.jpg "SpringCloudϵ�С�Spring Cloud Դ�����֮Eureka-��Դ������������")�� addInstance �����У����յ��õ��� +PeerAwareInstanceRegistryImpl.register ������ + +* leaseDuration ��ʾ��Լ����ʱ�䣬Ĭ����90s��Ҳ���ǵ�����˳���90sû���յ��ͻ��˵��������������޳��ýڵ� + +* ����super.register����ڵ�ע�� + +* ����Ϣ���Ƶ�Eureka Server��Ⱥ�е����������ϣ�ͬ����ʵ��Ҳ�ܼ򵥣����ǻ�ü�Ⱥ�е����нڵ㣬Ȼ���������ע�� + + + + ``` + public void register(final InstanceInfo info, final boolean isReplication) { + int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS; + if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() >0) { + leaseDuration = info.getLeaseInfo().getDurationInSecs(); //����ͻ������Լ�����������ʱʱ�䣬����ÿͻ��˵�ʱ�� + } + super.register(info, leaseDuration, isReplication); //�ڵ�ע�� + //���Ƶ�Eureka Server��Ⱥ�е������ڵ� + replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, + null, isReplication); + } + ``` + + + + + + + + + +**2.3.3 AbstractInstanceRegistry.register** +����˵��Eureka-Server�ķ���ע�ᣬʵ�����ǽ��ͻ��˴��ݹ�����ʵ�����ݱ��浽Eureka-Server�е�ConcurrentHashMap�С� + + + +``` +public void register(InstanceInfo registrant, int leaseDuration, boolean + isReplication) { + try { + read.lock(); + //��registry�л�õ�ǰʵ����Ϣ������appName + Map> gMap = + registry.get(registrant.getAppName()); + REGISTER.increment(isReplication); //����ע������������Ϣ�� + if (gMap == null) {//�����ǰappName�ǵ�һ��ע�ᣬ���ʼ��һ��ConcurrentHashMap + final ConcurrentHashMap> gNewMap = new + ConcurrentHashMap>(); + gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap); + if (gMap == null) { + gMap = gNewMap; + } + } + //��gMap�в�ѯ�Ѿ����ڵ�Lease��Ϣ��Lease���ķ���Ϊ��Լ��ʵ�������ѷ����ṩ�ߵ�ʵ����Ϣ��װ����һ��lease�������ṩ�˶��ڸķ���ʵ������Լ���� + Lease existingLease = gMap.get(registrant.getId()); + // ��instance�Ѿ������ǣ��Ϳͻ��˵�instance����Ϣ���Ƚϣ�ʱ�����µ��Ǹ���Ϊ��Чinstance��Ϣ + if (existingLease != null && (existingLease.getHolder() != null)) { + Long existingLastDirtyTimestamp = + existingLease.getHolder().getLastDirtyTimestamp(); + Long registrationLastDirtyTimestamp = + registrant.getLastDirtyTimestamp(); + logger.debug("Existing lease found (existing={}, provided={}", + existingLastDirtyTimestamp, registrationLastDirtyTimestamp); + // this is a > instead of a >= because if the timestamps are equal,we still take the remote transmitted + // InstanceInfo instead of the server local copy. + if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) { + logger.warn("There is an existing lease and the existing lease's dirty timestamp {} is greater" + + " than the one that is being registered {}", + existingLastDirtyTimestamp, registrationLastDirtyTimestamp); + logger.warn("Using the existing instanceInfo instead of the new instanceInfo as the registrant"); + registrant = existingLease.getHolder(); + } + } else { + //��lease������ʱ�����뵽��δ��룬 + synchronized (lock) { + if (this.expectedNumberOfClientsSendingRenews > 0) { + // Since the client wants to register it, increase the number of clients sending renews + this.expectedNumberOfClientsSendingRenews = + this.expectedNumberOfClientsSendingRenews + 1; + updateRenewsPerMinThreshold(); + } + } + logger.debug("No previous lease information found; it is new registration"); + } + //����һ��lease + Lease lease = new Lease(registrant, + leaseDuration); + if (existingLease != null) { + // ��ԭ������Lease����Ϣʱ������serviceUpTimestamp, ��֤����������ʱ��һֱ�ǵ�һ��ע����Ǹ� + lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp()); + } + gMap.put(registrant.getId(), lease); + synchronized (recentRegisteredQueue) {//���ӵ����ע��Ķ����� + recentRegisteredQueue.add(new Pair( + System.currentTimeMillis(), + registrant.getAppName() + "(" + registrant.getId() + ")")); + } + // ���ʵ��״̬�Ƿ����仯������Dz��Ҵ��ڣ��򸲸�ԭ����״̬ + if (!InstanceStatus.UNKNOWN.equals(registrant.getOverriddenStatus())) { + logger.debug("Found overridden status {} for instance {}. Checking to see if needs to be add to the " + + "overrides", registrant.getOverriddenStatus(), + registrant.getId()); + if (!overriddenInstanceStatusMap.containsKey(registrant.getId())) { + logger.info("Not found overridden id {} and hence adding it", + registrant.getId()); + overriddenInstanceStatusMap.put(registrant.getId(), + registrant.getOverriddenStatus()); + } + } + InstanceStatus overriddenStatusFromMap = + overriddenInstanceStatusMap.get(registrant.getId()); + if (overriddenStatusFromMap != null) { + logger.info("Storing overridden status {} from map", + overriddenStatusFromMap); + registrant.setOverriddenStatus(overriddenStatusFromMap); + } + // Set the status based on the overridden status rules + InstanceStatus overriddenInstanceStatus = + getOverriddenInstanceStatus(registrant, existingLease, isReplication); + registrant.setStatusWithoutDirty(overriddenInstanceStatus); + // �õ�instanceStatus���ж��Ƿ���UP״̬�� + if (InstanceStatus.UP.equals(registrant.getStatus())) { + lease.serviceUp(); + } + // ����ע������Ϊ���� + registrant.setActionType(ActionType.ADDED); + // ��Լ�����¼���У���¼��ʵ����ÿ�α仯�� ����ע����Ϣ��������ȡ + recentlyChangedQueue.add(new RecentlyChangedItem(lease)); + registrant.setLastUpdatedTimestamp(); + //�û���ʧЧ + invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), + registrant.getSecureVipAddress()); + logger.info("Registered instance {}/{} with status {} (replication={})", + registrant.getAppName(), registrant.getId(), + registrant.getStatus(), isReplication); + } finally { + read.unlock(); + } +} +``` + + + + + + + + + +**2.3.4 С��** +���ˣ����ǾͰѷ���ע���ڿͻ��˺ͷ���˵Ĵ�����������һ����ϸ�ķ�����ʵ������Eureka Server�ˣ���ѿͻ��˵ĵ�ַ��Ϣ���浽ConcurrentHashMap�д洢�����ҷ����ṩ�ߺ�ע������֮�䣬�Ὠ��һ�����������ơ����ڼ�ط����ṩ�ߵĽ���״̬�� + +**2.4 Eureka �Ķ༶�������** +Eureka Server��������������(registry��readWriteCacheMap��readOnlyCacheMap)�������ע����Ϣ��Ĭ������¶�ʱ����ÿ30s��readWriteCacheMapͬ����readOnlyCacheMap��ÿ60s��������90sδ��Լ�Ľڵ㣬Eureka Clientÿ30s��readOnlyCacheMap���·���ע����Ϣ�����ͻ��˷����ע�����registry���·���ע����Ϣ�� + +**2.4.1 �༶���������** +����ΪʲôҪ��ƶ༶�����أ�ԭ��ܼ򵥣����ǵ����ڴ��ģ�ķ���ע��͸���ʱ�����ֻ���޸�һ��ConcurrentHashMap���ݣ���ô�Ʊ���Ϊ���Ĵ��ڵ��¾�����Ӱ�����ܡ� + +��Eureka����APģ�ͣ�ֻ��Ҫ�������տ��þ��С��������������õ��༶������ʵ�ֶ�д���롣ע�᷽��д��ʱ��ֱ��д�ڴ�ע�����д���֮������ʧЧ��д���档 + +��ȡע����Ϣ�ӿ��ȴ�ֻ������ȡ��ֻ������û����ȥ��д����ȡ����д����û����ȥ�ڴ�ע�����ȡ����ֻ��ȡ���˴��ϸ��ӣ������ң���д�������»�дֻ������ + +* responseCacheUpdateIntervalMs �� readOnlyCacheMap ������µĶ�ʱ��ʱ������Ĭ��Ϊ30�� +* responseCacheAutoExpirationInSeconds : readWriteCacheMap �������ʱ�䣬Ĭ��Ϊ 180 �롣 + **2.4.2 ����ע��Ļ���ʧЧ** + ��AbstractInstanceRegistry.register��������󣬻����invalidateCache(registrant.getAppName(), registrant.getVIPAddress(),registrant.getSecureVipAddress()); ������ʹ�ö�д����ʧЧ�� + + + +``` +public void invalidate(Key... keys) { + for (Key key : keys) { + logger.debug("Invalidating the response cache key : {} {} {} {}, {}", + key.getEntityType(), key.getName(), key.getVersion(), + key.getType(), key.getEurekaAccept()); + readWriteCacheMap.invalidate(key); + Collection keysWithRegions = regionSpecificKeys.get(key); + if (null != keysWithRegions && !keysWithRegions.isEmpty()) { + for (Key keysWithRegion : keysWithRegions) { + logger.debug("Invalidating the response cache key : {} {} {} {} {}", + key.getEntityType(), key.getName(), + key.getVersion(), key.getType(), key.getEurekaAccept()); + readWriteCacheMap.invalidate(keysWithRegion); + } + } + } +} +``` + + + + + + + + + +**2.4.3 ��ʱͬ������** +ResponseCacheImpl�Ĺ��췽���У�������һ����ʱ�����������ᶨʱ���д�����е����ݱ仯�����и��º�ͬ���� + + + +``` +private TimerTask getCacheUpdateTask() { + return new TimerTask() { + @Override + public void run() { + logger.debug("Updating the client cache from response cache"); + for (Key key : readOnlyCacheMap.keySet()) { + if (logger.isDebugEnabled()) { + logger.debug("Updating the client cache from response cache for key : {} {} {} {}", + key.getEntityType(), key.getName(), + key.getVersion(), key.getType()); + } + try { + CurrentRequestVersion.set(key.getVersion()); + Value cacheValue = readWriteCacheMap.get(key); + Value currentCacheValue = readOnlyCacheMap.get(key); + if (cacheValue != currentCacheValue) { + readOnlyCacheMap.put(key, cacheValue); + } + } catch (Throwable th) { + logger.error("Error while updating the client cache from response cache for key {}", key.toStringCompact(), th); + } finally { + CurrentRequestVersion.remove(); + } + } + } + }; +} +``` + + + + + + + + + +**2.5 ������Լ** +��ν�ķ�����Լ����ʵ����һ�����������ơ��ͻ��˻ᶨ�ڷ�����������Լ����ô�򵥸���ҿ�һ�´����ʵ�� + +**2.5.1 initScheduledTasks** +�ͻ��˻��� +DiscoveryClient.initScheduledTasks �У�����һ���������Ķ�ʱ���� + + + +``` +// Heartbeat timer +scheduler.schedule( + new TimedSupervisorTask( + "heartbeat", + scheduler, + heartbeatExecutor, + renewalIntervalInSecs, + TimeUnit.SECONDS, + expBackOffBound, + new HeartbeatThread() + ), + renewalIntervalInSecs, TimeUnit.SECONDS); +``` + + + + + + + + + +**2.5.2 HeartbeatThread** +Ȼ�������ʱ�����У���ִ��һ�� HearbeatThread ���̣߳�����̻߳ᶨʱ����renew()������Լ�� + + + +``` +//ÿ��30s����һ���������� +private class HeartbeatThread implements Runnable { + public void run() { + if (renew()) { + lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis(); + } + } +} +``` + + + + + + + + + +**2.5.3 ������յ���������Ĵ���** +��ApplicationResource.getInstanceInfo����ӿ��У��᷵��һ��InstanceResource��ʵ�����ڸ�ʵ���£�������һ��statusUpdate�Ľӿ�������״̬ + + + +``` +@Path("{id}") +public InstanceResource getInstanceInfo(@PathParam("id") String id) { + return new InstanceResource(this, id, serverConfig, registry); +} +``` + + + + + + + + + +**2.5.4 InstanceResource.statusUpdate()** +�ڸ÷����У������ص��ע registry.statusUpdate ���������������� +AbstractInstanceRegistry.statusUpdate������ָ�������ṩ���ڷ���˴洢����Ϣ�еı仯�� + + + +``` +@PUT +@Path("status") +public Response statusUpdate( + @QueryParam("value") String newStatus, + @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication, + @QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) { + try { + if (registry.getInstanceByAppAndId(app.getName(), id) == null) { + logger.warn("Instance not found: {}/{}", app.getName(), id); + return Response.status(Status.NOT_FOUND).build(); + } + boolean isSuccess = registry.statusUpdate(app.getName(), id, + InstanceStatus.valueOf(newStatus), lastDirtyTimestamp, + "true".equals(isReplication)); + if (isSuccess) { + logger.info("Status updated: {} - {} - {}", app.getName(), id, + newStatus); + return Response.ok().build(); + } else { + logger.warn("Unable to update status: {} - {} - {}", app.getName(), + id, newStatus); + return Response.serverError().build(); + } + } catch (Throwable e) { + logger.error("Error updating instance {} for status {}", id, + newStatus); + return Response.serverError().build(); + } +} +``` + + + + + + + + + +**2.5.5 AbstractInstanceRegistry.statusUpdate** +����������У����õ�Ӧ�ö�Ӧ��ʵ���б���Ȼ�����Lease.renew()ȥ����������Լ�� + + + +``` +public boolean statusUpdate(String appName, String id, + InstanceStatus newStatus, String + lastDirtyTimestamp, + boolean isReplication) { + try { + read.lock(); + // ����״̬�Ĵ��� ״̬ͳ�� + STATUS_UPDATE.increment(isReplication); + // �ӱ������������ȡʵ����Ϣ�� + Map> gMap = registry.get(appName); + Lease lease = null; + if (gMap != null) { + lease = gMap.get(id); + } + // ʵ�������ڣ���ֱ�ӷ��أ���ʾʧ�� + if (lease == null) { + return false; + } else { + // ִ��һ��lease��renew������������Ҫ�Ǹ��������instance��������ʱ�䡣 + lease.renew(); + // ��ȡinstanceʵ����Ϣ + InstanceInfo info = lease.getHolder(); + // Lease is always created with its instance info object. + // This log statement is provided as a safeguard, in case this invariant is violated. + if (info == null) { + logger.error("Found Lease without a holder for instance id {}", + id); + } + // ��instance��Ϣ��Ϊ��ʱ������ʵ��״̬�����˱仯 + if ((info != null) && !(info.getStatus().equals(newStatus))) { + // �����״̬��UP��״̬����ô����һ��serviceUp() , ��Ҫ�Ǹ��·����ע��ʱ + �� + if (InstanceStatus.UP.equals(newStatus)) { + lease.serviceUp(); + } + // ��instance Id �����״̬��ӳ����Ϣ���븲�ǻ���MAP����ȥ + overriddenInstanceStatusMap.put(id, newStatus); + // Set it for transfer of overridden status to replica on + // ���ø���״̬��ʵ����Ϣ����ȥ + info.setOverriddenStatus(newStatus); + long replicaDirtyTimestamp = 0; + info.setStatusWithoutDirty(newStatus); + if (lastDirtyTimestamp != null) { + replicaDirtyTimestamp = Long.valueOf(lastDirtyTimestamp); + } + // If the replication's dirty timestamp is more than the existing one, just update + // it to the replica's. + // ���replicaDirtyTimestamp ��ʱ�����instance��getLastDirtyTimestamp() ,����� + + if (replicaDirtyTimestamp > info.getLastDirtyTimestamp()) { + info.setLastDirtyTimestamp(replicaDirtyTimestamp); + } + info.setActionType(ActionType.MODIFIED); + recentlyChangedQueue.add(new RecentlyChangedItem(lease)); + info.setLastUpdatedTimestamp(); + //����д���� + invalidateCache(appName, info.getVIPAddress(), + info.getSecureVipAddress()); + } + return true; + } + } finally { + read.unlock(); + } +} +``` + + + + + + + + + +���ˣ�������Լ���ܾͷ�������ˡ� + +**2.6 ������** +���Ǽ������о�����ķ��ֹ��̣����ǿͻ�����Ҫ�ܹ������������� + +��������ʱ���ȡָ�������ṩ�ߵĵ�ַ�б� +Eureka server�˵�ַ�����仯ʱ����Ҫ��̬��֪ +**2.6.1 DiscoveryClient����ʱ��ѯ** +���췽���У������ǰ�Ŀͻ���Ĭ�Ͽ�����fetchRegistry������eureka-server����ȡ���ݡ� + + + +``` +DiscoveryClient(ApplicationInfoManager applicationInfoManager, + EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, + Provider backupRegistryProvider, + EndpointRandomizer endpointRandomizer) { + if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) { + fetchRegistryFromBackup(); + } +} +``` + + + + + + + + + +**2.6.2 fetchRegistry** + + + +``` +private boolean fetchRegistry(boolean forceFullRegistryFetch) { + Stopwatch tracer = FETCH_REGISTRY_TIMER.start(); + try { + // If the delta is disabled or if it is the first time, get all + // applications + Applications applications = getApplications(); + if (clientConfig.shouldDisableDelta() + || + (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress())) + || forceFullRegistryFetch + || (applications == null) + || (applications.getRegisteredApplications().size() == 0) + || (applications.getVersion() == -1)) //Client application does not have latest library supporting delta + { + logger.info("Disable delta property : {}", + clientConfig.shouldDisableDelta()); + logger.info("Single vip registry refresh property : {}", + clientConfig.getRegistryRefreshSingleVipAddress()); + logger.info("Force full registry fetch : {}", + forceFullRegistryFetch); + logger.info("Application is null : {}", (applications == null)); + logger.info("Registered Applications size is zero : {}", + (applications.getRegisteredApplications().size() == 0)); + logger.info("Application version is -1: {}", + (applications.getVersion() == -1)); + getAndStoreFullRegistry(); + } else { + getAndUpdateDelta(applications); + } + applications.setAppsHashCode(applications.getReconcileHashCode()); + logTotalInstances(); + } catch (Throwable e) { + logger.error(PREFIX + "{} - was unable to refresh its cache! status = {}", appPathIdentifier, e.getMessage(), e); + return false; + } finally { + if (tracer != null) { + tracer.stop(); + } + } + // Notify about cache refresh before updating the instance remote status + onCacheRefreshed(); + // Update remote status based on refreshed data held in the cache + updateInstanceRemoteStatus(); + // registry was fetched successfully, so return true + return true; +} +``` + + + + + + + + + +**2.6.3 ��ʱˢ�±��ص�ַ�б�** +����ÿ��30s����һ�� +��DiscoveryClient�����ʱ�򣬻��ʼ��һЩ���������ǰ�����Ƿ������ˡ�������һ������̬���±��ط����ַ�б����� cacheRefreshTask �� + +�����������ִ�е���CacheRefreshThread����̡߳�����һ��������ִ�е����񣬾�����������һ�¡� + + + +``` +private void initScheduledTasks() { + if (clientConfig.shouldFetchRegistry()) { + // registry cache refresh timer + int registryFetchIntervalSeconds = + clientConfig.getRegistryFetchIntervalSeconds(); + int expBackOffBound = + clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); + cacheRefreshTask = new TimedSupervisorTask( + "cacheRefresh", + scheduler, + cacheRefreshExecutor, + registryFetchIntervalSeconds, + TimeUnit.SECONDS, + expBackOffBound, + new CacheRefreshThread() + ); + scheduler.schedule( + cacheRefreshTask, + registryFetchIntervalSeconds, TimeUnit.SECONDS); + } +``` + + + + + + + + + +**2.6.4 TimedSupervisorTask** +�������Ͽ���TimedSupervisorTask�ǹ̶����������������һ��������ʱ�ͻὫ��һ�����ڵļ��ʱ��������������ʱ����ôÿ�μ��ʱ�䶼������һ����һֱ�����ⲿ�����趨������Ϊֹ��һ���������ٳ�ʱ�����ʱ���ֻ��Զ��ָ�Ϊ��ʼֵ��������ƻ���ֵ��ѧϰ�ġ� + + + +``` +public void run() { + Future future = null; + try { + //ʹ��Future�������趨���̵߳ij�ʱʱ�䣬������ǰ�߳̾Ͳ������޵ȴ��� + future = executor.submit(task); + threadPoolLevelGauge.set((long) executor.getActiveCount()); + //ָ���ȴ����̵߳��ʱ�� + future.get(timeoutMillis, TimeUnit.MILLISECONDS); // block until done or timeout + //delay�Ǹ������õı�����������õ�������ǵ�ÿ��ִ������ɹ����Ὣdelay���� + delay.set(timeoutMillis); + threadPoolLevelGauge.set((long) executor.getActiveCount()); + } catch (TimeoutException e) { + logger.error("task supervisor timed out", e); + timeoutCounter.increment(); + long currentDelay = delay.get(); + //�����̳߳�ʱ��ʱ�򣬾Ͱ�delay���������������ᳬ���ⲿ����ʱ�趨�������ʱʱ�� + long newDelay = Math.min(maxDelay, currentDelay * 2); + //����Ϊ���µ�ֵ�����ǵ����̣߳���������CAS + delay.compareAndSet(currentDelay, newDelay); + } catch (RejectedExecutionException e) { + //һ���̳߳ص����������з����˴��������񣬴����˾ܾ����ԣ��ͻὫ������ͣ�� + if (executor.isShutdown() || scheduler.isShutdown()) { + logger.warn("task supervisor shutting down, reject the task", e); + } else { + logger.error("task supervisor rejected the task", e); + } + rejectedCounter.increment(); + } catch (Throwable e) { + //һ������δ֪���쳣����ͣ�������� + if (executor.isShutdown() || scheduler.isShutdown()) { + logger.warn("task supervisor shutting down, can't accept the task"); + } else { + logger.error("task supervisor threw an exception", e); + } + throwableCounter.increment(); + } finally { + //��������Ҫôִ����ϣ�Ҫô�����쳣������cancel�������������� + if (future != null) { + future.cancel(true); + } + //ֻҪ������û��ֹͣ������ָ���ȴ�ʱ��֮����ִ��һ��ͬ�������� + if (!scheduler.isShutdown()) { + //������������������ԭ��ֻҪû��ֹͣ�����������ٴ���һ��������ִ��ʱ��ʱdealy��ֵ�� + //�����ⲿ����ʱ����ij�ʱʱ��Ϊ30�루���췽�������timeout���������ʱ��Ϊ50��(���췽�������expBackOffBound) + //������һ������û�г�ʱ����ô����30���ʼ������ + //������һ������ʱ�ˣ���ô����50���ʼ�������쳣�������и����Զ��IJ��������Զ����60�볬���������50�룩 + scheduler.schedule(this, delay.get(), TimeUnit.MILLISECONDS); + } + } +} +``` + + + + + + + + + +**2.6.5 refreshRegistry** +��δ�����Ҫ�����߼� + +* �ж�remoteRegions�Ƿ����˱仯 + +* ����fetchRegistry��ȡ���ط����ַ���� + + + + ``` + @VisibleForTesting + void refreshRegistry() { + try { + boolean isFetchingRemoteRegionRegistries = + isFetchingRemoteRegionRegistries(); + boolean remoteRegionsModified = false; + //���������aws�����ϣ����ж����һ��Զ��������µ���Ϣ�͵�ǰԶ��������Ϣ���бȽϣ��������ȣ������ + String latestRemoteRegions = + clientConfig.fetchRegistryForRemoteRegions(); + if (null != latestRemoteRegions) { + String currentRemoteRegions = remoteRegionsToFetch.get(); + if (!latestRemoteRegions.equals(currentRemoteRegions)) { + //�ж����һ�� + } + boolean success = fetchRegistry(remoteRegionsModified); + if (success) { + registrySize = localRegionApps.get().size(); + lastSuccessfulRegistryFetchTimestamp = + System.currentTimeMillis(); + } + // ʡ�� + } catch (Throwable e) { + logger.error("Cannot fetch registry from server", e); + } + } + ``` + + + + + + + + + +**2.6.6 fetchRegistry** + + + + ``` + private boolean fetchRegistry(boolean forceFullRegistryFetch) { + Stopwatch tracer = FETCH_REGISTRY_TIMER.start(); + try { + // If the delta is disabled or if it is the first time, get all + // applications + // ȡ�����ػ���ķ����б���Ϣ + Applications applications = getApplications(); + //�ж϶��������ȷ���Ƿ񴥷�ȫ�����£�������һ�����㶼��ȫ�����£� + //1\. �Ƿ�����������£� + //2\. �Ƿ��ij��region�ر��ע�� + //3\. �ⲿ����ʱ�Ƿ�ͨ�����ָ��ȫ�����£� + //4\. ���ػ�δ������Ч�ķ����б���Ϣ�� + if (clientConfig.shouldDisableDelta() + || + (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress())) + || forceFullRegistryFetch + || (applications == null) + || (applications.getRegisteredApplications().size() == 0) + || (applications.getVersion() == -1)) //Client application does not + have latest library supporting delta + { + //����ȫ������ + getAndStoreFullRegistry(); + } else { + //������������ + getAndUpdateDelta(applications); + } + //���¼��������һ����hash�� + applications.setAppsHashCode(applications.getReconcileHashCode()); + logTotalInstances(); //��־��ӡ����Ӧ�õ�����ʵ����֮�� + } catch (Throwable e) { + logger.error(PREFIX + "{} - was unable to refresh its cache! status = {}", appPathIdentifier, e.getMessage(), e); + return false; + } finally { + if (tracer != null) { + tracer.stop(); + } + } + //�����ػ�����µ��¼��㲥��������ע��ļ�������ע��÷����ѱ�CloudEurekaClient����д + onCacheRefreshed(); + // Update remote status based on refreshed data held in the cache + //���ոո��µĻ����У�������Eureka server�ķ����б������а����˵�ǰӦ�õ�״̬�� + //��ǰʵ���ij�Ա����lastRemoteInstanceStatus����¼�������һ�θ��µĵ�ǰӦ��״̬�� + //��������״̬��updateInstanceRemoteStatus���������Ƚ� �������һ�£��͸���lastRemoteInstanceStatus�����ҹ㲥��Ӧ���¼� + updateInstanceRemoteStatus(); + // registry was fetched successfully, so return true + return true; + } + ``` + + + + + + + + + +**2.6.7 getAndStoreFullRegistry** +��eureka server�˻�ȡ����ע�����ĵĵ�ַ��Ϣ��Ȼ����²����õ����ػ��� localRegionApps �� + + + +``` +private void getAndStoreFullRegistry() throws Throwable { + long currentUpdateGeneration = fetchRegistryGeneration.get(); + logger.info("Getting all instance registry info from the eureka server"); + Applications apps = null; + EurekaHttpResponse httpResponse = + clientConfig.getRegistryRefreshSingleVipAddress() == null + ? eurekaTransport.queryClient.getApplications(remoteRegionsRef.get()) + : + eurekaTransport.queryClient.getVip(clientConfig.getRegistryRefreshSingleVipAddre + ss(), remoteRegionsRef.get()); + if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) { + apps = httpResponse.getEntity(); + } + logger.info("The response status is {}", httpResponse.getStatusCode()); + if (apps == null) { + logger.error("The application is null for some reason. Not storing this information"); + } else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, + currentUpdateGeneration + 1)) { + localRegionApps.set(this.filterAndShuffle(apps)); + logger.debug("Got full registry with apps hashcode {}", + apps.getAppsHashCode()); + } else { + logger.warn("Not updating applications as another thread is updating it already"); + } +} +``` + + + + + + + + + +**2.6.8 ����˲�ѯ�����ַ����** +ǰ������֪�����ͻ��˷�������ַ�IJ�ѯ�����֣�һ����ȫ������һ��������������ȫ����ѯ�����󣬻����Eureka-server��ApplicationsResource��getContainers������ + +�������������󣬻���� +ApplicationsResource.getContainerDifferential�� + +**2.6.9 ApplicationsResource.getContainers** +���տͻ��˷��͵Ļ�ȡȫ��ע����Ϣ���� + + + +``` +@GET +public Response getContainers(@PathParam("version") String version, + @HeaderParam(HEADER_ACCEPT) String acceptHeader, + @HeaderParam(HEADER_ACCEPT_ENCODING) String + acceptEncoding, + @HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) + String eurekaAccept, + @Context UriInfo uriInfo, + @Nullable @QueryParam("regions") String + regionsStr) { + boolean isRemoteRegionRequested = null != regionsStr && + !regionsStr.isEmpty(); + String[] regions = null; + if (!isRemoteRegionRequested) { + EurekaMonitors.GET_ALL.increment(); + } else { + regions = regionsStr.toLowerCase().split(","); + Arrays.sort(regions); // So we don't have different caches for same regions queried in different order. + EurekaMonitors.GET_ALL_WITH_REMOTE_REGIONS.increment(); + } + // EurekaServer�޷��ṩ���񣬷���403 + if (!registry.shouldAllowAccess(isRemoteRegionRequested)) { + return Response.status(Status.FORBIDDEN).build(); + } + CurrentRequestVersion.set(Version.toEnum(version)); + KeyType keyType = Key.KeyType.JSON;// ���÷������ݸ�ʽ��Ĭ��JSON + String returnMediaType = MediaType.APPLICATION_JSON; + if (acceptHeader == null || !acceptHeader.contains(HEADER_JSON_VALUE)) { + // ������յ�������ͷ��û�о����ʽ��Ϣ���򷵻ظ�ʽΪXML + keyType = Key.KeyType.XML; + returnMediaType = MediaType.APPLICATION_XML; + } + // ��������� + Key cacheKey = new Key(Key.EntityType.Application, + ResponseCacheImpl.ALL_APPS, + keyType, CurrentRequestVersion.get(), + EurekaAccept.fromString(eurekaAccept), regions + ); + // ���ز�ͬ�ı������͵����ݣ�ȥ������ȡ���ݵķ�������һ�� + Response response; + if (acceptEncoding != null && acceptEncoding.contains(HEADER_GZIP_VALUE)) { + response = Response.ok(responseCache.getGZIP(cacheKey)) + .header(HEADER_CONTENT_ENCODING, HEADER_GZIP_VALUE) + .header(HEADER_CONTENT_TYPE, returnMediaType) + .build(); + } else { + response = Response.ok(responseCache.get(cacheKey)) + .build(); + } + CurrentRequestVersion.remove(); + return response; +} +``` + + + + + + + + + +**2.6.10 responseCache.getGZIP** +�ӻ����ж�ȡ���ݡ� + + + +``` +public byte[] getGZIP(Key key) { + Value payload = getValue(key, shouldUseReadOnlyResponseCache); + if (payload == null) { + return null; + } + return payload.getGzipped(); +} +Value getValue(final Key key, boolean useReadOnlyCache) { + Value payload = null; + try { + if (useReadOnlyCache) { + final Value currentPayload = readOnlyCacheMap.get(key); + if (currentPayload != null) { + payload = currentPayload; + } else { + payload = readWriteCacheMap.get(key); + readOnlyCacheMap.put(key, payload); + } + } else { + payload = readWriteCacheMap.get(key); + } + } catch (Throwable t) { + logger.error("Cannot get value for key : {}", key, t); + } + return payload; +} +``` \ 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/Spring Cloud Gateway\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/Spring Cloud Gateway\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..9a6c22c --- /dev/null +++ "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/Spring Cloud Gateway\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,407 @@ +**ѧϰĿ��** + +1. Gateway����ԭ������ + **��1�� Bean��׼��** + ǰ��Ҳ������ô������ˣ��������Ǽ��� + spring-cloud-starter-gateway������֣�����һ��starter�����������˵����ȥ��spring.factories�ļ�������һ������Щ��Ҫ��bean���Զ�װ���IoC���������ˡ� + +![SpringCloudϵ�С�Spring Cloud Դ�����֮Gateway����-��Դ������������](https://dl-harmonyos.51cto.com/images/202207/788f3c1494307a2ad7d935811c9e62bab2c435.jpg "SpringCloudϵ�С�Spring Cloud Դ�����֮Gateway����-��Դ������������")1.������ +GatewayClassPathWarningAutoConfiguration��������� + + + +``` +@Configuration(proxyBeanMethods = false) +//��ǰ��������GatewayAutoConfiguration�������������֮ǰ���� +@AutoConfigureBefore(GatewayAutoConfiguration.class) +public class GatewayClassPathWarningAutoConfiguration { + ... + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet") + protected static class SpringMvcFoundOnClasspathConfiguration { + public SpringMvcFoundOnClasspathConfiguration() { + log.warn(BORDER + + "Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. " + + "Please remove spring-boot-starter-web dependency." + BORDER); + } + + } + @Configuration(proxyBeanMethods = false) + @ConditionalOnMissingClass("org.springframework.web.reactive.DispatcherHandler") + protected static class WebfluxMissingFromClasspathConfiguration { + + public WebfluxMissingFromClasspathConfiguration() { + log.warn(BORDER + "Spring Webflux is missing from the classpath, " + + "which is required for Spring Cloud Gateway at this time. " + + "Please add spring-boot-starter-webflux dependency." + BORDER); + } + } +} +``` + + + + + + + + + +������������ܿ���������ʵ���Ͼ�ͨ��ConditionOnClass��ConditionOnMissingClass��������������־��ӡ�Ĺ��ܣ����ClassPath���� +org.springframework.web.servlet.DispatcherServlet��Ļ�����ʵ����һ��Bean����Ȼ���ӡ��־����������spring-boot-starter-web�������Ȼ���ټ��ClassPath���Ƿ�����ȷ������webflux�����û�У����ӡ��־����spring-boot-starter-webflux������ + +2.����������GatewayAutoConfiguration + +��Ϊ����̫��������Ͳ�չʾ�ˣ�������оټ����Ƚ���Ҫ�� + +* PropertiesRouteDefinitionLocator�����ڴ������ļ���yml/properties���ж�ȡ·��������Ϣ�� +* RouteDefinitionLocator���� RouteDefinition ת��Ϊ Route +* RoutePredicateHandlerMapping�������� mvc ��HandlerMapping������������ Gatewayʵ�ֵġ�����ƥ���Ӧ������route +* GatewayProperties��yml������Ϣ��װ�� GatewayProperties ������ +* AfterRoutePredicateFactory������·�ɶ��Թ�����������Щ���Թ���������ʱ�Ѿ����ɶ�Ӧ��bean�����Dzſ����� yml ������һ�£�������Ч +* RetryGatewayFilterFactory������ Gateway ��������������Щ������������ʱ�Ѿ����ɶ�Ӧ��bean�����Dzſ����� yml ������һ�£�������Ч +* GlobalFilterʵ���ࣺȫ�ֹ����� + +3.HttpHandlerAutoConfiguration��WebFluxAutoConfiguration�����࣬��GatewayAutoConfiguration֮��ʵ�������ֱ�ʵ������HttpHandler��WebFluxConfigBean + +**��2�� ִ������** +��һ���н���Hystrix��ԭ������Hystrix�к���ҵ���߼�����ͨ����Ӧʽ�����ɵģ���ʵ�ϣ���Gateway��Ҳ���ǻ���ͬ���ı�̷��ͬ���ģ�Gateway������ͬSpringMVC����Ҳ�dz����ơ� + +��ǰ�������������ʱ�򣬴�����������£� + +1. ���ȱ�DispatcherHandler���������أ�Ȼ��������URI���н��� +2. Ȼ�����URIȥ����HandlerMapping����ȡ����Ҫִ�е�WebHandler +3. Ȼ��ѡ��һ�����ʵ�������HandlerAdapterִ�� +4. ִ��WebHandler + ������gateway����ʱ�����е����󶼻���뵽DispatcherHandler�е�handle��������������һ�𿴿�������� + + + +``` +@Override +public Mono handle(ServerWebExchange exchange) { + if (this.handlerMappings == null) { + return createNotFoundError(); + } + //�������webFlux����Ӧʽ��� + return Flux + // 1.������DZ������е� handlerMapping + .fromIterable(this.handlerMappings) + // 2.��ȡ��Ӧ��handlerMapping �����糣�õ� RequestMappingHandlerMapping��RoutePredicateHandlerMapping + .concatMap(mapping -> mapping.getHandler(exchange)) + .next() + .switchIfEmpty(createNotFoundError()) + // 3.��ȡ��Ӧ�������������ö�Ӧ�Ĵ����� + .flatMap(handler -> invokeHandler(exchange, handler)) + // 4.���ش������ + .flatMap(result -> handleResult(exchange, result)); +} +``` + + + + + + + + + +**2.1 getHandler** +������������getHandler������������Gateway�ĺ����߼����ڣ���getHandler�л�ȡ��Ӧ��HandlerMapping�� + +������ +AbstractHandlerMapping.getHandler��Դ�� + + + +``` +@Override +public Mono getHandler(ServerWebExchange exchange) { + //��һ�����ȡ·�ɵ�ʵ���࣬����뵽RoutePredicateHandlerMapping + return getHandlerInternal(exchange).map(handler -> { + if (logger.isDebugEnabled()) { + logger.debug(exchange.getLogPrefix() + "Mapped to " + handler); + } + ServerHttpRequest request = exchange.getRequest(); + if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { + CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null); + CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange); + config = (config != null ? config.combine(handlerConfig) : handlerConfig); + if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) { + return REQUEST_HANDLED_HANDLER; + } + } + return handler; + }); +} +``` + + + + + + + + + + + +``` +@Override +protected Mono getHandlerInternal(ServerWebExchange exchange) { + // don't handle requests on management port if set and different than server port + if (this.managementPortType == DIFFERENT && this.managementPort != null + && exchange.getRequest().getURI().getPort() == this.managementPort) { + return Mono.empty(); + } + exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName()); + //Ѱ�Ҳ�ƥ��·�� + return lookupRoute(exchange) + // .log("route-predicate-handler-mapping", Level.FINER) //name this + .flatMap((Function>) r -> { + //�Ƴ��������оɵ����� + exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); + if (logger.isDebugEnabled()) { + logger.debug( + "Mapping [" + getExchangeDesc(exchange) + "] to " + r); + } + //�Ѹ�·���������İ󶨣��������ؾ������ + exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); + //���� webHandler + return Mono.just(webHandler); + }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> { + exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); + if (logger.isTraceEnabled()) { + logger.trace("No RouteDefinition found for [" + + getExchangeDesc(exchange) + "]"); + } + }))); +} +``` + + + + + + + + + +����lookupRoute�������ҵ�yml�����õ����е�·�ɶ��Թ�����Before��After��Path�ȵȣ�����ִ��apply����������·��ƥ�䣬�ж��Ƿ���������ͨ����ִ��˳����springboot�Զ�����ʱ�Լ��ƶ� + + + +``` +protected Mono lookupRoute(ServerWebExchange exchange) { + // getRoutes ��ȡ���еĶ��Թ��� + return this.routeLocator.getRoutes() + .concatMap(route -> Mono.just(route).filterWhen(r -> { + exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId()); + // �Ȼ�ȡRoute�ڲ���predicate���� + //Ȼ�����apply���� ִ�ж��ԣ��ж������Ƿ�ͨ�� + return r.getPredicate().apply(exchange); + }).doOnError(e -> logger.error( + "Error applying predicate for route: " + route.getId(), + e)) + .onErrorResume(e -> Mono.empty())) + .next() + .map(route -> { + if (logger.isDebugEnabled()) { + logger.debug("Route matched: " + route.getId()); + } + validateRoute(route, exchange); + return route; + }); +} +``` + + + + + + + + + +����getRoutes()��������ͨ�� +RouteDefinitionRouteLocator�������ļ��л�ȡ����·�ɵģ�Ȼ����ҵ���·��ת����Route + + + +``` +@Override +public Flux getRoutes() { + // getRouteDefinitions() �������ļ��л�ȡ����·�� + Flux routes = this.routeDefinitionLocator.getRouteDefinitions() + // convertToRoute()�����ҵ���·��ת����Route + .map(this::convertToRoute); + ... +} +``` + + + + + + + + + + + +``` +public class Route implements Ordered { + private final String id; + private final URI uri; + private final int order; + private final AsyncPredicate predicate; + private final List gatewayFilters; + private final Map metadata; + ... +} +``` + + + + + + + + + +**2.2 invokeHandler** +Gateway��������һ��ƥ��·�ɺ󷵻ص���webHandler���͵ģ�����Ҳ��Ҫ�ҵ���Ӧ��HandlerAdaptor�������ȡ��Ӧ������������ invokeHandler(exchange, handler)�� + + + +``` +private Mono invokeHandler(ServerWebExchange exchange, Object handler) { + if (this.handlerAdapters != null) { + //�ҵ����е�HandlerAdapterȥƥ��WebFlux���� + for (HandlerAdapter handlerAdapter : this.handlerAdapters) { + if (handlerAdapter.supports(handler)) { + return handlerAdapter.handle(exchange, handler); + } + } + } + return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler)); +} +``` + + + + + + + + + +SimpleHandlerAdapter �е�handle�������� + + + +``` +@Override +public Mono handle(ServerWebExchange exchange, Object handler) { + //����WebHandler ���� + WebHandler webHandler = (WebHandler) handler; + Mono mono = webHandler.handle(exchange); + return mono.then(Mono.empty()); +} +``` + + + + + + + + + +����webHandler.handle�������Ǵ������й��������ķ������ù�����������globalFilters��gatewayFilters + + + +``` +@Override +public Mono handle(ServerWebExchange exchange) { + // 1\. ����·���������İ󶨹�ϵ����ȡ��Ӧ��·��Route + Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); + List gatewayFilters = route.getFilters(); + // 2\. �ռ����е� globalFilters ������List + //ע������ʹ����������ģʽ + List combined = new ArrayList<>(this.globalFilters); + // 3\. �� gatewayFilters Ҳ����List���γ�һ������������ + combined.addAll(gatewayFilters); + // 4\. ����order���� + AnnotationAwareOrderComparator.sort(combined); + if (logger.isDebugEnabled()) { + logger.debug("Sorted gatewayFilterFactories: " + combined); + } + // 5\. ִ�й��������е�ÿһ�������������� + return new DefaultGatewayFilterChain(combined).filter(exchange); +} +``` + + + + + + + + + +ע�⣺����װ����������ʱ���ǰ�globalFilters��gatewayFilters���ֹ��������Ž���List�У�������ô�����أ� + +����ʵ�õ���һ�� ������ �����ģʽ�� + +* ����������globalFilters�����Ȱ�globalFiltersת����GatewayFilterAdapter�� GatewayFilterAdapter���ڲ�������GlobalFilter��ͬʱҲʵ����GatewayFilter��ʹ globalFilters��gatewayFilters�� ������ ��GatewayFilterAdapter�й��棡 +* ����������gatewayFilters��ֱ�ӷ��뼴�ɣ� + **��3�� ���ؾ�������** + Gateway�ĸ��ؾ���ֻ��Ҫ��yml������ uri: lb://user����ʵ�ָ��ؾ��⣬�ײ�����ȫ�ֹ�����LoadBalancerClientFilter��filter����ȥ���ģ� + +�Զ�������� +http://localhost:9527/get/3Ϊ����9527Ϊ����Gateway�Ķ˿� + + + +``` +public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 1\. ����·���������İ󶨹�ϵ + // ��ȡԭʼ��url��http://localhost:9527/get/3 + URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); + String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR); + if (url == null + || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) { + return chain.filter(exchange); + } + addOriginalRequestUrl(exchange, url); + if (log.isTraceEnabled()) { + log.trace("LoadBalancerClientFilter url before: " + url); + } + // 2\. ͨ��ribbon�ĸ��ؾ����㷨�����ݷ�����ȥnacos����Eurekaѡ��һ��ʵ���� + // ��ʵ������user���������� url ��ַ��http://localhost:8080/get/3 + final ServiceInstance instance = choose(exchange); + if (instance == null) { + throw NotFoundException.create(properties.isUse404(), + "Unable to find instance for " + url.getHost()); + } + // 3\. �õ�ԭ���� uri ��http://localhost:9527/get/3 + URI uri = exchange.getRequest().getURI(); + String overrideScheme = instance.isSecure() ? "https" : "http"; + if (schemePrefix != null) { + overrideScheme = url.getScheme(); + } + // 4\. �÷���ʵ��instance��uri�滻ԭ����uri��ַ �õ� �µ�url + // �µ�url: http://localhost:8080/get/3 + URI requestUrl = loadBalancer.reconstructURI( + new DelegatingServiceInstance(instance, overrideScheme), uri); + if (log.isTraceEnabled()) { + log.trace("LoadBalancerClientFilter url chosen: " + requestUrl); + } + // 5\. �ٴμ�¼�����Ĺ�ϵ + exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl); + // 6\. ִ�й��������е������������� + return chain.filter(exchange); +} +``` \ 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/Spring Cloud Hystrix\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/Spring Cloud Hystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..c9323b5 --- /dev/null +++ "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/Spring Cloud Hystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,1187 @@ +ѧϰĿ�� + +1. ��дMini���Hystrix +2. RxJava֪ʶ���� +3. Hystrix�ĺ������̷��� +4. Դ����֤ + ��1�� ��дMini�� + �������Ѿ�����ҽ��ܹ���Hystrix�ĺ��Ĺ��ܺ�ʹ���ˣ����޷Ǿ����ṩ���۶ϡ�����������ȹ��ܣ������۶Ϻ͸�����Ŀ�ģ������ǽ������ʹ�ù�������ʵ����ĵ�������ע�⣺@EnableHystrix��@HystrixCommand��@HystrixCollapser������ͨ��ע�� @HystrixCommand�����߼̳� HystrixCommand ��ʵ�ֽ������Լ�һЩ����ϲ��Ȳ����� + +����ʽ����ԭ��֮ǰ����������Ҫ��ȷһ���㣬������ @HystrixCommand ע����ʵ�ַ��񽵼�����Hystrix ���ڲ��Dz���AOP�ķ�ʽ�������ش�������ģ�������ݣ�����Ҳ����ϸ�������������������ʵ��һ�¼��װ�� Hystrix �����һ�£���Ҫ��Ϊ���²��� + +- �����Լ���@HystrixCommand ע�⡣ +- ʵ����������Ĵ����߼��� +- ���Ե��á� + 1.�Զ���ע�� + + + + @Target({ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + @Documented + public @interface MyHystrixCommand { + //Ĭ�ϳ�ʱʱ�� + int timeout() default 1000; + //���˷��� + String fallback() default ""; + } + + + + + + + + + +2.�Զ��������� + + + + @Aspect //����Aspect֧�ֲ��ұ��Ϊһ�������� + @Component + public class MyHystrixCommandAspect { + ExecutorService executorService= Executors.newFixedThreadPool(10); + + //�����е� + @Pointcut(value = "@annotation(MyHystrixCommand)") + public void pointCut(){ + + } + //���е㷽���⻷��ִ�� @Around�൱��@Before��@AfterReturning���ܵ��ܺ� + @Around(value = "pointCut()&&@annotation(hystrixCommand)") + public Object doPointCut(ProceedingJoinPoint joinPoint, MyHystrixCommand hystrixCommand) throws Exception { + int timeout=hystrixCommand.timeout(); + Future future=executorService.submit(()->{ + try { + //ִ��proceed��������������Ŀ�귽��ִ�� + return joinPoint.proceed(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + return null; + }); + Object rs; + try { + //ͨ��get���첽�ȴ���ʵ�ֳ�ʱ + rs=future.get(timeout, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + future.cancel(true); + if(StringUtils.isBlank(hystrixCommand.fallback())){ + throw new Exception("fallback is null"); + } + //����fallback + rs=invokeFallback(joinPoint,hystrixCommand.fallback()); + } + return rs; + } + private Object invokeFallback(ProceedingJoinPoint joinPoint,String fallback) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + //��ȡ�������ķ���������Method + MethodSignature signature=(MethodSignature)joinPoint.getSignature(); //��ȡ������ͱ����������Ϣ + Method method=signature.getMethod(); + Class[] parameterTypes=method.getParameterTypes(); + //�õ��ص����� + try { + Method fallbackMethod=joinPoint.getTarget().getClass().getMethod(fallback,parameterTypes); + method.setAccessible(true); + //ͨ������ص� + return fallbackMethod.invoke(joinPoint.getTarget(),joinPoint.getArgs()); + } catch (Exception e) { + throw e; + } + } + } + + + + + + + + + +3.�Զ������ + + + + @RestController + public class MyHystrixController { + @Autowired + OrderServiceClient orderServiceClient; + @MyHystrixCommand(fallback = "fallback",timeout = 2000) + @GetMapping("/myhystrix/get/{num}") + public String get(@PathVariable("num") int num){ + return orderServiceClient.orderLists(num); + } + public String fallback(int num){ + return "�Զ���ע�ⷽ��������"; + } + } + + + + + + + + + +������http://localhost:8080/myhystrix/get/1ʱ�ᴥ����������Ϊ�ڷ���ˣ���num=1ʱ������3s�� + +OK���������Ǿ�ʵ����һ�����װ��HystrixCommand����������ֻ��ʵ����Hystrix�ĵ�һ����������һ��ע������棬�������ĵײ��߼�ԶԶû����ô�򵥣��ڽ�Դ��֮ǰ������������һ��RxJava��ʲô����ΪHystrix�ײ��߼��ǻ�����Ӧʽ���ʵ�ֵġ� + +��2�� RxJava���� +2.1 RxJava���� +RxJava ��һ����Ӧʽ��̣������������¼����첽�����⡣�����¼�������ʽ���á��߼�������ࡣ + +RxJava�۲���ģʽ�ĶԱ� + +- ��ͳ�۲�����һ�����۲��߶���۲��ߣ������۲��߷����ı�ʱ��ʱ֪ͨ���й۲��� +- RxJava��һ���۲��߶�����۲��ߣ����۲���������һ���������������ڱ��۲���֮�䳯��һ�����򴫵ݣ�ֱ�����ݸ��۲��� �� + ��ʵ˵���ˣ�������RxJava�д���2�ָ��һ���DZ��۲��ߣ�һ���ǹ۲��ߣ���������۲��߶�����ͬһ���۲��ߵ�ʱ����ô���ű��۲������ij���¼���ʱ��ͻ�ȥ�ص��۲��ߡ� + +2.2 �۲��� + +Observer + + + + Observer observer = new Observer() { + @Override + public void onCompleted() { + System.out.println("�����۲�������Complete�¼������ø÷���"); + } + @Override + public void onError(Throwable throwable) { + System.out.println("��Error�¼�������Ӧ"); + } + @Override + public void onNext(Object o) { + System.out.println("��Next�¼�������Ӧ:" + o); + } + }; + + + + + + + + + + + + //Subscriber�� = RxJava ���õ�һ��ʵ���� Observer �ij����࣬�� Observer �ӿڽ�������չ + Subscriber subscriber = new Subscriber() { + @Override + public void onCompleted() { + System.out.println("�����۲�������Complete�¼������ø÷���"); + } + @Override + public void onError(Throwable throwable) { + System.out.println("��Error�¼�������Ӧ"); + } + @Override + public void onNext(Object o) { + System.out.println("��Next�¼�������Ӧ:" + o); + } + }; + + + + + + + + + +Subscriber ��������Observer �ӿڵ����� + +���߻���ʹ�÷�ʽһ�£���RxJava��subscribe�����У�Observer���ȱ�ת����Subscriber��ʹ�ã� +Subscriber������� Observer �ӿڽ�������չ������������������ + +- onStart()���ڻ�δ��Ӧ�¼�ǰ���ã�������һЩ��ʼ��������������subscribe ���ڵ��̵߳��ã������л��̣߳����Բ��ܽ��н���UI���±��絯����Щ�� +- unsubscribe()������ȡ�����ġ��ڸ÷��������ú󣬹۲��߽����ٽ�����Ӧ�¼���������onStop�����п��Ե��ô˷����������ġ����ø÷���ǰ����ʹ�� isUnsubscribed() �ж�״̬��ȷ�����۲���Observable�Ƿ񻹳��й۲���Subscriber�����á� + 2.3 ���۲��� + RxJava �ṩ�˶��ַ������� �������۲��߶���Observable������������� + + + + // ����1��just(T...)��ֱ�ӽ�����IJ������η��ͳ��� + Observable observable = Observable.just("A", "B", "C"); + // �������ε��ã� + // onNext("A"); + // onNext("B"); + // onNext("C"); + // onCompleted(); + // ����2��fromArray(T[]) / from(Iterable) : ����������� / Iterable ��ֳɾ����������η��ͳ��� + String[] words = {"A", "B", "C"}; + Observable observable = Observable.fromArray(words); + // �������ε��ã� + // onNext("A"); + // onNext("B"); + // onNext("C"); + // onCompleted(); + + + + + + + + + +2.4 ���� + + + + observable.subscribe(observer); //�������Ĺ�ϵ + + + + + + + + + +2.5 ���� + + + + public class RxJavaDemo { + // ReactiveX Java ��Ӧʽ��̿��(android�� + // Java stream() java8 + //�۲���ģʽ + public static void main(String[] args) throws ExecutionException, InterruptedException { + final String[] datas = new String[]{"�¼�1"}; + // ����ִ����Ļص����� ��ֹ�������� + //����Observable����ǰ�����ص���call���������������������쳣��ֹ + final Action0 onComplated = new Action0() { + @Override + public void call() { + System.out.println("���۲���Ҫ������"); + } + }; + //���۲��� + Observable observable = Observable.defer(new Func0>() { + @Override + public Observable call() { + Observable observable1 = Observable.from(datas); + return observable1.doOnCompleted(onComplated); + } + }); + // Observable observable = Observable.just("�¼�1","�¼�2","����"); + //�۲��� + Observer observer = new Observer() { + @Override + public void onCompleted() { + System.out.println("��Comlate�¼�������Ӧ"); + } + @Override + public void onError(Throwable throwable) { + System.out.println("��Error�¼�������Ӧ"); + } + @Override + public void onNext(Object o) { + System.out.println("��Next�¼�������Ӧ:" + o); + } + }; + observable.subscribe(observer); //�������Ĺ�ϵ + + // String s = observable.toBlocking().toFuture().get();//�첽�ȴ���� + // System.out.println(s); + } + } + + + + + + + + + +OK�������ָ�����ʹ��RxJava����ˣ����������ǿ�ʼߣԴ�롣 + +��3�� Դ����� +���Ϲ����ṩ��Դ������ͼ����ͼ�Ͽ��Կ���������ʵ������ȥɨ�����HystrixCommandע��ķ�����Ȼ������������أ�ִ��������߼���������涨��������������execute��queue����ѡһ���е��ã�Ȼ����������������߼������������HystrixCommandע�⣬������Hystrix��@EnableHystrixע�⡣ + + + + @SpringBootApplication + @EnableFeignClients("com.example.clients") + //@EnableDiscoveryClient //ע����ʾUser����ע�� + @EnableHystrix //ע�ⷽʽ����Hystrix + public class HystrixEclipseUserApplication { + + public static void main(String[] args) { + SpringApplication.run(HystrixEclipseUserApplication.class, args); + } + + } + + + + + + + + + +���뵽@EnableHystrixע���� + + + + @Target({ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Inherited + @EnableCircuitBreaker + public @interface EnableHystrix { + } + //����@EnableHystrix�̳���@EnableCircuitBreaker + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Inherited + @Import(EnableCircuitBreakerImportSelector.class) + public @interface EnableCircuitBreaker { + } + + + + + + + + + +�����ⲽ���룬�����źܶ�ѧ��springboot��ͬѧ������Ϥ�ˣ������õ���Importע�⣬�ǿ϶���������һЩ�������ˣ�Ȼ�������ٽ� +EnableCircuitBreakerImportSelector����; + + + + @Order(Ordered.LOWEST_PRECEDENCE - 100) + public class EnableCircuitBreakerImportSelector + extends SpringFactoryImportSelector { + @Override + protected boolean isEnabled() { + return getEnvironment().getProperty("spring.cloud.circuit.breaker.enabled", + Boolean.class, Boolean.TRUE); + } + } + + + + + + + + + +EnableCircuitBreakerImportSelector�̳���SpringFactoryImportSelector������SpringFactoryImportSelector�������������Ϥ�Ĵ��룬��ʵ����DeferredImportSelector�ӿڣ�ʵ����selectImports������selectImports������������ļ�spring.factories����ض�Ӧ���� org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker������������spring.facotries�ļ��� + + + + org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration,\ + org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerAutoConfiguration,\ + org.springframework.cloud.netflix.hystrix.ReactiveHystrixCircuitBreakerAutoConfiguration,\ + org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration + org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\ + org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration + + + + + + + + + +��ӦEnableAutoConfiguration����Щʵ������spring������ʱ��ͨ���Զ�װ����ƻ�ȥʵ��������ע�뵽IoC�����У��������Ǻ��Ĺ�ע +HystrixCircuitBreakerConfiguration�ࡣ + + + + @Configuration(proxyBeanMethods = false) + public class HystrixCircuitBreakerConfiguration { + //�����Ǻ��ĵ�����bean + @Bean + public HystrixCommandAspect hystrixCommandAspect() { + return new HystrixCommandAspect(); + } + ... + } + + + + + + + + + +���뵽����������лᷢ�֣����������Ҫ���������ע����Ϊ�����@HystrixCommand��@HystrixCollapser����ִ��������ע�����εķ���ʱ���ᱻ����ִ�� +methodsAnnotatedWithHystrixCommand + +3.1 HystrixCommandAspect + + + + @Aspect + public class HystrixCommandAspect { + private static final Map META_HOLDER_FACTORY_MAP; + static { + //ͨ����̬����������ע�����������ʵ���� + META_HOLDER_FACTORY_MAP = ImmutableMap.builder() + .put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory()) + .put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory()) + .build(); + } + //���������ע��HystrixCommand + @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)") + public void hystrixCommandAnnotationPointcut() { + } + //���������ע��HystrixCollapser������ϲ��� + @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)") + public void hystrixCollapserAnnotationPointcut() { + } + //����֪ͨ + @Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()") + public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable { + //��ȡĿ�귽�� + Method method = getMethodFromTarget(joinPoint); + Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint); + //ֻ����������ע���ע�ķ��� + if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) { + throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser " + + "annotations at the same time"); + } + //���ݲ�ͬ��ע�⣬ѡ���Ӧ��metaHolderFactory, ����MetaHolder, MetaHolder ���������������Ϣ + MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method)); + //��ȡĿ�귽���ĵ�Ԫ���ݣ�����ǩ���������� + MetaHolder metaHolder = metaHolderFactory.create(joinPoint); + /** + * ����������CommandCollapser �� GenericCommand ��ͬ���� ��GenericObservableCommand���첽�� + * GenericCommand���кܶ�super������ͨ��HystrixCommandBuilderFactory.getInstance().create(metaHolder) ������һ��HystrixCommandBuilder��ΪGenericCommad�IJ��� + * new GenericCommand ͨ��super��AbstractHystrixCommand�� + * AbstractHystrixCommand ͨ��super��HystrixCommand�� + * HystrixCommand���յ���AbstractCommand һ·���� + * һ����AbstractCommand�з����� + */ + HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder); + //���ݷ���ֵ�ƶ�ִ������ + ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ? + metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType(); + //���ݲ�ͬ���������ͣ�ִ��������ؽ�� + Object result; + try { + //�Ƿ�����Ӧʽ�ģ�����������Щ����ͬ���Ļ�������߼��� + if (!metaHolder.isObservable()) { + //executeִ�� + result = CommandExecutor.execute(invokable, executionType, metaHolder); + } else { + result = executeObservable(invokable, executionType, metaHolder); + } + } catch (HystrixBadRequestException e) { + throw e.getCause(); + } catch (HystrixRuntimeException e) { + throw hystrixRuntimeExceptionToThrowable(metaHolder, e); + } + return result; + } + //HystrixCommand��ʱ��MetaHolder�Ĵ��� + private static class CommandMetaHolderFactory extends MetaHolderFactory { + @Override + public MetaHolder create(Object proxy, Method method, Object obj, Object[] args, final ProceedingJoinPoint joinPoint) { + //��ȡע��HystrixCommand + HystrixCommand hystrixCommand = method.getAnnotation(HystrixCommand.class); + //���ݷ��ؽ���ƶ��������ͣ�����֪�������ַ�ʽִ�� + ExecutionType executionType = ExecutionType.getExecutionType(method.getReturnType()); + MetaHolder.Builder builder = metaHolderBuilder(proxy, method, obj, args, joinPoint); + if (isCompileWeaving()) { + builder.ajcMethod(getAjcMethodFromTarget(joinPoint)); + } + //����û�ж��ٲ���������Ҫ��һ��hystrixCommand������ע�������ʲô + return builder.defaultCommandKey(method.getName()) + .hystrixCommand(hystrixCommand) + .observableExecutionMode(hystrixCommand.observableExecutionMode()) //ִ��ģʽ + .executionType(executionType) //ִ�з�ʽ + .observable(ExecutionType.OBSERVABLE == executionType) + .build(); + } + } + } + //��ö��ExecutionType���� + public static ExecutionType getExecutionType(Class type) { + if (Future.class.isAssignableFrom(type)) { + return ExecutionType.ASYNCHRONOUS; + } else if (Observable.class.isAssignableFrom(type)) { + return ExecutionType.OBSERVABLE; + } else { + return ExecutionType.SYNCHRONOUS; + } + } + + + + + + + + + +�����ص������ͬ��������ͨ���������ǿ��Կ���HystrixInvokable �� GenericCommand������ͬ����Ŀ��� CommandExecutor.execute(invokable, executionType, metaHolder) + + + + public class CommandExecutor { + public static Object execute(HystrixInvokable invokable, ExecutionType executionType, MetaHolder metaHolder) throws RuntimeException { + Validate.notNull(invokable); + Validate.notNull(metaHolder); + + switch (executionType) { + case SYNCHRONOUS: { + //�ص㿴ͬ������������Ȱ�GenericCommand ת��HystrixExecutable ��ִ��execute + return castToExecutable(invokable, executionType).execute(); + } + case ASYNCHRONOUS: { + // ǿת��HystrixExecutable �첽ִ�� + HystrixExecutable executable = castToExecutable(invokable, executionType); + // ����� fallback�����������첽ִ�У���ִ�в����ذ�װ��� + if (metaHolder.hasFallbackMethodCommand() + && ExecutionType.ASYNCHRONOUS == metaHolder.getFallbackExecutionType()) { + return new FutureDecorator(executable.queue()); + } + return executable.queue(); + } + case OBSERVABLE: { + // ǿת�� HystrixObservable + HystrixObservable observable = castToObservable(invokable); + // �ж�ִ��ģʽ�Dz��Ǽ���/���裬��ѡ��ģʽִ�� + return ObservableExecutionMode.EAGER == metaHolder.getObservableExecutionMode() ? observable.observe() : observable.toObservable(); + } + default: + throw new RuntimeException("unsupported execution type: " + executionType); + } + } + } + + + + + + + + + +���������Ҫ����ִ������Ӵ����п��Կ�������������ִ�����ͣ��ֱ���ͬ�����첽���Լ���Ӧʽ�����У���Ӧʽ�ַ�ΪCold Observable��observable.toObservable()�� �� HotObservable��observable.observe()��Ĭ�ϵ�executionType=SYNCHRONOUS ��ͬ������ + +- execute()��ͬ��ִ�У�����һ����һ�Ķ���������������ʱ�׳��쳣�� +- queue()���첽ִ�У�����һ�� Future ���󣬰�����ִ�н����󷵻صĵ�һ����� +- observe()�������������һ�� Observable ���������������Ķ������������Ѿ������������ѵ��ˡ� +- toObservable()�������������һ�� Observable ���������������Ķ���������Ҫ�����Լ��ֶ����IJ����ѵ��� + ��ͼ��ϵ���£� + +ͨ��GenericCommandһ�������Ϸ������ն�λ��HystrixCommand�и�execute() + + + + public abstract class HystrixCommand extends AbstractCommand implements HystrixExecutable, HystrixInvokableInfo, HystrixObservable { + //ͬ��ִ�� + public R execute() { + try { + //ͨ��queue().get()��ͬ��ִ�У���װ�첽�����Ľ���� + return queue().get(); + } catch (Exception e) { + throw Exceptions.sneakyThrow(decomposeException(e)); + } + } + //�첽ִ�У�ʲôʱ��get()���ɵ����߾�����get()��ʱ������� + public Future queue() { + //���Ĵ��������ն�λ����AbstractCommand���toObservable()�� + // toObservableת��ΪObservable,toBlockingת��ΪBlockingObservable, + // toFutureת��ΪFuture,�����Observable�Ĵ����Ͷ��� + final Future delegate = toObservable().toBlocking().toFuture(); + final Future f = new Future() { + ..... + @Override + public R get() throws InterruptedException, ExecutionException { + return delegate.get(); + } + @Override + public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return delegate.get(timeout, unit); + } + }; + //���⴦�����£��Ѿ�ִ�����ˣ�get()Ҳ���������� + if (f.isDone()) { + try { + f.get(); + return f; + } catch (Exception e) { + ... + } + } + return f; + } + } + + + + + + + + + +�����������У��ص����ˣ�������һ�� +java.util.concurrent.Future ��Ȼ����� get��ʱ��ί�ɸ� delegate���� delegate������ toObservable().toBlocking().toFuture(); ����������������������ô��롣�������ڵ��ص�Ӧ�÷��� toObservable() �����У� + +3.2 toObservable +ͨ��Observable����һ�����۲��ߣ�������۲��߻ᱻtoObservable().toBlocking().toFuture() ��ʵ�������д���ĺ��ĺ������ȥ����һЩ�۶��߼��ж���ִ����ʵ��ҵ���߼�����ִ��fallback�Ļص�������Ȼ�󽫽�����ظ�Future������� run() ��������ִ��������ҵ���߼������������Ҫ�������¼����£� + +- ����һ�ѵĶ�������Ҳ��֪����Щ�����Ǹ�ɶ�ģ�����Ҫ�� +- �ж��Ƿ����˻��棬������ˣ�����Ҳ�����ˣ���ȥ����������Observable��ʽ����һ�������� +- ����һ�����۲��ߣ�������۲��ߺ����ȥ�ص���ʵҵ���߼�����fallback�� + +�����߼���������۲��߻�ȥִ��applyHystrixSemantics����Ķ��� + + + + public Observable toObservable() { + final AbstractCommand _cmd = this; + // ����ִ����Ļص����� ��ֹ�������� + //����Observable����ǰ�����ص���call���������������������쳣��ֹ + final Action0 terminateCommandCleanup = new Action0() { + ... + }; + // ��������Ϊ��ȡ�����洢�ӳ٣����˱�׼������ + //ȡ������ʱ�ļ�������лص��� call���� + final Action0 unsubscribeCommandCleanup = new Action0() { + @Override + public void call() { + ... + } + }; + // ִ������ʱ�Ļص� + final Func0> applyHystrixSemantics = new Func0>() { + @Override + public Observable call() { + if (commandState.get().equals(CommandState.UNSUBSCRIBED)) { + // ������ֹ�������̡� + return Observable.never(); + } + //����ִ�������Observable + return applyHystrixSemantics(_cmd); + } + }; + final Func1 wrapWithAllOnNextHooks = new Func1() { + @Override + public R call(R r) { + ... + } + }; + final Action0 fireOnCompletedHook = new Action0() { + @Override + public void call() { + ... + } + }; + // ����Observable,���ø��ִ������� + return Observable.defer(new Func0>() { + @Override + public Observable call() { + // ������������־, CAS��֤����ִֻ��һ�� + if (!commandState.compareAndSet(CommandState.NOT_STARTED, CommandState.OBSERVABLE_CHAIN_CREATED)) { + IllegalStateException ex = new IllegalStateException("This instance can only be executed once. Please instantiate a new instance."); + //TODO make a new error type for this + throw new HystrixRuntimeException(FailureType.BAD_REQUEST_EXCEPTION, _cmd.getClass(), getLogMessagePrefix() + " command executed multiple times - this is not permitted.", ex, null); + } + // ���ʼʱ��� + commandStartTimestamp = System.currentTimeMillis(); + // ��ӡ��־ + if (properties.requestLogEnabled().get()) { + // log this command execution regardless of what happened + if (currentRequestLog != null) { + currentRequestLog.addExecutedCommand(_cmd); + } + } + // ���濪�أ�����KEY�������Hystrix�����󻺴湦�ܣ�hystrix֧�ֽ�һ������������������ + // ��һ��������ͬkey������ֱ�Ӵӻ�����ȡ������������������� + final boolean requestCacheEnabled = isRequestCachingEnabled(); + final String cacheKey = getCacheKey(); + // ��������������棬����ͼ�ӻ����ȡ��Ĭ�� false + if (requestCacheEnabled) { + HystrixCommandResponseFromCache fromCache = (HystrixCommandResponseFromCache) requestCache.get(cacheKey); + if (fromCache != null) { + isResponseFromCache = true; + return handleRequestCacheHitAndEmitValues(fromCache, _cmd); + } + } + // ����ִ�������Observable + // ����Observable, applyHystrixSemantics() ������Observable + Observable hystrixObservable = + Observable.defer(applyHystrixSemantics) + .map(wrapWithAllOnNextHooks); + Observable afterCache; + // put in cache ������������������ + if (requestCacheEnabled && cacheKey != null) { + // wrap it for caching + HystrixCachedObservable toCache = HystrixCachedObservable.from(hystrixObservable, _cmd); + HystrixCommandResponseFromCache fromCache = (HystrixCommandResponseFromCache) requestCache.putIfAbsent(cacheKey, toCache); + if (fromCache != null) { + // another thread beat us so we'll use the cached value instead + toCache.unsubscribe(); + isResponseFromCache = true; + return handleRequestCacheHitAndEmitValues(fromCache, _cmd); + } else { + // we just created an ObservableCommand so we cast and return it + afterCache = toCache.toObservable(); + } + } else { + afterCache = hystrixObservable; + } + // �������ڻص����� + return afterCache + //����Observable����ǰ�����ص������������������쳣��ֹ + .doOnTerminate(terminateCommandCleanup) + //ȡ������ʱ�ļ��� + .doOnUnsubscribe(unsubscribeCommandCleanup) + //Observable������ֹʱ�ļ��� + .doOnCompleted(fireOnCompletedHook); + } + }); + } + + + + + + + + + +���������������߼�applyHystrixSemantics + + + + Observable hystrixObservable = + Observable.defer(applyHystrixSemantics) + .map(wrapWithAllOnNextHooks); + + + + + + + + + + + + final Func0> applyHystrixSemantics = new Func0>() { + @Override + public Observable call() { + if (commandState.get().equals(CommandState.UNSUBSCRIBED)) { + return Observable.never(); + } + return applyHystrixSemantics(_cmd); + } + }; + + + + + + + + + +���ﴫ���_cmd��һ��GenericCommand�����ջ�ִ�е����GenericCommand�е�run������ + +circuitBreaker.allowRequest() ������ж��Ƿ����۶�״̬�ģ�true��ʾû�д����۶�״̬������ִ�У����򣬵��� handleShortCircuitViaFallback ʵ�ַ��񽵼������ջ�ص��������Զ����fallback�����С� + +�����ǰhystrix����δ�۶�״̬���� + +- getExecutionSemaphore �жϵ�ǰ�����Ƿ�Ϊ�ź��������̳߳أ���ȻĬ�����̳߳أ�Ȼ���ٵ���tryAcquireʱд����Ϊtrue�� + +����executeCommandAndObserve�� + + + + private Observable applyHystrixSemantics(final AbstractCommand _cmd) { + + executionHook.onStart(_cmd); + + // �Ƿ��������󣬼���·���Ƿ��� ������Ҳ�кü������ + if (circuitBreaker.allowRequest()) { + // �ź�����ȡ + final TryableSemaphore executionSemaphore = getExecutionSemaphore(); + final AtomicBoolean semaphoreHasBeenReleased = new AtomicBoolean(false); + + // �ź��ͷŻص� + final Action0 singleSemaphoreRelease = new Action0() { + @Override + public void call() { + if (semaphoreHasBeenReleased.compareAndSet(false, true)) { + executionSemaphore.release(); + } + } + }; + + // �쳣�ص� + final Action1 markExceptionThrown = new Action1() { + @Override + public void call(Throwable t) { + eventNotifier.markEvent(HystrixEventType.EXCEPTION_THROWN, commandKey); + } + }; + + // ��ȡ�źţ������ض�Ӧ�� Observable + // �Ƿ����ź�����Դ���룬δ������ com.netflix.hystrix.AbstractCommand.TryableSemaphoreNoOp#tryAcquire Ĭ�Ϸ���ͨ�� + if (executionSemaphore.tryAcquire()) { + try { + executionResult = executionResult.setInvocationStartTime(System.currentTimeMillis()); + return executeCommandAndObserve(_cmd) // ִ��������������ǻص������Բ��� + .doOnError(markExceptionThrown) + .doOnTerminate(singleSemaphoreRelease) + .doOnUnsubscribe(singleSemaphoreRelease); + } catch (RuntimeException e) { + return Observable.error(e); + } + } else { + // ��ȡ�ź�ʧ���򽵼� + return handleSemaphoreRejectionViaFallback(); + } + } else { + // ��·���Ѵ򿪣�ֱ�ӽ��� + return handleShortCircuitViaFallback(); + } + } + + + + + + + + + +������һ��ִ��ʧ�ܽ��뽵�����߼�����������ֱ�ӽ��뵽 HystrixCommand#getFallbackObservable + + + + public abstract class HystrixCommand extends AbstractCommand implements HystrixExecutable, HystrixInvokableInfo, HystrixObservable { + @Override + final protected Observable getFallbackObservable() { + return Observable.defer(new Func0>() { + @Override + public Observable call() { + try { + return Observable.just(getFallback()); + } catch (Throwable ex) { + return Observable.error(ex); + } + } + }); + } + } + + + + + + + + + +�����getFallback���ջ�ص������Զ���fallback������ + +�ص�executeCommandAndObserve�����������Ҫ���������������� + +- ���岻ͬ�Ļص���doOnNext��doOnCompleted��onErrorResumeNext��doOnEach�� +- ����executeCommandWithSpecifiedIsolation�� + +��ִ�����ʱ���Կ��������� Observable.lift����ʵ��ִ�����ʱ���ܡ� + + + + private Observable executeCommandAndObserve(final AbstractCommand _cmd) { + final HystrixRequestContext currentRequestContext = HystrixRequestContext.getContextForCurrentThread(); + // Action��Func���Ƕ����һ��������Action���޷���ֵ��Func���з���ֵ + // doOnNext�еĻص���������ִ��֮ǰִ�еIJ��� + final Action1 markEmits = new Action1() { + @Override + public void call(R r) { + if (shouldOutputOnNextEvents()) { + executionResult = executionResult.addEvent(HystrixEventType.EMIT); + eventNotifier.markEvent(HystrixEventType.EMIT, commandKey); + } + if (commandIsScalar()) { + long latency = System.currentTimeMillis() - executionResult.getStartTimestamp(); + eventNotifier.markCommandExecution(getCommandKey(), properties.executionIsolationStrategy().get(), (int) latency, executionResult.getOrderedList()); + eventNotifier.markEvent(HystrixEventType.SUCCESS, commandKey); + executionResult = executionResult.addEvent((int) latency, HystrixEventType.SUCCESS); + circuitBreaker.markSuccess(); + } + } + }; + // doOnCompleted�еĻص�������ִ����Ϻ�ִ�еIJ��� + final Action0 markOnCompleted = new Action0() { + @Override + public void call() { + if (!commandIsScalar()) { + long latency = System.currentTimeMillis() - executionResult.getStartTimestamp(); + eventNotifier.markCommandExecution(getCommandKey(), properties.executionIsolationStrategy().get(), (int) latency, executionResult.getOrderedList()); + eventNotifier.markEvent(HystrixEventType.SUCCESS, commandKey); + executionResult = executionResult.addEvent((int) latency, HystrixEventType.SUCCESS); + circuitBreaker.markSuccess(); + } + } + }; + // onErrorResumeNext�еĻص�������ִ��ʧ�ܺ�Ļ����߼� + final Func1> handleFallback = new Func1>() { + @Override + public Observable call(Throwable t) { + Exception e = getExceptionFromThrowable(t); + executionResult = executionResult.setExecutionException(e); + if (e instanceof RejectedExecutionException) { + // �̵߳���ʧ�ܻص� + return handleThreadPoolRejectionViaFallback(e); + } else if (t instanceof HystrixTimeoutException) { + // ��ʱ�ص� + return handleTimeoutViaFallback(); + } else if (t instanceof HystrixBadRequestException) { + // HystrixBadRequestException �쳣�ص� + return handleBadRequestByEmittingError(e); + } else { + if (e instanceof HystrixBadRequestException) { + eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey); + return Observable.error(e); + } + // �������� + return handleFailureViaFallback(e); + } + } + }; + // doOnEach�еĻص���`Observable`ÿ����һ�����ݶ���ִ������ص����������������� + final Action1> setRequestContext = new Action1>() { + @Override + public void call(Notification rNotification) { + setRequestContextIfNeeded(currentRequestContext); + } + }; + // ������Ӧ�� Observable��ʵ�� �̸߳��롢������ �Ȳ��� + Observable execution; + // �ж� ��ʱ��ع����Ƿ�� + if (properties.executionTimeoutEnabled().get()) { + // HystrixObservableTimeoutOperator ת����Ӧ�� Observable + execution = executeCommandWithSpecifiedIsolation(_cmd) + .lift(new HystrixObservableTimeoutOperator(_cmd)); + } else { + execution = executeCommandWithSpecifiedIsolation(_cmd); + } + //���ûص� + return execution.doOnNext(markEmits) + .doOnCompleted(markOnCompleted) + .onErrorResumeNext(handleFallback) + .doOnEach(setRequestContext); + } + + + + + + + + + +3.3 executeCommandWithSpecifiedIsolation +������������Ǹ��ݵ�ǰ��ͬ����Դ�������ִ�в�ͬ���߼���THREAD��SEMAPHORE�� + + + + private Observable executeCommandWithSpecifiedIsolation(final AbstractCommand _cmd) { + // �̸߳���, �Ƿ��� THREAD ��Դ���뽵�� + if (properties.executionIsolationStrategy().get() == ExecutionIsolationStrategy.THREAD) { + //����һ��Observable + return Observable.defer(new Func0>() { + @Override + public Observable call() { + executionResult = executionResult.setExecutionOccurred(); + if (!commandState.compareAndSet(CommandState.OBSERVABLE_CHAIN_CREATED, CommandState.USER_CODE_EXECUTED)) { + return Observable.error(new IllegalStateException("execution attempted while in state : " + commandState.get().name())); + } + + metrics.markCommandStart(commandKey, threadPoolKey, ExecutionIsolationStrategy.THREAD); + + // �������ڰ�װ�߳��г�ʱ�����������أ����Ҳ��������κμ����������������߼� + if (isCommandTimedOut.get() == TimedOutStatus.TIMED_OUT) { + // the command timed out in the wrapping thread so we will return immediately + // and not increment any of the counters below or other such logic + return Observable.error(new RuntimeException("timed out before executing run()")); + } + + // �����߳����� + if (threadState.compareAndSet(ThreadState.NOT_USING_THREAD, ThreadState.STARTED)) { + //we have not been unsubscribed, so should proceed + HystrixCounters.incrementGlobalConcurrentThreads(); + threadPool.markThreadExecution(); + // store the command that is being run + endCurrentThreadExecutingCommand = Hystrix.startCurrentThreadExecutingCommand(getCommandKey()); + executionResult = executionResult.setExecutedInThread(); + + try { + executionHook.onThreadStart(_cmd); + executionHook.onRunStart(_cmd); + executionHook.onExecutionStart(_cmd); + //���� Observable,����������ջ᷵��һ����װ�����ǵ�run()�߼���Observable + return getUserExecutionObservable(_cmd); + } catch (Throwable ex) { + return Observable.error(ex); + } + } else { + //command has already been unsubscribed, so return immediately + return Observable.error(new RuntimeException("unsubscribed before executing run()")); + } + } + }).doOnTerminate(new Action0() { + @Override + public void call() { + if (threadState.compareAndSet(ThreadState.STARTED, ThreadState.TERMINAL)) { + handleThreadEnd(_cmd); + } + if (threadState.compareAndSet(ThreadState.NOT_USING_THREAD, ThreadState.TERMINAL)) { + } + } + }).doOnUnsubscribe(new Action0() { + @Override + public void call() { + if (threadState.compareAndSet(ThreadState.STARTED, ThreadState.UNSUBSCRIBED)) { + handleThreadEnd(_cmd); + } + if (threadState.compareAndSet(ThreadState.NOT_USING_THREAD, ThreadState.UNSUBSCRIBED)) { + } + } + }).subscribeOn(threadPool.getScheduler(new Func0() { + @Override + public Boolean call() { + return properties.executionIsolationThreadInterruptOnTimeout().get() && _cmd.isCommandTimedOut.get() == TimedOutStatus.TIMED_OUT; + } + })); + } else { + // �ź������� + return Observable.defer(new Func0>() { + @Override + public Observable call() { + executionResult = executionResult.setExecutionOccurred(); + if (!commandState.compareAndSet(CommandState.OBSERVABLE_CHAIN_CREATED, CommandState.USER_CODE_EXECUTED)) { + return Observable.error(new IllegalStateException("execution attempted while in state : " + commandState.get().name())); + } + metrics.markCommandStart(commandKey, threadPoolKey, ExecutionIsolationStrategy.SEMAPHORE); + endCurrentThreadExecutingCommand = Hystrix.startCurrentThreadExecutingCommand(getCommandKey()); + try { + executionHook.onRunStart(_cmd); + executionHook.onExecutionStart(_cmd); + // ������ִ�� + return getUserExecutionObservable(_cmd); + } catch (Throwable ex) { + //If the above hooks throw, then use that as the result of the run method + return Observable.error(ex); + } + } + }); + } + } + + + + + + + + + +- �ж��Ƿ����������������ǻ��ڶ�·��ʵ�֣������·���򿪣�����ж�Ӧ�ص�������ʧ�ܻ򽵼����� +- ��� ��·�� �رգ�����������Ȼ�ȡ�źţ���ȡʧ��������Ӧ�ص��� +- ��ȡ�ɹ������ɷ��� executeCommandAndObserve ������Ӧ�� Observable ʵ�� �̸߳��롢������ �Ȳ�����ͬʱע���˶�Ӧ�� �������ڻص��� + 3.4 getUserExecutionObservable + Ȼ���ִ�� HystrixCommand#getExecutionObservable + + + + abstract class AbstractCommand implements HystrixInvokableInfo, HystrixObservable { + private Observable getUserExecutionObservable(final AbstractCommand _cmd) { + Observable userObservable; + try { + userObservable = getExecutionObservable(); + } catch (Throwable ex) { + userObservable = Observable.error(ex); + } + return userObservable + .lift(new ExecutionHookApplication(_cmd)) + .lift(new DeprecatedOnRunHookApplication(_cmd)); + } + } + public abstract class HystrixCommand extends AbstractCommand implements HystrixExecutable, HystrixInvokableInfo, HystrixObservable { + @Override + final protected Observable getExecutionObservable() { + return Observable.defer(new Func0>() { + @Override + public Observable call() { + try { + return Observable.just(run()); + } catch (Throwable ex) { + return Observable.error(ex); + } + } + }).doOnSubscribe(new Action0() { + @Override + public void call() { + // Save thread on which we get subscribed so that we can interrupt it later if needed + executionThread.set(Thread.currentThread()); + } + }); + } + } + + + + + + + + + +��� run() �����������Ѿ������ˣ�����������ҵ��ִ�з����� + + + + @ThreadSafe + public class GenericCommand extends AbstractHystrixCommand { + @Override + protected Object run() throws Exception { + LOGGER.debug("execute command: {}", getCommandKey().name()); + return process(new Action() { + @Override + Object execute() { + return getCommandAction().execute(getExecutionType()); + } + }); + } + } + + + + + + + + + +���յ��õ������Լ���ҵ���߼��� + +�����ܽ�һ�����������ҵ���߼�ͼ�� 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/Spring Cloud LoadBalancer\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/Spring Cloud LoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..9704860 --- /dev/null +++ "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/Spring Cloud LoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,662 @@ +# Spring Cloud LoadBalancer + +## ���� + +> Spring Cloud LoadBalancerĿǰSpring�ٷ��Ƿ���spring-cloud-commons�Spring Cloud���°汾Ϊ2021.0.2 +> +> [Spring Cloud LoadBalancer �����ĵ���ַ](https://docs.spring.io/spring-cloud-commons/docs/3.1.2/reference/html/#spring-cloud-loadbalancer) [https://docs.spring.io/spring-cloud-commons/docs/3.1.2/reference/html/#spring-cloud-loadbalancer](https://docs.spring.io/spring-cloud-commons/docs/3.1.2/reference/html/#spring-cloud-loadbalancer) +> +> [Spring Cloud�����ĵ���ַ](https://docs.spring.io/spring-cloud/docs/current/reference/html/) [https://docs.spring.io/spring-cloud/docs/current/reference/html/](https://docs.spring.io/spring-cloud/docs/current/reference/html/) + +һ����Netflix Ribbonֹͣ���£�Spring Cloud LoadBalancer��Spring Cloud�ٷ��Լ��ṩ�Ŀͻ��˸��ؾ�����,�����ʵ�֣��������Ribbon�� + +* �������ؾ�������Ϊ����˸��ؾ�����(�����ز���⸺��)�Ϳͻ��˲���⸺�ء� + * ���ز���Ӳ�������F5�����������LVS������nginx�ȡ� + * �ͻ��˲����Spring Cloud LoadBalancer����Ϊһ���ͻ���ȥ���ָ���ά�������б����Զ������ľ��⸺�ز��ԣ��������ѯ��С�����Ľ�˿ȸ�ȵȣ��� + +Spring Cloud�ṩ���Լ��Ŀͻ��˸���ƽ���������ʵ�֡����ڸ��ؾ�����ƣ�������ReactiveLoadBalancer�ӿڣ����ṩ�˻���round-robin��ѯ��Random�����ʵ�֡�Ϊ�˴���ӦʽServiceInstanceListSupplier��ѡ��ʵ������Ҫʹ��ServiceInstanceListSupplier��Ŀǰ֧��ServiceInstanceListSupplier�Ļ��ڷ����ֵ�ʵ�֣���ʵ��ʹ����·���еķ��ֿͻ��˴�Service Discovery�м������õ�ʵ���� + +����ͨ����������������Spring Cloud LoadBalance + +``` +spring: + cloud: + loadbalancer: + enabled: false + +``` + +## ����ʾ�� + +ǰ��simple-ecommerce��Ŀ�������ڸ�Pom����������������ϸ���Կ���ǰ�������<>������Spring Cloud�İ汾Ϊ2021.0.1��ǰ������Ҳ��˵����Spring Cloud Alibaba������spring-cloud-starter-alibaba-nacos-discovery����������spring-cloud-loadbalancer�� + +> ע�������Hoxton֮ǰ�İ汾��Ĭ�ϸ��ؾ�����ΪRibbon����Ҫ�Ƴ�Ribbon���ú���������spring.cloud.loadbalancer.ribbon.enabled: false�� + +�������Spring Boot��Ŀ�������������������������starterҲ������Spring Boot Caching and Evictor. + +``` + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + +``` + +����ʹ��Spring�ٷ��ṩ�˸��ؾ���Ŀͻ���֮һRestTemplate��RestTemplate��Spring�ṩ�����ڷ���Rest����Ŀͻ��ˣ�RestTemplate�ṩ�˶��ֱ�ݷ���Զ��Http����ķ������ܹ������߿ͻ��˵ı�дЧ�ʡ�Ĭ������£�RestTemplateĬ������jdk��HTTP���ӹ��ߡ�����RestTemplateConfig�����࣬��ע @LoadBalancedע�⣬Ĭ��ʹ�õ�ReactiveLoadBalancerʵ����RoundRobinLoadBalancer�� + +``` +package cn.itxs.ecom.order.config; + +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @LoadBalanced + @Bean + public RestTemplate restTemplate() { + return new RestTemplate() ; + } +} + +``` + +����΢�����ж�������������deductRest���� + +``` +package cn.itxs.ecom.order.controller; + +import cn.itxs.ecom.commons.service.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +/** + * @Name ��OrderController + * @Description ������������ + * @Author ��itxs + * @Date ��2022/4/10 20:15 + * @Version ��1.0 + * @History �� + */ +@RestController +public class OrderController { + + @Autowired + OrderService orderService; + + @Autowired + private RestTemplate restTemplate; + + @RequestMapping("/create/{userId}/{commodityCode}/{count}") + public String create(@PathVariable("userId") String userId,@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count){ + return orderService.create(userId,commodityCode,count).toString(); + } + + @RequestMapping("/deductRest/{commodityCode}/{count}") + public String deductRest(@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count){ + String url = "http://ecom-storage-service/deduct/"+commodityCode+"/"+count; + return restTemplate.getForObject(url, String.class); + } +} + +``` + +ǰ��server.port�����Ƿ���Nacos�����������������ע��Nacos�������ĵ����÷��ڱ��������ļ�bootstrap.yml��ֱ�����Ϊ4080��4081��4082����3��������ʵ��������������΢���� + +``` +server: + port: 4080 + +``` + +![image-20220505191143684](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/938570a0b63b56671a862e8bda11577a.png) + +�鿴nacos�������-�����б���������飬���Կ���3�������Ŀ��ʵ����1������΢����ʵ�� + +![image-20220505182432182](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/33fa03d937354fe00bb0bb2f3dd5c805.png) + +����6�ζ���dedect�ӿڣ�[http://localhost:4070/deductRest/1001/1](http://localhost:4070/deductRest/1001/1) ���Ӳ��ԵĽ��Ҳ��֤��LoadBalancerĬ������ѯ���ؾ�����ԡ� + +![image-20220505192217715](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1fc20b1d482d2a4e3924c5707ac2ff19.png) + +## ���ؾ����㷨�л� + +�����Զ��帺�ؾ���������CustomLoadBalancerConfiguration + +``` +package cn.itxs.ecom.order.config; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; + +public class CustomLoadBalancerConfiguration { + + @Bean + ReactorLoadBalancer randomLoadBalancer(Environment environment, + LoadBalancerClientFactory loadBalancerClientFactory) { + String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); + return new RandomLoadBalancer(loadBalancerClientFactory + .getLazyProvider(name, ServiceInstanceListSupplier.class), + name); + } +} + +``` + +RestTemplateConfig������LoadBalancerClientָ������������࣬value��ֵΪ�ṩ��Ҳ���ǿ��΢�������ơ� + +``` +package cn.itxs.ecom.order.config; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +@LoadBalancerClient(value = "ecom-storage-service", configuration = CustomLoadBalancerConfiguration.class) +public class RestTemplateConfig { + + @LoadBalanced + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build() ; + } +} + +``` + +��η��ʶ���dedect�ӿڲ���ȷ�����л�Ϊ������ؾ�����ԡ� + +## ���ɷ�ʽ + +�����ṩ3�м���Spring Cloud LoadBalancer�ķ�ʽ�����˵�һ��������ʹ�ù�����֧��Spring Web Flux��Ӧʽ��̣�WebClient�Ǵ�Spring WebFlux 5.0�汾��ʼ�ṩ��һ���������Ļ�����Ӧʽ��̵Ľ���Http����Ŀͻ��˹��ߡ�������Ӧʽ��̵Ļ���Reactor�ġ�WebClient���ṩ�˱�׼Http����ʽ��Ӧ��get��post��put��delete�ȷ�������������������Ӧ������ + +![image-20220506233248585](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/ad44f363342f540b1cb1805b20be6ef0.png) + +�ڶ���΢����������spring-boot-starter-webflux���� + +``` + + org.springframework.boot + spring-boot-starter-webflux + + +``` + +����΢����������WebClientConfig������ + +``` +package cn.itxs.ecom.order.config; + +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +public class WebClientConfig { + @LoadBalanced + @Bean + WebClient.Builder webClientBuilder() { + return WebClient.builder(); + } + + @Bean + WebClient webClient() { + return webClientBuilder().build(); + } +} + +``` + +����΢���񶩵�������������WebClient�ӿ�ʵ�֣��������� + +``` + @Autowired + private WebClient webClient; + + @RequestMapping(value = "/deductWebClient/{commodityCode}/{count}") + public Mono deductWebClient(@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count) { + String url = "http://ecom-storage-service/deduct/"+commodityCode+"/"+count; + // ����WebClient + Mono result = webClient.get().uri(url) + .retrieve().bodyToMono(String.class); + return result; + } + +``` + +������������΢���� + +![image-20220506234934948](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/ec48e022accfb3298069c96b7e3799e7.png) + +���ʶ����������еļ����WebClient�ӿڣ�[http://localhost:4070/deductWebClient/1001/1](http://localhost:4070/deductWebClient/1001/1) ��������سɹ� + +![image-20220506234627330](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/5f737fc9d8806dc072ce3e1fdf983de6.png) + +���ǻ��������û��ڹ������ķ�ʽ��ͨ��WebClientʹ��ReactiveLoadBalancer�������Ŀ��������Spring Cloud LoadBalancer starter������Spring -webflux����·���У�ReactorLoadBalancerExchangeFilterFunction�����Զ����õġ� + +����΢���񶩵�������������WebClientʹ��ReactiveLoadBalancer�ӿ�ʵ�֣��������� + +``` + @Autowired + private ReactorLoadBalancerExchangeFilterFunction lbFunction; + + @RequestMapping(value = "/deductWebFluxReactor/{commodityCode}/{count}") + public Mono deductWebFluxReactor(@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count) { + String url = "/deduct/"+commodityCode+"/"+count; + Mono result = WebClient.builder().baseUrl("http://ecom-storage-service") + .filter(lbFunction) + .build() + .get() + .uri(url) + .retrieve() + .bodyToMono(String.class); + return result; + } + +``` + +������������΢���� + +![image-20220507000930179](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3546b9913f1253a1b23039ad6f7546d1.png) + +���ʶ����������еļ����WebClient�ӿڣ�[http://localhost:4070/deductWebFluxReactor/1001/1](http://localhost:4070/deductWebFluxReactor/1001/1) ��������سɹ� + +![image-20220507000746900](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0bc2b04d23d11bc85009c7e040900b2b.png) + +����LoadBalancer�������ṩ�ܶ��������ܣ�����Ȥ��������ϸ���ĺͶ���ʵ�� + +![image-20220507001132987](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1d8f35933945b317a2dd71627fdfa748.png) + +## ԭ�� + +### RestTemplate + +Spring Cloud LoadBalancerԴ����������ȴ�RestTemplate���ؾ���ļ�ʵ�����������֣�����֮����֧��Spring Web Flux��Ӧʽ��̵�ʵ��ԭ��˼��Ҳ����ͬ������ͨ���ͻ�������������������������ʵ�ָ��ؾ��⡣��RestTemplate��Դ���п���֪����̳���InterceptingHttpAccessor������ + +![image-20220508142236428](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c3ee7af69f54aeed5f1026823779fee8.png) + +��InterceptingHttpAccessor���������ṩ��һ������setInterceptors��������������������������Ҫʵ��ClientHttpRequestInterceptor�ӿڼ��ɣ���ʵ��Զ���������˽ӿ�֮ǰ���ȵ�����������intercept������������������൱��Servlet�����е�Filter���� + +``` + // ����ʵ���ڳ�����InterceptingHttpAccessor�� + // RestTemplate.InterceptingHttpAccessor#setInterceptors + public void setInterceptors(List interceptors) { + Assert.noNullElements(interceptors, "'interceptors' must not contain null elements"); + // Take getInterceptors() List as-is when passed in here + if (this.interceptors != interceptors) { + this.interceptors.clear(); + this.interceptors.addAll(interceptors); + AnnotationAwareOrderComparator.sort(this.interceptors); + } + } + +``` + +![image-20220508142443637](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/5062f793bb2bd7f996eb944f77af7137.png) + +### **LoadBalancerAutoConfiguration** + +�ӹ�������֪��Spring Cloud LoadBalancer����spring-cloud-commons�����Ҳ��Ϊ����ĵ�@LoadBalancedע��Ҳ������spring-cloud-commons��ʵ�֣�����SpringBoot�Զ�װ���ԭ���Ȳ鿴��������ʵ���߼������ѷ���spring-cloud-commons�������Զ�������LoadBalancerAutoConfiguration��ReactorLoadBalancerClientAutoConfiguration�� + +![image-20220509001530634](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b5be12e0dd8d515aa07613738efab122.png) + +����������������ʱ��@ConditionalΪ����ע�⣩�����Զ�����LoadBalancerInterceptor��ע�뵽RestTemplate�С� + +![image-20220508143752218](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/676f6d81529228511c862c780b0c4532.png) + +### **LoadBalancerLnterceptor** + +LoadBalancerInterceptorʵ����ClientHttpRequestInterceptor�ӿڣ����Ҳʵ��intercept����������ʵ�ָ��ؾ�������ش����� + +![image-20220508144048248](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/bb587d8122fee4973eba2fc00ed1af3f.png) + +### **LoadBalancerClient** + +LoadBalancerClient���ڽ��и��ؾ����߼����̳���ServiceInstanceChooser�ӿڣ��ӷ����б���ѡ���һ�������ַ���е��á���LoadBalancerClient�ִ�������execute()��������������ִ������ģ�reconstructURI()�������ع�URL�� + +![image-20220508144435104](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/a3590c0ab24cb5ee04cf1cf513724fc0.png) + +����LoadBalancerClient�ӿ�Spring Cloud LoadBalancer���ṩĬ��ʵ��ΪBlockingLoadBalancerClient + +![image-20220508144750601](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/256e4815a982966d7d6bfb82e0e97f67.png) + +``` +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class BlockingLoadBalancerClient implements LoadBalancerClient { + + private final ReactiveLoadBalancer.Factory loadBalancerClientFactory; + + /** + * @deprecated in favour of + * {@link BlockingLoadBalancerClient#BlockingLoadBalancerClient(ReactiveLoadBalancer.Factory)} + */ + @Deprecated + public BlockingLoadBalancerClient(LoadBalancerClientFactory loadBalancerClientFactory, + LoadBalancerProperties properties) { + this.loadBalancerClientFactory = loadBalancerClientFactory; + } + + public BlockingLoadBalancerClient(ReactiveLoadBalancer.Factory loadBalancerClientFactory) { + this.loadBalancerClientFactory = loadBalancerClientFactory; + } + + @Override + public T execute(String serviceId, LoadBalancerRequest request) throws IOException { + String hint = getHint(serviceId); + LoadBalancerRequestAdapter lbRequest = new LoadBalancerRequestAdapter<>(request, + new DefaultRequestContext(request, hint)); + Set supportedLifecycleProcessors = getSupportedLifecycleProcessors(serviceId); + supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest)); + ServiceInstance serviceInstance = choose(serviceId, lbRequest); + // ѡ����� + if (serviceInstance == null) { + supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete( + new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, new EmptyResponse()))); + throw new IllegalStateException("No instances available for " + serviceId); + } + return execute(serviceId, serviceInstance, lbRequest); + } + + @Override + public T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) + throws IOException { + DefaultResponse defaultResponse = new DefaultResponse(serviceInstance); + Set supportedLifecycleProcessors = getSupportedLifecycleProcessors(serviceId); + Request lbRequest = request instanceof Request ? (Request) request : new DefaultRequest<>(); + supportedLifecycleProcessors + .forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, new DefaultResponse(serviceInstance))); + try { + T response = request.apply(serviceInstance); + Object clientResponse = getClientResponse(response); + supportedLifecycleProcessors + .forEach(lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS, + lbRequest, defaultResponse, clientResponse))); + return response; + } + catch (IOException iOException) { + supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete( + new CompletionContext<>(CompletionContext.Status.FAILED, iOException, lbRequest, defaultResponse))); + throw iOException; + } + catch (Exception exception) { + supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete( + new CompletionContext<>(CompletionContext.Status.FAILED, exception, lbRequest, defaultResponse))); + ReflectionUtils.rethrowRuntimeException(exception); + } + return null; + } + + private Object getClientResponse(T response) { + ClientHttpResponse clientHttpResponse = null; + if (response instanceof ClientHttpResponse) { + clientHttpResponse = (ClientHttpResponse) response; + } + if (clientHttpResponse != null) { + try { + return new ResponseData(clientHttpResponse, null); + } + catch (IOException ignored) { + } + } + return response; + } + + private Set getSupportedLifecycleProcessors(String serviceId) { + return LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors( + loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class), + DefaultRequestContext.class, Object.class, ServiceInstance.class); + } + + @Override + public URI reconstructURI(ServiceInstance serviceInstance, URI original) { + return LoadBalancerUriTools.reconstructURI(serviceInstance, original); + } + + @Override + public ServiceInstance choose(String serviceId) { + return choose(serviceId, REQUEST); + } + + // ͨ����ͬ�ĸ��ؾ���ͻ���ʵ��ѡ��ͬ�ķ��� + @Override + public ServiceInstance choose(String serviceId, Request request) { + ReactiveLoadBalancer loadBalancer = loadBalancerClientFactory.getInstance(serviceId); + if (loadBalancer == null) { + return null; + } + Response loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block(); + if (loadBalancerResponse == null) { + return null; + } + return loadBalancerResponse.getServer(); + } + + private String getHint(String serviceId) { + LoadBalancerProperties properties = loadBalancerClientFactory.getProperties(serviceId); + String defaultHint = properties.getHint().getOrDefault("default", "default"); + String hintPropertyValue = properties.getHint().get(serviceId); + return hintPropertyValue != null ? hintPropertyValue : defaultHint; + } + +} + +``` + +### **LoadBalancerClientFactory** + +BlockingLoadBalancerClient�г���LoadBalancerClientFactoryͨ��������getInstance������ȡ����ĸ��ؾ���ͻ��ˡ�ͨ��������LoadBalancerClientFactory��ȡ����ĸ��ؾ�����ʵ���������loadBalancer.choose(request)������ӿ�choose()����ʵ�ָ��ݸ��ؾ����㷨ѡ����һ����������ɸ��ؾ��⣬��ReactiveLoadBalancer getInstance(String serviceId) ��Ĭ��ʵ��LoadBalancerClientFactory +![image-20220508190132565](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4014445b0c2ea7189a232b5d257fb938.png) + +LoadBalancerClientFactory�ͻ���ʵ���˲�ͬ�ĸ��ؾ����㷨��������ѯ������ȡ�LoadBalancerClientFactory�̳���NamedContextFactory��NamedContextFactory�̳�ApplicationContextAware��ʵ��Spring ApplicationContext���������� + +![image-20220508190412076](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/18f62338fdb636327fcdb2d08d33661b.png) + +### ReactiveLoadBalancer + +ReactiveLoadBalancer���ؾ�����ʵ�ַ���ѡ��Spring Cloud Balancer��ʵ������ѯRoundRobinLoadBalancer�����RandomLoadBalancer��NacosLoadBalancer�㷨�� + +![image-20220508235128931](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f19b9fd0d246dbae37b05e67b1a79805.png) + +### LoadBalancerClientConfiguration + +���û����ʽָ�����ؾ����㷨��Ĭ��ȱʡֵΪRoundRobinLoadBalancer + +``` + @Bean + @ConditionalOnMissingBean + public ReactorLoadBalancer reactorServiceInstanceLoadBalancer(Environment environment, + LoadBalancerClientFactory loadBalancerClientFactory) { + String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); + return new RoundRobinLoadBalancer( + loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); + } + +``` + +![image-20220508235645313](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c8ae46d4cedb33f40939edb4f6fde542.png) + +### **LoadBalancerRequestFactory** + +LoadBalancerRequest���������createRequest�������ڴ���LoadBalancerRequest�����ڲ�����LoadBalancerClient����Ҳ������BlockingLoadBalancerClient�� + +![image-20220509000049541](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c928dcb312df87a44c33ee77a296ee84.png) + +���ճ���Ŀ�У�һ�㸺�ؾ��ⶼ�ǽ��Feignʹ�ã�����������ʱ����������Feign����LoadBalancer���Զ�������FeignLoadBalancerAutoConfiguration��ʵ�� + +### ReactorLoadBalancerClientAutoConfiguration + +����Ҳ��һ�»���WebClient��@Loadbalanced�����̵����룬�����������ؾ��������ReactorLoadBalancerClientAutoConfiguration��һ���Զ�װ�����࣬����Ŀ�������� WebClient �� ReactiveLoadBalancer ��֮���Զ�װ�����̾Ϳ�ʼ���У������ʼ��һ��ʵ���� ExchangeFilterFunction ��ʵ�����ں����ʵ������Ϊ��������ע�뵽WebClient��������������Ȥ�������о� + +![image-20220509001650781](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/869a4ea9a6e1409b84c927db08cebea1.png) + +## �Զ��帺�ؾ����� + +���������֪��LoadBalancerClientFactory�Ǵ����ͻ��������ؾ������Ϳͻ�������ʵ���Ĺ����������ݿͻ������ƴ���һ��Spring ApplicationContext����������ȡ�����bean����˽��뵽LoadBalancerClientFactory���У���Ҫȥʵ�������ӽӿ�ReactorServiceInstanceLoadBalancer����Ϊȥ��ȡ���ؾ�����ʵ����ʱ����ͨ��ȥ�����в���ReactorServiceInstanceLoadBalancer���͵�bean��ʵ�ֵģ����Բ���RandomLoadBalancerʵ�ִ��� + +``` +package org.springframework.cloud.loadbalancer.core; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.DefaultResponse; +import org.springframework.cloud.client.loadbalancer.EmptyResponse; +import org.springframework.cloud.client.loadbalancer.Request; +import org.springframework.cloud.client.loadbalancer.Response; + +public class RandomLoadBalancer implements ReactorServiceInstanceLoadBalancer { + + private static final Log log = LogFactory.getLog(RandomLoadBalancer.class); + + private final String serviceId; + + private ObjectProvider serviceInstanceListSupplierProvider; + + /** + * @param serviceInstanceListSupplierProvider a provider of + * {@link ServiceInstanceListSupplier} that will be used to get available instances + * @param serviceId id of the service for which to choose an instance + */ + public RandomLoadBalancer(ObjectProvider serviceInstanceListSupplierProvider, + String serviceId) { + this.serviceId = serviceId; + this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; + } + + @SuppressWarnings("rawtypes") + @Override + public Mono> choose(Request request) { + ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider + .getIfAvailable(NoopServiceInstanceListSupplier::new); + return supplier.get(request).next() + .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances)); + } + + private Response processInstanceResponse(ServiceInstanceListSupplier supplier, + List serviceInstances) { + Response serviceInstanceResponse = getInstanceResponse(serviceInstances); + if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { + ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer()); + } + return serviceInstanceResponse; + } + + private Response getInstanceResponse(List instances) { + if (instances.isEmpty()) { + if (log.isWarnEnabled()) { + log.warn("No servers available for service: " + serviceId); + } + return new EmptyResponse(); + } + int index = ThreadLocalRandom.current().nextInt(instances.size()); + + ServiceInstance instance = instances.get(index); + + return new DefaultResponse(instance); + } + +} + +``` + +��������ʵ�ֽ��м򵥷�д���� + +``` +package cn.itxs.ecom.order.config; + +import java.util.List; +import java.util.Random; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.cloud.client.loadbalancer.DefaultResponse; +import org.springframework.cloud.client.loadbalancer.EmptyResponse; +import org.springframework.cloud.client.loadbalancer.Request; +import org.springframework.cloud.client.loadbalancer.Response; +import reactor.core.publisher.Mono; + +public class ItxsRandomLoadBalancerClient implements ReactorServiceInstanceLoadBalancer { + // �����б� + private ObjectProvider serviceInstanceListSupplierProvider; + + public ItxsRandomLoadBalancerClient(ObjectProvider serviceInstanceListSupplierProvider) { + this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; + } + + @Override + public Mono> choose(Request request) { + ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(); + return supplier.get().next().map(this::getInstanceResponse); + } + + /** + * ʹ���������ȡ���� + * @param instances + * @return + */ + private Response getInstanceResponse( + List instances) { + System.out.println("ItxsRandomLoadBalancerClient start"); + if (instances.isEmpty()) { + return new EmptyResponse(); + } + + System.out.println("ItxsRandomLoadBalancerClient random"); + // ����㷨 + int size = instances.size(); + Random random = new Random(); + ServiceInstance instance = instances.get(random.nextInt(size)); + + return new DefaultResponse(instance); + } +} + +``` + +������CustomLoadBalancerConfiguration�滻Ϊ�������� + +``` +package cn.itxs.ecom.order.config; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.context.annotation.Bean; + +public class CustomLoadBalancerConfiguration { + + @Bean + public ReactorServiceInstanceLoadBalancer customLoadBalancer(ObjectProvider serviceInstanceListSupplierProvider) { + return new ItxsRandomLoadBalancerClient(serviceInstanceListSupplierProvider); + } +} + +``` + +�������΢����Ͷ���΢���񣬷���http://localhost:4070/deductRest/1001/1 ������̨�Ѵ�ӡ�Զ���ItxsRandomLoadBalancerClient�е���־�ͳɹ����ʽ�� + +![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 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/Spring Cloud OpenFeign\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/Spring Cloud OpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..7a54f90 --- /dev/null +++ "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/Spring Cloud OpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,895 @@ +���� | ��ľľ�� +��Դ |����ͷ�� + +**ѧϰĿ��** + +1. Ϊʲô��һ��ע�����ʵ��Զ�̹��̵����أ��Ƶ����ײ��ʵ�������̣� +2. OpenFeign��ôʵ��RPC�Ļ������ܵ� +3. ͨ��Դ����֤ + **��1�� OpenFeign�������Ƶ�** + Ҫ��ȷOpenFeign���������������ǻ���Ҫ��ȷ���ĺ���Ŀ����ʲô�� + +˵���ˣ�OpenFeign����ĵ�Ŀ������ÿͻ�����Զ�̵��ù����в���Ҫ��ʲô����IJ�����ֻҪ�õ�һ������Ȼ����øö���ķ����ͺ��ˣ�ʣ�µIJ���������OpenFeignȥ������ɣ���ʣ��һЩʲô�����أ� + +1. ���ȿ϶��DZ�֤����ͨ�ţ������Ǵ󵨵ز²�һ�£�OpenFeign��ʵ�ײ�����Ƿ�װ������ĵ�ַ���˿ڡ���������Լ���Ӧ�IJ����� +2. ��Σ�������Ҫ�ö���ȥ���󷽷��������������Զ�̵ķ����������϶����򵥣�����Ҳ�󵨲²�һ�£��ö���Ҳ��OpenFeign�����Ǵ����ġ� +3. Ȼ���ڵ��ù����У�����������ɶ�̨�������ṩ�ģ������漰�����ؾ����ˣ���϶�Ҳ��OpenFeign����������ˡ� +4. ������������⣬������һ�£���Ȼ���ڷ�����Ǽ�Ⱥ��������Ƿ���˵ĵ�ַ�Ͷ˿ڻ���Ҫһ��ע��������ע�ᣬ��϶�Ҳ�����ɿͻ�������ɣ���Ϊ�ͻ���ֻ��עҵ����롣���붼�����룬Ҳ��OpenFeign������ˡ� + +OK�������Ƶ���OpenFeignӦ����ɵ���ҪĿ�꣬����������������������������ô���ġ� +![SpringCloudϵ�С�Spring Cloud Դ�����֮OpenFeign-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b45564c706ab4113cc23492cc0796907b851f8.jpg "SpringCloudϵ�С�Spring Cloud Դ�����֮OpenFeign-��Դ������������")֮ǰ�������н���һ�����������ʲô�����ֻҪ�Ǽ���spring����springboot�Ļ�����һ������ͨ��spring����springbootȥ����bean����Ĵ����ģ���ͨ�������õ�����֮����ȥ���ö���ĺ��ķ�������OpenFeign�ڼ���springboot��ʱ������ҲӦ���������� + +1. ���Ե�һ����OpenFeign����springboot��ͨ��springboot�õ�����bean����������ͼ�е�userService���� +2. �������϶����򵥣�������ֻ��getUser�Ĺ��ܡ�����һ�룬spring����������ǿ������ʲô�����أ�����������������������;ͺ�֮�����ˣ��������� +3. ���ô�������ķ���ʱ�������Ƚ��뵽invoke�У������invoke�У�������ǿ�����˸��ؾ���LoadBalance����Ϊ������������getUser��ʱ��Ҫ֪�������ǵ�����̨�������ķ��� +4. ���ؾ�������͵�ƴ�Ӿ����http�������������ͷ�������ַ������˿��ˡ� + OK�����Ϸ�����OpenFeign�ײ�Ҫʵ�ֵľ��幦�ܣ�Ҳ���������Ĵ������̣���ô����������ͨ��Դ������֤һ�£����Dz�����ô��ġ� + +**��2�� Դ����֤** + +![SpringCloudϵ�С�Spring Cloud Դ�����֮OpenFeign-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/93070e354fbacedc2b85274846d2dde7dd170a.jpg "SpringCloudϵ�С�Spring Cloud Դ�����֮OpenFeign-��Դ������������")**2.1 EnableFeignClients** +���Ǵ��������ע��������룬���ע�⿪����FeignClient�Ľ������̡� + + + +``` +@EnableFeignClients(basePackages = "com.example.client") +``` + + + + + + + + + +���ע����������£����õ���һ��@Importע�⣬����֪��Import����������һ��������ģ�������ȥ��һ��FeignClientsRegistrar�Ķ��塣 + + + +``` +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +@Import(FeignClientsRegistrar.class) +public @interface EnableFeignClients { +} +``` + + + + + + + + + +FeignClientsRegistrarʵ���� +ImportBeanDefinitionRegistrar������һ����̬ע��bean�Ľӿڣ�Spring Boot������ʱ�򣬻�ȥ����������е�registerBeanDefinitions��ʵ�ֶ�̬Bean��װ�ء�registerBeanDefinitions����spring��������ʱִ��invokeBeanFactoryPostProcessors������Ȼ�����Ӧ������н���ע�ᣬ��������������ImportSelector�� + + + +``` +class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware,EnvironmentAware { + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) { + registerDefaultConfiguration(metadata, registry); + registerFeignClients(metadata, registry); + } +} +``` + + + + + + + + + +**2.1.1 ImportBeanDefinitionRegistrar** +�򵥸������ʾһ�� +ImportBeanDefinitionRegistrar�����á� + +* ����һ����Ҫ��װ�ص�IOC�����е���HelloService + + + +``` +public class HelloService { +} +``` + + + + + + + + + +* ����һ��Registrar��ʵ�֣�����һ��bean��װ�ص�IOC���� + + + +``` +public class FeignImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + BeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClassName(HelloService.class.getName()); + registry.registerBeanDefinition("helloService",beanDefinition); + } +} +``` + + + + + + + + + +* ����һ��ע���� + + + + ``` + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE}) + @Documented + @Import({FeignImportBeanDefinitionRegistrar.class}) + public @interface EnableFeignTest { + } + ``` + + + + + + + + + +* ������ + + + +``` +@EnableFeignClients(basePackages = "com.example.clients") +@EnableFeignTest +@SpringBootApplication +public class OpenfeignUserServiceApplication { + public static void main(String[] args) { + ConfigurableApplicationContext context = SpringApplication.run(OpenfeignUserServiceApplication.class, args); + System.out.println(context.getBean(HelloService.class)); + } + +} +``` + + + + + + + + + +* ͨ�������ʾ���Է��֣�HelloService���bean �Ѿ�װ�ص���IOC������ + ����Ƕ�̬װ�صĹ���ʵ�֣��������@Configuration����ע�룬����˺ܶ������ԡ� ok���ٻص�FeignClient�Ľ��������� + +**2.1.2 FeignClientsRegistrar** + +* registerDefaultConfiguration �����ڲ��� SpringBoot �������ϼ���Ƿ���@EnableFeignClients, �и�ע��Ļ��� ����� Feign �����ص�һЩ��������ע�� + +* registerFeignClients �����ڲ��� classpath �У� ɨ���� @FeignClient ���ε��࣬ ��������ݽ���Ϊ BeanDefinition , ����ͨ������ Spring ����е�BeanDefinitionReaderUtils.resgisterBeanDefinition �������������� FeignClientBeanDeifinition ���ӵ� spring ������. + + + + ``` + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) { + //ע��@EnableFeignClients�ж���defaultConfiguration�����µ��࣬��װ��FeignClientSpecification��ע�ᵽSpring������ + //��@FeignClient����һ�����ԣ�configuration����������DZ�ʾ����FeignClient�Զ���������࣬����Ҳ��ͨ������registerClientConfiguration������ע���FeignClientSpecification�������� + //���ԣ����������ȫ������@EnableFeignClients�����õ�����Ϊ���׵����ã��ڸ���@FeignClient���õľ����Զ��������� + registerDefaultConfiguration(metadata, registry); + registerFeignClients(metadata, registry); + } + ``` + + + + + + + + + +**2.2 registerDefaultConfiguration** + + + + ``` + private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { + // ��ȡ��metadata�й���EnableFeignClients������ֵ��ֵ�ԡ� + Map defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true); + //���������defaultConfiguration ��������,���û��ʹ��Ĭ�ϵ�configuration + if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { + String name; + if (metadata.hasEnclosingClass()) { + name = "default." + metadata.getEnclosingClassName(); + } else { + name = "default." + metadata.getClassName(); + } + //����ע�� + this.registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); + } + + } + ``` + + + + + + + + + + + +``` +private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { + //ʹ��BeanDefinitionBuilder������BeanDefinition,����������ע�� + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class); + builder.addConstructorArgValue(name); + builder.addConstructorArgValue(configuration); + registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); +} +``` + + + + + + + + + +���������BeanDefinitionRegistry��spring������ڶ�̬ע��BeanDefinition��Ϣ�Ľӿڣ�����registerBeanDefinition�������Խ�BeanDefinitionע�ᵽSpring�����У�����name���Ծ���ע���BeanDefinition�����ƣ���������ע����һ��FeignClientSpecification�Ķ��� + +FeignClientSpecificationʵ���� +NamedContextFactory.Specification�ӿڣ�����Feignʵ��������Ҫһ����������ķ����У��������Զ������õ����ʵ����SpringCloudʹ��NamedContextFactory����һЩ�е�����������ApplicationContext���ö�Ӧ��Specification����Щ�������д���ʵ������ + +NamedContextFactory��3�����ܣ� + +* ����AnnotationConfigApplicationContext�����ġ� +* ���������д�������ȡbeanʵ���� +* ������������ʱ������е�feignʵ���� + NamedContextFactory�и��dz���Ҫ������FeignContext�����ڴ洢����OpenFeign�����ʵ���� + + + +``` +public class FeignContext extends NamedContextFactory { + public FeignContext() { + super(FeignClientsConfiguration.class, "feign", "feign.client.name"); + } + } +``` + + + + + + + + + +FeignContext�����ﹹ�����أ� + +���ü��� +pring-cloud-openfeign-core-2.2.3.RELEASE.jar!\META-INF\spring.factories![SpringCloudϵ�С�Spring Cloud Դ�����֮OpenFeign-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d830f20713aca01438472786f5f84c7058232b.jpg "SpringCloudϵ�С�Spring Cloud Դ�����֮OpenFeign-��Դ������������")**2.2.1 FeignAutoConfiguration** + +![SpringCloudϵ�С�Spring Cloud Դ�����֮OpenFeign-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/894c73791fbf7215782217d7132bc57f004085.jpg "SpringCloudϵ�С�Spring Cloud Դ�����֮OpenFeign-��Դ������������") + +��Ĭ�ϵ�FeignClientsConfiguration��Ϊ�������ݸ����캯�� + +FeignContext������ʱ��Ὣ֮ǰFeignClientSpecificationͨ��setConfigurations���ø�context�����ġ� + +**2.2.2 createContext** +��������� +org.springframework.cloud.context.named.NamedContextFactory#createContext������ + +FeignContext�ĸ����createContext�����Ὣ���� +AnnotationConfigApplicationContextʵ������ʵ������Ϊ��ǰ�����ĵ��������ģ����ڹ���feign����IJ�ͬʵ�����ڵ���FeignClientFactoryBean��getObject����ʱ���á���createContext���������Ļὲ�⣩ + + + +``` +protected AnnotationConfigApplicationContext createContext(String name) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + //��ȡname����Ӧ��configuration,����о�ע�ᵽ��context�� + if (this.configurations.containsKey(name)) { + for (Class configuration : this.configurations.get(name) + .getConfiguration()) { + context.register(configuration); + } + } + //ע��default��Configuration,Ҳ���� FeignClientsRegistrar����registerDefaultConfiguration������ע���Configuration + for (Map.Entry entry : this.configurations.entrySet()) { + if (entry.getKey().startsWith("default.")) { + for (Class configuration : entry.getValue().getConfiguration()) { + context.register(configuration); + } + } + } + //ע��PropertyPlaceholderAutoConfiguration + context.register(PropertyPlaceholderAutoConfiguration.class, + this.defaultConfigType); + //��Environment��propertySources����Դ + context.getEnvironment().getPropertySources().addFirst(new MapPropertySource( + this.propertySourceName, + Collections.singletonMap(this.propertyName, name))); + if (this.parent != null) { + // Uses Environment from parent as well as beans + context.setParent(this.parent); + // jdk11 issue + // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101 + context.setClassLoader(this.parent.getClassLoader()); + } + context.setDisplayName(generateDisplayName(name)); + context.refresh(); + return context; +} +``` + + + + + + + + + +����NamedContextFactoryʵ����DisposableBean�����Ե�ʵ��������ʱ������ + + + +``` +@Override +public void destroy() { + Collection values = this.contexts.values(); + for (AnnotationConfigApplicationContext context : values) { + // This can fail, but it never throws an exception (you see stack traces + // logged as WARN). + context.close(); + } + this.contexts.clear(); +} +``` + + + + + + + + + +�ܽ᣺NamedContextFactory�ᴴ���� +AnnotationConfigApplicationContextʵ��������name��ΪΨһ��ʶ��Ȼ��ÿ��AnnotationConfigApplicationContextʵ������ע�Ჿ�������࣬�Ӷ����Ը���һϵ�еĻ������������ɵ����ʵ���������Ϳ��Ի���name������һϵ�е����ʵ����Ϊ��ͬ��FeignClient׼����ͬ�������ʵ���� + +**2.3 registerFeignClients** +���������Ҫ��ɨ����·�������е�@FeignClientע�⣬Ȼ����ж�̬Bean��ע�롣�����ջ���� registerFeignClient ������ + + + +``` +public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) { + //ʡ�Դ���... + registerFeignClient(registry, annotationMetadata, attributes); +} +``` + + + + + + + + + +����������У�����ȥ��װBeanDefinition��Ҳ����Bean�Ķ��壬Ȼ��ע�ᵽSpring IOC������ + + + +``` +private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map attributes) { + String className = annotationMetadata.getClassName(); + BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class); + //ʡ�Դ���... + BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition,className,new String[] { alias }); + BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); +} +``` + + + + + + + + + +���ǹ�עһ�£�BeanDefinitionBuilder����������һ��BeanDefinition�ģ�����ͨ��genericBeanDefinition �������ģ����Ҵ�����һ��FeignClientFactoryBean���ࡣ + +���ǿ��Է��֣�FeignClient����̬ע�����һ��FactoryBean + +> Spring Cloud FengnClientʵ����������Spring�Ĵ������������ɴ����࣬����������Ż�����е�FeignClient��BeanDefinition����ΪFeignClientFactoryBean���ͣ���FeignClientFactoryBean�̳���FactoryBean������һ������Bean�� +> +> ��Spring�У�FactoryBean��һ������Bean��������������Bean�� +> +> ���� Bean ��һ������� Bean, ���� Bean ����������˵�� ���߼����Ǹ�֪������� Bean ����ͨ�� Bean ���ǹ��� Bean, ֻ�ǰ��������Ļ�ȡ Bean ��ʽȥ���ã� ������bean ��󷵻ص�ʵ�����ǹ���Bean ������ ����ִ�й��� Bean �� getObject �߼����ص�ʾ������Ҳ������ʵ��������Bean��ʱ���ȥ��������getObject������ + + + +``` +public static BeanDefinitionBuilder genericBeanDefinition(Class beanClass) { + BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition()); + builder.beanDefinition.setBeanClass(beanClass); + return builder; +} +``` + + + + + + + + + +����˵��FeignClient��ע������ӿڣ���ͨ�� +FeignClientFactoryBean.getObject()����������һ���������� + +**2.3.1 FeignClientFactoryBean.getObject** +getObject���õ���getTarget����������applicationContextȡ��FeignContext��FeignContext�̳���NamedContextFactory������������ͳһά��feign�и���feign�ͻ����໥����������ġ� + +���ţ�����feign.builder���ڹ���ʱ����FeignContext��ȡ���õ�Encoder��Decoder�ȸ�����Ϣ��FeignContext����ƪ���Ѿ��ᵽ��Ϊÿ��Feign�ͻ��˷�����һ�����������ǵĸ���������spring���� + +������Feign.Builder֮�����ж��Ƿ���ҪLoadBalance�������Ҫ����ͨ��LoadBalance�ķ��������á�ʵ�����������յ��õ���Target.target()������ + + + +``` +@Override +public Object getObject() throws Exception { + return getTarget(); +} + T getTarget() { + //ʵ����Feign�����Ķ���FeignContext + FeignContext context = this.applicationContext.getBean(FeignContext.class); + Feign.Builder builder = feign(context);//����Builder���� + if (!StringUtils.hasText(this.url)) {//���urlΪ�գ����߸��ؾ��⣬�����и��ؾ��⹦�ܵĴ����� + if (!this.name.startsWith("http")) { + this.url = "http://" + this.name; + } + else { + this.url = this.name; + } + this.url += cleanPath(); + return (T) loadBalance(builder, context,new HardCodedTarget<>(this.type, this.name,this.url)); + } + //���ָ����url��������Ĭ�ϵĴ����� + if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) { + this.url = "http://" + this.url; + } + String url = this.url + cleanPath(); + //����FeignContext��getInstance������ȡClient���� + Client client = getOptional(context, Client.class); + if (client != null) { + if (client instanceof LoadBalancerFeignClient) { + // not load balancing because we have a url, + // but ribbon is on the classpath, so unwrap + client = ((LoadBalancerFeignClient) client).getDelegate(); + } + if (client instanceof FeignBlockingLoadBalancerClient) { + // not load balancing because we have a url, + // but Spring Cloud LoadBalancer is on the classpath, so unwrap + client = ((FeignBlockingLoadBalancerClient) client).getDelegate(); + } + builder.client(client); + }//����Ĭ�ϴ����� + Targeter targeter = get(context, Targeter.class); + return (T) targeter.target(this, builder, context,new HardCodedTarget<>(this.type, this.name,url)); +} +``` + + + + + + + + + +**2.3.2 loadBalance** +���ɾ߱����ؾ���������feign�ͻ��ˣ�Ϊfeign�ͻ��˹�����󶨸��ؾ���ͻ���. + +Client client = (Client)this.getOptional(context, Client.class); ���������л�ȡһ��Client��Ĭ����LoadBalancerFeignClient�� + +������FeignRibbonClientAutoConfiguration����Զ�װ�����У�ͨ��Importʵ�ֵ� + + + +``` +@Import({ HttpClientFeignLoadBalancedConfiguration.class,OkHttpFeignLoadBalancedConfiguration.class,DefaultFeignLoadBalancedConfiguration.class }) +``` + + + + + + + + + + + +``` +protected T loadBalance(Builder builder, FeignContext context, + HardCodedTarget target) { + Client client = (Client)this.getOptional(context, Client.class); + if (client != null) { + builder.client(client); + Targeter targeter = (Targeter)this.get(context, Targeter.class); + return targeter.target(this, builder, context, target); + } else { + throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?"); + } +} +``` + + + + + + + + + +**2.3.3 DefaultTarget.target** + + + +``` +@Override +public T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTarget target) { + return feign.target(target); +} +``` + + + + + + + + + +**2.3.4 ReflectiveFeign.newInstance** +�����������������һ����̬�����ķ����������ɶ�̬����֮ǰ�������ContractЭ�飨Э��������򣬽����ӿ����ע����Ϣ���������ڲ���MethodHandler�Ĵ�����ʽ�� + +��ʵ�ֵĴ����п��Կ�����Ϥ��Proxy.newProxyInstance�������������ࡣ��������Ҫ��ÿ������Ľӿڷ��������ض��Ĵ���ʵ�֣�������������һ��MethodHandler�ĸ�����Ƕ�Ӧ���������InvocationHandler�� + + + +``` +public T newInstance(Target target) { + //���ݽӿ����ContractЭ�������ʽ�������ӿ����ϵķ�����ע�⣬ת�����ڲ���MethodHandler������ʽ + Map nameToHandler = this.[targetToHandlersByName.apply(target)]; + Map methodToHandler = new LinkedHashMap(); + List defaultMethodHandlers = new LinkedList(); + Method[] var5 = target.type().getMethods(); + int var6 = var5.length; + for(int var7 = 0; var7 < var6; ++var7) { + Method method = var5[var7]; + if (method.getDeclaringClass() != Object.class) { + if (Util.isDefault(method)) { + DefaultMethodHandler handler = new DefaultMethodHandler(method); + defaultMethodHandlers.add(handler); + methodToHandler.put(method, handler); + } else { + methodToHandler.put(method,nameToHandler.get(Feign.configKey(target.type(), method))); + } + } + } + InvocationHandler handler = this.factory.create(target, methodToHandler); + // ����Proxy.newProxyInstance Ϊ�ӿ��ഴ����̬ʵ�֣������е�����ת����InvocationHandler ������ + T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler); + Iterator var12 = defaultMethodHandlers.iterator(); + while(var12.hasNext()) { + DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next(); + defaultMethodHandler.bindTo(proxy); + } + return proxy; +} +``` + + + + + + + + + +**2.4 �ӿڶ���IJ�������** +����FeignClient�ӿڵ�������������Ӧ���������ݡ� + +**2.4.1 targetToHandlersByName.apply(target)** +����ContractЭ����򣬽����ӿ����ע����Ϣ���������ڲ����֣� + +targetToHandlersByName.apply(target);������ӿڷ����ϵ�ע�⣬�Ӷ��������������ȵ��ض���������Ϣ��Ȼ������һ��SynchronousMethodHandler Ȼ����Ҫά��һ����map������InvocationHandler��ʵ��FeignInvocationHandler�С� + + + +``` +public Map apply(Target target) { + List metadata = contract.parseAndValidateMetadata(target.type()); + Map result = new LinkedHashMap(); + for (MethodMetadata md : metadata) { + BuildTemplateByResolvingArgs buildTemplate; + if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) + { + buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder,target); + } else if (md.bodyIndex() != null) { + buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder,queryMapEncoder, target); + } else { + buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder,target); + } + if (md.isIgnored()) { + result.put(md.configKey(), args -> { + throw new IllegalStateException(md.configKey() + " is not a method handled by feign"); + }); + } else { + result.put(md.configKey(), + factory.create(target, md, buildTemplate, options, decoder,errorDecoder)); + } + } + return result; +} +``` + + + + + + + + + +**2.4.2 SpringMvcContract** +��ǰSpring Cloud ΢�����������У�Ϊ�˽���ѧϰ�ɱ���������Spring MVC�IJ���ע������� �����������Ҳ����˵ ��д�ͻ�������ӿں���д����˴���һ�����ͻ��˺ͷ���˿���ͨ��SDK�ķ�ʽ����Լ�����ͻ���ֻ��Ҫ�������˷�����SDK API���Ϳ���ʹ������ӿڵı��뷽ʽ�Խӷ��� + +����̳���Contract.BaseContract��ʵ����ResourceLoaderAware�ӿڣ� + +�����þ��Ƕ�RequestMapping��RequestParam��RequestHeader��ע����н����ġ� + +**2.5 OpenFeign���ù���** +��ǰ��ķ����У�����֪��OpenFeign���շ��ص���һ��# +ReflectiveFeign.FeignInvocationHandler�Ķ��� + +��ô���ͻ��˷�������ʱ������뵽 +FeignInvocationHandler.invoke�����У������Ҷ�֪��������һ����̬������ʵ�֡� + + + +``` +public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (!"equals".equals(method.getName())) { + if ("hashCode".equals(method.getName())) { + return this.hashCode(); + } else { + return "toString".equals(method.getName()) ? this.toString() : + ((MethodHandler)this.dispatch.get(method)).invoke(args); + } + } else { + try { + Object otherHandler = args.length > 0 && args[0] != null ? + Proxy.getInvocationHandler(args[0]) : null; + return this.equals(otherHandler); + } catch (IllegalArgumentException var5) { + return false; + } + } +} +``` + + + + + + + + + +�����ţ���invoke�����У������ this.dispatch.get(method)).invoke(args) ��this.dispatch.get(method) �᷵��һ��SynchronousMethodHandler,�������ش����� + +�����������ݲ���������ɵ�RequestTemplate�������������Http�����ģ�棬�������¡� + + + +``` +public Object invoke(Object[] argv) throws Throwable { + RequestTemplate template = this.buildTemplateFromArgs.create(argv); + Options options = this.findOptions(argv); + Retryer retryer = this.retryer.clone(); + while(true) { + try { + return this.executeAndDecode(template, options); + } catch (RetryableException var9) { + RetryableException e = var9; + try { + retryer.continueOrPropagate(e); + } catch (RetryableException var8) { + Throwable cause = var8.getCause(); + if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP + && cause != null) { + throw cause; + } + throw var8; + } + if (this.logLevel != Level.NONE) { + this.logger.logRetry(this.metadata.configKey(), this.logLevel); + } + } + } +} +``` + + + + + + + + + +**2.5.1 executeAndDecode** +���������Ĵ��룬�����Ѿ���restTemplateƴװ��ɣ�����Ĵ�������һ�� executeAndDecode() �������÷���ͨ��RequestTemplate����Request�������Ȼ������Http Client��ȡresponse������ȡ��Ӧ��Ϣ�� + + + +``` +Object executeAndDecode(RequestTemplate template, Options options) throws Throwable { + //ת��ΪHttp������ + Request request = this.targetRequest(template); + if (this.logLevel != Level.NONE) { + this.logger.logRequest(this.metadata.configKey(), this.logLevel,request); + } + long start = System.nanoTime(); + Response response; + try { + //����Զ��ͨ�� + response = this.client.execute(request, options); + //��ȡ���ؽ�� + response = response.toBuilder().request(request).requestTemplate(template).build(); + } catch (IOException var16) { + if (this.logLevel != Level.NONE) { + this.logger.logIOException(this.metadata.configKey(), this.logLevel,var16, this.elapsedTime(start)); + } + throw FeignException.errorExecuting(request, var16); + } + long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + boolean shouldClose = true; + Response var10; + try { + if (this.logLevel != Level.NONE) { + response = this.logger.logAndRebufferResponse(this.metadata.configKey(), this.logLevel,response, elapsedTime); + } + if (Response.class != this.metadata.returnType()) { + Object result; + Object var21; + if (response.status() >= 200 && response.status() < 300) { + if (Void.TYPE == this.metadata.returnType()) { + var10 = null; + return var10; + } + result = this.decode(response); + shouldClose = this.closeAfterDecode; + var21 = result; + return var21; + } + if (this.decode404 && response.status() == 404 && Void.TYPE != this.metadata.returnType()) { + result = this.decode(response); + shouldClose = this.closeAfterDecode; + var21 = result; + return var21; + } + throw this.errorDecoder.decode(this.metadata.configKey(), response); + } + if (response.body() == null) { + var10 = response; + return var10; + } + if (response.body().length() != null && (long)response.body().length() <= 8192L) { + byte[] bodyData = Util.toByteArray(response.body().asInputStream()); + Response var11 = response.toBuilder().body(bodyData).build(); + return var11; + } + shouldClose = false; + var10 = response; + } catch (IOException var17) { + if (this.logLevel != Level.NONE) { + this.logger.logIOException(this.metadata.configKey(), this.logLevel,var17, elapsedTime); + } + throw FeignException.errorReading(request, response, var17); + } finally { + if (shouldClose) { + Util.ensureClosed(response.body()); + } + } + return var10; +} +``` + + + + + + + + + +**2.5.2 Client.execute** +Ĭ�ϲ���JDK�� HttpURLConnection ����Զ�̵��á� + + + +``` +@Override +public Response execute(Request request, Options options) throws IOException { + HttpURLConnection connection = convertAndSend(request, options); + return convertResponse(connection, request); +} +Response convertResponse(HttpURLConnection connection, Request request) throws IOException { + int status = connection.getResponseCode(); + String reason = connection.getResponseMessage(); + if (status < 0) { + throw new IOException(format("Invalid status(%s) executing %s %s",status,connection.getRequestMethod(),connection.getURL())); + } + Map> headers = new LinkedHashMap<>(); + for (Map.Entry> field : + connection.getHeaderFields().entrySet()) { + // response message + if (field.getKey() != null) { + headers.put(field.getKey(), field.getValue()); + } + } + Integer length = connection.getContentLength(); + if (length == -1) { + length = null; + } + InputStream stream; + if (status >= 400) { + stream = connection.getErrorStream(); + } else { + stream = connection.getInputStream(); + } + return Response.builder() + .status(status) + .reason(reason) + .headers(headers) + .request(request) + .body(stream, length) + .build(); +} +``` + + + 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/Spring Cloud Ribbon\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/Spring Cloud Ribbon\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..57606b3 --- /dev/null +++ "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/Spring Cloud Ribbon\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,947 @@ +**ѧϰĿ��** + +1. �Ƶ�Ribbon�ĺ������� +2. ��дһ�����װ��Ribbon +3. ͨ��Դ����֤�Ƶ������� + **��1�� ���������Ƶ�** + ��ʵRibbon�ĺ������̺ܼ򵥣�������ʹ�ù������޷Ǿ���������һ��spring-cloud-starter-netflix-ribbon��jar����Ȼ���ڳ���������ʱ��ע����һ��RestTemplate�����ڸö�������������һ��@LoadBalancedע�⣬Ȼ����ͨ��RestTemplate����ȥ����URL��ʱ����ܸ��ݲ�ͬ�ĸ��ؾ������ȥ����ͬ�ķ��������ע�⣬����˵���jar����������ʲô�����أ� + +��������Ҫ���ף�spring-cloud-starter-netflix-ribbon���jar����������������֪�������ǻ���starter����ģ������϶�������springboot���Զ�װ��ԭ����������������ʱ���ṩ��һ���Զ������࣬����������Ҫ�õĶ���ע�뵽IoC��������ȥ�ˣ������ӹ���ɡ� + +Ȼ��Ҫ��RestTemplate����ȥ����Ŀ������ʱ�����ʱ�����ǿ϶���Ҫ����ʵ��IP�Ͷ˿����滻����������һ����ʵ���Ǻ��IJ��裬��Ҫ��ô���أ���ô����������֮ǰ����ַ�Ͷ˿���è��̫�ӻ�����ʵ���أ� + +�������ճ������У�����Ӧ��֪������һ����������һ����������ʵ������������������԰ɣ����ԣ�ʵ���Ͼ����ڻ�ȡRestTemplate�����ʱ�򣬽��ö�������������һ������������RestTemplate����ִ��ij��������ʱ�򣬶���ȥ����������ִ��һ�顣Ȼ��������ˡ� + +���������ͼ�Ƶ����£�![SpringCloudϵ�С�RibbonԴ�����-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1790fb324161913dd79098940f8102d652cd86.jpg "SpringCloudϵ�С�RibbonԴ�����-��Դ������������")**��2�� ���װ�Ribbonʵ��** +����������Ƶ����̣����ǽ�������ʵ��һ�����װ��Ribbon�� + +����IJ�������Ҫ��������˼·�� + +1.����Ҫʵ��һ��starter�������������springboot����springboot��������ʱ������õ���Ӧ��RestTemplate��bean���󡣴���һ��Maven��quickstart��Ŀ + +2.Ȼ������ + + + +``` + + + + 4.0.0 + + com.example + myribbon-spring-cloud-starter + 1.0-SNAPSHOT + + myribbon-spring-cloud-starter + + http://www.example.com + + + UTF-8 + 1.8 + 1.8 + 2.3.2.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + + + + + org.springframework.cloud + spring-cloud-commons + 2.2.6.RELEASE + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + +``` + + + + + + + + + +3.���������� + + + +``` +@Configuration +public class MyRibbonAutoConfiguration { + //���װ��Ribbon�ǿ������ȥ��ɸ��ؾ����㷨�Լ���ʵ��ip�Ͷ˿��滻�� + @Bean + public LoadBalancerClient loadBalancerClient(){ + return new MyLoadBalancerClient(); + } + //�ռ����д�MyLoadBalancedע���RestTemplate���� + @MyLoadBalanced + @Autowired(required = false) + private List restTemplates = Collections.emptyList(); + @Bean + @ConditionalOnMissingBean + public LoadBalancerRequestFactory loadBalancerRequestFactory( + LoadBalancerClient loadBalancerClient) { + return new LoadBalancerRequestFactory(loadBalancerClient); + } + //����Ǻ��ĵ������� + @Bean + public MyLoadBalancerInterceptor myLoadBalancerInterceptor( + LoadBalancerClient loadBalancerClient, + LoadBalancerRequestFactory requestFactory){ + return new MyLoadBalancerInterceptor(loadBalancerClient,requestFactory); + } + //�ռ�����RestTemplate���������ﶼ������һ�������� + @Bean + public SmartInitializingSingleton smartInitializingSingleton( + final MyLoadBalancerInterceptor myLoadBalancerInterceptor){ + return ()->{ + for (RestTemplate restTemplate : MyRibbonAutoConfiguration.this.restTemplates) { + List list = new ArrayList<>( + restTemplate.getInterceptors()); + list.add(myLoadBalancerInterceptor); + restTemplate.setInterceptors(list); + } + }; + } +} +``` + + + + + + + + + +4.����MyLoadBalancerClient + + + +``` +public class MyLoadBalancerClient implements LoadBalancerClient { + @Autowired + AbstractEnvironment environment; + //1.����������������������� + @Override + public T execute(String serviceId, LoadBalancerRequest request) throws IOException { + ServiceInstance server = this.choose(serviceId); + return execute(serviceId, server, request); + } + //3.����ִ��Http���� + @Override + public T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException { + T returnVal = null; + try { + returnVal = request.apply(serviceInstance); + } catch (Exception e) { + e.printStackTrace(); + } + return returnVal; + } + //4.��һ����������ʵ��ip��port�滻������ + @Override + public URI reconstructURI(ServiceInstance instance, URI original) { + String host = instance.getHost(); + int port = instance.getPort(); + if (host.equals(original.getHost()) + && port == original.getPort() + ) { + return original; + } + try { + StringBuilder sb = new StringBuilder(); + sb.append("http").append("://"); + if (!Strings.isNullOrEmpty(original.getRawUserInfo())) { + sb.append(original.getRawUserInfo()).append("@"); + } + sb.append(host); + if (port >= 0) { + sb.append(":").append(port); + } + sb.append(original.getRawPath()); + if (!Strings.isNullOrEmpty(original.getRawQuery())) { + sb.append("?").append(original.getRawQuery()); + } + if (!Strings.isNullOrEmpty(original.getRawFragment())) { + sb.append("#").append(original.getRawFragment()); + } + URI newURI = new URI(sb.toString()); + return newURI; + }catch (URISyntaxException e){ + throw new RuntimeException(e); + } + } + //2.���ؾ����㷨����һ�����з����ip�Ͷ˿�ѡ���������õ�������㷨 + @Override + public ServiceInstance choose(String serviceId) { + Server instance = new Server(serviceId,null,"127.0.0.1",8080); + String sr = environment.getProperty(serviceId+".ribbon.listOfServers"); + if (!StringUtils.isEmpty(sr)){ + String[] arr = sr.split(",",-1); + Random selector = new Random(); + int next = selector.nextInt(arr.length); + String a = arr[next]; + String[] srr = a.split(":",-1); + instance.setHost(srr[0]); + instance.setPort(Integer.parseInt(srr[1])); + } + return instance; + } +} +``` + + + + + + + + + +5.���������߼���ʵ�ܼ򵥣���������������http����֮ǰ����ִ���ҵ��߼� + + + +``` +public class MyLoadBalancerInterceptor implements ClientHttpRequestInterceptor { + private LoadBalancerClient loadBalancerClient; + private LoadBalancerRequestFactory requestFactory; + public MyLoadBalancerInterceptor(LoadBalancerClient loadBalancerClient, + LoadBalancerRequestFactory requestFactory) { + this.loadBalancerClient = loadBalancerClient; + this.requestFactory = requestFactory; + } + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + final URI originalUri = request.getURI(); + String serviceName = originalUri.getHost(); + return this.loadBalancerClient.execute(serviceName, + this.requestFactory.createRequest(request, body, execution)); + } +} +``` + + + + + + + + + +6.�����Լ���ע�� + + + +``` +@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Qualifier +public @interface MyLoadBalanced { +} +``` + + + + + + + + + +7.����һ���Լ���Serverʵ���� + + + +``` +public class Server implements ServiceInstance { + private String serviceId; + private String instanceId; + private String host; + private int port; + public Server(String serviceId, String instanceId, String host, int port) { + this.serviceId = serviceId; + this.instanceId = instanceId; + this.host = host; + this.port = port; + } + public Server() { + } + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + public void setHost(String host) { + this.host = host; + } + public void setPort(int port) { + this.port = port; + } + @Override + public String getInstanceId() { + return null; + } + @Override + public String getServiceId() { + return null; + } + @Override + public String getHost() { + return host; + } + @Override + public int getPort() { + return port; + } + @Override + public boolean isSecure() { + return false; + } + @Override + public URI getUri() { + return null; + } + @Override + public Map getMetadata() { + return null; + } + @Override + public String getScheme() { + return null; + } +} +``` + + + + + + + + + +8.дspring.factories�ļ� + + + +``` +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.example.config.MyRibbonAutoConfiguration +``` + + + + + + + + + +9.���jar�������ԣ����Ե�ʱ����Ҫ�������ļ� + + + +``` +.ribbon.listOfServers=127.0.0.1:2223,127.0.0.1:2222 +``` + + + + + + + + + +**��3�� Դ����֤** +**3.1 @LoadBalanced** +���ϽڿεĴ��뿴������ֻ����RestTemplate�������һ��@LoadBalance,�Ϳ���ʵ�ָ��ؾ����ˣ��������ǵ������@LoadBalance����һ�£������ע��������һ��@Qualifierע�⡣��ע���޶��ĸ�beanӦ�ñ��Զ�ע�롣��Spring�޷��жϳ��ĸ�beanӦ�ñ�ע��ʱ��@Qualifierע����������������bean���Զ�ע�룬���Ӽ����롣 + + + +``` +/** + * Annotation to mark a RestTemplate or WebClient bean to be configured to use a + * LoadBalancerClient. + * @author Spencer Gibb + */ +@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Qualifier +public @interface LoadBalanced { + +} +``` + + + + + + + + + +��ע���п���֪�������ע����������RestTemplate����ǣ���ʹ�ø��ؾ���ͻ��ˣ�LoadBalancerClient���������������ԣ����������ɵ�RestTemplate��bean��������ôһ��ע�⣬���bean�ͻ�����LoadBalancerClient�� + +**3.2 LoadBalancerClient** +��ô�����ٿ���LoadBalancerClient�Ĵ��룺 + + + +``` +public interface LoadBalancerClient extends ServiceInstanceChooser { + T execute(String serviceId, LoadBalancerRequest request) throws IOException; + T execute(String serviceId, ServiceInstance serviceInstance, + LoadBalancerRequest request) throws IOException; + URI reconstructURI(ServiceInstance instance, URI original); +} +public interface ServiceInstanceChooser { + ServiceInstance choose(String serviceId); +} +``` + + + + + + + + + +LoadBalancerClient��һ���ӿڣ����������������� + +ServiceInstance choose(String serviceId);�ӷ������ϾͿ��Կ������Ǹ��ݴ����serviceId�������������Ӹ��ؾ�������ѡ��һ������ʵ��������ʵ��ͨ��ServiceInstance������ʾ�� +execute������ʹ�ôӸ��ؾ�������ѡ��ķ���ʵ����ִ���������ݡ� +URI reconstructURI(ServiceInstance instance, URI original);�����������¹���һ��URI�ģ����ǵ������ڴ����У�ͨ��RestTemplate�������ʱ��д���Ƿ������ɣ���������ͻ����������URI����ת��������host+port��ͨ��host+port����ʽȥ������� +**3.3 �Զ�װ��** +��springboot����֮�󣬻�ͨ���Զ�װ���Զ�ȥ +spring-cloud-netflix-ribbon���jar����META-INFĿ¼��spring.factories�ļ������ҽ�RibbonAutoConfiguration���������ע�롣����RibbonAutoConfiguration����������Ϊ����@AutoConfigureBeforeע�⣬�����ֻ����LoadBalancerAutoConfiguration�����ࡣ��LoadBalancerAutoConfiguration���У�spring�����Ὣ���б�@LoadBalanceע�����ε�beanע�뵽IOC������ + + + +``` +@LoadBalanced +@Autowired(required = false) +private List restTemplates = Collections.emptyList(); +``` + + + + + + + + + +ͬʱ����LoadBalancerAutoConfiguration�������л���Ϊÿ��RestTemplateʵ������LoadBalancerInterceptor�������� + +��RibbonAutoConfiguration����ע����LoadBalancerClient�ӿڵ�ʵ����RibbonLoadBalancerClient + + + +``` +@Bean +@ConditionalOnMissingBean(LoadBalancerClient.class) +public LoadBalancerClient loadBalancerClient() { + return new RibbonLoadBalancerClient(springClientFactory()); +} +``` + + + + + + + + + +**3.4 ������** +�������Զ��������У���restTemplateʵ��������LoadBalancerInterceptor�����������ԣ�����restTemplate����http����ʱ���ͻ�ִ�������������intercept������ + +intercept�����У������request.getURI()����ȡ�����uri���ٻ�ȡhost�������ڷ���http�����ʱ�����õķ�������Ϊhost�����ԣ�����ͻ��õ����������ٵ��þ���LoadBalancerClientʵ����execute�������������� + +LoadBalancerClient��ʵ����ΪRibbonLoadBalancerClient�����յĸ��ؾ�������������ִ�У����ԣ�����Ҫ��������RibbonLoadBalancerClient���߼��� + +�ȿ���RibbonLoadBalancerClient�е�execute������ + + + +``` +public T execute(String serviceId, LoadBalancerRequest request, Object hint) + throws IOException { + ILoadBalancer loadBalancer = getLoadBalancer(serviceId); + Server server = getServer(loadBalancer, hint); + if (server == null) { + throw new IllegalStateException("No instances available for " + serviceId); + } + RibbonServer ribbonServer = new RibbonServer(serviceId, server, + isSecure(server, serviceId), + serverIntrospector(serviceId).getMetadata(server)); + + return execute(serviceId, ribbonServer, request); +} + +@Override +public T execute(String serviceId, ServiceInstance serviceInstance, + LoadBalancerRequest request) throws IOException { + Server server = null; + if (serviceInstance instanceof RibbonServer) { + server = ((RibbonServer) serviceInstance).getServer(); + } + if (server == null) { + throw new IllegalStateException("No instances available for " + serviceId); + } + + RibbonLoadBalancerContext context = this.clientFactory + .getLoadBalancerContext(serviceId); + RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server); + try { + T returnVal = request.apply(serviceInstance); + statsRecorder.recordStats(returnVal); + return returnVal; + } + // catch IOException and rethrow so RestTemplate behaves correctly + catch (IOException ex) { + statsRecorder.recordStats(ex); + throw ex; + } + catch (Exception ex) { + statsRecorder.recordStats(ex); + ReflectionUtils.rethrowRuntimeException(ex); + } + return null; +} +``` + + + + + + + + + +��������ΪserviceId�ֶδ���������ͨ��getLoadBalancer��ȡloadBalancer���ٸ���loadBalancer��ȡserver��������getServer�Ĵ��룺 + + + +``` +protected Server getServer(ILoadBalancer loadBalancer, Object hint) { + if (loadBalancer == null) { + return null; + } + // Use 'default' on a null hint, or just pass it on? + return loadBalancer.chooseServer(hint != null ? hint : "default"); +} +``` + + + + + + + + + +���loadBalancerΪ�գ���ֱ�ӷ��ؿգ�����͵���loadBalancer��chooseServer��������ȡ��Ӧ��server�� + +��һ��ILoadBalancer��һ���ӿڣ�����������һϵ�и��ؾ���ʵ�ֵķ����� + + + +``` +public interface ILoadBalancer { + public void addServers(List newServers); + public Server chooseServer(Object key); + public void markServerDown(Server server); + @Deprecated + public List getServerList(boolean availableOnly); + public List getReachableServers(); + public List getAllServers(); +} +``` + + + + + + + + + +��Щ�������Ƚ�ֱ�ۣ������׾��ܲ³��Ǹ�ɶ�ģ�addServers����������һ��server���ϣ�chooseServer��ѡ��һ��server��markServerDown�������ij���������ߣ�getReachableServers��ȡ���õ�Server���ϣ�getAllServers�ǻ�ȡ���е�server���ϡ� +ILoadBalancer�кܶ�ʵ�֣��Ǿ������õ��ĸ����أ���RibbonAutoConfiguration����ע��SpringClientFactory��ͨ��RibbonClientConfiguration�࿴��������������ڳ�ʼ����ʱ�򣬷�����ZoneAwareLoadBalancer��Ϊ���ؾ������� + + + +``` +@Bean +@ConditionalOnMissingBean +public ILoadBalancer ribbonLoadBalancer(IClientConfig config, + ServerList serverList, ServerListFilter serverListFilter, + IRule rule, IPing ping, ServerListUpdater serverListUpdater) { + if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) { + return this.propertiesFactory.get(ILoadBalancer.class, config, name); + } + return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList, + serverListFilter, serverListUpdater); +} +``` + + + + + + + + + +**3.5 ZoneAwareLoadBalancer** +ZoneAwareLoadBalancer�������п��Կ�������������ؾ�������zone���й�ϵ�ġ����濴��ZoneAwareLoadBalancer�е�chooseServer������ + +> eureka�ṩ��region��zone�������������з��������������������������ѷ��AWS�� +> region�����Լ�����Ϊ�����ϵķ������������޵��������߻����������ٻ��߱����ȵȣ�û�о����С�����ơ�������Ŀ�����������������к�������region�� +> zone�����Լ�����Ϊregion�ڵľ������������˵region����Ϊ������Ȼ�󱱾��������������Ϳ����ڴ�region֮�»��ֳ�zone1,zone2����zone�� + + + +``` +@Override +public Server chooseServer(Object key) { + //ֻ�е����ؾ�������ά����ʵ��������Zone����ĸ�������1��ʱ��Ż�ִ��ѡ����� + //������ʹ�ø����ʵ�� + if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) { + logger.debug("Zone aware logic disabled or there is only one zone"); + return super.chooseServer(key); + } + Server server = null; + try { + LoadBalancerStats lbStats = getLoadBalancerStats(); + //Ϊ��ǰ���ؾ������е�����Zone����ֱ𴴽����գ�������zoneSnapshot�У���Щ�����е��������ں������㷨 + Map zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats); + logger.debug("Zone snapshots: {}", zoneSnapshot); + if (triggeringLoad == null) { + triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty( + "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d); + } + + if (triggeringBlackoutPercentage == null) { + triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty( + "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d); + } + //��ÿ���Zone����ļ��ϣ�getAvailableZones��ͨ��zoneSnapshotʵ�ֿ���������ѡ + Set availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get()); + logger.debug("Available zones: {}", availableZones); + if (availableZones != null && availableZones.size() < zoneSnapshot.keySet().size()) { + //���ѡ��һ��Zone���� + String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones); + logger.debug("Zone chosen: {}", zone); + if (zone != null) { + //��ö�Ӧ����ĸ��ؾ����� + BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone); + //ѡ�����ķ���ʵ�� + //��chooseServer�н���ʹ��IRule�ӿڵ�choose������ѡ��������ʵ���������IRule�ӿڵ�ʵ�ֻ�ʵ��ZoneAvoidanceRule����ѡ����ķ���ʵ���� + server = zoneLoadBalancer.chooseServer(key); + } + } + } catch (Exception e) { + logger.error("Error choosing server using zone aware logic for load balancer={}", name, e); + } + if (server != null) { + return server; + } else { + logger.debug("Zone avoidance logic is not invoked."); + return super.chooseServer(key); + } +} +``` + + + + + + + + + +������������server��zone�Ϳ�������ѡ������ʵ��������һ��Server���� + +���������ļ��������� + +1. setServerListForZones : �������Zone���з��񻮷� +2. chooseServer����Ҳ����Ҫ��zone�йصļ��㣬��ȻĬ�ϵ�Zoneֻ��һ��������ֱ���ǵ��õĸ����chooseServer��key) +3. getLoadBalancer(String zone) ����zoneȥ��ȡLoadBalancer +4. setRule(IRule rule) Ϊÿ�����ؾ��������ù��� + ������Կ�������ʵ�����Ҫ�����Zone����һЩ���ദ�������ǽ�ԭ������ͬһ����ķ���ʵ�����ٸ��ݵ������л��֡���Ҳ��Ϊ���ܹ�������Ӧ�����õġ� + +**3.5.1 DynamicServerListLoadBalancer** + +> ZoneAwareLoadBalancer�ĸ��� + +����ఴ��������˵���Ƕ�̬���ط����б�ʹ�õġ������м����Ƚ���Ҫ�ķ��� + +1. updateListOfServers:�����б������±��ػ���ķ����б� +2. enableAndInitLearnNewServersFeature:���������б����¶�ʱ���� +3. ��������:@Monitor + +DynamicServerListLoadBalancer�ĺ��ľ��ǻ�ȡ�����б�����Eureka��Ĭ����ͨ��DomainExtractingServerList����ȡ�����������org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientConfiguration#ribbonServerList������û�м���Eureka����ʱ���� + +**3.5.2 BaseLoadBalancer** + +> DynamicServerListLoadBalancer�ĸ��� + +����Ĭ��ֵ + +* Ĭ�ϵĸ��ؾ������RoundRobinRule +* Ĭ�ϵ�Ping����SerialPingStrategy +* ���з���ʵ��������allServerList +* ���߷���ʵ��������upServerList + ![SpringCloudϵ�С�RibbonԴ�����-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b2db0bc66c3045288a762907ff72e01883bf20.jpg "SpringCloudϵ�С�RibbonԴ�����-��Դ������������")�����๹���н���Ӧ�ĸ��ؾ������ping���ԣ�ping�ȴ��ݹ��� + +**ping����Щʲô��** + +PingTask��Ϊһ���߳����񣬾��Ƕ��ڼ������Ƿ񶼴���ServerListUpdater������»��Ʋ���ͻ������ribbon�Լ�ά����һ�׷�������ƣ���Ҫ��Ϊ�˽��ͷ���ʧ�ܵĸ��ʡ�Ĭ����ʹ��eurekaʱ��ping��ʹ�õ���NIWSDiscoveryPing����ɷ��񱣻��⡣��eureka �� ServerListUpdater��ˢ�·����б���![SpringCloudϵ�С�RibbonԴ�����-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/27db4c518aa7c100d427761832a9fa19fa8f04.jpg "SpringCloudϵ�С�RibbonԴ�����-��Դ������������")�����и����õĶ�ʱ��������˳��ķ������Ҿ����������Լ�д��ʱ��Ҳ����ʹ�á� + +������ͬһ����ʱ�������ִ��ʱ�䳬���˶�ʱ���ڣ���ô��һ����ʱ��������һ����ʱ����û��ִ����ʱ������ȡ����![SpringCloudϵ�С�RibbonԴ�����-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f2d669b11f17530d2e62603d45949e934a5931.jpg "SpringCloudϵ�С�RibbonԴ�����-��Դ������������")����Ҳ���˺ܶ������ƣ��������з���ʵ����һ���µĶ���ʱʹ�õ��Ƕ��������Ǹ���allServers����ֻ�ܶ�����д��![SpringCloudϵ�С�RibbonԴ�����-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/711c81e67b2c1cd3fb91005f53ea1e1352ad9a.png "SpringCloudϵ�С�RibbonԴ�����-��Դ������������")�ڷ���ping�󣬽����ͨ���ķ������newUpList�У����ͨ��д������upServerList��ס�� + +�������ֻ����һ��д���Ҳ��ܶ���![SpringCloudϵ�С�RibbonԴ�����-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/047543857174aa1130b0747d1d149ffd6d5632.jpg "SpringCloudϵ�С�RibbonԴ�����-��Դ������������")������ping�ڼ������й��ڶ�д����ԭ�����ʹ�á� + +��Ҫ���̾��ǣ� + +1. ��ȡȫ������ʵ���б� +2. ������ʵ���Ƿ���pingServers +3. ������״̬�����ı�ķ���changedServers +4. ���������ߵķ���newUpList +5. ��newUpList��ֵ��upServerList ���߷���ʵ���б��� + +������pingServers���Ǽ�������� + +**BaseLoadBalancer���������ܼ���** + +1. ��allServerList��upServerList�Ķ�д������ +2. �ṩ��allServerList��upServerList����ɾ�Ĺ��� +3. �ṩ��PingTask��ping�Ķ�ʱ���񣩣�Pinger��ping��ִ������ +4. ���ڸ��ؾ������ѡ�����rule.choose(key) +5. �ṩĬ�ϵ�ping����SerialPingStrategy + **3.6 LoadBalancerRequest** + ͨ��ZoneAwareLoadBalancerѡ������Server֮���ٰ�װ��RibbonServer����֮ǰ���ص�server�Ǹö����е�һ���ֶΣ�����֮�⣬���з�����serviceId���Ƿ���Ҫʹ��https����Ϣ�����ͨ��LoadBalancerRequest��apply������������server�����󣬴Ӷ�ʵ���˸��ؾ��⡣ + +������apply�����Ķ��壺 + + + +``` +public interface LoadBalancerRequest { + T apply(ServiceInstance instance) throws Exception; +} +``` + + + + + + + + + +������ʱ�������ribbonServer���󣬱�����ServiceInstance���͵Ķ�����н��ա�ServiceInstance��һ���ӿڣ������˷�������ϵͳ�У�ÿ��ʵ����Ҫ�ṩ����Ϣ������serviceId��host��port�ȡ� + +LoadBalancerRequest��һ���ӿڣ����ջ�ͨ��ʵ�����apply����ȥִ�У�ʵ��������LoadBalancerInterceptor�е���RibbonLoadBalancerClient��execute����ʱ����������һ�������࣬����ͨ���鿴LoadBalancerInterceptor�Ĵ��뿴���� + +����LoadBalancerRequest�������ʱ�򣬾���д��apply������apply�����У����½���һ��ServiceRequestWrapper���ڲ��࣬������У�����д��getURI������getURI���������loadBalancer��reconstructURI����������uri�� + +��������Ѿ����Դ���֪��Ribbonʵ�ָ��ؾ���������ˣ�������RestTemplate������ע�⣬�ͻ���LoadBalancerClient�Ķ�������������Ҳ����RibbonLoadBalancerClient��ͬʱ�� +LoadBalancerAutoConfiguration��������ã�����һ��LoadBalancerInterceptor�������õ���������������restTemplate������ЩrestTemplate������LoadBalancerInterceptor�������� + +��ͨ��restTemplate��������ʱ���ͻᾭ����������������������У��ͻ����RibbonLoadBalancerClient�еķ�������ȡ�����ݷ�������ͨ�����ؾ��ⷽ����ȡ������ʵ����Ȼ��ȥ�������ʵ���� + +**3.7 ��ȡ�����б�** +����˵����Щ������ζ�������и��ؾ���ģ����ǻ��и����⣬���������ʵ�����Ǵ�Eureka Server�ϻ�ȡ���ģ������ʵ���б�����λ�ȡ���أ���ô��֤���ʵ���б��е�ʵ���ǿ��õ��أ� + +��RibbonLoadBalancerClientѡ��ʵ����ʱ����ͨ��ILoadBalancer��ʵ������ݸ��ؾ����㷨ѡ�����ʵ���ģ�Ҳ����ZoneAwareLoadBalancer��chooseServer�е��߼����Ǿ����������������鿴ZoneAwareLoadBalancer�ļ̳й�ϵ�����Կ�������ͼ��ʾ��![SpringCloudϵ�С�RibbonԴ�����-��Դ������������](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/425d8ff567d9ac6171f6036dab1726a7da04a3.jpg "SpringCloudϵ�С�RibbonԴ�����-��Դ������������")���Կ�������������ILoadBalancer�ӿڣ�AbstractLoadBalancer��̳�������ӿڣ�BaseLoadBalancer�̳���AbstractLoadBalancer�࣬ +DynamicServerListLoadBalancer�̳���BaseLoadBalancer��ZoneAwareLoadBalancer�̳���DynamicServerListLoadBalancer�� + +ILoadBalancer�ӿڵĴ����Ѿ������ˣ����ڿ���AbstractLoadBalancer�Ĵ��룺 + + + +``` +public abstract class AbstractLoadBalancer implements ILoadBalancer { + public enum ServerGroup{ + ALL, + STATUS_UP, + STATUS_NOT_UP + } + /** + * delegate to {@link #chooseServer(Object)} with parameter null. + */ + public Server chooseServer() { + return chooseServer(null); + } + /** + * List of servers that this Loadbalancer knows about + * + * @param serverGroup Servers grouped by status, e.g., {@link ServerGroup#STATUS_UP} + */ + public abstract List getServerList(ServerGroup serverGroup); + /** + * Obtain LoadBalancer related Statistics + */ + public abstract LoadBalancerStats getLoadBalancerStats(); +} +``` + + + + + + + + + +����һ�������࣬�������һ��ö�٣��������������󷽷��������chooseServer������ + +�����ٿ�BaseLoadBalancer�࣬BaseLoadBalancer������Ǹ��ؾ�������һ������ʵ���࣬��������Կ�������������list�� + + + +``` +@Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL) +protected volatile List allServerList = Collections + .synchronizedList(new ArrayList()); +@Monitor(name = PREFIX + "UpServerList", type = DataSourceType.INFORMATIONAL) +protected volatile List upServerList = Collections + .synchronizedList(new ArrayList()); +``` + + + + + + + + + +�������Ͽ��������ά�����з����ʵ���б�����ά��״̬Ϊup��ʵ���б��� +���һ����Կ���BaseLoadBalancer��ʵ�ֵ�ILoadBalancer�ӿ��еķ�����������������������ȡ���õķ����б����ͻ��upServerList���أ���ȡ���еķ����б����ͻ��allServerList���ء� + + + +``` +@Override +public List getReachableServers() { + return Collections.unmodifiableList(upServerList); +} +@Override +public List getAllServers() { + return Collections.unmodifiableList(allServerList); +} +``` + + + + + + + + + +���������ٿ�DynamicServerListLoadBalancer�ࡣ����ͷ�ϵ�ע�Ϳ���֪�����������Զ�̬�Ļ�ȡ�����б�����������filter�Է����б����й��ˡ� + +��DynamicServerListLoadBalancer���У��ܿ���������һ��ServerList���͵�serverListImpl�ֶΣ�ServerList��һ���ӿڣ����������������� + + + +``` +public interface ServerList { + public List getInitialListOfServers(); + /** + * Return updated list of servers. This is called say every 30 secs + * (configurable) by the Loadbalancer's Ping cycle + * + */ + public List getUpdatedListOfServers(); +} +``` + + + + + + + + + +getInitialListOfServers�ǻ�ȡ��ʼ���ķ����б��� +getUpdatedListOfServers�ǻ�ȡ���µķ����б��� +ServerList�ж��ʵ���࣬�����õ��ĸ��أ������� +EurekaRibbonClientConfiguration�����ҵ�������Ribbon��Eureka��ϵ��Զ������࣬����Ŀǰ����û������Eureka����ͨ�������ļ����ã����Ի���ConfigurationBasedServerList�ࡣ \ No newline at end of file From 441dfb0b9270f971d381dad324ba2ce1ed13282a Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 29 Apr 2023 13:09:30 +0800 Subject: [PATCH 03/20] modify springcloud catalog --- ReadMe.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 9b3a2c6..dd7184c 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -292,7 +292,30 @@ * [Spring Cloud Zuul](docs/Spring全家桶/SpringCloud/Spring Cloud Zuul.md) * [SpringCloud概述](docs/Spring全家桶/SpringCloud/SpringCloud概述.md) -## SpringCloud源码分析 +## SpringCloud 源码分析 +* [Spring Cloud Config源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Config源码分析.md) +* [Spring Cloud Eureka源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Eureka源码分析.md) +* [Spring Cloud Gateway源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Gateway源码分析.md) +* [Spring Cloud Hystrix源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Hystrix源码分析.md) +* [Spring Cloud LoadBalancer源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud LoadBalancer源码分析.md) +* [Spring Cloud OpenFeign源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud OpenFeign源码分析.md) +* [Spring Cloud Ribbon源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Ribbon源码分析.md) + +## SpringCloud Alibaba +* [SpringCloud Alibaba概览](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba概览.md) +* [SpringCloud Alibaba nacos](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba nacos.md) +* [SpringCloud Alibaba RocketMQ](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba RocketMQ.md) +* [SpringCloud Alibaba sentinel](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba sentinel.md) +* [SpringCloud Alibaba skywalking](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba skywalking.md) +* [SpringCloud Alibaba seata](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba seata.md) + +## SpringCloud Alibaba源码分析 +* [Spring Cloud Seata源码分析](docs/Spring全家桶/SpringCloudAlibaba源码分析/Spring Cloud Seata源码分析.md) +* [Spring Cloud Sentinel源码分析](docs/Spring全家桶/SpringCloudAlibaba源码分析/Spring Cloud Sentinel源码分析.md) +* [SpringCloudAlibaba nacos源码分析:概览](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:概览.md) +* [SpringCloudAlibaba nacos源码分析:服务发现](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:服务发现.md) +* [SpringCloudAlibaba nacos源码分析:服务注册](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:服务注册.md) +* [SpringCloudAlibaba nacos源码分析:配置中心](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:配置中心.md) todo From 84fc0ce3f739add6893c826ccf01ba08582661dd Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 29 Apr 2023 13:26:11 +0800 Subject: [PATCH 04/20] ok --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index dd7184c..99dc059 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -280,6 +280,7 @@ * [springboot 自动装配(三):自动装配顺序](docs/Spring全家桶/SpringBoot源码解析/springboot 自动装配(三):自动装配顺序.md) ## SpringCloud +* [SpringCloud概述](docs/Spring全家桶/SpringCloud/SpringCloud概述.md) * [Spring Cloud Config](docs/Spring全家桶/SpringCloud/Spring Cloud Config.md) * [Spring Cloud Consul](docs/Spring全家桶/SpringCloud/Spring Cloud Consul.md) * [Spring Cloud Eureka](docs/Spring全家桶/SpringCloud/Spring Cloud Eureka.md) @@ -290,7 +291,6 @@ * [Spring Cloud Ribbon](docs/Spring全家桶/SpringCloud/Spring Cloud Ribbon.md) * [Spring Cloud Sleuth](docs/Spring全家桶/SpringCloud/Spring Cloud Sleuth.md) * [Spring Cloud Zuul](docs/Spring全家桶/SpringCloud/Spring Cloud Zuul.md) -* [SpringCloud概述](docs/Spring全家桶/SpringCloud/SpringCloud概述.md) ## SpringCloud 源码分析 * [Spring Cloud Config源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Config源码分析.md) From a4a197262eee9484131aad5d6d2f4c1e8f4ad763 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 29 Apr 2023 13:36:13 +0800 Subject: [PATCH 05/20] ok --- ReadMe.md | 1 + ...20\347\240\201\345\210\206\346\236\220.md" | 527 ++++++++++++++++++ 2 files changed, 528 insertions(+) create mode 100644 "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud RocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git a/ReadMe.md b/ReadMe.md index 99dc059..386e637 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -316,6 +316,7 @@ * [SpringCloudAlibaba nacos源码分析:服务发现](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:服务发现.md) * [SpringCloudAlibaba nacos源码分析:服务注册](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:服务注册.md) * [SpringCloudAlibaba nacos源码分析:配置中心](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:配置中心.md) +* [Spring Cloud RocketMQ源码分析](docs/Spring全家桶/SpringCloudAlibaba源码分析/Spring Cloud RocketMQ源码分析.md) todo 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/Spring Cloud RocketMQ\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/Spring Cloud RocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..35fcfef --- /dev/null +++ "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/Spring Cloud RocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,527 @@ +### һ��NameServer���� + +Դ����ڣ�NamesrvStartup#main + +##### 1.NamesrvController controller = createNamesrvController(args); + +* ��������в��� +* �����������ö���NamesrvConfig��NettyServerConfig +* ���� -c ��-p���� +* ���RocketMQ_HOME�������� +* final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);����controller +* controller.getConfiguration().registerConfig(properties); ע������������Ϣ + +##### 2.start(controller); + +* controller.initialize()�� ִ�г�ʼ�� + �� this.kvConfigManager.load(); ����KV���� + �� this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);����NettyServer���紦������ + �� this.remotingExecutor =Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_")); ����Netty�����������̳߳� + �� this.registerProcessor(); ע��NameServer��Processor ע�ᵽRemotingServer�� + �� NamesrvController.this.routeInfoManager.scanNotActiveBroker() ������ʱ�����Ƴ�����Ծ��Broker + �� NamesrvController.this.kvConfigManager.printAllPeriodically() ��ʱ��ӡKV������Ϣ +* Runtime.getRuntime().addShutdownHook ע��رչ��ӣ��ڹرշ���ʱ�ͷ���Դ +* controller.start()�� ����controller + +NameServer��������Ҫ�������� +1.ά��broker�ķ����ַ��Ϣ�������и��� +2.��Producer��consumer�ṩBroker�ķ����б� + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3245844-46ca880d83fb583b.png) + + + +image.png + + + +### ����Broker���� + +Դ����ڣ�Brokerstartup#main + +##### 1.createBrokerController(args) + +* �����ĸ��������ö���BrokerConfig��NettyServerConfig��NettyClientConfig��MessageStoreConfig +* BrokerConfigֻ���� -c���� +* RocketMq_HOME����������� +* RemotingUtil.string2SocketAddress(addr) ��namesrvAddr��ַ���в�� +* messageStoreConfig.getBrokerRole() ͨ��BrokerId�ж����ӣ�masterId=0��Deldger��Ⱥ������Broker�ڵ�ID����-1 +* ���� -p��-m���������������IJ������ӵ��ĸ��������ö����� +* BrokerController controller = new BrokerController ����brokerController�����ĸ����������ഫ�� +* controller.getConfiguration().registerConfig(properties); ����ע�ᣨ���£����� +* controller.initialize(); ��ʼ��controller + �� ���ش����ϵ������ļ���topicConfigManager��consumerOffsetManager��subscriptionGroupManager��consumerFilterManager + �� this.messageStore =new DefaultMessageStore() ������Ϣ�洢��� + �� this.messageStore.load() ���ش����ļ� + �� this.remotingServer = new NettyRemotingServer ����Netty������� + �� this.fastRemotingServer = new NettyRemotingServer ���fastRemotingServer��RemotingServer���ܻ�����࣬����VIP�˿����� + �� ������dz�ʼ��һЩ�̳߳� + �� this.registerProcessor(); brokerע��һЩProcessor�������� +* Runtime.getRuntime().addShutdownHook ע��رչ��� + +##### 2.start(BrokerController controller) + +* this.messageStore.start(); ��������������Ҫ��Ϊ�˽�CommitLog��д���¼��ַ���ComsumeQueue��IndexFile +* ��������Netty����remotingServer��fastRemotingServer +* this.fileWatchService.start(); �ļ��������� +* this.brokerOuterAPI.start(); brokerOuterAPI��������Ϊһ��Netty�ͻ��ˣ����ⷢ�������������緢������ +* this.pullRequestHoldService.start(); ����ѯ������ͣ���� +* this.filterServerManager.start(); ʹ��filter���й��� +* BrokerController.this.registerBrokerAll() Broker���ĵ�����ע������,��Ҫ���þ��ǽ�brokerע�ᵽNamesrv�� + +broker�ĺ������ã� +**1.��Ϊclientʱ����nameServer����������Ϣ�����������״̬���** +**2.��Ϊ�����ʱ�����ڴ洢��Ϣ����Ӧconsumer�˵�����** + +### ����Netty����ע���� + +### �ġ�Broker����ע����� + +Դ����ڣ�BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister()) + + + +``` +public synchronized void registerBrokerAll(final boolean checkOrderConfig, boolean oneway, boolean forceRegister) { + TopicConfigSerializeWrapper topicConfigWrapper = this.getTopicConfigManager().buildTopicConfigSerializeWrapper(); + + if (!PermName.isWriteable(this.getBrokerConfig().getBrokerPermission()) + || !PermName.isReadable(this.getBrokerConfig().getBrokerPermission())) { + ConcurrentHashMap topicConfigTable = new ConcurrentHashMap(); + for (TopicConfig topicConfig : topicConfigWrapper.getTopicConfigTable().values()) { + TopicConfig tmp = + new TopicConfig(topicConfig.getTopicName(), topicConfig.getReadQueueNums(), topicConfig.getWriteQueueNums(), + this.brokerConfig.getBrokerPermission()); + topicConfigTable.put(topicConfig.getTopicName(), tmp); + } + topicConfigWrapper.setTopicConfigTable(topicConfigTable); + } + //������DZȽϹؼ��ĵط������ж��Ƿ���Ҫע�ᣬȻ�����doRegisterBrokerAll��������ȥע�ᡣ + if (forceRegister || needRegister(this.brokerConfig.getBrokerClusterName(), + this.getBrokerAddr(), + this.brokerConfig.getBrokerName(), + this.brokerConfig.getBrokerId(), + this.brokerConfig.getRegisterBrokerTimeoutMills())) { + doRegisterBrokerAll(checkOrderConfig, oneway, topicConfigWrapper); + } +} + +``` + + + + + +``` +// Brokerע������ĵIJ��� +private void doRegisterBrokerAll(boolean checkOrderConfig, boolean oneway, + TopicConfigSerializeWrapper topicConfigWrapper) { + // ע��broker���� + List registerBrokerResultList = this.brokerOuterAPI.registerBrokerAll( + this.brokerConfig.getBrokerClusterName(), + this.getBrokerAddr(), + this.brokerConfig.getBrokerName(), + this.brokerConfig.getBrokerId(), + this.getHAServerAddr(), + topicConfigWrapper, + this.filterServerManager.buildNewFilterServerList(), + oneway, + this.brokerConfig.getRegisterBrokerTimeoutMills(), + this.brokerConfig.isCompressedRegister()); + + if (registerBrokerResultList.size() > 0) { + RegisterBrokerResult registerBrokerResult = registerBrokerResultList.get(0); + if (registerBrokerResult != null) { + //ע���걣�����ӽڵ�ĵ�ַ + if (this.updateMasterHAServerAddrPeriodically && registerBrokerResult.getHaServerAddr() != null) { + this.messageStore.updateHaMasterAddress(registerBrokerResult.getHaServerAddr()); + } + + this.slaveSynchronize.setMasterAddr(registerBrokerResult.getMasterAddr()); + + if (checkOrderConfig) { + this.getTopicConfigManager().updateOrderTopicConfig(registerBrokerResult.getKvTable()); + } + } + } +} + +``` + + + + + +``` +public List registerBrokerAll( + final String clusterName, + final String brokerAddr, + final String brokerName, + final long brokerId, + final String haServerAddr, + final TopicConfigSerializeWrapper topicConfigWrapper, + final List filterServerList, + final boolean oneway, + final int timeoutMills, + final boolean compressed) { + //ʹ��CopyOnWriteArrayList����������ȫ�� + final List registerBrokerResultList = new CopyOnWriteArrayList<>(); + // ��ȡ����nameServer�ĵ�ַ��Ϣ + List nameServerAddressList = this.remotingClient.getNameServerAddressList(); + if (nameServerAddressList != null && nameServerAddressList.size() > 0) { + + final RegisterBrokerRequestHeader requestHeader = new RegisterBrokerRequestHeader(); + requestHeader.setBrokerAddr(brokerAddr); + requestHeader.setBrokerId(brokerId); + requestHeader.setBrokerName(brokerName); + requestHeader.setClusterName(clusterName); + requestHeader.setHaServerAddr(haServerAddr); + requestHeader.setCompressed(compressed); + + RegisterBrokerBody requestBody = new RegisterBrokerBody(); + requestBody.setTopicConfigSerializeWrapper(topicConfigWrapper); + requestBody.setFilterServerList(filterServerList); + final byte[] body = requestBody.encode(compressed); + final int bodyCrc32 = UtilAll.crc32(body); + requestHeader.setBodyCrc32(bodyCrc32); + //ͨ��CountDownLatch����֤������NameServer�����ע�����һ������� + final CountDownLatch countDownLatch = new CountDownLatch(nameServerAddressList.size()); + for (final String namesrvAddr : nameServerAddressList) { + brokerOuterExecutor.execute(new Runnable() { + @Override + public void run() { + try { + RegisterBrokerResult result = registerBroker(namesrvAddr, oneway, timeoutMills, requestHeader, body); + if (result != null) { + registerBrokerResultList.add(result); + } + + log.info("register broker[{}]to name server {} OK", brokerId, namesrvAddr); + } catch (Exception e) { + log.warn("registerBroker Exception, {}", namesrvAddr, e); + } finally { + countDownLatch.countDown(); + } + } + }); + } + + try { + countDownLatch.await(timeoutMills, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + } + } + + return registerBrokerResultList; +} + +``` + + + +NameServer�������� + + + +``` +//NameServer��������ĺ��Ĵ��� +@Override + public RemotingCommand processRequest(ChannelHandlerContext ctx, + RemotingCommand request) throws RemotingCommandException { + + if (ctx != null) { + log.debug("receive request, {} {} {}", + request.getCode(), + RemotingHelper.parseChannelRemoteAddr(ctx.channel()), + request); + } + + switch (request.getCode()) { + case RequestCode.PUT_KV_CONFIG: + return this.putKVConfig(ctx, request); + case RequestCode.GET_KV_CONFIG: + return this.getKVConfig(ctx, request); + case RequestCode.DELETE_KV_CONFIG: + return this.deleteKVConfig(ctx, request); + case RequestCode.QUERY_DATA_VERSION: + return queryBrokerTopicConfig(ctx, request); + case RequestCode.REGISTER_BROKER: //Brokerע�����������汾Ĭ���ǵ�ǰ��ܰ汾 + Version brokerVersion = MQVersion.value2Version(request.getVersion()); + if (brokerVersion.ordinal() >= MQVersion.Version.V3_0_11.ordinal()) { + return this.registerBrokerWithFilterServer(ctx, request); //��ǰ�汾 + } else { + return this.registerBroker(ctx, request); + } + case RequestCode.UNREGISTER_BROKER: + return this.unregisterBroker(ctx, request); + case RequestCode.GET_ROUTEINFO_BY_TOPIC: + return this.getRouteInfoByTopic(ctx, request); + case RequestCode.GET_BROKER_CLUSTER_INFO: + return this.getBrokerClusterInfo(ctx, request); + case RequestCode.WIPE_WRITE_PERM_OF_BROKER: + return this.wipeWritePermOfBroker(ctx, request); + case RequestCode.GET_ALL_TOPIC_LIST_FROM_NAMESERVER: + return getAllTopicListFromNameserver(ctx, request); + case RequestCode.DELETE_TOPIC_IN_NAMESRV: + return deleteTopicInNamesrv(ctx, request); + case RequestCode.GET_KVLIST_BY_NAMESPACE: + return this.getKVListByNamespace(ctx, request); + case RequestCode.GET_TOPICS_BY_CLUSTER: + return this.getTopicsByCluster(ctx, request); + case RequestCode.GET_SYSTEM_TOPIC_LIST_FROM_NS: + return this.getSystemTopicListFromNs(ctx, request); + case RequestCode.GET_UNIT_TOPIC_LIST: + return this.getUnitTopicList(ctx, request); + case RequestCode.GET_HAS_UNIT_SUB_TOPIC_LIST: + return this.getHasUnitSubTopicList(ctx, request); + case RequestCode.GET_HAS_UNIT_SUB_UNUNIT_TOPIC_LIST: + return this.getHasUnitSubUnUnitTopicList(ctx, request); + case RequestCode.UPDATE_NAMESRV_CONFIG: + return this.updateConfig(ctx, request); + case RequestCode.GET_NAMESRV_CONFIG: + return this.getConfig(ctx, request); + default: + break; + } + return null; +} + +``` + + + +ʵ�ʾ��ǽ�broker��Ϣע�ᵽrouteInfo�У� + + + +``` +public RemotingCommand registerBrokerWithFilterServer(ChannelHandlerContext ctx, RemotingCommand request) + throws RemotingCommandException { + final RemotingCommand response = RemotingCommand.createResponseCommand(RegisterBrokerResponseHeader.class); + final RegisterBrokerResponseHeader responseHeader = (RegisterBrokerResponseHeader) response.readCustomHeader(); + final RegisterBrokerRequestHeader requestHeader = + (RegisterBrokerRequestHeader) request.decodeCommandCustomHeader(RegisterBrokerRequestHeader.class); + + if (!checksum(ctx, request, requestHeader)) { + response.setCode(ResponseCode.SYSTEM_ERROR); + response.setRemark("crc32 not match"); + return response; + } + + RegisterBrokerBody registerBrokerBody = new RegisterBrokerBody(); + + if (request.getBody() != null) { + try { + registerBrokerBody = RegisterBrokerBody.decode(request.getBody(), requestHeader.isCompressed()); + } catch (Exception e) { + throw new RemotingCommandException("Failed to decode RegisterBrokerBody", e); + } + } else { + registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setCounter(new AtomicLong(0)); + registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setTimestamp(0); + } + //routeInfoManager���ǹ���·����Ϣ�ĺ�������� + RegisterBrokerResult result = this.namesrvController.getRouteInfoManager().registerBroker( + requestHeader.getClusterName(), + requestHeader.getBrokerAddr(), + requestHeader.getBrokerName(), + requestHeader.getBrokerId(), + requestHeader.getHaServerAddr(), + registerBrokerBody.getTopicConfigSerializeWrapper(), + registerBrokerBody.getFilterServerList(), + ctx.channel()); + + responseHeader.setHaServerAddr(result.getHaServerAddr()); + responseHeader.setMasterAddr(result.getMasterAddr()); + + byte[] jsonValue = this.namesrvController.getKvConfigManager().getKVListByNamespace(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG); + response.setBody(jsonValue); + + response.setCode(ResponseCode.SUCCESS); + response.setRemark(null); + return response; +} + +``` + + + +### �塢Producer������Ϣ + +Դ����ڣ�DefaultMQProducer#start +1.this.defaultMQProducerImpl.start(); ���������� + + + +``` +public void start(final boolean startFactory) throws MQClientException { + switch (this.serviceState) { + case CREATE_JUST: + // Ĭ�Ͼ���CREATE_JUST + this.serviceState = ServiceState.START_FAILED; + + this.checkConfig(); + //�޸ĵ�ǰ��instanceNameΪ��ǰ����ID + if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) { + this.defaultMQProducer.changeInstanceNameToPID(); + } + //�ͻ��˺��ĵ�MQ�ͻ��˹��� ����������Ϣ�����ߣ�������������������Ϣ�ķ����ߵķ���ע�� + this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook); + //ע��MQ�ͻ��˹���ʾ�� + boolean registerOK = mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this); + if (!registerOK) { + this.serviceState = ServiceState.CREATE_JUST; + throw new MQClientException("The producer group[" + this.defaultMQProducer.getProducerGroup() + + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL), + null); + } + + this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo()); + //����ʾ�� --���пͻ������������mQClientFactory���� + if (startFactory) { + mQClientFactory.start(); + } + + log.info("the producer [{}] start OK. sendMessageWithVIPChannel={}", this.defaultMQProducer.getProducerGroup(), + this.defaultMQProducer.isSendMessageWithVIPChannel()); + this.serviceState = ServiceState.RUNNING; + break; + case RUNNING: + case START_FAILED: + case SHUTDOWN_ALREADY: + throw new MQClientException("The producer service state not OK, maybe started once, " + + this.serviceState + + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK), + null); + default: + break; + } + // �����е�broker�������� + this.mQClientFactory.sendHeartbeatToAllBrokerWithLock(); + + this.startScheduledTask(); + +} + +``` + + + +### ����Consumer������Ϣ + +���Ѷ���ڣ�DefaultMQPushConsumer#start +this.defaultMQPushConsumerImpl.start(); + + + +``` +public synchronized void start() throws MQClientException { + switch (this.serviceState) { + case CREATE_JUST: + log.info("the consumer [{}] start beginning. messageModel={}, isUnitMode={}", this.defaultMQPushConsumer.getConsumerGroup(), + this.defaultMQPushConsumer.getMessageModel(), this.defaultMQPushConsumer.isUnitMode()); + this.serviceState = ServiceState.START_FAILED; + + this.checkConfig(); + + this.copySubscription(); + + if (this.defaultMQPushConsumer.getMessageModel() == MessageModel.CLUSTERING) { + this.defaultMQPushConsumer.changeInstanceNameToPID(); + } + //�ͻ���ʾ��������������Ҳ�ǽ���������������ġ� + this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQPushConsumer, this.rpcHook); + //���ؾ������ + this.rebalanceImpl.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup()); + this.rebalanceImpl.setMessageModel(this.defaultMQPushConsumer.getMessageModel()); + this.rebalanceImpl.setAllocateMessageQueueStrategy(this.defaultMQPushConsumer.getAllocateMessageQueueStrategy()); + this.rebalanceImpl.setmQClientFactory(this.mQClientFactory); + + this.pullAPIWrapper = new PullAPIWrapper( + mQClientFactory, + this.defaultMQPushConsumer.getConsumerGroup(), isUnitMode()); + this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList); + + if (this.defaultMQPushConsumer.getOffsetStore() != null) { + this.offsetStore = this.defaultMQPushConsumer.getOffsetStore(); + } else { + //��������Կ������㲥ģʽ�뼯Ⱥģʽ������������offset�洢�ĵط���һ���� + switch (this.defaultMQPushConsumer.getMessageModel()) { + //�㲥ģʽ���������߱��ش洢offset + case BROADCASTING: + this.offsetStore = new LocalFileOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup()); + break; + //��Ⱥģʽ����BrokerԶ�˴洢offset + case CLUSTERING: + this.offsetStore = new RemoteBrokerOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup()); + break; + default: + break; + } + this.defaultMQPushConsumer.setOffsetStore(this.offsetStore); + } + this.offsetStore.load(); + //˳�����Ѽ�������ConsumeMessageOrderlyService + if (this.getMessageListenerInner() instanceof MessageListenerOrderly) { + this.consumeOrderly = true; + this.consumeMessageService = + new ConsumeMessageOrderlyService(this, (MessageListenerOrderly) this.getMessageListenerInner()); + //�������Ѽ�������ConsumeMessageConcurrentlyService + } else if (this.getMessageListenerInner() instanceof MessageListenerConcurrently) { + this.consumeOrderly = false; + this.consumeMessageService = + new ConsumeMessageConcurrentlyService(this, (MessageListenerConcurrently) this.getMessageListenerInner()); + } + + this.consumeMessageService.start(); + //ע�������ߡ������������ƣ��ͻ���ֻҪ��Ҫ��ע�ἴ�ɣ���������mQClientFactoryһ�������� + boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this); + if (!registerOK) { + this.serviceState = ServiceState.CREATE_JUST; + this.consumeMessageService.shutdown(defaultMQPushConsumer.getAwaitTerminationMillisWhenShutdown()); + throw new MQClientException("The consumer group[" + this.defaultMQPushConsumer.getConsumerGroup() + + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL), + null); + } + + mQClientFactory.start(); + log.info("the consumer [{}] start OK.", this.defaultMQPushConsumer.getConsumerGroup()); + this.serviceState = ServiceState.RUNNING; + break; + case RUNNING: + case START_FAILED: + case SHUTDOWN_ALREADY: + throw new MQClientException("The PushConsumer service state not OK, maybe started once, " + + this.serviceState + + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),null); + default: + break; + } + + this.updateTopicSubscribeInfoWhenSubscriptionChanged(); + this.mQClientFactory.checkClientInBroker(); + this.mQClientFactory.sendHeartbeatToAllBrokerWithLock(); + this.mQClientFactory.rebalanceImmediately(); + } + +``` + + + +**1��consumer�˵�����ģʽ��** +�� ��Ⱥģʽ����Ⱥģʽ��ÿ��consumer������䲻ͬ����Ϣ +�� �㲥ģʽ���㲥ģʽ��ÿ����Ϣ�����͸�����consumer +**2������offset�洢��** +�� �㲥ģʽ��this.offsetStore = new LocalFileOffsetStore(); �洢��ÿ��consumer�� +�� ��Ⱥģʽ��this.offsetStore = new RemoteBrokerOffsetStore(); �洢��broker�� + + + +���ߣ���Ҷ�컨 +���ӣ�https://www.jianshu.com/p/8dd4cfeae39d +��Դ������ +����Ȩ���������С���ҵת������ϵ���߻����Ȩ������ҵת����ע�������� \ No newline at end of file From 0a89ef2682af4a8a103abacbb028505e53f9fb91 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 29 Apr 2023 14:23:55 +0800 Subject: [PATCH 06/20] Modify spring all catalog --- ReadMe.md | 168 +++++++++--------- ...05\344\270\216\345\220\257\345\212\250.md" | 0 ...50\345\206\214\346\265\201\347\250\213.md" | 0 ...05\351\205\215\350\277\207\347\250\213.md" | 0 ...\207\206\345\244\207 SpringApplication.md" | 0 ...345\244\207IOC\345\256\271\345\231\250.md" | 0 ...20\350\241\214\347\216\257\345\242\203.md" | 0 ...14\346\210\220\345\220\257\345\212\250.md" | 0 ...01\347\250\213\346\200\273\347\273\223.md" | 0 ...345\212\250IOC\345\256\271\345\231\250.md" | 0 ...50\350\243\205\351\205\215\347\261\273.md" | 0 ...05\351\205\215\351\241\272\345\272\217.md" | 0 ...41\344\273\266\346\263\250\350\247\243.md" | 0 .../SpringCloud/SpringCloudConfig.md" | 0 .../SpringCloud/SpringCloudConsul.md" | 0 .../SpringCloud/SpringCloudEureka.md" | 0 .../SpringCloud/SpringCloudGateway.md" | 0 .../SpringCloud/SpringCloudHystrix.md" | 0 .../SpringCloud/SpringCloudLoadBalancer.md" | 0 .../SpringCloud/SpringCloudOpenFeign.md" | 0 .../SpringCloud/SpringCloudRibbon.md" | 0 .../SpringCloud/SpringCloudSleuth.md" | 0 .../SpringCloud/SpringCloudZuul.md" | 0 .../SpringCloudAlibabaNacos.md" | 0 .../SpringCloudAlibabaRocketMQ.md" | 0 .../SpringCloudAlibabaSeata.md" | 0 .../SpringCloudAlibabaSentinel.md" | 0 .../SpringCloudAlibabaSkywalking.md" | 0 ...ngCloudAlibaba\346\246\202\350\247\210.md" | 0 ...15\345\212\241\345\217\221\347\216\260.md" | 0 ...15\345\212\241\346\263\250\345\206\214.md" | 0 ...20\357\274\232\346\246\202\350\247\210.md" | 0 ...15\347\275\256\344\270\255\345\277\203.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...13\345\214\226\346\265\201\347\250\213.md" | 0 ...13\345\214\226\346\265\201\347\250\213.md" | 0 ...23\346\236\204\346\200\273\347\273\223.md" | 0 ...6@EnableWebMvc\346\263\250\350\247\243.md" | 0 ...\231\250\345\220\257\345\212\250Tomcat.md" | 0 ...271\213\350\216\267\345\217\226Handler.md" | 0 ...241\214Handler\346\226\271\346\263\225.md" | 0 ...20\357\274\210\344\270\212\357\274\211.md" | 0 ...20\357\274\210\344\270\213\357\274\211.md" | 0 ...7\274\232cglib\344\273\243\347\220\206.md" | 0 ...357\274\232aop\346\200\273\347\273\223.md" | 0 ...50\346\200\201\344\273\243\347\220\206.md" | 0 ...13\345\212\241\347\273\204\344\273\266.md" | 0 ...\347\232\204\345\244\204\347\220\20601.md" | 0 ...47\350\241\214\346\265\201\347\250\213.md" | 0 ...\347\232\204\345\244\204\347\220\20603.md" | 0 ...\347\232\204\345\244\204\347\220\20604.md" | 0 ...\347\232\204\345\244\204\347\220\20602.md" | 0 ...01\347\250\213\346\246\202\350\247\210.md" | 0 ...13\344\273\266\345\244\204\347\220\206.md" | 0 ...53\346\217\217\346\265\201\347\250\213.md" | 0 ...an\347\232\204\345\210\233\345\273\272.md" | 0 ...xt\347\232\204\345\210\233\345\273\272.md" | 0 ...47\350\241\214BeanFactoryPostProcessor.md" | 0 ...04\345\210\235\345\247\213\345\214\226.md" | 0 ...6\263\250\345\206\214BeanPostProcessor.md" | 0 ...01\347\250\213\346\200\273\347\273\223.md" | 0 ...20\347\232\204\345\244\204\347\220\206.md" | 0 ...06\345\244\207\345\267\245\344\275\234.md" | 0 ...\273\266\344\271\213ApplicationContext.md" | 0 ...\344\273\266\344\271\213BeanDefinition.md" | 0 ...204\344\273\266\344\271\213BeanFactory.md" | 0 ...66\344\271\213BeanFactoryPostProcessor.md" | 0 ...4\273\266\344\271\213BeanPostProcessor.md" | 0 ...@ComponentScan\346\263\250\350\247\243.md" | 0 ...220\206@Import\346\263\250\350\247\243.md" | 0 ...7\220\206@Bean\346\263\250\350\247\243.md" | 0 ...06@Conditional\346\263\250\350\247\243.md" | 0 ...47\350\241\214\351\241\272\345\272\217.md" | 0 ...13\344\273\266\346\234\272\345\210\266.md" | 0 ...06\350\256\272\345\237\272\347\237\263.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...\346\263\250\350\247\243@EventListener.md" | 0 ...43\347\232\204\345\244\204\347\220\206.md" | 0 87 files changed, 84 insertions(+), 84 deletions(-) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/springboot\346\211\223\345\214\205\344\270\216\345\220\257\345\212\250.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\346\211\223\345\214\205\344\270\216\345\220\257\345\212\250.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot web\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet \347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet \347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot web\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc \350\243\205\351\205\215\350\277\207\347\250\213.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc \350\243\205\351\205\215\350\277\207\347\250\213.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207 SpringApplication.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207 SpringApplication.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\207\206\345\244\207IOC\345\256\271\345\231\250.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\207\206\345\244\207IOC\345\256\271\345\231\250.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232\345\207\206\345\244\207\350\277\220\350\241\214\347\216\257\345\242\203.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232\345\207\206\345\244\207\350\277\220\350\241\214\347\216\257\345\242\203.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\345\256\214\346\210\220\345\220\257\345\212\250.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\345\256\214\346\210\220\345\220\257\345\212\250.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250IOC\345\256\271\345\231\250.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250IOC\345\256\271\345\231\250.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\200\357\274\211\357\274\232\345\212\240\350\275\275\350\207\252\345\212\250\350\243\205\351\205\215\347\261\273.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\200\357\274\211\357\274\232\345\212\240\350\275\275\350\207\252\345\212\250\350\243\205\351\205\215\347\261\273.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\211\357\274\211\357\274\232\350\207\252\345\212\250\350\243\205\351\205\215\351\241\272\345\272\217.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\211\357\274\211\357\274\232\350\207\252\345\212\250\350\243\205\351\205\215\351\241\272\345\272\217.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\272\214\357\274\211\357\274\232\346\235\241\344\273\266\346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\272\214\357\274\211\357\274\232\346\235\241\344\273\266\346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Config.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConfig.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Consul.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConsul.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Eureka.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudEureka.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Gateway.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudGateway.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Hystrix.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudHystrix.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud LoadBalancer.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudLoadBalancer.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud OpenFeign.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudOpenFeign.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Ribbon.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudRibbon.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Sleuth.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudSleuth.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Zuul.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudZuul.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba nacos.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaNacos.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba RocketMQ.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaRocketMQ.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba seata.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSeata.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba sentinel.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSentinel.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba skywalking.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSkywalking.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba\346\246\202\350\247\210.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibaba\346\246\202\350\247\210.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\345\217\221\347\216\260.md" => "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\345\217\221\347\216\260.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\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" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\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" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud RocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Seata\346\272\220\347\240\201\345\210\206\346\236\220.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Sentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Config\346\272\220\347\240\201\345\210\206\346\236\220.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Eureka\346\272\220\347\240\201\345\210\206\346\236\220.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Gateway\346\272\220\347\240\201\345\210\206\346\236\220.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Hystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud LoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud OpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Ribbon\346\272\220\347\240\201\345\210\206\346\236\220.md" => "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" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/DispatcherServlet \345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/DispatcherServlet\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/RequestMapping \345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/RequestMapping\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC \346\225\264\344\275\223\346\272\220\347\240\201\347\273\223\346\236\204\346\200\273\347\273\223.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\225\264\344\275\223\346\272\220\347\240\201\347\273\223\346\236\204\346\200\273\347\273\223.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC demo \344\270\216@EnableWebMvc \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\347\232\204Demo\344\270\216@EnableWebMvc\346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/Spring \345\256\271\345\231\250\345\220\257\345\212\250 Tomcat.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\256\271\345\231\250\345\220\257\345\212\250Tomcat.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\344\271\213\350\216\267\345\217\226 Handler.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\344\271\213\350\216\267\345\217\226Handler.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\344\271\213\346\211\247\350\241\214 Handler \346\226\271\346\263\225.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\344\271\213\346\211\247\350\241\214Handler\346\226\271\346\263\225.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator \345\210\206\346\236\220\357\274\210\344\270\212\357\274\211.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\212\357\274\211.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator \345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib \344\273\243\347\220\206.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib\344\273\243\347\220\206.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop \346\200\273\347\273\223.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop\346\200\273\347\273\223.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk \345\212\250\346\200\201\344\273\243\347\220\206.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk\345\212\250\346\200\201\344\273\243\347\220\206.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\270\200\357\274\211\357\274\232\350\256\244\350\257\206\344\272\213\345\212\241\347\273\204\344\273\266.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\200\357\274\211\357\274\232\350\256\244\350\257\206\344\272\213\345\212\241\347\273\204\344\273\266.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\270\211\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 01.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\211\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20601.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\272\214\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\346\211\247\350\241\214\346\265\201\347\250\213.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\214\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\346\211\247\350\241\214\346\265\201\347\250\213.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\272\224\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 03.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\224\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20603.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\345\205\255\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 04.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\205\255\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20604.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\345\233\233\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 02.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\233\233\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20602.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\246\202\350\247\210.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\246\202\350\247\210.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\203\357\274\211\357\274\232\345\233\275\351\231\205\345\214\226\344\270\216\344\272\213\344\273\266\345\244\204\347\220\206.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\203\357\274\211\357\274\232\345\233\275\351\231\205\345\214\226\344\270\216\344\272\213\344\273\266\345\244\204\347\220\206.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\214\205\347\232\204\346\211\253\346\217\217\346\265\201\347\250\213.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\214\205\347\232\204\346\211\253\346\217\217\346\265\201\347\250\213.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\271\235\357\274\211\357\274\232\345\215\225\344\276\213 bean \347\232\204\345\210\233\345\273\272.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\271\235\357\274\211\357\274\232\345\215\225\344\276\213bean\347\232\204\345\210\233\345\273\272.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232ApplicationContext \347\232\204\345\210\233\345\273\272.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232ApplicationContext\347\232\204\345\210\233\345\273\272.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\346\211\247\350\241\214 BeanFactoryPostProcessor.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\346\211\247\350\241\214BeanFactoryPostProcessor.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\253\357\274\211\357\274\232\345\256\214\346\210\220 BeanFactory \347\232\204\345\210\235\345\247\213\345\214\226.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\253\357\274\211\357\274\232\345\256\214\346\210\220BeanFactory\347\232\204\345\210\235\345\247\213\345\214\226.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\346\263\250\345\206\214 BeanPostProcessor.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\346\263\250\345\206\214BeanPostProcessor.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\357\274\211\357\274\232\345\220\257\345\212\250\345\256\214\346\210\220\347\232\204\345\244\204\347\220\206.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\357\274\211\357\274\232\345\220\257\345\212\250\345\256\214\346\210\220\347\232\204\345\244\204\347\220\206.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250\345\211\215\347\232\204\345\207\206\345\244\207\345\267\245\344\275\234.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250\345\211\215\347\232\204\345\207\206\345\244\207\345\267\245\344\275\234.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 ApplicationContext.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213ApplicationContext.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 BeanDefinition.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanDefinition.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring \347\273\204\344\273\266\344\271\213 BeanFactory.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactory.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 BeanFactoryPostProcessor.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactoryPostProcessor.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 BeanPostProcessor.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanPostProcessor.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan\346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import\346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean\346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional\346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring \346\216\242\347\247\230\344\271\213 AOP \347\232\204\346\211\247\350\241\214\351\241\272\345\272\217.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213AOP\347\232\204\346\211\247\350\241\214\351\241\272\345\272\217.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring \346\216\242\347\247\230\344\271\213 Spring \344\272\213\344\273\266\346\234\272\345\210\266.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213Spring\344\272\213\344\273\266\346\234\272\345\210\266.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\270\200\357\274\211\357\274\232\347\220\206\350\256\272\345\237\272\347\237\263.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\270\200\357\274\211\357\274\232\347\220\206\350\256\272\345\237\272\347\237\263.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\272\214\357\274\211\357\274\232\346\272\220\347\240\201\345\210\206\346\236\220.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\272\214\357\274\211\357\274\232\346\272\220\347\240\201\345\210\206\346\236\220.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243 @EventListener.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\347\273\204\345\220\210\346\263\250\350\247\243\347\232\204\345\244\204\347\220\206.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\273\204\345\220\210\346\263\250\350\247\243\347\232\204\345\244\204\347\220\206.md" (100%) diff --git a/ReadMe.md b/ReadMe.md index 386e637..af654ba 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -172,53 +172,53 @@ * [Spring源码剖析:Spring事务源码剖析](docs/Spring全家桶/Spring源码分析/Spring源码剖析:Spring事务源码剖析.md) ### AOP -* [AnnotationAwareAspectJAutoProxyCreator 分析(上)](docs/Spring全家桶/Spring源码分析/SpringAOP/AnnotationAwareAspectJAutoProxyCreator 分析(上).md) -* [AnnotationAwareAspectJAutoProxyCreator 分析(下)](docs/Spring全家桶/Spring源码分析/SpringAOP/AnnotationAwareAspectJAutoProxyCreator 分析(下).md) +* [AnnotationAwareAspectJAutoProxyCreator 分析(上)](docs/Spring全家桶/Spring源码分析/SpringAOP/AnnotationAwareAspectJAutoProxyCreator分析(上).md) +* [AnnotationAwareAspectJAutoProxyCreator 分析(下)](docs/Spring全家桶/Spring源码分析/SpringAOP/AnnotationAwareAspectJAutoProxyCreator分析(下).md) * [AOP示例demo及@EnableAspectJAutoProxy](docs/Spring全家桶/Spring源码分析/SpringAOP/AOP示例demo及@EnableAspectJAutoProxy.md) -* [SpringAop(四):jdk 动态代理](docs/Spring全家桶/Spring源码分析/SpringAOP/SpringAop(四):jdk 动态代理.md) -* [SpringAop(五):cglib 代理](docs/Spring全家桶/Spring源码分析/SpringAOP/SpringAop(五):cglib 代理.md) -* [SpringAop(六):aop 总结](docs/Spring全家桶/Spring源码分析/SpringAOP/SpringAop(六):aop 总结.md) +* [SpringAop(四):jdk 动态代理](docs/Spring全家桶/Spring源码分析/SpringAOP/SpringAop(四):jdk动态代理.md) +* [SpringAop(五):cglib 代理](docs/Spring全家桶/Spring源码分析/SpringAOP/SpringAop(五):cglib代理.md) +* [SpringAop(六):aop 总结](docs/Spring全家桶/Spring源码分析/SpringAOP/SpringAop(六):aop总结.md) ### 事务 -* [spring 事务(一):认识事务组件](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(一):认识事务组件.md) -* [spring 事务(二):事务的执行流程](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(二):事务的执行流程.md) -* [spring 事务(三):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(三):事务的隔离级别与传播方式的处理 01.md) -* [spring 事务(四):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(四):事务的隔离级别与传播方式的处理 02.md) -* [spring 事务(五):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(五):事务的隔离级别与传播方式的处理 03.md) -* [spring 事务(六):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/spring 事务(六):事务的隔离级别与传播方式的处理 04.md) +* [spring 事务(一):认识事务组件](docs/Spring全家桶/Spring源码分析/Spring事务/Spring事务(一):认识事务组件.md) +* [spring 事务(二):事务的执行流程](docs/Spring全家桶/Spring源码分析/Spring事务/Spring事务(二):事务的执行流程.md) +* [spring 事务(三):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/Spring事务(三):事务的隔离级别与传播方式的处理01.md) +* [spring 事务(四):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/Spring事务(四):事务的隔离级别与传播方式的处理02.md) +* [spring 事务(五):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/Spring事务(五):事务的隔离级别与传播方式的处理03.md) +* [spring 事务(六):事务的隔离级别与传播方式的处理](docs/Spring全家桶/Spring源码分析/Spring事务/Spring事务(六):事务的隔离级别与传播方式的处理04.md) ### 启动流程 -* [spring 启动流程(一):启动流程概览](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(一):启动流程概览.md) -* [spring 启动流程(二):ApplicationContext 的创建](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(二):ApplicationContext 的创建.md) -* [spring 启动流程(三):包的扫描流程](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(三):包的扫描流程.md) -* [spring 启动流程(四):启动前的准备工作](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(四):启动前的准备工作.md) -* [spring 启动流程(五):执行 BeanFactoryPostProcessor](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(五):执行 BeanFactoryPostProcessor.md) -* [spring 启动流程(六):注册 BeanPostProcessor](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(六):注册 BeanPostProcessor.md) -* [spring 启动流程(七):国际化与事件处理](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(七):国际化与事件处理.md) -* [spring 启动流程(八):完成 BeanFactory 的初始化](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(八):完成 BeanFactory 的初始化.md) -* [spring 启动流程(九):单例 bean 的创建](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(九):单例 bean 的创建.md) -* [spring 启动流程(十):启动完成的处理](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(十):启动完成的处理.md) -* [spring 启动流程(十一):启动流程总结](docs/Spring全家桶/Spring源码分析/Spring启动流程/spring 启动流程(十一):启动流程总结.md) +* [spring启动流程(一):启动流程概览](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(一):启动流程概览.md) +* [spring启动流程(二):ApplicationContext 的创建](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(二):ApplicationContext的创建.md) +* [spring启动流程(三):包的扫描流程](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(三):包的扫描流程.md) +* [spring启动流程(四):启动前的准备工作](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(四):启动前的准备工作.md) +* [spring启动流程(五):执行 BeanFactoryPostProcessor](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(五):执行BeanFactoryPostProcessor.md) +* [spring启动流程(六):注册 BeanPostProcessor](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(六):注册BeanPostProcessor.md) +* [spring启动流程(七):国际化与事件处理](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(七):国际化与事件处理.md) +* [spring启动流程(八):完成 BeanFactory 的初始化](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(八):完成BeanFactory的初始化.md) +* [spring启动流程(九):单例 bean 的创建](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(九):单例bean的创建.md) +* [spring启动流程(十):启动完成的处理](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(十):启动完成的处理.md) +* [spring启动流程(十一):启动流程总结](docs/Spring全家桶/Spring源码分析/Spring启动流程/Spring启动流程(十一):启动流程总结.md) ### 组件分析 -* [spring 组件之 ApplicationContext](docs/Spring全家桶/Spring源码分析/Spring组件分析/spring 组件之 ApplicationContext.md) -* [spring 组件之 BeanDefinition](docs/Spring全家桶/Spring源码分析/Spring组件分析/spring 组件之 BeanDefinition.md) -* [Spring 组件之 BeanFactory](docs/Spring全家桶/Spring源码分析/Spring组件分析/Spring 组件之 BeanFactory.md) -* [spring 组件之 BeanFactoryPostProcessor](docs/Spring全家桶/Spring源码分析/Spring组件分析/spring 组件之 BeanFactoryPostProcessor.md) -* [spring 组件之 BeanPostProcessor](docs/Spring全家桶/Spring源码分析/Spring组件分析/spring 组件之 BeanPostProcessor.md) +* [spring 组件之 ApplicationContext](docs/Spring全家桶/Spring源码分析/Spring组件分析/Spring组件之ApplicationContext.md) +* [spring 组件之 BeanDefinition](docs/Spring全家桶/Spring源码分析/Spring组件分析/Spring组件之BeanDefinition.md) +* [Spring 组件之 BeanFactory](docs/Spring全家桶/Spring源码分析/Spring组件分析/Spring组件之BeanFactory.md) +* [spring 组件之 BeanFactoryPostProcessor](docs/Spring全家桶/Spring源码分析/Spring组件分析/Spring组件之BeanFactoryPostProcessor.md) +* [spring 组件之 BeanPostProcessor](docs/Spring全家桶/Spring源码分析/Spring组件分析/Spring组件之BeanPostProcessor.md) ### 重要机制探秘 -* [ConfigurationClassPostProcessor(一):处理 @ComponentScan 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(一):处理@ComponentScan 注解.md) -* [ConfigurationClassPostProcessor(三):处理 @Import 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(三):处理@Import 注解.md) -* [ConfigurationClassPostProcessor(二):处理 @Bean 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(二):处理@Bean 注解.md) -* [ConfigurationClassPostProcessor(四):处理 @Conditional 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(四):处理@Conditional 注解.md) -* [Spring 探秘之 AOP 的执行顺序](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring 探秘之 AOP 的执行顺序.md) -* [Spring 探秘之 Spring 事件机制](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring 探秘之 Spring 事件机制.md) -* [spring 探秘之循环依赖的解决(一):理论基石](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/spring 探秘之循环依赖的解决(一):理论基石.md) -* [spring 探秘之循环依赖的解决(二):源码分析](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/spring 探秘之循环依赖的解决(二):源码分析.md) +* [ConfigurationClassPostProcessor(一):处理 @ComponentScan 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(一):处理@ComponentScan注解.md) +* [ConfigurationClassPostProcessor(三):处理 @Import 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(三):处理@Import注解.md) +* [ConfigurationClassPostProcessor(二):处理 @Bean 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(二):处理@Bean注解.md) +* [ConfigurationClassPostProcessor(四):处理 @Conditional 注解](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/ConfigurationClassPostProcessor(四):处理@Conditional注解.md) +* [Spring 探秘之 AOP 的执行顺序](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring探秘之AOP的执行顺序.md) +* [Spring 探秘之 Spring 事件机制](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring探秘之Spring事件机制.md) +* [spring 探秘之循环依赖的解决(一):理论基石](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring探秘之循环依赖的解决(一):理论基石.md) +* [spring 探秘之循环依赖的解决(二):源码分析](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring探秘之循环依赖的解决(二):源码分析.md) * [spring 探秘之监听器注解 @EventListener](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/spring 探秘之监听器注解@EventListener.md) -* [spring 探秘之组合注解的处理](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/spring 探秘之组合注解的处理.md) +* [spring 探秘之组合注解的处理](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring探秘之组合注解的处理.md) ## SpringMVC @@ -238,13 +238,13 @@ * [SpringMVC源码分析:DispatcherServlet的初始化与请求转发 ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC源码分析:DispatcherServlet的初始化与请求转发.md) * [SpringMVC源码分析:DispatcherServlet如何找到正确的Controller ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC源码分析:DispatcherServlet如何找到正确的Controller.md) * [SpringMVC源码剖析:消息转换器HttpMessageConverter与@ResponseBody注解](docs/Spring全家桶/SpringMVC/SpringMVC源码剖析:消息转换器HttpMessageConverter与@ResponseBody注解.md) -* [DispatcherServlet 初始化流程 ](docs/Spring全家桶/SpringMVC源码分析/DispatcherServlet 初始化流程.md) -* [RequestMapping 初始化流程 ](docs/Spring全家桶/SpringMVC源码分析/RequestMapping 初始化流程.md) -* [Spring 容器启动 Tomcat ](docs/Spring全家桶/SpringMVC源码分析/Spring 容器启动 Tomcat.md) -* [SpringMVC demo 与@EnableWebMvc 注解 ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC demo 与@EnableWebMvc 注解.md) -* [SpringMVC 整体源码结构总结 ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC 整体源码结构总结.md) -* [请求执行流程(一)之获取 Handler ](docs/Spring全家桶/SpringMVC源码分析/请求执行流程(一)之获取 Handler.md) -* [请求执行流程(二)之执行 Handler 方法 ](docs/Spring全家桶/SpringMVC源码分析/请求执行流程(二)之执行 Handler 方法.md) +* [DispatcherServlet 初始化流程 ](docs/Spring全家桶/SpringMVC源码分析/DispatcherServlet初始化流程.md) +* [RequestMapping 初始化流程 ](docs/Spring全家桶/SpringMVC源码分析/RequestMapping初始化流程.md) +* [Spring 容器启动 Tomcat ](docs/Spring全家桶/SpringMVC源码分析/Spring容器启动Tomcat.md) +* [SpringMVC demo 与@EnableWebMvc 注解 ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC的Demo与@EnableWebMvc注解.md) +* [SpringMVC 整体源码结构总结 ](docs/Spring全家桶/SpringMVC源码分析/SpringMVC整体源码结构总结.md) +* [请求执行流程(一)之获取 Handler ](docs/Spring全家桶/SpringMVC源码分析/请求执行流程(一)之获取Handler.md) +* [请求执行流程(二)之执行 Handler 方法 ](docs/Spring全家桶/SpringMVC源码分析/请求执行流程(二)之执行Handler方法.md) ## SpringBoot @@ -265,58 +265,58 @@ ## SpringBoot源码分析 * [@SpringBootApplication 注解](docs/Spring全家桶/SpringBoot源码解析/@SpringBootApplication 注解.md) -* [springboot web应用(一):servlet 组件的注册流程](docs/Spring全家桶/SpringBoot源码解析/springboot web应用(一):servlet 组件的注册流程.md) -* [springboot web应用(二):WebMvc 装配过程](docs/Spring全家桶/SpringBoot源码解析/springboot web应用(二):WebMvc 装配过程.md) +* [springboot web应用(一):servlet 组件的注册流程](docs/Spring全家桶/SpringBoot源码解析/SpringBootWeb应用(一):servlet 组件的注册流程.md) +* [springboot web应用(二):WebMvc 装配过程](docs/Spring全家桶/SpringBoot源码解析/SpringBootWeb应用(二):WebMvc 装配过程.md) -* [SpringBoot 启动流程(一):准备 SpringApplication](docs/Spring全家桶/SpringBoot源码解析/SpringBoot 启动流程(一):准备 SpringApplication.md) -* [SpringBoot 启动流程(二):准备运行环境](docs/Spring全家桶/SpringBoot源码解析/SpringBoot 启动流程(二):准备运行环境.md) -* [SpringBoot 启动流程(三):准备IOC容器](docs/Spring全家桶/SpringBoot源码解析/SpringBoot 启动流程(三):准备IOC容器.md) -* [springboot 启动流程(四):启动IOC容器](docs/Spring全家桶/SpringBoot源码解析/springboot 启动流程(四):启动IOC容器.md) -* [springboot 启动流程(五):完成启动](docs/Spring全家桶/SpringBoot源码解析/springboot 启动流程(五):完成启动.md) -* [springboot 启动流程(六):启动流程总结](docs/Spring全家桶/SpringBoot源码解析/springboot 启动流程(六):启动流程总结.md) +* [SpringBoot 启动流程(一):准备 SpringApplication](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(一):准备 SpringApplication.md) +* [SpringBoot 启动流程(二):准备运行环境](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(二):准备运行环境.md) +* [SpringBoot 启动流程(三):准备IOC容器](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(三):准备IOC容器.md) +* [springboot 启动流程(四):启动IOC容器](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(四):启动IOC容器.md) +* [springboot 启动流程(五):完成启动](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(五):完成启动.md) +* [springboot 启动流程(六):启动流程总结](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(六):启动流程总结.md) -* [springboot 自动装配(一):加载自动装配类](docs/Spring全家桶/SpringBoot源码解析/springboot 自动装配(一):加载自动装配类.md) -* [springboot 自动装配(二):条件注解](docs/Spring全家桶/SpringBoot源码解析/springboot 自动装配(二):条件注解.md) -* [springboot 自动装配(三):自动装配顺序](docs/Spring全家桶/SpringBoot源码解析/springboot 自动装配(三):自动装配顺序.md) +* [springboot 自动装配(一):加载自动装配类](docs/Spring全家桶/SpringBoot源码解析/SpringBoot自动装配(一):加载自动装配类.md) +* [springboot 自动装配(二):条件注解](docs/Spring全家桶/SpringBoot源码解析/SpringBoot自动装配(二):条件注解.md) +* [springboot 自动装配(三):自动装配顺序](docs/Spring全家桶/SpringBoot源码解析/SpringBoot自动装配(三):自动装配顺序.md) ## SpringCloud * [SpringCloud概述](docs/Spring全家桶/SpringCloud/SpringCloud概述.md) -* [Spring Cloud Config](docs/Spring全家桶/SpringCloud/Spring Cloud Config.md) -* [Spring Cloud Consul](docs/Spring全家桶/SpringCloud/Spring Cloud Consul.md) -* [Spring Cloud Eureka](docs/Spring全家桶/SpringCloud/Spring Cloud Eureka.md) -* [Spring Cloud Gateway](docs/Spring全家桶/SpringCloud/Spring Cloud Gateway.md) -* [Spring Cloud Hystrix](docs/Spring全家桶/SpringCloud/Spring Cloud Hystrix.md) -* [Spring Cloud LoadBalancer](docs/Spring全家桶/SpringCloud/Spring Cloud LoadBalancer.md) -* [Spring Cloud OpenFeign](docs/Spring全家桶/SpringCloud/Spring Cloud OpenFeign.md) -* [Spring Cloud Ribbon](docs/Spring全家桶/SpringCloud/Spring Cloud Ribbon.md) -* [Spring Cloud Sleuth](docs/Spring全家桶/SpringCloud/Spring Cloud Sleuth.md) -* [Spring Cloud Zuul](docs/Spring全家桶/SpringCloud/Spring Cloud Zuul.md) +* [Spring Cloud Config](docs/Spring全家桶/SpringCloud/SpringCloudConfig.md) +* [Spring Cloud Consul](docs/Spring全家桶/SpringCloud/SpringCloudConsul.md) +* [Spring Cloud Eureka](docs/Spring全家桶/SpringCloud/SpringCloudEureka.md) +* [Spring Cloud Gateway](docs/Spring全家桶/SpringCloud/SpringCloudGateway.md) +* [Spring Cloud Hystrix](docs/Spring全家桶/SpringCloud/SpringCloudHystrix.md) +* [Spring Cloud LoadBalancer](docs/Spring全家桶/SpringCloud/SpringCloudLoadBalancer.md) +* [Spring Cloud OpenFeign](docs/Spring全家桶/SpringCloud/SpringCloudOpenFeign.md) +* [Spring Cloud Ribbon](docs/Spring全家桶/SpringCloud/SpringCloudRibbon.md) +* [Spring Cloud Sleuth](docs/Spring全家桶/SpringCloud/SpringCloudSleuth.md) +* [Spring Cloud Zuul](docs/Spring全家桶/SpringCloud/SpringCloudZuul.md) ## SpringCloud 源码分析 -* [Spring Cloud Config源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Config源码分析.md) -* [Spring Cloud Eureka源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Eureka源码分析.md) -* [Spring Cloud Gateway源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Gateway源码分析.md) -* [Spring Cloud Hystrix源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Hystrix源码分析.md) -* [Spring Cloud LoadBalancer源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud LoadBalancer源码分析.md) -* [Spring Cloud OpenFeign源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud OpenFeign源码分析.md) -* [Spring Cloud Ribbon源码分析](docs/Spring全家桶/SpringCloud源码分析/Spring Cloud Ribbon源码分析.md) +* [Spring Cloud Config源码分析](docs/Spring全家桶/SpringCloud源码分析/SpringCloudConfig源码分析.md) +* [Spring Cloud Eureka源码分析](docs/Spring全家桶/SpringCloud源码分析/SpringCloudEureka源码分析.md) +* [Spring Cloud Gateway源码分析](docs/Spring全家桶/SpringCloud源码分析/SpringCloudGateway源码分析.md) +* [Spring Cloud Hystrix源码分析](docs/Spring全家桶/SpringCloud源码分析/SpringCloudHystrix源码分析.md) +* [Spring Cloud LoadBalancer源码分析](docs/Spring全家桶/SpringCloud源码分析/SpringCloudLoadBalancer源码分析.md) +* [Spring Cloud OpenFeign源码分析](docs/Spring全家桶/SpringCloud源码分析/SpringCloudOpenFeign源码分析.md) +* [Spring Cloud Ribbon源码分析](docs/Spring全家桶/SpringCloud源码分析/SpringCloudRibbon源码分析.md) ## SpringCloud Alibaba -* [SpringCloud Alibaba概览](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba概览.md) -* [SpringCloud Alibaba nacos](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba nacos.md) -* [SpringCloud Alibaba RocketMQ](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba RocketMQ.md) -* [SpringCloud Alibaba sentinel](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba sentinel.md) -* [SpringCloud Alibaba skywalking](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba skywalking.md) -* [SpringCloud Alibaba seata](docs/Spring全家桶/SpringCloudAlibaba/SpringCloud Alibaba seata.md) +* [SpringCloud Alibaba概览](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibaba概览.md) +* [SpringCloud Alibaba nacos](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaNacos.md) +* [SpringCloud Alibaba RocketMQ](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaRocketMQ.md) +* [SpringCloud Alibaba sentinel](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaSentinel.md) +* [SpringCloud Alibaba skywalking](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaSkywalking.md) +* [SpringCloud Alibaba seata](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaSeata.md) ## SpringCloud Alibaba源码分析 -* [Spring Cloud Seata源码分析](docs/Spring全家桶/SpringCloudAlibaba源码分析/Spring Cloud Seata源码分析.md) -* [Spring Cloud Sentinel源码分析](docs/Spring全家桶/SpringCloudAlibaba源码分析/Spring Cloud Sentinel源码分析.md) -* [SpringCloudAlibaba nacos源码分析:概览](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:概览.md) -* [SpringCloudAlibaba nacos源码分析:服务发现](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:服务发现.md) -* [SpringCloudAlibaba nacos源码分析:服务注册](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:服务注册.md) -* [SpringCloudAlibaba nacos源码分析:配置中心](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibaba nacos源码分析:配置中心.md) -* [Spring Cloud RocketMQ源码分析](docs/Spring全家桶/SpringCloudAlibaba源码分析/Spring Cloud RocketMQ源码分析.md) +* [Spring Cloud Seata源码分析](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudSeata源码分析.md) +* [Spring Cloud Sentinel源码分析](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudSentinel源码分析.md) +* [SpringCloudAlibaba nacos源码分析:概览](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibabaNacos源码分析:概览.md) +* [SpringCloudAlibaba nacos源码分析:服务发现](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibabaNacos源码分析:服务发现.md) +* [SpringCloudAlibaba nacos源码分析:服务注册](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibabaNacos源码分析:服务注册.md) +* [SpringCloudAlibaba nacos源码分析:配置中心](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibabaNacos源码分析:配置中心.md) +* [Spring Cloud RocketMQ源码分析](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudRocketMQ源码分析.md) todo diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/springboot\346\211\223\345\214\205\344\270\216\345\220\257\345\212\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\346\211\223\345\214\205\344\270\216\345\220\257\345\212\250.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/springboot\346\211\223\345\214\205\344\270\216\345\220\257\345\212\250.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\346\211\223\345\214\205\344\270\216\345\220\257\345\212\250.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot web\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet \347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet \347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot web\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet \347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet \347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot web\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc \350\243\205\351\205\215\350\277\207\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc \350\243\205\351\205\215\350\277\207\347\250\213.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot web\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc \350\243\205\351\205\215\350\277\207\347\250\213.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc \350\243\205\351\205\215\350\277\207\347\250\213.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207 SpringApplication.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207 SpringApplication.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207 SpringApplication.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207 SpringApplication.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\207\206\345\244\207IOC\345\256\271\345\231\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\207\206\345\244\207IOC\345\256\271\345\231\250.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\207\206\345\244\207IOC\345\256\271\345\231\250.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\207\206\345\244\207IOC\345\256\271\345\231\250.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232\345\207\206\345\244\207\350\277\220\350\241\214\347\216\257\345\242\203.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232\345\207\206\345\244\207\350\277\220\350\241\214\347\216\257\345\242\203.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232\345\207\206\345\244\207\350\277\220\350\241\214\347\216\257\345\242\203.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232\345\207\206\345\244\207\350\277\220\350\241\214\347\216\257\345\242\203.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\345\256\214\346\210\220\345\220\257\345\212\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\345\256\214\346\210\220\345\220\257\345\212\250.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\345\256\214\346\210\220\345\220\257\345\212\250.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\345\256\214\346\210\220\345\220\257\345\212\250.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250IOC\345\256\271\345\231\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250IOC\345\256\271\345\231\250.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250IOC\345\256\271\345\231\250.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250IOC\345\256\271\345\231\250.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\200\357\274\211\357\274\232\345\212\240\350\275\275\350\207\252\345\212\250\350\243\205\351\205\215\347\261\273.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\200\357\274\211\357\274\232\345\212\240\350\275\275\350\207\252\345\212\250\350\243\205\351\205\215\347\261\273.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\200\357\274\211\357\274\232\345\212\240\350\275\275\350\207\252\345\212\250\350\243\205\351\205\215\347\261\273.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\200\357\274\211\357\274\232\345\212\240\350\275\275\350\207\252\345\212\250\350\243\205\351\205\215\347\261\273.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\211\357\274\211\357\274\232\350\207\252\345\212\250\350\243\205\351\205\215\351\241\272\345\272\217.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\211\357\274\211\357\274\232\350\207\252\345\212\250\350\243\205\351\205\215\351\241\272\345\272\217.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\211\357\274\211\357\274\232\350\207\252\345\212\250\350\243\205\351\205\215\351\241\272\345\272\217.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\211\357\274\211\357\274\232\350\207\252\345\212\250\350\243\205\351\205\215\351\241\272\345\272\217.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\272\214\357\274\211\357\274\232\346\235\241\344\273\266\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\272\214\357\274\211\357\274\232\346\235\241\344\273\266\346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/springboot \350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\272\214\357\274\211\357\274\232\346\235\241\344\273\266\346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\272\214\357\274\211\357\274\232\346\235\241\344\273\266\346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Config.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConfig.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Config.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConfig.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Consul.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConsul.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Consul.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConsul.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Eureka.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudEureka.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Eureka.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudEureka.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Gateway.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudGateway.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Gateway.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudGateway.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Hystrix.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudHystrix.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Hystrix.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudHystrix.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud LoadBalancer.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudLoadBalancer.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud LoadBalancer.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudLoadBalancer.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud OpenFeign.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudOpenFeign.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud OpenFeign.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudOpenFeign.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Ribbon.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudRibbon.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Ribbon.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudRibbon.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Sleuth.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudSleuth.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Sleuth.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudSleuth.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Zuul.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudZuul.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/Spring Cloud Zuul.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudZuul.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba nacos.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaNacos.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba nacos.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaNacos.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba RocketMQ.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaRocketMQ.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba RocketMQ.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaRocketMQ.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba seata.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSeata.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba seata.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSeata.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba sentinel.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSentinel.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba sentinel.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSentinel.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba skywalking.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSkywalking.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba skywalking.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSkywalking.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba\346\246\202\350\247\210.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibaba\346\246\202\350\247\210.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloud Alibaba\346\246\202\350\247\210.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibaba\346\246\202\350\247\210.md" 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/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\345\217\221\347\216\260.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\345\217\221\347\216\260.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\345\217\221\347\216\260.md" rename to "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\345\217\221\347\216\260.md" 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/SpringCloudAlibaba nacos\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\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" rename to "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" 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/SpringCloudAlibaba nacos\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" rename to "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" 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/SpringCloudAlibaba nacos\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibaba nacos\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" rename to "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" 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/Spring Cloud RocketMQ\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud RocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "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" 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/Spring Cloud Seata\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Seata\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "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" 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/Spring Cloud Sentinel\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Sentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "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" 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/Spring Cloud Config\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Config\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "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" 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/Spring Cloud Eureka\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Eureka\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "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" 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/Spring Cloud Gateway\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Gateway\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "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" 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/Spring Cloud Hystrix\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Hystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "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" 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/Spring Cloud LoadBalancer\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud LoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "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" 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/Spring Cloud OpenFeign\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud OpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "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" 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/Spring Cloud Ribbon\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" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/Spring Cloud Ribbon\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "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" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/DispatcherServlet \345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/DispatcherServlet\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/DispatcherServlet \345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/DispatcherServlet\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/RequestMapping \345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/RequestMapping\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/RequestMapping \345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/RequestMapping\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC \346\225\264\344\275\223\346\272\220\347\240\201\347\273\223\346\236\204\346\200\273\347\273\223.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\225\264\344\275\223\346\272\220\347\240\201\347\273\223\346\236\204\346\200\273\347\273\223.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC \346\225\264\344\275\223\346\272\220\347\240\201\347\273\223\346\236\204\346\200\273\347\273\223.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\225\264\344\275\223\346\272\220\347\240\201\347\273\223\346\236\204\346\200\273\347\273\223.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC demo \344\270\216@EnableWebMvc \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\347\232\204Demo\344\270\216@EnableWebMvc\346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC demo \344\270\216@EnableWebMvc \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\347\232\204Demo\344\270\216@EnableWebMvc\346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/Spring \345\256\271\345\231\250\345\220\257\345\212\250 Tomcat.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\256\271\345\231\250\345\220\257\345\212\250Tomcat.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/Spring \345\256\271\345\231\250\345\220\257\345\212\250 Tomcat.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\256\271\345\231\250\345\220\257\345\212\250Tomcat.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\344\271\213\350\216\267\345\217\226 Handler.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\344\271\213\350\216\267\345\217\226Handler.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\344\271\213\350\216\267\345\217\226 Handler.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\344\271\213\350\216\267\345\217\226Handler.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\344\271\213\346\211\247\350\241\214 Handler \346\226\271\346\263\225.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\344\271\213\346\211\247\350\241\214Handler\346\226\271\346\263\225.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\344\271\213\346\211\247\350\241\214 Handler \346\226\271\346\263\225.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\344\271\213\346\211\247\350\241\214Handler\346\226\271\346\263\225.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator \345\210\206\346\236\220\357\274\210\344\270\212\357\274\211.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\212\357\274\211.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator \345\210\206\346\236\220\357\274\210\344\270\212\357\274\211.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\212\357\274\211.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator \345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator \345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib \344\273\243\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib\344\273\243\347\220\206.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib \344\273\243\347\220\206.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib\344\273\243\347\220\206.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop \346\200\273\347\273\223.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop\346\200\273\347\273\223.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop \346\200\273\347\273\223.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop\346\200\273\347\273\223.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk \345\212\250\346\200\201\344\273\243\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk\345\212\250\346\200\201\344\273\243\347\220\206.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk \345\212\250\346\200\201\344\273\243\347\220\206.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk\345\212\250\346\200\201\344\273\243\347\220\206.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\270\200\357\274\211\357\274\232\350\256\244\350\257\206\344\272\213\345\212\241\347\273\204\344\273\266.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\200\357\274\211\357\274\232\350\256\244\350\257\206\344\272\213\345\212\241\347\273\204\344\273\266.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\270\200\357\274\211\357\274\232\350\256\244\350\257\206\344\272\213\345\212\241\347\273\204\344\273\266.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\200\357\274\211\357\274\232\350\256\244\350\257\206\344\272\213\345\212\241\347\273\204\344\273\266.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\270\211\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 01.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\211\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20601.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\270\211\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 01.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\211\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20601.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\272\214\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\346\211\247\350\241\214\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\214\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\346\211\247\350\241\214\346\265\201\347\250\213.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\272\214\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\346\211\247\350\241\214\346\265\201\347\250\213.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\214\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\346\211\247\350\241\214\346\265\201\347\250\213.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\272\224\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 03.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\224\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20603.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\344\272\224\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 03.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\224\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20603.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\345\205\255\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 04.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\205\255\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20604.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\345\205\255\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 04.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\205\255\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20604.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\345\233\233\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 02.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\233\233\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20602.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/spring \344\272\213\345\212\241\357\274\210\345\233\233\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\206 02.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\233\233\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20602.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\246\202\350\247\210.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\246\202\350\247\210.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\246\202\350\247\210.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\246\202\350\247\210.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\203\357\274\211\357\274\232\345\233\275\351\231\205\345\214\226\344\270\216\344\272\213\344\273\266\345\244\204\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\203\357\274\211\357\274\232\345\233\275\351\231\205\345\214\226\344\270\216\344\272\213\344\273\266\345\244\204\347\220\206.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\203\357\274\211\357\274\232\345\233\275\351\231\205\345\214\226\344\270\216\344\272\213\344\273\266\345\244\204\347\220\206.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\203\357\274\211\357\274\232\345\233\275\351\231\205\345\214\226\344\270\216\344\272\213\344\273\266\345\244\204\347\220\206.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\214\205\347\232\204\346\211\253\346\217\217\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\214\205\347\232\204\346\211\253\346\217\217\346\265\201\347\250\213.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\214\205\347\232\204\346\211\253\346\217\217\346\265\201\347\250\213.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\214\205\347\232\204\346\211\253\346\217\217\346\265\201\347\250\213.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\271\235\357\274\211\357\274\232\345\215\225\344\276\213 bean \347\232\204\345\210\233\345\273\272.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\271\235\357\274\211\357\274\232\345\215\225\344\276\213bean\347\232\204\345\210\233\345\273\272.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\271\235\357\274\211\357\274\232\345\215\225\344\276\213 bean \347\232\204\345\210\233\345\273\272.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\271\235\357\274\211\357\274\232\345\215\225\344\276\213bean\347\232\204\345\210\233\345\273\272.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232ApplicationContext \347\232\204\345\210\233\345\273\272.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232ApplicationContext\347\232\204\345\210\233\345\273\272.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232ApplicationContext \347\232\204\345\210\233\345\273\272.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232ApplicationContext\347\232\204\345\210\233\345\273\272.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\346\211\247\350\241\214 BeanFactoryPostProcessor.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\346\211\247\350\241\214BeanFactoryPostProcessor.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\346\211\247\350\241\214 BeanFactoryPostProcessor.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\346\211\247\350\241\214BeanFactoryPostProcessor.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\253\357\274\211\357\274\232\345\256\214\346\210\220 BeanFactory \347\232\204\345\210\235\345\247\213\345\214\226.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\253\357\274\211\357\274\232\345\256\214\346\210\220BeanFactory\347\232\204\345\210\235\345\247\213\345\214\226.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\253\357\274\211\357\274\232\345\256\214\346\210\220 BeanFactory \347\232\204\345\210\235\345\247\213\345\214\226.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\253\357\274\211\357\274\232\345\256\214\346\210\220BeanFactory\347\232\204\345\210\235\345\247\213\345\214\226.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\346\263\250\345\206\214 BeanPostProcessor.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\346\263\250\345\206\214BeanPostProcessor.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\346\263\250\345\206\214 BeanPostProcessor.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\346\263\250\345\206\214BeanPostProcessor.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\357\274\211\357\274\232\345\220\257\345\212\250\345\256\214\346\210\220\347\232\204\345\244\204\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\357\274\211\357\274\232\345\220\257\345\212\250\345\256\214\346\210\220\347\232\204\345\244\204\347\220\206.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\357\274\211\357\274\232\345\220\257\345\212\250\345\256\214\346\210\220\347\232\204\345\244\204\347\220\206.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\357\274\211\357\274\232\345\220\257\345\212\250\345\256\214\346\210\220\347\232\204\345\244\204\347\220\206.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250\345\211\215\347\232\204\345\207\206\345\244\207\345\267\245\344\275\234.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250\345\211\215\347\232\204\345\207\206\345\244\207\345\267\245\344\275\234.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/spring \345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250\345\211\215\347\232\204\345\207\206\345\244\207\345\267\245\344\275\234.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250\345\211\215\347\232\204\345\207\206\345\244\207\345\267\245\344\275\234.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 ApplicationContext.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213ApplicationContext.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 ApplicationContext.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213ApplicationContext.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 BeanDefinition.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanDefinition.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 BeanDefinition.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanDefinition.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring \347\273\204\344\273\266\344\271\213 BeanFactory.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactory.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring \347\273\204\344\273\266\344\271\213 BeanFactory.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactory.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 BeanFactoryPostProcessor.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactoryPostProcessor.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 BeanFactoryPostProcessor.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactoryPostProcessor.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 BeanPostProcessor.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanPostProcessor.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/spring \347\273\204\344\273\266\344\271\213 BeanPostProcessor.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanPostProcessor.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan\346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan\346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import\346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import\346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean\346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean\346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional\346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional\346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring \346\216\242\347\247\230\344\271\213 AOP \347\232\204\346\211\247\350\241\214\351\241\272\345\272\217.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213AOP\347\232\204\346\211\247\350\241\214\351\241\272\345\272\217.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring \346\216\242\347\247\230\344\271\213 AOP \347\232\204\346\211\247\350\241\214\351\241\272\345\272\217.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213AOP\347\232\204\346\211\247\350\241\214\351\241\272\345\272\217.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring \346\216\242\347\247\230\344\271\213 Spring \344\272\213\344\273\266\346\234\272\345\210\266.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213Spring\344\272\213\344\273\266\346\234\272\345\210\266.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring \346\216\242\347\247\230\344\271\213 Spring \344\272\213\344\273\266\346\234\272\345\210\266.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213Spring\344\272\213\344\273\266\346\234\272\345\210\266.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\270\200\357\274\211\357\274\232\347\220\206\350\256\272\345\237\272\347\237\263.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\270\200\357\274\211\357\274\232\347\220\206\350\256\272\345\237\272\347\237\263.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\270\200\357\274\211\357\274\232\347\220\206\350\256\272\345\237\272\347\237\263.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\270\200\357\274\211\357\274\232\347\220\206\350\256\272\345\237\272\347\237\263.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\272\214\357\274\211\357\274\232\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/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\272\214\357\274\211\357\274\232\346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\272\214\357\274\211\357\274\232\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\272\214\357\274\211\357\274\232\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243 @EventListener.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243 @EventListener.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\347\273\204\345\220\210\346\263\250\350\247\243\347\232\204\345\244\204\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\273\204\345\220\210\346\263\250\350\247\243\347\232\204\345\244\204\347\220\206.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/spring \346\216\242\347\247\230\344\271\213\347\273\204\345\220\210\346\263\250\350\247\243\347\232\204\345\244\204\347\220\206.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\273\204\345\220\210\346\263\250\350\247\243\347\232\204\345\244\204\347\220\206.md" From 1fbeaa51bcd415625cac6c5989beb8ace9db90cb Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 29 Apr 2023 14:33:11 +0800 Subject: [PATCH 07/20] done --- ReadMe.md | 10 ++++------ .../@SpringBootApplication\346\263\250\350\247\243.md" | 0 ...346\263\250\345\206\214\346\265\201\347\250\213.md" | 0 ...350\243\205\351\205\215\350\277\207\347\250\213.md" | 0 ...74\232\345\207\206\345\244\207SpringApplication.md" | 0 5 files changed, 4 insertions(+), 6 deletions(-) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/@SpringBootApplication \346\263\250\350\247\243.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/@SpringBootApplication\346\263\250\350\247\243.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet \347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet\347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc \350\243\205\351\205\215\350\277\207\347\250\213.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc\350\243\205\351\205\215\350\277\207\347\250\213.md" (100%) rename "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207 SpringApplication.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207SpringApplication.md" (100%) diff --git a/ReadMe.md b/ReadMe.md index af654ba..dd4899f 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -264,11 +264,11 @@ * [基于SpringBoot中的开源监控工具SpringBootAdmin](docs/Spring全家桶/SpringBoot/基于SpringBoot中的开源监控工具SpringBootAdmin.md) ## SpringBoot源码分析 -* [@SpringBootApplication 注解](docs/Spring全家桶/SpringBoot源码解析/@SpringBootApplication 注解.md) -* [springboot web应用(一):servlet 组件的注册流程](docs/Spring全家桶/SpringBoot源码解析/SpringBootWeb应用(一):servlet 组件的注册流程.md) -* [springboot web应用(二):WebMvc 装配过程](docs/Spring全家桶/SpringBoot源码解析/SpringBootWeb应用(二):WebMvc 装配过程.md) +* [@SpringBootApplication 注解](docs/Spring全家桶/SpringBoot源码解析/@SpringBootApplication注解.md) +* [springboot web应用(一):servlet 组件的注册流程](docs/Spring全家桶/SpringBoot源码解析/SpringBootWeb应用(一):servlet组件的注册流程.md) +* [springboot web应用(二):WebMvc 装配过程](docs/Spring全家桶/SpringBoot源码解析/SpringBootWeb应用(二):WebMvc装配过程.md) -* [SpringBoot 启动流程(一):准备 SpringApplication](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(一):准备 SpringApplication.md) +* [SpringBoot 启动流程(一):准备 SpringApplication](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(一):准备SpringApplication.md) * [SpringBoot 启动流程(二):准备运行环境](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(二):准备运行环境.md) * [SpringBoot 启动流程(三):准备IOC容器](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(三):准备IOC容器.md) * [springboot 启动流程(四):启动IOC容器](docs/Spring全家桶/SpringBoot源码解析/SpringBoot启动流程(四):启动IOC容器.md) @@ -318,8 +318,6 @@ * [SpringCloudAlibaba nacos源码分析:配置中心](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudAlibabaNacos源码分析:配置中心.md) * [Spring Cloud RocketMQ源码分析](docs/Spring全家桶/SpringCloudAlibaba源码分析/SpringCloudRocketMQ源码分析.md) -todo - # 设计模式 * [设计模式学习总结](docs/Java/design-parttern/设计模式学习总结.md) diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/@SpringBootApplication \346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/@SpringBootApplication\346\263\250\350\247\243.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/@SpringBootApplication \346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/@SpringBootApplication\346\263\250\350\247\243.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet \347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet\347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet \347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet\347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc \350\243\205\351\205\215\350\277\207\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc\350\243\205\351\205\215\350\277\207\347\250\213.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc \350\243\205\351\205\215\350\277\207\347\250\213.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc\350\243\205\351\205\215\350\277\207\347\250\213.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207 SpringApplication.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207SpringApplication.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207 SpringApplication.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207SpringApplication.md" From 914bb6a35867a2b56a0370f35ea84d2d91494100 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 29 Apr 2023 16:41:05 +0800 Subject: [PATCH 08/20] ok --- ...5\220\254\345\231\250\346\263\250\350\247\243EventListener.md" | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" (100%) diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" From 55ddd5d1f7ce95c7c29a273b1bf39123ce88700a Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 29 Apr 2023 16:43:18 +0800 Subject: [PATCH 09/20] ok --- ReadMe.md | 2 +- ...20\254\345\231\250\346\263\250\350\247\243@EventListener.md" | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" (100%) diff --git a/ReadMe.md b/ReadMe.md index dd4899f..6aabce3 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -217,7 +217,7 @@ * [Spring 探秘之 Spring 事件机制](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring探秘之Spring事件机制.md) * [spring 探秘之循环依赖的解决(一):理论基石](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring探秘之循环依赖的解决(一):理论基石.md) * [spring 探秘之循环依赖的解决(二):源码分析](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring探秘之循环依赖的解决(二):源码分析.md) -* [spring 探秘之监听器注解 @EventListener](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/spring 探秘之监听器注解@EventListener.md) +* [spring 探秘之监听器注解 @EventListener](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/spring探秘之监听器注解@EventListener.md) * [spring 探秘之组合注解的处理](docs/Spring全家桶/Spring源码分析/Spring重要机制探秘/Spring探秘之组合注解的处理.md) ## SpringMVC diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" From 5f733385caa951c7e6a1126ea3632f7971ea612b Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 29 Apr 2023 16:49:03 +0800 Subject: [PATCH 10/20] done --- ...5\220\254\345\231\250\346\263\250\350\247\243EventListener.md" | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" => "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" (100%) diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" similarity index 100% rename from "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243@EventListener.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" From 8aa85dd33915575a5290e82e104095a99b2bfb24 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 29 Apr 2023 17:06:37 +0800 Subject: [PATCH 11/20] add reference --- .../SpringCloud/SpringCloudConfig.md" | 11 ++++++++++- .../SpringCloud/SpringCloudConsul.md" | 7 +++++++ .../SpringCloud/SpringCloudEureka.md" | 10 +++++++++- .../SpringCloud/SpringCloudGateway.md" | 13 +++++++++++-- .../SpringCloud/SpringCloudHystrix.md" | 10 +++++++++- .../SpringCloud/SpringCloudLoadBalancer.md" | 8 ++++++++ .../SpringCloud/SpringCloudOpenFeign.md" | 9 ++++++++- .../SpringCloud/SpringCloudRibbon.md" | 10 +++++++++- .../SpringCloud/SpringCloudSleuth.md" | 10 +++++++++- .../SpringCloud/SpringCloudZuul.md" | 6 ++++++ .../SpringCloud\346\246\202\350\277\260.md" | 8 +++++++- ...\234\215\345\212\241\346\263\250\345\206\214.md" | 10 +++++++++- ...\236\220\357\274\232\346\246\202\350\247\210.md" | 9 ++++++++- ...\205\215\347\275\256\344\270\255\345\277\203.md" | 9 ++++++++- ...\272\220\347\240\201\345\210\206\346\236\220.md" | 9 ++++++++- ...\272\220\347\240\201\345\210\206\346\236\220.md" | 10 +++++++++- ...\272\220\347\240\201\345\210\206\346\236\220.md" | 7 +++++++ ...\272\220\347\240\201\345\210\206\346\236\220.md" | 10 +++++++++- ...\272\220\347\240\201\345\210\206\346\236\220.md" | 10 +++++++++- ...\272\220\347\240\201\345\210\206\346\236\220.md" | 10 +++++++++- ...\272\220\347\240\201\345\210\206\346\236\220.md" | 9 +++++++-- ...\272\220\347\240\201\345\210\206\346\236\220.md" | 10 +++++++++- ...\272\220\347\240\201\345\210\206\346\236\220.md" | 7 +++++++ ...\272\220\347\240\201\345\210\206\346\236\220.md" | 10 +++++++++- 24 files changed, 201 insertions(+), 21 deletions(-) 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) -ͼ17��Spring Cloud Bus ����֪ͨ \ No newline at end of file +ͼ17��Spring 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) -ͼ16��Hystrix ��ط���������� \ No newline at end of file +ͼ16��Hystrix ��ط���������� + +# �ο����� +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��Ӧ�ķ��������run������������run�ġ�������ͻ�ʵʱ�����첽���С�����д�ĺô������ǽ�д�ʹ�����ȫ�����ˡ�ͨ�����������ܵ��ڴ���У�������������飬�������ĺô���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 ��Ȼ��Ҳ�����һЩ���ɺ��ӵ�ȱ�㣬���磬ע�͹��٣����뻹�кܴ���ع��ռ䣬tenant��namespace�����������ʹ�á� -����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����һ���ύ������seata��ATģʽԴ�������ϡ� \ 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����һ���ύ������seata��ATģʽԴ�������ϡ� + +# �ο����� +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�����ҵ�������Ribbon��Eureka��ϵ��Զ������࣬����Ŀǰ����û������Eureka����ͨ�������ļ����ã����Ի���ConfigurationBasedServerList�ࡣ \ No newline at end of file +EurekaRibbonClientConfiguration�����ҵ�������Ribbon��Eureka��ϵ��Զ������࣬����Ŀǰ����û������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 From 84e574939817d4c3e42e95c322ccf755791146e0 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Thu, 25 May 2023 21:47:10 +0800 Subject: [PATCH 12/20] kafka series --- ...57\274\210Java\347\211\210\357\274\211.md" | 173 +++++ ...er\347\232\204\350\277\207\347\250\213.md" | 112 +++ ...7\274\232Kafka\344\273\213\347\273\215.md" | 122 ++++ ...20\346\200\273\347\273\223\347\257\207.md" | 636 ++++++++++++++++++ ...15\347\275\256\346\200\273\347\273\223.md" | 517 ++++++++++++++ ...66\346\236\204\344\273\213\347\273\215.md" | 298 ++++++++ ...45\344\275\234\345\216\237\347\220\206.md" | 127 ++++ ...25\351\242\230\345\244\247\345\205\250.md" | 518 ++++++++++++++ 8 files changed, 2503 insertions(+) create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" 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 From 68d65ba2a393f20ae92fa4745dfb22650edb25d1 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Fri, 26 May 2023 23:15:13 +0800 Subject: [PATCH 13/20] add mq doc --- ...00\350\207\264\346\200\247\357\274\211.md" | 62 ++ ...72\346\234\254\346\246\202\345\277\265.md" | 62 ++ ...66\350\277\237\346\266\210\346\201\257.md" | 62 ++ ...01\344\270\216\350\277\207\346\273\244.md" | 62 ++ ...7\344\270\216\346\266\210\350\264\271 .md" | 62 ++ ...57\345\242\203\346\220\255\345\273\272.md" | 62 ++ ...2\345\272\217\346\266\210\350\264\271 .md" | 62 ++ ...66\350\277\237\351\230\237\345\210\227.md" | 173 ++++++ ...73\344\277\241\351\230\237\345\210\227.md" | 578 ++++++++++++++++++ 9 files changed, 1185 insertions(+) create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" 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��һЩ��������ͻ����Ĵ��ʼ���� + +RocketMQ��4������ɣ��ֱ��ǣ����Ʒ���Name Server������Ϣ���У�Brokers���������ߣ�producer���������ߣ�consumer������4���ֶ����Խ���ˮƽ��չ���Ӷ����ⵥ����ϣ�����ͼ�� + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +����RocketMQ�����ϵ�һ��ͼ���dz��������г���4�����֣����Ҷ��Ǽ�Ⱥģʽ���������Ǿͷֱ�˵һ˵��4���֡� + +## ���Ʒ���NameServer�� + +Name Server���ݵĽ�ɫ��һ��ע�����ģ���Zookeeper�����ò�ࡣ������Ҫ���������������£� + +* broker�Ĺ�����broker��Ⱥ���Լ�����Ϣע�ᵽNameServer��NameServer�ṩ�������Ƽ��ÿһ��broker�Ƿ������� +* ·�ɹ�����ÿһ��NameServer��������broker��Ⱥ�Ͷ��е���Ϣ���Ա�ͻ��ˣ������ߺ������ߣ���ѯ�� + +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��һЩ��������ͻ����Ĵ��ʼ���� + +RocketMQ��4������ɣ��ֱ��ǣ����Ʒ���Name Server������Ϣ���У�Brokers���������ߣ�producer���������ߣ�consumer������4���ֶ����Խ���ˮƽ��չ���Ӷ����ⵥ����ϣ�����ͼ�� + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +����RocketMQ�����ϵ�һ��ͼ���dz��������г���4�����֣����Ҷ��Ǽ�Ⱥģʽ���������Ǿͷֱ�˵һ˵��4���֡� + +## ���Ʒ���NameServer�� + +Name Server���ݵĽ�ɫ��һ��ע�����ģ���Zookeeper�����ò�ࡣ������Ҫ���������������£� + +* broker�Ĺ�����broker��Ⱥ���Լ�����Ϣע�ᵽNameServer��NameServer�ṩ�������Ƽ��ÿһ��broker�Ƿ������� +* ·�ɹ�����ÿһ��NameServer��������broker��Ⱥ�Ͷ��е���Ϣ���Ա�ͻ��ˣ������ߺ������ߣ���ѯ�� + +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��һЩ��������ͻ����Ĵ��ʼ���� + +RocketMQ��4������ɣ��ֱ��ǣ����Ʒ���Name Server������Ϣ���У�Brokers���������ߣ�producer���������ߣ�consumer������4���ֶ����Խ���ˮƽ��չ���Ӷ����ⵥ����ϣ�����ͼ�� + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +����RocketMQ�����ϵ�һ��ͼ���dz��������г���4�����֣����Ҷ��Ǽ�Ⱥģʽ���������Ǿͷֱ�˵һ˵��4���֡� + +## ���Ʒ���NameServer�� + +Name Server���ݵĽ�ɫ��һ��ע�����ģ���Zookeeper�����ò�ࡣ������Ҫ���������������£� + +* broker�Ĺ�����broker��Ⱥ���Լ�����Ϣע�ᵽNameServer��NameServer�ṩ�������Ƽ��ÿһ��broker�Ƿ������� +* ·�ɹ�����ÿһ��NameServer��������broker��Ⱥ�Ͷ��е���Ϣ���Ա�ͻ��ˣ������ߺ������ߣ���ѯ�� + +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��һЩ��������ͻ����Ĵ��ʼ���� + +RocketMQ��4������ɣ��ֱ��ǣ����Ʒ���Name Server������Ϣ���У�Brokers���������ߣ�producer���������ߣ�consumer������4���ֶ����Խ���ˮƽ��չ���Ӷ����ⵥ����ϣ�����ͼ�� + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +����RocketMQ�����ϵ�һ��ͼ���dz��������г���4�����֣����Ҷ��Ǽ�Ⱥģʽ���������Ǿͷֱ�˵һ˵��4���֡� + +## ���Ʒ���NameServer�� + +Name Server���ݵĽ�ɫ��һ��ע�����ģ���Zookeeper�����ò�ࡣ������Ҫ���������������£� + +* broker�Ĺ�����broker��Ⱥ���Լ�����Ϣע�ᵽNameServer��NameServer�ṩ�������Ƽ��ÿһ��broker�Ƿ������� +* ·�ɹ�����ÿһ��NameServer��������broker��Ⱥ�Ͷ��е���Ϣ���Ա�ͻ��ˣ������ߺ������ߣ���ѯ�� + +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��һЩ��������ͻ����Ĵ��ʼ���� + +RocketMQ��4������ɣ��ֱ��ǣ����Ʒ���Name Server������Ϣ���У�Brokers���������ߣ�producer���������ߣ�consumer������4���ֶ����Խ���ˮƽ��չ���Ӷ����ⵥ����ϣ�����ͼ�� + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +����RocketMQ�����ϵ�һ��ͼ���dz��������г���4�����֣����Ҷ��Ǽ�Ⱥģʽ���������Ǿͷֱ�˵һ˵��4���֡� + +## ���Ʒ���NameServer�� + +Name Server���ݵĽ�ɫ��һ��ע�����ģ���Zookeeper�����ò�ࡣ������Ҫ���������������£� + +* broker�Ĺ�����broker��Ⱥ���Լ�����Ϣע�ᵽNameServer��NameServer�ṩ�������Ƽ��ÿһ��broker�Ƿ������� +* ·�ɹ�����ÿһ��NameServer��������broker��Ⱥ�Ͷ��е���Ϣ���Ա�ͻ��ˣ������ߺ������ߣ���ѯ�� + +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��һЩ��������ͻ����Ĵ��ʼ���� + +RocketMQ��4������ɣ��ֱ��ǣ����Ʒ���Name Server������Ϣ���У�Brokers���������ߣ�producer���������ߣ�consumer������4���ֶ����Խ���ˮƽ��չ���Ӷ����ⵥ����ϣ�����ͼ�� + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +����RocketMQ�����ϵ�һ��ͼ���dz��������г���4�����֣����Ҷ��Ǽ�Ⱥģʽ���������Ǿͷֱ�˵һ˵��4���֡� + +## ���Ʒ���NameServer�� + +Name Server���ݵĽ�ɫ��һ��ע�����ģ���Zookeeper�����ò�ࡣ������Ҫ���������������£� + +* broker�Ĺ�����broker��Ⱥ���Լ�����Ϣע�ᵽNameServer��NameServer�ṩ�������Ƽ��ÿһ��broker�Ƿ������� +* ·�ɹ�����ÿһ��NameServer��������broker��Ⱥ�Ͷ��е���Ϣ���Ա�ͻ��ˣ������ߺ������ߣ���ѯ�� + +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��һЩ��������ͻ����Ĵ��ʼ���� + +RocketMQ��4������ɣ��ֱ��ǣ����Ʒ���Name Server������Ϣ���У�Brokers���������ߣ�producer���������ߣ�consumer������4���ֶ����Խ���ˮƽ��չ���Ӷ����ⵥ����ϣ�����ͼ�� + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +����RocketMQ�����ϵ�һ��ͼ���dz��������г���4�����֣����Ҷ��Ǽ�Ⱥģʽ���������Ǿͷֱ�˵һ˵��4���֡� + +## ���Ʒ���NameServer�� + +Name Server���ݵĽ�ɫ��һ��ע�����ģ���Zookeeper�����ò�ࡣ������Ҫ���������������£� + +* broker�Ĺ�����broker��Ⱥ���Լ�����Ϣע�ᵽNameServer��NameServer�ṩ�������Ƽ��ÿһ��broker�Ƿ������� +* ·�ɹ�����ÿһ��NameServer��������broker��Ⱥ�Ͷ��е���Ϣ���Ա�ͻ��ˣ������ߺ������ߣ���ѯ�� + +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\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 Streams��Kafka Connect��Spring ��ܺͲ��������ߡ���ʵ�����о�չʾ�� Uber��CrowdStrike ��ɣ̹����������Լ��˹�ģ�����ɿ���ʵʱ�������� + + + + + +Apache Kafka ��Ϊ������ҵ�ܹ���ϲ���ļ����м������ʹ����������ս�ԣ���ҵҲ�������� Kafka ����������Ϊ��ԭ������ƽ̨������ (iPaaS)�� + + + + + +### Apache Kafka �������е���Ϣ����ģʽ + + + + + +���ҿ�ʼ��ƪ����֮ǰ����������֪�����������**���ڡ�JMS����Ϣ���к� Apache Kafka���IJ���ϵ��**��һ���֣� + + + + + +* 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 MQ��TIBCO EMS �� RabbitMQ����ֲ�ʽ�ύ��־���� Kafka���Ĺ�����ʽ��ͬ��������������ԭ����Ϣ�����е� DLQ ������Ϣ����ϵͳ����Щԭ����һ��һ��ӳ�䵽 Kafka�����磬MQ ϵͳ�е���Ϣ����ÿ����Ϣ�� TTL������ʱ�䣩�����ڡ� + + + + + +��ˣ�**�� Kafka �н���Ϣ���� DLQ ����Ҫԭ������Ϣ��ʽ�������Ϣ������Ч/ȱʧ**�� + + + + + +### Apache Kafka �����Ŷ��е�������� + + + + + +Kafka �е����Ŷ�����һ������ Kafka ���⣬����**���պʹ洢���ڴ�����޷�����һ�����ܵ��д�������Ϣ**���˸�������ʹ�����´�����Ϣ������Ϣ����������������Ч��Ϣ�Ĵ����ֹͣ�������� + + + + + +### Kafka Broker �ܱ��������ܶ˵��ṩ������ + + + + + +**Kafka �ܹ���֧�� broker** r �е�DLQ������أ�Kafka ���������ִ�΢������ͬ��ԭ���ϣ�ʹ�á��ƹܵ������ܶ˵㡱ԭ�������Ϊʲô�봫ͳ��Ϣ������ȣ�Kafka ����չ�����֮�á����˺ʹ����������ڿͻ���Ӧ�ó����С� + + + + + +������ƽ̨�������������ʵ�ָ��ɾ�������������ơ�**ÿ��΢�����Ӧ�ó���ͨ���Լ�ѡ��ļ�����ͨ�ŷ�ʽ�ʹ�������ʵ�����߼�**�� + + + + + +�ڴ�ͳ���м������Ϣ�����У������ṩ�������߼�����������еĿ���չ�Ժ�����Խϲ��Ϊֻ���м���ŶӲ���ʵ�ּ����߼��� + + + + + +### ���κα�������Զ���ʵ�� Kafka ���Ŷ��� + + + + + +Kafka �е����Ŷ��ж�������ʹ�õĿ�ܡ�һЩ���Ϊ�����������Ŷ����ṩ�˿��伴�õĹ��ܡ����ǣ�ʹ��Java��Go��C++��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 Flink��Storm��Beam �����ƹ��ߡ����ǣ����� Kafka ԭ���ġ�����ζ���������ڵ�������չ�ҿɿ��Ļ����ܹ��й��������Ķ˵����������� + + + + + +������ֱ�ʹ�� Java��JVM ��̬ϵͳ�������� 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 ���Ŷ��еİ����о� + + + + + +�����ǿ���**Uber��CrowdStrike �� 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 Streams��Kafka Connect��Spring ��ܻ� Kafka �IJ��������ߡ� + + + + + +�Ų���CrowdStrike ��ɣ̹�����еİ����о��������������������Ǻ�����ʵ�֡���������µ�Ӧ�ó����ܹ�ʱ����Ҫ��һ��ʼ�Ϳ��ǵ���һ�㡣**ʹ�� Apache Kafka ����ʵʱ�����������������������ֻ�������ܹ�����������Ϊʱ���ܳɹ�**�����Ŷ��������ೡ���ľ���ѡ�� \ No newline at end of file From 2c59f8da32e7e2928dedcf96fb141e4674b16354 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Wed, 31 May 2023 00:13:17 +0800 Subject: [PATCH 14/20] =?UTF-8?q?=E7=9B=91=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/monitor/Spring Actuator.md | 578 +++++++++++++++++++++++++++++++ docs/monitor/SpringBoot Admin.md | 495 ++++++++++++++++++++++++++ 2 files changed, 1073 insertions(+) create mode 100644 docs/monitor/Spring Actuator.md create mode 100644 docs/monitor/SpringBoot Admin.md 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�����д���һ������ΪAuditEventRepository��Bean�ġ� + +�鿴�������ϴ�����̳̣������϶��ǽ�����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�� + +���⣬ǰ���ᵽ��Ҫ�õ�AuditEventRepository��Bean�������ʼ��һ����Ӧ��Bean�� + + + + +```` +@Configuration +public class AuditEventConfig { + + @Bean + public InMemoryAuditEventRepository repository(){ + return new InMemoryAuditEventRepository(); + } +} +```` + +InMemoryAuditEventRepository��AuditEventRepository�ӿڵ�Ψһʵ���ࡣ + +������Ŀ��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�˵�᷵��Spring����������bean�ı��������͡��Ƿ�������������Ϣ�� + +����·��Ϊ[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` + +�������ռλ������queryAll�������key����ִ�н�����£� + +![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����������ֱ�ӷ��ʿ�����ֻ��UP��DOWN�����ڼ��������ݿ⣬ͬʱ�Ѹ���ֵ����Ϊ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) + +״̬ΪDOWN�������db���������⣬״̬ΪDOWN�������������error��չʾ�����Կ����ǽ�������ʱ�����ˡ���ʵ���У����ǿ���ͨ��health�˿ڼ�����ݿ⡢Redis��MongoDB�����̵Ƚ��������ActuatorԤ����Ĵ�����Ϊ��DataSourceHealthIndicator, DiskSpaceHealthIndicator, MongoHealthIndicator, RedisHealthIndicator�ȡ� + +����ÿ��ָ�궼���Ե����Ľ��п����͹رգ������ݿ��Ϊ���� + + +```` +management: + health: + db: + enabled: true` +```` + +## info�˵� + +/info �˵������鿴�����ļ� application����info��ͷ��������Ϣ��Ĭ������� 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�˵���caches��caches-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�˵��ϸ�֣�����ͨ��name����ijһ��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 �����ṩӦ�õ����¼����Ϣ�� + +* ���Ӧ�����й����еĸ�����Ϣ�� +* ����ָ����Ϣ������JVM��Tomcat��������Ϣ�� +* ����������Ϣ������ϵͳ���ԡ�ϵͳ���������Լ�Ӧ��������Ϣ�� +* �鿴���д�����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 +```` + +* ��������������@EnableAdminServer������admin-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-server��admin-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) + + + + + +* ����ָ����Ϣ������JVM��Tomcat��������Ϣ�� + +![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 Admin���Spring 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.yml��������admin-server��admin-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-server����Spring 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.��������cookie��csrf���� + .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-server��admin-security-server������Spring 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 From 86135b7d0109ae0d1b51efc3cb016efe4b5973bc Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 26 Aug 2023 20:04:58 +0800 Subject: [PATCH 15/20] add mq list --- ReadMe.md | 19 +++++++++++++++++++ ...47\344\270\216\346\266\210\350\264\271.md" | 0 ...72\345\272\217\346\266\210\350\264\271.md" | 0 3 files changed, 19 insertions(+) rename "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" => "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" (100%) rename "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" => "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" (100%) diff --git a/ReadMe.md b/ReadMe.md index 6aabce3..0c11cc7 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -394,6 +394,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/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" similarity index 100% rename from "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" rename to "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" 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" similarity index 100% rename from "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" rename to "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" From 183bf66bea9599a14c8598d6df28fcad26248d35 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sat, 26 Aug 2023 20:33:28 +0800 Subject: [PATCH 16/20] ok --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 0c11cc7..866d360 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -394,7 +394,7 @@ todo # 消息队列 ## Kafka -* [消息队列kafka详解:Kafka 快速上手(Java版)](docs/mq/kafka/消息队列kafka详解:Kafka 快速上手(Java版).md) +* [消息队列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) From 757b11887fb3b0d8f1029ae80e5bb238229acbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=9F=E5=B0=8F=E6=B9=96?= <362294931@qq.com> Date: Wed, 16 Apr 2025 20:07:30 +0800 Subject: [PATCH 17/20] Update ReadMe.md --- ReadMe.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ReadMe.md b/ReadMe.md index 866d360..269ed3e 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -30,6 +30,16 @@

+

+ +

+ + ef9a0076f464d69ba16e99e7e8f26a87.png + +

+ +Swiftproxy-90M+全球高质量纯净住宅IP,注册可领免费500MB测试流量,动态流量不过期!使用折扣码:GHB5立享九折优惠! + # Java基础 ## 基础知识 From 0922bb9ffc366c5501dd8236e5d8fdc359226fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=9F=E5=B0=8F=E6=B9=96?= <362294931@qq.com> Date: Mon, 21 Apr 2025 18:31:22 +0800 Subject: [PATCH 18/20] Update ReadMe.md --- ReadMe.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 269ed3e..b5d6559 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,4 +1,3 @@ -
@@ -33,8 +32,8 @@

- - ef9a0076f464d69ba16e99e7e8f26a87.png + + ef9a0076f464d69ba16e99e7e8f26a87.png

From 921aa16bcd882bcb0a667f8c5a1899914b9ffef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=9F=E5=B0=8F=E6=B9=96?= <362294931@qq.com> Date: Mon, 21 Apr 2025 18:32:10 +0800 Subject: [PATCH 19/20] Update ReadMe.md --- ReadMe.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReadMe.md b/ReadMe.md index b5d6559..74fc288 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,3 +1,4 @@ +
From e89aca28d56b5c6e03ce37f20abc6781388b2b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=9F=E5=B0=8F=E6=B9=96?= <362294931@qq.com> Date: Tue, 20 May 2025 18:36:36 +0800 Subject: [PATCH 20/20] Update ReadMe.md --- ReadMe.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 74fc288..84572da 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -32,14 +32,6 @@

-

- - ef9a0076f464d69ba16e99e7e8f26a87.png - -

- -Swiftproxy-90M+全球高质量纯净住宅IP,注册可领免费500MB测试流量,动态流量不过期!使用折扣码:GHB5立享九折优惠! - # Java基础 ## 基础知识