1. Project Overview & Quickstart (doocs/source-code-hunter)
File: README.md
互联网公司常用框架源码赏析
“技术深度” 与 “技术广度”是对开发者来说最为重要的两个维度,本项目致力于从源码层面,剖析和挖掘互联网行业主流技术的底层实现原理,为广大开发者 “提升技术深度” 提供便利。
加入我们,一起通读互联网行业主流框架及中间件源码,成为强大的 “源码猎人”,目前开放的有 Spring 全家桶、Mybatis、Netty、Dubbo 框架,及 Redis、Tomcat 中间件等,让我们一起开拓新的领地,揭开这些源码的神秘面纱。
本项目主要用于记录框架及中间件源码的阅读经验、个人理解及解析,希望能够使阅读源码变成一件简单有趣,且有价值的事情,抽空更新中... (如果本项目对您有帮助,请 watch、star、fork 素质三连一波,鼓励一下作者,谢谢)
网站
Spring 系列
IoC 容器
- BeanDefinition 的资源定位过程
- 将 bean 解析封装成 BeanDefinition
- 将 BeanDefinition 注册进 IoC 容器
- 依赖注入(DI)
- BeanFactoryPostProcessor
- BeanPostProcessor
- Spring BeanFactory 源码解析
- 循环依赖
AOP
SpringMVC
- IoC 容器 在 Web 环境 中的启动
- SpringMVC 的设计与实现
- SpringMVC 跨域解析
- Spring-MVC-HandlerMapping
- Spring-mvc-MappingRegistry
SpringJDBC
- 努力编写中...
Spring 事务
Spring 源码故事(瞎编版)
Spring 整体脉络
Spring 类解析
Spring5 新特性
Spring RMI
Spring Message
SpringBoot
- SpringBoot run 方法解析
- SpringBoot 配置加载解析
- SpringBoot 自动装配
- SpringBoot ConfigurationProperties
- SpringBoot 日志系统
- SpringBoot ConditionalOnBean
Spring Cloud
SpringSecurity
MyBatis
基础支持层
核心处理层
类解析
- Mybatis-Cache
- Mybatis-log
- Mybatis-Reflector
- Mybatis-Alias
- Mybatis-Cursor
- Mybatis-DataSource
- Mybatis-DynamicSqlSource
- Mybatis-MapperMethod
- Mybatis-MetaObject
- Mybatis-MethodSignature
- Mybatis-ObjectWrapper
- Mybatis-ParamNameResolver
- Mybatis-SqlCommand
- Mybatis-GenericTokenParser
Netty
网络 IO 技术基础
JDK1.8 NIO 包 核心组件源码剖析
Netty 粘拆包及解决方案
Netty 多协议开发
基于 Netty 开发服务端及客户端
Netty 主要组件的源码分析
- ByteBuf 组件
- Channel 组件 和 Unsafe 组件
- EventLoop 组件
- ChannelPipeline 和 ChannelHandler 组件
- Future 和 Promise 组件
Netty 高级特性
Netty 技术细节源码分析
- FastThreadLocal 源码分析
- Recycler 对象池原理分析
- MpscLinkedQueue 队列原理分析
- HashedWheelTimer 时间轮原理分析
- HashedWheelTimer & schedule
- ByteBuf 的内存泄漏原因与检测原理
- 内存池之 PoolChunk 设计与实现
- 内存池之从内存池申请内存
Dubbo
架构设计
SPI 机制
注册中心
远程通信
RPC
集群
Tomcat
Servlet 与 Servlet 容器
Web 容器
Redis
Nacos
Sentinel
RocketMQ
- RocketMQ NameServer 与 Broker 的通信
- RocketMQ 生产者启动流程
- RocketMQ 消息发送流程
- RocketMQ 消息发送存储流程
- RocketMQ MappedFile 内存映射文件详解
- RocketMQ ConsumeQueue 详解
- RocketMQ CommitLog 详解
- RocketMQ IndexFile 详解
- RocketMQ 消费者启动流程
- RocketMQ 消息拉取流程
- RocketMQ Broker 处理拉取消息请求流程
- RocketMQ 消息消费流程
番外篇(JDK 1.8)
基础类库
集合
- HashMap 类 源码赏析
- ConcurrentHashMap 类 源码赏析
- LinkedHashMap 类 源码赏析
- ArrayList 类 源码赏析
- LinkedList 类 源码赏析
- HashSet 类 源码赏析
- TreeSet 类 源码赏析
并发编程
- JUC 并发包 UML 全量类图
- Executor 线程池组件 源码赏析
- Lock 锁组件 源码赏析
- 详解 AbstractQueuedSynchronizer 抽象类
- Semaphore 类 源码赏析
学习心得
个人经验
设计模式
- 从 Spring 及 Mybatis 框架源码中学习设计模式(创建型)
- 从 Spring 及 Mybatis 框架源码中学习设计模式(行为型)
- 从 Spring 及 Mybatis 框架源码中学习设计模式(结构型)
多线程
Stars 趋势
Doocs 社区优质项目
GitHub 技术社区 Doocs,致力于打造一个内容完整、持续成长的互联网开发者学习生态圈!以下是 Doocs 的一些优秀项目,欢迎各位开发者朋友持续保持关注。
| # | 项目 | 描述 | 热度 |
|---|---|---|---|
| 1 | advanced-java | 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识。 | |
| 2 | leetcode | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解。 | |
| 3 | source-code-hunter | 互联网常用组件框架源码分析。 | |
| 4 | jvm | Java 虚拟机底层原理知识总结。 | |
| 5 | coding-interview | 代码面试题集,包括《剑指 Offer》、《编程之美》等。 | |
| 6 | md | 一款高度简洁的微信 Markdown 编辑器。 | |
| 7 | technical-books | 值得一看的技术书籍列表。 |
贡献者
感谢以下所有朋友对 GitHub 技术社区 Doocs 所做出的贡献,参与项目维护请戳这儿。
公众号
Doocs 技术社区旗下唯一公众号「Doocs」,欢迎扫码关注,专注分享技术领域相关知识及行业最新资讯。当然,也可以加我个人微信(备注:GitHub),拉你进技术交流群。
关注「Doocs」公众号,回复 PDF,即可获取 互联网 Java 工程师进阶知识完全扫盲 项目离线 PDF 文档(283 页精华),学习更加方便!
File: docs/SpringCloud/spring-cloud-commons-source-note.md
说明
Author: haitaoss
源码阅读仓库: spring-cloud-commons
参考资料和需要掌握的知识:
Spring Cloud 介绍
SpringCloud 是在 SpringBoot 的基础上构建的。Spring Cloud 以两个库的形式提供了许多特性:Spring Cloud Context 和 Spring Cloud Commons。Spring Cloud Context 为 SpringCloud 应用程序的 ApplicationContext 提供扩展机制(引导上下文、加密、刷新属性和环境端点)。Spring Cloud Commons 是一组抽象(服务注册、负载均衡、熔断器等 API) 和 通用类,用于不同的 Spring Cloud 实现(例如 Spring Cloud Netflix 和 Spring Cloud Consul)
是基于 Spring Boot 的自动装配原理实现的,其实就是定义了很多自动配置类,所以在 SpringCloud 的环境下 启动 SpringBoot 程序 会有很多功能。
核心功能源码分析
BootstrapApplicationListener (bootstrap.properties 读取原理)
前置知识:SprinBoot 加载 application.yml 的原理
示例代码
@EnableAutoConfiguration
public class Main {
public static void main(String[] args) {
// 是否创建 bootstrapContext
System.setProperty("spring.cloud.bootstrap.enabled", "true");
// 设置 bootstrapContext 中属性文件的搜索目录 或者是 属性文件
System.setProperty("spring.cloud.bootstrap.location", "");
System.setProperty("spring.cloud.bootstrap.additional-location",
"optional:classpath:/config/haitao/,classpath:/haitao.properties");
// 设置 bootstrapContext 默认属性文件的名字
// System.setProperty("spring.cloud.bootstrap.name", "bootstrap-haitao");
// 设置 profile
// System.setProperty("spring.profiles.active", "haitao");
// 测试读取属性
ConfigurableApplicationContext context = SpringApplication.run(Main.class, args);
ConfigurableEnvironment environment = context.getEnvironment();
Stream.iterate(1, i -> i + 1).limit(5).map(i -> "p" + i).forEach(
name -> System.out.println(String.format("key:%s \t valus: %s", name, environment.getProperty(name))));
}
}BootstrapApplicationListener 是用于完成 SpringCloud 的接入的,主要是完成 bootstrapContext 的创建、bootstrap 属性的加载、设置 bootstrapContext 为父容器。下面是 BootstrapApplicationListener 被触发的入口和核心逻辑
/* Detailed source-code truncated for AI context efficiency. */BootstrapImportSelectorConfiguration
用来扩展 bootstrapContext 中的配置类
// 类的声明如下
@Configuration(proxyBeanMethods = false)
@Import(BootstrapImportSelector.class)
public class BootstrapImportSelectorConfiguration {}/**
* BootstrapImportSelectorConfiguration 会通过 @Import 导入 {@link BootstrapImportSelector}
* 其回调方法 {@link BootstrapImportSelector#selectImports(AnnotationMetadata)} 的逻辑是
* 1. 读取 META-INF/spring.factories 获取key为BootstrapConfiguration的值
* 2. 获取属性 spring.cloud.bootstrap.sources 的值
* 3. 合并第一第二的值,然后排序
* 4. 会将值注册到容器中,作为容器的配置类
*
* 而
* spring-cloud-context.jar!/META-INF/spring.factories 中定义了
* org.springframework.cloud.bootstrap.BootstrapConfiguration = org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration
*
* 也就是 {@link PropertySourceBootstrapConfiguration} 会注册到 bootstrapContext 中,
* 它是 ApplicationContextInitializer 类型的,最终会用来初始化 context
* */PropertySourceBootstrapConfiguration
示例代码
public class MyPropertySourceLocator implements PropertySourceLocator {
public MyPropertySourceLocator() {
System.out.println("MyPropertySourceLocator...构造器");
}
@Resource
private ApplicationContext applicationContext;
@Value("${dynamicConfigFile}")
private String filePath;
@Override
public PropertySource<?> locate(Environment environment) {
PropertySource<?> propertySource;
try {
// 也可以改成网络资源
propertySource = new YamlPropertySourceLoader()
.load("haitao-propertySource", applicationContext.getResource(filePath)).get(0);
} catch (IOException e) {
throw new RuntimeException(e);
}
return propertySource;
}
}META-INF/spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
cn.haitaoss.BootstrapProperties.BootstrapConfiguration.MyPropertySourceLocator/**
* PropertySourceBootstrapConfiguration 是用来初始化IOC容器的,其初始化逻辑是扩展IOC容器的Environment,
* 可以自定义 PropertySourceLocator 用来扩展 Environment
*
* {@link PropertySourceBootstrapConfiguration#initialize(ConfigurableApplicationContext)}
*
* 1. 通过依赖注入对属性赋值
* @Autowired(required = false)
* private List propertySourceLocators = new ArrayList<>();
*
* 2. 排序
* AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
*
* 3. 遍历 propertySourceLocators , 然后回调方法得到 PropertySource 收集起来
* for (PropertySourceLocator locator : this.propertySourceLocators) {
* Collection> source = locator.locateCollection(environment);
* sourceList.addAll(source);
* }
*
* 4. 根据属性值决定插入到Environment的顺序
* spring.cloud.config.overrideSystemProperties 默认是 true
* spring.cloud.config.allowOverride 默认是 true
* spring.cloud.config.overrideNone 默认是 false
*
* if !allowOverride || (!overrideNone && overrideSystemProperties)
* 通过 PropertySourceLocator 得到的 PropertySource 会添加到最前面,也就是优先生效
* else if overrideNone
* 通过 PropertySourceLocator 得到的 PropertySource 会添加到最后面,也就是兜底生效
* else if !overrideSystemProperties
* 通过 PropertySourceLocator 得到的 PropertySource 会放在 systemEnvironment 的后面
* else if overrideSystemProperties
* 通过 PropertySourceLocator 得到的 PropertySource 会放在 systemEnvironment 的前面
* else
* 通过 PropertySourceLocator 得到的 PropertySource 会添加到最后面,也就是兜底生效
*
* 注:也就是可以通过这三个属性值,决定最终 Environment 属性的读取顺序
*
* */@RefreshScope 和 @ConfigurationProperties bean 的更新
示例代码
@SpringBootApplication
public class Main {
/**
* 总结用法:
*
* 可以通过属性 spring.cloud.refresh.refreshable spring.cloud.refresh.extraRefreshable
* 代替 @RefreshScope
*
* 可以设置属性 spring.cloud.refresh.enabled=false 取消 @RefreshScope 的自动注入 是
* spring.cloud.refresh.never-refreshable 属性记录的类就不重会新绑定属性
*/
public static void main(String[] args) {
// TODOHAITAO: 2023/4/6 访问验证属性更新 GET http://127.0.0.1:8080/actuator/refresh
// 启用 bootstrap 属性的加载
System.setProperty("spring.cloud.bootstrap.enabled", "true");
// 通过配置属性的方式,扩展bean为 refresh scope 的
System.setProperty("spring.cloud.refresh.refreshable",
Arrays.asList(RefreshScopeBean1.class.getName(), RefreshScopeBean2.class.getName()).stream()
.collect(Collectors.joining(",")));
System.setProperty("spring.cloud.refresh.extraRefreshable",
Arrays.asList(Object.class.getName()).stream().collect(Collectors.joining(",")));
// 设置 bootstrapContext 会默认加载的 bean
System.setProperty("spring.cloud.bootstrap.sources","cn.haitaoss.RefreshScope.config.MyPropertySourceLocator");
}
}/**
* 只是列举了我觉得比较重要的,并不是全部内容
* spring-cloud-context.jar!/META-INF/spring.factories
*
* org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
* org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
* org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
* org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration
*
* org.springframework.context.ApplicationListener=\
* org.springframework.cloud.bootstrap.BootstrapApplicationListener
*
* org.springframework.cloud.bootstrap.BootstrapConfiguration=\
* org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration
* */ConfigurationPropertiesRebinderAutoConfiguration
- 记录 @ConfigurationProperties 的 bean
- 接收 EnvironmentChangeEvent 事件,对记录的 bean 进行重新初始化从而完成属性的更新
/**
* ConfigurationPropertiesRebinderAutoConfiguration
* 注册 ConfigurationPropertiesBeans :
* - 是 BeanPostProcessor 其作用是记录有 @ConfigurationProperties注解 标注的bean
*
* 注册 ConfigurationPropertiesRebinder :
* - 实现了 ApplicationListener<EnvironmentChangeEvent> 接口,还依赖 ConfigurationPropertiesBeans。
* 收到事件的处理逻辑是,遍历 ConfigurationPropertiesBeans 记录的 bean 对其进行重新绑定。其实就是 回调bean的销毁方法,然后对bean重新初始化而已,
* 而不是直接从BeanFactory中删除bean。
*
* 注:
* 1. 会过滤掉是 refresh scope 的bean,因为 refresh 作用域的bean由其他类进行刷新
* 2. 是 spring.cloud.refresh.never-refreshable 属性记录的类就不要重新绑定属性
*
* 实现了 SmartInitializingSingleton ,会在 单例bean实例化完后被回调,其回调方法的逻辑是使用 ConfigurationPropertiesRebinder 对父容器中的bean进行重新绑定
*
* */RefreshAutoConfiguration
- 注册 refresh scope 到 BeanFactory 中
- 接收 RefreshEvent 事件,更新 Environment 和 清空 refresh scope 中记录的 bean
- 使用 @RefreshScope 标注的 bean,最终生成的是代理对象,每次执行代理对象的方法,都会从 refresh scope 中获取 bean 得到调用方法的对象,从而能保证更新之后,获取的对象也是新的
/* Detailed source-code truncated for AI context efficiency. */RefreshEndpointAutoConfiguration
/**
* RefreshEndpointAutoConfiguration
* 注册 RefreshEndpoint :
* 只定义了一个 @WriteOperation , 该方法的逻辑是会执行 ContextRefresher#refresh 方法,从而 更新 Environment,刷新 refresh 作用域
* POST http://localhost:8080/actuator/refresh 可以触发该操作
*
* 注:得导入 spring-boot-starters-actuator 才会生效
*
* ...还注册了很多 bean , 暂时不看了 , 用到再看吧
* */@EnableDiscoveryClient
/**
* {@link EnableDiscoveryClient}
* @EnableDiscoveryClient(autoRegister=true)
* 1. 的目的是将 META-INF/spring.factories 中 key 是 `EnableDiscoveryClient.class.getName()` 的类注册到 BeanFactory 中
* 2. 若 autoRegister==true , 还会注册 AutoServiceRegistrationConfiguration 到 BeanFactory 中,
* 这个类的目的很简单,就是注册 AutoServiceRegistrationProperties 到BeanFactory中
* 3. 若 autoRegister==false, 会设置属性 spring.cloud.service-registry.auto-registration.enabled 为 false,
* 这个为 false 就会导致 AutoServiceRegistrationAutoConfiguration 的 @conditional注解不匹配,从而不会生效
*
* 总结:注册 AutoServiceRegistrationProperties 和 META-INF/spring.factories 中的类 到容器中
*
*
* 其实 @EnableDiscoveryClient 没啥用了,因为 spring-cloud-commons.jar!/META-INF/spring.factories 中声明了
* org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
* org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration
* 也就是 AutoServiceRegistrationAutoConfiguration 会自动注入。但是需要 DiscoveryClient 的bean能被发现,
* 必须得自己注册到BeanFactory中,不能使用 META-INF/spring.factories 设置 key 是 `EnableDiscoveryClient.class.getName()`
*
* {@link AutoServiceRegistrationAutoConfiguration}
* 通过 @PostConstruct 的方式让校验方法被调用,是用来校验 AutoServiceRegistration 类型的bean是否存在,但是默认是不会报错的
* 可以设置 spring.cloud.service-registry.auto-registration.failFast=true 的方式,启用校验。
*
* 总结:@EnableDiscoveryClient 的核心功能是可以将声明在 META-INF/spring.factories 中的类注册到容器中(key是 `EnableDiscoveryClient.class.getName()`)
* */@LoadBalanced
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface LoadBalanced {}/**
* @LoadBalanced 的作用
* 1. 其本质是一个 @Qualifier 。在依赖注入的过滤候选bean时会校验 @Qualifier 的值
* 2. 作为标记注解,比如 LoadBalancerWebClientBuilderBeanPostProcessor 会过滤出有 @LoadBalanced 的bean进行处理
* */public class Config {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public RestTemplate restTemplate2() {
return new RestTemplate();
}
}
public class LoadBalancerAutoConfiguration {
// 只会注入 restTemplate 不会注入 restTemplate2
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
}@LoadBalancerClient 和@LoadBalancerClients
@LoadBalancerClient 映射成 LoadBalancerClientSpecification 然后注册到 BeanFactory 中。
@LoadBalancerClients 其实就是多个 @LoadBalancerClient
@Configuration(proxyBeanMethods = false)
@Import(LoadBalancerClientConfigurationRegistrar.class)
public @interface LoadBalancerClient {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
Class<?>[] configuration() default {};
}/**
* LoadBalancerClientConfigurationRegistrar 是 ImportBeanDefinitionRegistrar 的实现类,所以IOC容器解析到 @Import(LoadBalancerClientConfigurationRegistrar.class)
* 会回调其方法
* {@link LoadBalancerClientConfigurationRegistrar#registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry)}
* 1. 获取注解的元数据信息
* Map<String, Object> client = metadata.getAnnotationAttributes(LoadBalancerClient.class.getName(), true);
*
* 2. 获取name,获取的是注解的value属性值或者是注解的name属性值
* String name = getClientName(client);
*
* 3. 映射成BeanDefinition然后注册到BeanFactory中
*
* BeanDefinitionBuilder builder = BeanDefinitionBuilder
* .genericBeanDefinition(LoadBalancerClientSpecification.class);
* builder.addConstructorArgValue(name);
* builder.addConstructorArgValue(client.get("configuration"));
* registry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition());
*
* 注:LoadBalancerClientSpecification 是构造 LoadBalancerClientFactory 依赖的bean
* */DiscoveryClient
spring-cloud-commons.jar!/META-INF/spring.factories的部分内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration,\DiscoveryClient 是用来获取注册中心注册了多少实例,单独看是没用的得结合 负载均衡的实现逻辑 才能明白。
/**
* SimpleDiscoveryClientAutoConfiguration
* 注册 SimpleDiscoveryProperties , 通过 @ConfigurationProperties 将配置文件中的信息绑定到属性中
* 注册 SimpleDiscoveryClient 是 DiscoveryClient 接口的实现类, 其依赖 SimpleDiscoveryProperties , 其职责是根据 serviceId 返回 List<ServiceInstance>
*
* CompositeDiscoveryClientAutoConfiguration
* 注册 CompositeDiscoveryClient 是 DiscoveryClient 的实现类,其作用是用来聚合 List<DiscoveryClient> 的
**/LoadBalancerAutoConfiguration
spring-cloud-commons.jar!/META-INF/spring.factories的部分内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\这个配置类主要是定义了 LoadBalancerInterceptor 用来拦截 RestTemplate 的执行,其拦截逻辑是委托给
LoadBalancerClient 来做。又定义了 LoadBalancerRequestFactory 用于生成 LoadBalancerClient 的参数,
而 LoadBalancerRequestFactory 会使用 LoadBalancerRequestTransformer 对 HttpRequest 进行增强,
所以我们可以自定义 LoadBalancerRequestTransformer 的 bean 对 负载均衡的请求 进行修改。
/**
* org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration
* 满足这个条件 @ConditionalOnBean(LoadBalancerClient.class) 配置类才会生效,而 LoadBalancerClient 唯一的实现在
* spring-cloud-loadbalancer.jar 中。LoadBalancerClient 是用来执行请求的,让请求变成负载均衡的方式
*
* 注册 SmartInitializingSingleton,其逻辑是使用 RestTemplateCustomizer 对 @LoadBalanced的RestTemplate 进行自定义
*
* 注册 LoadBalancerRequestFactory,其依赖 LoadBalancerClient 和 List<LoadBalancerRequestTransformer>
* 是用来生成 LoadBalancerRequest 的(其实就是使用 LoadBalancerRequestTransformer 对 HttpRequest 进行增强)
* `LoadBalancerRequest<ClientHttpResponse> request = requestFactory.createRequest(rawRequest, body, execution);
* LoadBalancerClient.execute(serviceName, request);`
*
* 注册 LoadBalancerInterceptor 是 ClientHttpRequestInterceptor 接口的实现类,其依赖于 LoadBalancerClient、LoadBalancerRequestFactory
* ClientHttpRequestInterceptor 是用来拦截 RestTemplate 执行请求的
*
* 注册 RestTemplateCustomizer 依赖 LoadBalancerInterceptor , 会将 LoadBalancerInterceptor 设置给 RestTemplate
* List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
* list.add(loadBalancerInterceptor);
* restTemplate.setInterceptors(list);
**/LoadBalancerClient
示例代码
@EnableAutoConfiguration
@RestController
@Import({ LoadBalancerClientConfig.class, LoadBalancerOtherConfig.class })
public class Main extends BaseApp {
public static void main(String[] args) {
/**
* TODOHAITAO: 2023/4/7 验证方式 运行 Main、Client1、Client2 然后访问:
* - 堵塞式 GET http://localhost:8080/s1
* - 响应式 GET http://localhost:8080/2/s1
*/
// 采用那种方式对 RestTemplate 进行增强,看
// org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration
System.setProperty("spring.cloud.loadbalancer.retry.enabled", "false");
System.setProperty("spring.profiles.active", "loadbalance");
ConfigurableApplicationContext context = SpringApplication.run(Main.class);
}
}负载均衡会使用 LoadBalancerClient 来执行请求的,大致逻辑是通过 DiscoveryClient 得到 serviceId 有哪些实例,再通过负载均衡策略的逻辑筛选出唯一的实例,然后根据这个实例的 url 执行请求。
spring-cloud-loadbalancer.jar!/META-INF/spring.factories的部分内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.LoadBalancerStatsAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfigurationLoadBalancerAutoConfiguration
提供了@LoadBalancerClient 用于简易的注册 LoadBalancerClientSpecification
/**
* org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration
*
* 注册 LoadBalancerZoneConfig , ZonePreferenceServiceInstanceListSupplier 会依赖这个bean来过滤出 List<ServiceInstance>
*
* 注册 LoadBalancerClientFactory 其继承 NamedContextFactory。
* 会根据 name 创建一个IOC容器,IOC容器默认有两个配置类:PropertyPlaceholderAutoConfiguration、LoadBalancerClientConfiguration
* 并依赖 LoadBalancerClientSpecification 用来扩展配置类,创建的IOC容器会缓存到Map中。
*
* LoadBalancerClientConfiguration 其目的是注册了 ServiceInstanceListSupplier、ReactorLoadBalancer<ServiceInstance>
* 这两个bean是用来实现负载均衡策略得到唯一的 ServiceInstance 的。而且都有 @ConditionalOnMissingBean 条件,若我们想自定义
* 可以设置 LoadBalancerClientSpecification 扩展配置类。
*
* 可以使用 @LoadBalancerClient 或者直接注册 LoadBalancerClientSpecification 类型的bean到容器中,
**/LoadBalancerStatsAutoConfiguration
一般来说 LoadBalancerClient 执行负载均衡请求时会 回调 LoadBalancerLifecycle 接口的方法
/**
* LoadBalancerStatsAutoConfiguration
* 注册 MicrometerStatsLoadBalancerLifecycle 其实现了 LoadBalancerLifecycle 接口。比如 BlockingLoadBalancerClient
* 执行请求时会回调 LoadBalancerLifecycle 的方法做通知
* */BlockingLoadBalancerClientAutoConfiguration
注册 LoadBalancerClient 的实现类到容器中
/**
* BlockingLoadBalancerClientAutoConfiguration
* 注册 BlockingLoadBalancerClient 其实现了LoadBalancerClient接口,依赖 LoadBalancerClientFactory 和 properties
* BlockingLoadBalancerClient 的核心逻辑是接收 HttpRequest 解析 Uri 得到 serviceId ,然后使用 serviceId 负载均衡得到唯一的 serviceInstance
* 然后再执行 HttpRequest
*
* */负载均衡的 RestTemplate 执行请求的流程
使用 restTemplate 发送请求,最终会委托给 ClientHttpRequestInterceptor 执行请求
# 大致是这么个流程,可以细看下面的代码分析
RestTemplate -> (RetryLoadBalancerInterceptor|LoadBalancerInterceptor)
-> LoadBalancerClientFactory -> BlockingLoadBalancerClient
-> ReactorLoadBalancer -> ServiceInstanceListSupplier -> DiscoveryClient
-> LoadBalancerRequestTransformer
-> 执行请求/**
* 例如:
* restTemplate.getForEntity("http://serviceName/xx", String.class)
*
* {@link RestTemplate#getForEntity(String, Class, Object...)}
* {@link RestTemplate#execute(String, HttpMethod, RequestCallback, ResponseExtractor, Object...)}
* {@link RestTemplate#doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor)}
* {@link AbstractClientHttpRequest#execute()}
* {@link AbstractBufferingClientHttpRequest#executeInternal(HttpHeaders)}
* {@link InterceptingClientHttpRequest#executeInternal(HttpHeaders, byte[])}
* {@link InterceptingClientHttpRequest.InterceptingRequestExecution#execute(HttpRequest, byte[])}
* 在这里回调 ClientHttpRequestInterceptor 的方法,从而实现拦截请求的执行
**/public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
// 委托给迭代器执行逻辑。比如 LoadBalancerInterceptor 其实就是修改 request 然后又递归回调该方法
return nextInterceptor.intercept(request, body, this);
}
else {
HttpMethod method = request.getMethod();
Assert.state(method != null, "No standard HTTP method");
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
// 拷贝请求头内容
request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
// 执行请求
return delegate.execute();
}
}LoadBalancerInterceptor
默认注入的是 RetryLoadBalancerInterceptor 而不是 LoadBalancerInterceptor。可以设置 spring.cloud.loadbalancer.retry.enabled=false 让 LoadBalancerInterceptor 生效
/**
* LoadBalancerInterceptor 的执行逻辑
* {@link LoadBalancerInterceptor#intercept(HttpRequest, byte[], ClientHttpRequestExecution)}
* 1. 获取serviceName
* final URI originalUri = request.getURI();
* String serviceName = originalUri.getHost();
*
* 2. 使用 LoadBalancerRequestFactory 构造出 LoadBalancerRequest,构造逻辑其实就是使用 LoadBalancerRequestTransformer 对 HttpRequest 进行增强
* LoadBalancerRequest lbRequest = requestFactory.createRequest(request, body, execution);
*
* 3. 委托给 LoadBalancerClient 执行请求
* loadBalancerClient.execute(serviceName, lbRequest)
* 默认是这个实现类的方法 {@link BlockingLoadBalancerClient#execute(String, LoadBalancerRequest)}
*
**/RetryLoadBalancerInterceptor
整体逻辑和 LoadBalancerInterceptor 是一样的,只不过是使用 RetryTemplate 来执行,根据重试策略重复执行而已。
会使用 LoadBalancedRetryFactory 来生成 LoadBalancedRetryPolicy、BackOffPolicy、RetryListener 这三个东西是用来决定该如何重试,默认是有一个 BlockingLoadBalancedRetryPolicy 会根据属性信息生成 LoadBalancedRetryPolicy、BackOffPolicy,若我们有需要可以自定义 LoadBalancedRetryFactory bean 注册到容器中,因为
@Configuration
public class BlockingLoadBalancerRetryConfig {
@Bean
@ConditionalOnMissingBean
LoadBalancedRetryFactory loadBalancedRetryFactory(LoadBalancerProperties properties) {
return new BlockingLoadBalancedRetryFactory(properties);
}
}# 指示应在同一ServiceInstance 上重试请求的次数(对每个选定实例单独计数)
spring.cloud.loadbalancer.retry.maxRetriesOnSameServiceInstance=1
# 指示新选择的 ServiceInstance 应重试请求的次数
spring.cloud.loadbalancer.retry.maxRetriesOnNextServiceInstance=1
# 总是重试失败请求的状态代码
spring.cloud.loadbalancer.retry.retryableStatusCodes=1
# 设置最小回退持续时间(默认为5毫秒)
spring.cloud.loadbalancer.retry.backoff.minBackoff=1
# 设置最大回退持续时间(默认为最大长值毫秒)
spring.cloud.loadbalancer.retry.backoff.maxBackoff=1
# 设置用于计算每个调用的实际回退持续时间的抖动(默认为0.5)
spring.cloud.loadbalancer.retry.backoff.jitter=1/* Detailed source-code truncated for AI context efficiency. */BlockingLoadBalancerClient#execute
- 执行请求过程中会回调 LoadBalancerLifecycle 生命周期方法
- 负载均衡得到 serviceInstance 构造出 HttpRequest 后,会使用 LoadBalancerRequestTransformer 对 HttpRequest 进行增强
text注:不建议将 LoadBalancerLifecycle、ServiceInstanceListSupplier、ReactorLoadBalancer 注册到应用程序中,而是通过 LoadBalancerClientSpecification 的方式为每一个 serviceInstance 设置独立的bean,从而实现不同的 serviceInstance 使用不同的 负载均衡策略
/* Detailed source-code truncated for AI context efficiency. */ReactorLoadBalancer
示例代码
@LoadBalancerClient(name = "s1", configuration = { MyLoadBalancer.class, MyServiceInstanceListSupplier.class })
@LoadBalancerClients({ @LoadBalancerClient(name = "s2", configuration = MyRandomLoadBalancer.class),
@LoadBalancerClient(name = "s3", configuration = MyRoundRobinLoadBalancer.class), })
public class LoadBalancerClientConfig {
}/**
* ReactorLoadBalancer 是一个负载均衡器,会根据其负载均衡逻辑从 ServiceInstanceListSupplier 返回的 List<ServiceInstance> 中筛选出唯一的 ServiceInstance。
*
* 再执行负载均衡请求时会用到 ReactorLoadBalancer
* {@link BlockingLoadBalancerClient#execute(String, LoadBalancerRequest)}
* ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
*
* 而 LoadBalancerClientFactory 继承 NamedContextFactory,获取实例的特点是 每个serviceId对应一个IOC容器,实例是从对应的IOC容器中得到的,
* LoadBalancerClientFactory 构造的IOC容器默认会注册配置类 LoadBalancerClientConfiguration,
* 而 LoadBalancerClientConfiguration 其目的是注册了 ServiceInstanceListSupplier、ReactorLoadBalancer<ServiceInstance>
* 而且都有 @ConditionalOnMissingBean 条件,若我们想自定义可以给IOC容器设置配置类,从而让 @ConditionalOnMissingBean 不匹配。
* 可以使用 @LoadBalancerClient 或者直接注册 LoadBalancerClientSpecification 类型的bean到容器中。
* */public interface ReactorLoadBalancer<T> extends ReactiveLoadBalancer<T> {
/**
* Choose the next server based on the load balancing algorithm.
* @param request - an input request
* @return - mono of response
*/
@SuppressWarnings("rawtypes")
Mono<Response<T>> choose(Request request);
default Mono<Response<T>> choose() {
return choose(REQUEST);
}
}// 这只是伪代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {
@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RoundRobinLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
@Bean
@ConditionalOnMissingBean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withCaching().build(context);
}
}ServiceInstanceListSupplier
示例代码
public class MyServiceInstanceListSupplier {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
//.withDiscoveryClient() // 通过 ReactiveDiscoveryClient 获取 List<ServiceInstance>
.withBlockingDiscoveryClient() // 通过 DiscoveryClient 获取 List<ServiceInstance>
// 下面配置的是通过什么方式 过滤 List<ServiceInstance>
// .withZonePreference() // spring.cloud.loadbalancer.zone" 属性值与 serviceInstance.getMetadata().get("zone") 进行匹配
// .withBlockingHealthChecks() // spring.cloud.loadbalancer.healthCheck.* 属性定义的的规则来过滤
// .withRequestBasedStickySession() spring.cloud.loadbalancer.stickySession.instanceIdCookieName 属性值过滤 serviceInstance.getInstanceId()
// .withSameInstancePreference()
.withCaching() // 会使用到 LoadBalancerCacheManager 缓存 List<ServiceInstance>
.build(context);
}
}/**
* ServiceInstanceListSupplier 是用来返回 List<ServiceInstance> 的。一般是根据 ReactiveDiscoveryClient 或者 DiscoveryClient 得到 serviceId 注册的 List<ServiceInstance>,
* 然后根据其逻辑过滤掉不满足的,再返回最终的 List<ServiceInstance>
*
* 提供了Builder快速构建
* 响应式(获取 ReactiveDiscoveryClient 的bean得到有哪些服务):ServiceInstanceListSupplier.builder().withDiscoveryClient().build(context);
* 非响应式(获取 DiscoveryClient 的bean得到有哪些服务):ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().build(context);
*
*
* 这个 LoadBalancerClientConfiguration 配置类,定义了很多种 ServiceInstanceListSupplier。可以通过设置属性值决定应用哪种
* spring.cloud.loadbalancer.configurations=[default | zone-preference | health-check | request-based-sticky-session | same-instance-preference]
* */public interface ServiceInstanceListSupplier extends Supplier<Flux<List<ServiceInstance>>> {
String getServiceId();
default Flux<List<ServiceInstance>> get(Request request) {
return get();
}
static ServiceInstanceListSupplierBuilder builder() {
return new ServiceInstanceListSupplierBuilder();
}
}WebClient.Builder 实现负载均衡
WebClient.Builder 是执行响应式请求的工具类。下面是让 WebClient.Builder 具有负载均衡能力的实现逻辑。
spring-cloud-commons.jar!/META-INF/spring.factories的部分内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerClientAutoConfiguration/**
* {@link LoadBalancerBeanPostProcessorAutoConfiguration}
* 注册 LoadBalancerWebClientBuilderBeanPostProcessor 其依赖 DeferringLoadBalancerExchangeFilterFunction。
* 其是一个后置处理器,会在 postProcessBeforeInitialization 时过滤类型是 WebClient.Builder 且有 @LoadBalanced 的bean ,为 bean 增加filter
* `((WebClient.Builder) bean).filter(DeferringLoadBalancerExchangeFilterFunction)`
*
* 注册 DeferringLoadBalancerExchangeFilterFunction 其依赖 LoadBalancedExchangeFilterFunction,没啥用具体逻辑是委托给 LoadBalancedExchangeFilterFunction 执行的。
*
* {@link ReactorLoadBalancerClientAutoConfiguration}
* 注册 ReactorLoadBalancerExchangeFilterFunction 其依赖 ReactiveLoadBalancer.Factory、LoadBalancerProperties。
* 其定义了负载均衡的实现逻辑,比如要回调 LoadBalancerLifecycle 的方法,要通过 ReactiveLoadBalancer.Factory 负载均衡得到 ServiceInstance
*
* Tips:LoadBalancerClientFactory 是 ReactiveLoadBalancer.Factory 的实现类。
*
* 注册 RetryableLoadBalancerExchangeFilterFunction 其依赖 ReactiveLoadBalancer.Factory、LoadBalancerProperties、LoadBalancerRetryPolicy。
* 大致逻辑和 ReactorLoadBalancerExchangeFilterFunction 一样,只不过增加了重试的实现
*
* 注册 LoadBalancerRetryPolicy 其依赖 LoadBalancerProperties。
* 就是重试策略,没啥特别的
*
*
* 总结:通过 LoadBalancerWebClientBuilderBeanPostProcessor 给 WebClient.Builder 增加 Filter,所以使用 WebClient.Builder 执行请求时会执行 Filter 的逻辑。
* DeferringLoadBalancerExchangeFilterFunction 的逻辑是 回调 LoadBalancerLifecycle 的方法,使用 LoadBalancerClientFactory 生成的负载均衡器得到唯一的 ServiceInstance,
* 根据 ServiceInstance 的信息 修改请求的信息,从而实现负载均衡请求。
*
* WebClient.Builder -> ReactorLoadBalancerExchangeFilterFunction -> LoadBalancerLifecycle -> LoadBalancerClientFactory
* */File: docs/SpringCloud/spring-cloud-gateway-source-note.md
说明
Author: haitaoss
源码阅读仓库: spring-cloud-gateway
参考资料和需要掌握的知识:
Spring Cloud Gateway 介绍
功能:接收请求并根据匹配的路由进行转发。
术语:
- Route: 是路由规则的描述。它由 ID、目标 URI、Predicate 集合和Filter 集合组成。如果 Predicate 为真,则路由匹配。
- Predicate: 这是一个 Java 8 函数接口。输入类型是
ServerWebExchange,所以可以匹配 HTTP 请求中的任何内容,例如 Header 或参数。 - Filter: 这些是使用特定工厂构建的
GatewayFilter的实例。使用这个可以在发送下游请求之前或之后修改请求和响应。
Spring Cloud Gateway 是基于 Spring WebFlux 实现的,是通过注册 WebFlux 的生命周期组件实现控制请求执行。
# Spring WebFlux 处理请求的生命周期
客户端请求 -> WebFlux服务 -> WebFilter -> DispatcherHandler -> HandlerMapping -> HandlerAdapter -> 执行Handler方法Gateway 通过注册 RoutePredicateHandlerMapping 实现核心逻辑
Gateway 自动装配
spring-cloud-gateway-server.jar!META-INF/spring.factories的部分内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayResilience4JCircuitBreakerAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,\
org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration,\
org.springframework.cloud.gateway.config.SimpleUrlHandlerMappingGlobalCorsAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayReactiveOAuth2AutoConfigurationspring-cloud-gateway-webflux.jar!META-INF/spring.factories的内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.webflux.config.ProxyResponseAutoConfiguration
org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebFlux=\
org.springframework.cloud.gateway.webflux.config.ProxyResponseAutoConfigurationspring-cloud-gateway-mvc.jar!META-INF/spring.factories的内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.mvc.config.ProxyResponseAutoConfiguration
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc=\
org.springframework.cloud.gateway.mvc.config.ProxyResponseAutoConfigurationGatewayClassPathWarningAutoConfiguration
作用:检验启动环境不能是 SpringMVC
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
public class GatewayClassPathWarningAutoConfiguration {
@Configuration(proxyBeanMethods = false)
// SpringMVC 会存在这个类,所以条件会满足,这个类就会注册到BeanFactory中
@ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
protected static class SpringMvcFoundOnClasspathConfiguration {
public SpringMvcFoundOnClasspathConfiguration() {
// 实例化就直接抛出异常
throw new MvcFoundOnClasspathException();
}
}
}GatewayAutoConfiguration
类图
RouteLocator 是为了得到
Flux<Route>,可以使用 RouteLocatorBuilder 很方便的生成 RouteLocator。RouteDefinitionRouteLocator 是会根据 RouteDefinition 生成 Route ,而 RouteDefinition 是由 RouteDefinitionLocator 生成的。
Route 是由 AsyncPredicate 和 GatewayFilter 组成的。而 AsyncPredicate 由 RoutePredicateFactory 生成,GatewayF 创建 ilter 由 GatewayFilterFactory
RoutePredicateHandlerMapping 通过 RouteLocator 得到的
Flux<Route>,遍历执行Route.getPredicate().apply(ServerWebExchange)返回true说明命中了路由规则,将命中的 Route 存到 ServerWebExchange 中,然后执行 FilteringWebHandler 。FilteringWebHandler 的逻辑就是执行 GlobalFilter + GatewayFilter
源码
可以自定义这些类型的 bean 实现功能的扩展:RouteLocator、HttpHeaderFilter、GlobalFilter 、GatewayFilterFactory、RoutePredicateFactory
默认通过 @Bean 注册了很多的 GlobalFilter、GatewayFilterFactory、RoutePredicateFactory 且都是有条件注解的,可以通过设置属性不进行默认注册。主要是有这三个条件注解
/* Detailed source-code truncated for AI context efficiency. */GatewayResilience4JCircuitBreakerAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@ConditionalOnClass({ DispatcherHandler.class, ReactiveResilience4JAutoConfiguration.class,
ReactiveCircuitBreakerFactory.class, ReactiveResilience4JCircuitBreakerFactory.class })
public class GatewayResilience4JCircuitBreakerAutoConfiguration {
/**
* SpringCloudCircuitBreakerResilience4JFilterFactory 实现 GatewayFilterFactory 接口,
* 其核心逻辑是使用 ReactiveCircuitBreaker 来执行业务逻辑,当 出现异常 或者 路由请求返回的状态码是期望值 就
* 直接使用 DispatcherHandler 来执行 fallbackUrl,可以理解成使用 fallbackUrl 重新执行一次请求。
* 并且会往 ServerWebExchange 设置一个key记录异常对象。
*/
@Bean
@ConditionalOnBean(ReactiveResilience4JCircuitBreakerFactory.class)
@ConditionalOnEnabledFilter
public SpringCloudCircuitBreakerResilience4JFilterFactory springCloudCircuitBreakerResilience4JFilterFactory(
ReactiveResilience4JCircuitBreakerFactory reactiveCircuitBreakerFactory,
ObjectProvider<DispatcherHandler> dispatcherHandler) {
return new SpringCloudCircuitBreakerResilience4JFilterFactory(reactiveCircuitBreakerFactory, dispatcherHandler);
}
/**
* FallbackHeadersGatewayFilterFactory 实现 GatewayFilterFactory 接口,
* 其核心逻辑:如果请求是 fallbackUrl 执行的(通过异常key判断),那就设置一些请求头
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledFilter
public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() {
return new FallbackHeadersGatewayFilterFactory();
}
}GatewayNoLoadBalancerClientAutoConfiguration
/* Detailed source-code truncated for AI context efficiency. */GatewayMetricsAutoConfiguration
/* Detailed source-code truncated for AI context efficiency. */GatewayRedisAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(ReactiveRedisTemplate.class)
@ConditionalOnClass({ RedisTemplate.class, DispatcherHandler.class })
@ConditionalOnProperty(name = "spring.cloud.gateway.redis.enabled", matchIfMissing = true)
class GatewayRedisAutoConfiguration {
/**
* RedisRouteDefinitionRepository 实现 RouteDefinitionRepository 接口。
* 使用 redis 作为缓存层,存储 RouteDefinition
*/
@Bean
@ConditionalOnProperty(value = "spring.cloud.gateway.redis-route-definition-repository.enabled",
havingValue = "true")
@ConditionalOnClass(ReactiveRedisTemplate.class)
public RedisRouteDefinitionRepository redisRouteDefinitionRepository(
ReactiveRedisTemplate<String, RouteDefinition> reactiveRedisTemplate) {
return new RedisRouteDefinitionRepository(reactiveRedisTemplate);
}
// ...
}GatewayDiscoveryClientAutoConfiguration
/* Detailed source-code truncated for AI context efficiency. */SimpleUrlHandlerMappingGlobalCorsAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SimpleUrlHandlerMapping.class)
@ConditionalOnProperty(name = "spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping",
matchIfMissing = false)
public class SimpleUrlHandlerMappingGlobalCorsAutoConfiguration {
@Autowired
private GlobalCorsProperties globalCorsProperties;
@Autowired
private SimpleUrlHandlerMapping simpleUrlHandlerMapping;
/**
* 为 SimpleUrlHandlerMapping 配置 跨域配置信息
*/
@PostConstruct
void config() {
simpleUrlHandlerMapping.setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
}
}GatewayReactiveLoadBalancerClientAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ ReactiveLoadBalancer.class, LoadBalancerAutoConfiguration.class, DispatcherHandler.class })
@EnableConfigurationProperties(GatewayLoadBalancerProperties.class)
public class GatewayReactiveLoadBalancerClientAutoConfiguration {
/**
* ReactiveLoadBalancerClientFilter 实现 GlobalFilter 接口。
* 作用:url 没有 lb 协议 就放行,有 lb 就使用 LoadBalancerClientFactory 得到负载均衡后的 uri,修改 ServerWebExchange 放行filter
*/
@Bean
@ConditionalOnBean(LoadBalancerClientFactory.class)
@ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class)
@ConditionalOnEnabledGlobalFilter
public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory,
GatewayLoadBalancerProperties properties) {
return new ReactiveLoadBalancerClientFilter(clientFactory, properties);
}
/**
* LoadBalancerServiceInstanceCookieFilter 实现 GlobalFilter 接口
* 作用:是负载均衡的路由,就添加一个Cookie键值对
*/
@Bean
@ConditionalOnBean({ ReactiveLoadBalancerClientFilter.class, LoadBalancerClientFactory.class })
@ConditionalOnMissingBean
@ConditionalOnEnabledGlobalFilter
public LoadBalancerServiceInstanceCookieFilter loadBalancerServiceInstanceCookieFilter(
LoadBalancerClientFactory loadBalancerClientFactory) {
return new LoadBalancerServiceInstanceCookieFilter(loadBalancerClientFactory);
}
}GatewayReactiveOAuth2AutoConfiguration
这是 Spring Security 的组件,没研究过,不知道具体的作用是啥,暂时不管了。
ProxyResponseAutoConfiguration
ProxyResponseAutoConfiguration 是 HandlerMethodArgumentResolver 接口的实现类,是用来解析方法参数的。它支持解析 ProxyExchange 类型的参数。ProxyExchange 可用来执行 Http 请求,感觉好鸡肋...
又因为 SpringWebFlux 和 SpringMVC 的执行流程是类似的,定义的类名也是一样的(包不同),所以就搞了两套。
注:HandlerMethodArgumentResolver 是 SpringMVC 、SpringWebFlux 的内容,不细说了。
核心源码
@ConditionalOnEnabledGlobalFilter、@ConditionalOnEnabledFilter、@ConditionalOnEnabledPredicate
类的定义
@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE, ElementType.METHOD })
@Conditional(OnEnabledGlobalFilter.class)
public @interface ConditionalOnEnabledGlobalFilter {
Class<? extends GlobalFilter> value() default OnEnabledGlobalFilter.DefaultValue.class;
}
@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE, ElementType.METHOD })
@Conditional(OnEnabledFilter.class)
public @interface ConditionalOnEnabledFilter {
Class<? extends GatewayFilterFactory<?>> value() default OnEnabledFilter.DefaultValue.class;
}
@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE, ElementType.METHOD })
@Conditional(OnEnabledPredicate.class)
public @interface ConditionalOnEnabledPredicate {
Class<? extends RoutePredicateFactory<?>> value() default OnEnabledPredicate.DefaultValue.class;
}因为 @ConditionalOnEnabledGlobalFilter 上标注了 @Conditional,所以在 ConfigurationClassPostProcessor 解析配置类时,会执行 OnEnabledGlobalFilter#matches(ConditionContext,AnnotatedTypeMetadata) 结果是true才会将 bean 注册到 BeanFactory 中
/**
* {@link Condition#matches(ConditionContext, AnnotatedTypeMetadata)}
* {@link SpringBootCondition#matches(ConditionContext, AnnotatedTypeMetadata)}
* {@link OnEnabledComponent#getMatchOutcome(ConditionContext, AnnotatedTypeMetadata)}
* 1. 拿到类型。若注解的 value 不是默认值就返回value值,否则就拿到方法的返回值类型。
* Class<? extends T> candidate = getComponentType(annotationClass(), context, metadata);
* 2. 确定匹配结果。前缀 + 类处理后的值 + 后缀 作为key,从 Environment 获取值,值是false则不匹配,否则匹配
* determineOutcome(candidate, context.getEnvironment())
*
* Tips: OnEnabledComponent 定义了三个抽象方法,由子类决定返回值是啥
* normalizeComponentName() 得到 类处理后的值
* annotationClass() 得到 注解
* defaultValueClass() 得到 默认值
* */可以通过这种方式让默认注入的失效。
spring.cloud.gateway.global-filter.XX.enabled=false
spring.cloud.gateway.filter.XX.enabled=false
spring.cloud.gateway.predicate.XX.enabled=falseNettyRoutingFilter
是非常重要的 GlobalFilter。Gateway 是通过它执行 http、https 协议的请求,依赖 HttpClient 执行请求。
public class NettyRoutingFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return Integer.MAX_VALUE; // 最大值,说明这是最后要执行的 GatewayFilter
}
@Override
@SuppressWarnings("Duplicates")
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
String scheme = requestUrl.getScheme();
// 已经路由 或者 不是 http、https 就放行
if (isAlreadyRouted(exchange) || (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme))) {
return chain.filter(exchange);
}
// 设置一个属性,标记 已经路由了
setAlreadyRouted(exchange);
/**
* 遍历执行所有的 HttpHeadersFilter 得到 HttpHeaders。
* 也就是说可以对最终要执行的 请求头 进行加工
*
* 注:HttpHeadersFilter 是从BeanFactory中获取的,所以我们可以自定义 HttpHeadersFilter 达到扩展的目的
* */
HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);
// 根据 Route 的元数据构造 HttpClient 然后执行请求
Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange)
.headers()
.responseConnection((res, connection) -> {});
// 放行
return responseFlux.then(chain.filter(exchange));
}
}WebsocketRoutingFilter
是非常重要的 GlobalFilter。Gateway 是通过它执行 ws、wss 协议的请求,依赖 WebSocketService 执行请求。
public class WebsocketRoutingFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
// 在 NettyRoutingFilter 之前执行
return Integer.MAX_VALUE - 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 有请求头 upgrade=WebSocket ,那就将 http、https 转成 ws、wss 协议
changeSchemeIfIsWebSocketUpgrade(exchange);
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
String scheme = requestUrl.getScheme();
// 已经路由过 或者 不是 ws、wss 协议 就放行
if (isAlreadyRouted(exchange) || (!"ws".equals(scheme) && !"wss".equals(scheme))) {
return chain.filter(exchange);
}
// 标记路由了
setAlreadyRouted(exchange);
HttpHeaders headers = exchange.getRequest().getHeaders();
/**
* 遍历执行所有的 HttpHeadersFilter 得到 HttpHeaders。
* 也就是说可以对最终要执行的 请求头 进行加工
*
* 注:HttpHeadersFilter 是从BeanFactory中获取的,所以我们可以自定义 HttpHeadersFilter 达到扩展的目的
* */
HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);
List<String> protocols = getProtocols(headers);
// 使用 webSocketService 执行请求。且不在执行后续的filter
return this.webSocketService.handleRequest(exchange,
new ProxyWebSocketHandler(requestUrl, this.webSocketClient, filtered, protocols));
}
}RoutePredicateHandlerMapping
RoutePredicateHandlerMapping 是由 GatewayAutoConfiguration 注册的。
大致流程:由 RouteLocator 得到 Flux<Route> ,遍历执行 Route.getPredicate().apply(exchange) == true
就将 Route 存到 exchange 中然后返回 FilteringWebHandler,最后会由 HandlerAdapter 执行 FilteringWebHandler 。
/* Detailed source-code truncated for AI context efficiency. */RouteLocator
RouteLocator 是由 GatewayAutoConfiguration 注册的,因为标注了 @Primary,所以 RoutePredicateHandlerMapping 通过依赖注入拿到的是 CachingRouteLocator。
CachingRouteLocator 是对 CompositeRouteLocator 的代理。功能:
- 使用 ConcurrentHashMap 缓存结果。
- 它实现
ApplicationListener<RefreshRoutesEvent>接口,接收事件的处理逻辑是 更新缓存结果,然后发布 RefreshRoutesResultEvent 事件
CompositeRouteLocator 是用来聚合容器中所有的 RouteLocator 的,默认的 RouteLocator 是 RouteDefinitionRouteLocator
Tips:若想通过编码的方式生成 RouteLocator 可以使用 RouteLocatorBuilder。
public class CachingRouteLocator
implements Ordered, RouteLocator, ApplicationListener<RefreshRoutesEvent>, ApplicationEventPublisherAware {
private final RouteLocator delegate;
private final Map<String, List> cache = new ConcurrentHashMap<>();
private ApplicationEventPublisher applicationEventPublisher;
public CachingRouteLocator(RouteLocator delegate) {
// 是这个 CompositeRouteLocator
this.delegate = delegate;
// 使用 ConcurrentHashMap 缓存 Route,缓存中没有就执行 fetch 方法得到
routes = CacheFlux.lookup(cache, CACHE_KEY, Route.class).onCacheMissResume(this::fetch);
}
private Flux<Route> fetch() {
// 通过 delegate 得到,然后再对 Route 进行排序
return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
}
@Override
public Flux<Route> getRoutes() {
return this.routes;
}
public Flux<Route> refresh() {
// 清空缓存
this.cache.clear();
return this.routes;
}
@Override
public void onApplicationEvent(RefreshRoutesEvent event) {
// 收到事件,就执行 fetch() 也就是会会重新解析得到 List<Route>
fetch().collect(Collectors.toList()).subscribe(
list -> Flux.fromIterable(list).materialize().collect(Collectors.toList()).subscribe(signals -> {
// 发布 RefreshRoutesResultEvent 事件
applicationEventPublisher.publishEvent(new RefreshRoutesResultEvent(this));
// 重新设置缓存内容
cache.put(CACHE_KEY, signals);
}, this::handleRefreshError), this::handleRefreshError);
}
}RouteDefinitionRouteLocator
RouteDefinitionRouteLocator 是由 GatewayAutoConfiguration 注册的。它依赖 RouteDefinitionLocator 得到 Flux<RouteDefinition>。根据 RouteDefinition 记录的 PredicateDefinition 的 name 得到 RoutePredicateFactory<Config>, 使用 ConfigurationService 用来 实例化、属性绑定、属性校验得到泛型 Config 的实例对象,最后 RoutePredicateFactory 根据 Config 生成 AsyncPredicate。
根据 RouteDefinition 记录的 FilterDefinition 的 name 得到 GatewayFilterFactory<Config>, 使用 ConfigurationService 用来 实例化、属性绑定、属性校验得到泛型 Config 的实例对象,最后 GatewayFilterFactory 根据 Config 生成 GatewayFilter。
然后使用 AsyncPredicate、GatewayFilter 构造出 Route 实例
/* Detailed source-code truncated for AI context efficiency. */RouteDefinitionLocator
RouteDefinitionLocator 是由 GatewayAutoConfiguration 注册的,因为标注了 @Primary,所以 RouteDefinitionRouteLocator 通过依赖注入拿到的是 CompositeRouteDefinitionLocator。
CompositeRouteDefinitionLocator 的作用是聚合容器中所有的 RouteDefinitionLocator。默认是注册了 PropertiesRouteDefinitionLocator 和 InMemoryRouteDefinitionRepository
PropertiesRouteDefinitionLocator
PropertiesRouteDefinitionLocator 是由 GatewayAutoConfiguration 注册的。
public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
public PropertiesRouteDefinitionLocator(GatewayProperties properties) {
/**
* 依赖注入得到的
*
* GatewayProperties 标注了 @ConfigurationProperties("spring.cloud.gateway")
* 所以会通过属性绑定设置值
* */
this.properties = properties;
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
// 直接返回 properties.getRoutes()
return Flux.fromIterable(this.properties.getRoutes());
}
}看 PredicateDefinition、FilterDefinition 的构造器,就能明白属性文件为啥可以写 Weight=group1,8
InMemoryRouteDefinitionRepository
InMemoryRouteDefinitionRepository 是由 GatewayAutoConfiguration 注册的。它主要是实现了 RouteDefinitionRepository 接口,而 RouteDefinitionRepository 继承 RouteDefinitionLocator,RouteDefinitionWriter 接口。
RouteDefinitionRepository 的职责是通过缓存的方式记录 RouteDefinition,而不是通过属性 映射成 RouteDefinition。而 AbstractGatewayControllerEndpoint 会依赖 RouteDefinitionWriter 的实例,用来缓存通过接口方式注册的 RouteDefinition。
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
// 线程安全的
private final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
/**
* Gateway Endpoint 会依赖 RouteDefinitionRepository 类型的bean 记录通过 Endpoint 动态添加的 RouteDefinition
*
* 源码在这里
* {@link AbstractGatewayControllerEndpoint#save(String, RouteDefinition)}
* */
return route.flatMap(r -> {
if (ObjectUtils.isEmpty(r.getId())) {
return Mono.error(new IllegalArgumentException("id may not be empty"));
}
// 存到缓存中
routes.put(r.getId(), r);
return Mono.empty();
});
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id -> {
if (routes.containsKey(id)) {
// 从缓存中移除
routes.remove(id);
return Mono.empty();
}
return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId)));
});
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
// 返回缓存中的值
Map<String, RouteDefinition> routesSafeCopy = new LinkedHashMap<>(routes);
return Flux.fromIterable(routesSafeCopy.values());
}
}AbstractGatewayControllerEndpoint
GatewayControllerEndpoint 和 GatewayLegacyControllerEndpoint 是由 GatewayAutoConfiguration 注册的,默认是注册 GatewayControllerEndpoint ,可以设置属性spring.cloud.gateway.actuator.verbose.enabled=false 变成让 GatewayLegacyControllerEndpoint 生效。
主要是提供这些功能:查看 RouteDefinitions、Routes、GlobalFilters、routefilters、routepredicates、更新或者新增 RouteDefinition、刷新 RouteDefinition。
更新或新增 RouteDefinition 是依赖 RouteDefinitionRepository 进行缓存。
刷新 RouteDefinition 是会发布 RefreshRoutesEvent 事件,该事件会有 CachingRouteLocator 处理
RouteRefreshListener
RouteRefreshListener 是由 GatewayAutoConfiguration 注册的
public class RouteRefreshListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 这是 IOC 容器 refresh 的最后阶段会发布的事件
if (event instanceof ContextRefreshedEvent) {
ContextRefreshedEvent refreshedEvent = (ContextRefreshedEvent) event;
// 不是
if (!WebServerApplicationContext.hasServerNamespace(refreshedEvent.getApplicationContext(), "management")) {
/**
* 重设
*
* 其实就是发布一个 RefreshRoutesEvent 事件,
* 该事件会由 CachingRouteLocator 接收,会对 List<Route> 进行缓存
* {@link CachingRouteLocator#onApplicationEvent(RefreshRoutesEvent)}
* */
reset();
}
}
else if (event instanceof RefreshScopeRefreshedEvent || event instanceof InstanceRegisteredEvent) {
reset();
}
else if (event instanceof ParentHeartbeatEvent) {
ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
resetIfNeeded(e.getValue());
}
else if (event instanceof HeartbeatEvent) {
HeartbeatEvent e = (HeartbeatEvent) event;
resetIfNeeded(e.getValue());
}
}
private void resetIfNeeded(Object value) {
if (this.monitor.update(value)) {
reset();
}
}
private void reset() {
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
}FilteringWebHandler
FilteringWebHandler 是由 GatewayAutoConfiguration 注册的。
拿到容器中的 List<GlobaFilter> + Route.getFilters() 对 Filter 进行排序,紧接着按顺序执行所有的 GatewayFilter。这么说是不准确的,只有每个 filter 都执行chain.fiter 才会执行所有的 GatewayFilter,这其实就是责任链模式。
优先级最高(最后执行) 的 Filter 是 NettyRoutingFilter ,它是用来执行 http、https 请求的,也就是完成路由的职责。
/* Detailed source-code truncated for AI context efficiency. */2. Official Technical Reference & Guides (doocs/doocs.github.io)
File: README.md
Introduction
Hello, everyone! Welcome to the Doocs Open Source community. We are currently focusing on sharing various development-related knowledge.
See the 中文文档 for Chinese README.
Sites
Owner
Yang Libin, focus on the inner workings of web applications to build out the "server-side" of web applications.
- Wechat: YLB0109
- Email: [email protected]
- GitHub: https://github.com/yanglbme
- GitCode: https://gitcode.com/yanglbme
- Gitee: https://gitee.com/yanglbme
Projects
Doocs now has the following hot projects:
| # | Project | Description | Popularity |
|---|---|---|---|
| 1 | advanced-java | Core interview questions & answers for experienced Java(Backend) developers. | |
| 2 | leetcode | LeetCode solutions in any programming language. | |
| 3 | source-code-hunter | Source code analysis of frameworks and components. | |
| 4 | jvm | A summary of the underlying principles of Java Virtual Machine. | |
| 5 | coding-interview | A collection of interview questions for programmers. The main contents include ‘Coding Interviews: Questions, Analysis and Solutions’, ‘Beauty of Programming’, etc. | |
| 6 | md | An online wechat markdown editor. | |
| 7 | technical-books | Awesome programming books list. |
How to join
If you want to go fast, go alone. If you want to go far, go together.
I believe many of you have a deep understanding that participating in open source projects is very helpful for personal growth. If you love open source or you are curious about it, join Doocs, work with us, maintain our projects and grow together. You can join us in the following two ways:
- Comment on the relevant discussion on GitHub to express interest in joining the community.
- Send an email to [email protected] with the GitHub ID to request to join the community.
By default, your Doocs membership will be hidden from view aftering joining us. If you'd like to display the organization icon in your profile, you can switch from "Private" to "Public" here: https://github.com/orgs/doocs/people. We also recommend setting it to Public.
Once a member of the community, the member can fork any project of interest and make modifications, and then submit a pull request for review. If the submission is deemed to be in compliance with the project's standards, the member may be added as a collaborator to the project.
It is worth noting that initially, new members may not have direct write access to the projects, as the maintainers need to assess the quality of the submissions. Therefore, it is recommended that new members make submissions regularly to become familiar with the GitHub workflow and to improve their submissions over time.
You can refer to this article if you are a new open-source contributor.
Overall, the Doocs community seems to be a valuable resource for programmers interested in learning about internet backend development and open-source development. The community offers various resources and projects that can help programmers improve their skills and contribute to the open-source community.
Doocs is looking forward to your joining.
Contributors
This organization exists thanks to all the people who contribute.
Contact
File: docs/en/index.md
https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "Doocs"
text: "Developer-friendly Open-source Community"
actions:
- theme: alt
text: GitHub
link: https://github.com/doocs
- theme: brand
text: Enter
link: /en/intro
features:
title: advanced-java
details: Comprehensive guide for Internet Java engineers, covering topics like high concurrency, distributed systems, high availability, microservices, and massive data processing.
icon: 🧠
link: https://github.com/doocs/advanced-javatitle: leetcode
details: Solutions to LeetCode problems implemented in various programming languages, including answers to "Cracking the Coding Interview (6th Edition)" and "Offer in Sword (2nd Edition)".
icon: 🧩
link: https://github.com/doocs/leetcodetitle: source-code-hunter
details: Source code analysis of commonly used Internet component frameworks.
icon: 🔍
link: https://github.com/doocs/source-code-huntertitle: jvm
details: Summary of Java Virtual Machine (JVM) internals and principles.
icon: ⚙️
link: https://github.com/doocs/jvmtitle: coding-interview
details: Coding interview question collections, including problems from Cracking the Coding Interview and Programming Pearls.
icon: 💼
link: https://github.com/doocs/coding-interviewtitle: md
details: A highly minimalist Markdown editor for WeChat.
icon: ✍️
link: https://github.com/doocs/mdtitle: technical-books
details: A list of must-read technical books.
icon: 📚
link: https://github.com/doocs/technical-books
File: docs/en/intro.md
Introduction
Hello, everyone! Welcome to the Doocs Open Source community. We are currently focusing on sharing various development-related knowledge.
Sites
Owner
Yang Libin, focus on the inner workings of web applications to build out the "server-side" of web applications.
- Wechat: YLB0109
- Email: [email protected]
- GitHub: https://github.com/yanglbme
- GitCode: https://gitcode.com/yanglbme
- Gitee: https://gitee.com/yanglbme
Projects
Doocs now has the following hot projects:
| # | Project | Description | Popularity |
|---|---|---|---|
| 1 | advanced-java | Core interview questions & answers for experienced Java(Backend) developers. | |
| 2 | leetcode | LeetCode solutions in any programming language. | |
| 3 | source-code-hunter | Source code analysis of frameworks and components. | |
| 4 | jvm | A summary of the underlying principles of Java Virtual Machine. | |
| 5 | coding-interview | A collection of interview questions for programmers. The main contents include ‘Coding Interviews: Questions, Analysis and Solutions’, ‘Beauty of Programming’, etc. | |
| 6 | md | An online wechat markdown editor. | |
| 7 | technical-books | Awesome programming books list. |
How to join
If you want to go fast, go alone. If you want to go far, go together.
I believe many of you have a deep understanding that participating in open source projects is very helpful for personal growth. If you love open source or you are curious about it, join Doocs, work with us, maintain our projects and grow together. You can join us in the following two ways:
- Comment on the relevant issue on GitHub to express interest in joining the community.
- Send an email to [email protected] with the GitHub ID to request to join the community.
By default, your Doocs membership will be hidden from view aftering joining us. If you'd like to display the organization icon in your profile, you can switch from "Private" to "Public" here: https://github.com/orgs/doocs/people. We also recommend setting it to Public.
Once a member of the community, the member can fork any project of interest and make modifications, and then submit a pull request for review. If the submission is deemed to be in compliance with the project's standards, the member may be added as a collaborator to the project.
It is worth noting that initially, new members may not have direct write access to the projects, as the maintainers need to assess the quality of the submissions. Therefore, it is recommended that new members make submissions regularly to become familiar with the GitHub workflow and to improve their submissions over time.
You can refer to this article if you are a new open-source contributor.
Overall, the Doocs community seems to be a valuable resource for programmers interested in learning about internet backend development and open-source development. The community offers various resources and projects that can help programmers improve their skills and contribute to the open-source community.
Doocs is looking forward to your joining.
Contributors
This organization exists thanks to all the people who contribute.
Contact
File: docs/index.md
https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "Doocs"
text: "开发者友好的开源社区"
actions:
- theme: alt
text: GitHub
link: https://github.com/doocs
- theme: brand
text: 进入
link: /intro
features:
title: advanced-java
details: 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识。
icon: 🧠
link: https://github.com/doocs/advanced-javatitle: leetcode
details: 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解。
icon: 🧩
link: https://github.com/doocs/leetcodetitle: source-code-hunter
details: 互联网常用组件框架源码分析。
icon: 🔍
link: https://github.com/doocs/source-code-huntertitle: jvm
details: Java 虚拟机底层原理知识总结。
icon: ⚙️
link: https://github.com/doocs/jvmtitle: coding-interview
details: 代码面试题集,包括《剑指 Offer》、《编程之美》等。
icon: 💼
link: https://github.com/doocs/coding-interviewtitle: md
details: 一款高度简洁的微信 Markdown 编辑器。
icon: ✍️
link: https://github.com/doocs/mdtitle: technical-books
details: 值得一看的技术书籍列表。
icon: 📚
link: https://github.com/doocs/technical-books
File: docs/intro.md
介绍
你好呀,欢迎来到 Doocs 开源社区。我们是一个非常友好的技术社区,目前专注于分享技术领域相关知识。
站点
发起人
杨立滨,专注于互联网后端领域。
- 微信: YLB0109
- 邮箱:[email protected]
- GitCode: https://gitcode.com/yanglbme
- Gitee:https://gitee.com/yanglbme
- GitHub: https://github.com/yanglbme
项目
目前 Doocs 有以下多个热门项目,欢迎 Star ⭐ 关注~
| # | 项目 | 描述 | 热度 |
|---|---|---|---|
| 1 | advanced-java | 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识。 | |
| 2 | leetcode | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解。 | |
| 3 | source-code-hunter | 互联网常用组件框架源码分析。 | |
| 4 | jvm | Java 虚拟机底层原理知识总结。 | |
| 5 | coding-interview | 代码面试题集,包括《剑指 Offer》、《编程之美》等。 | |
| 6 | md | 一款高度简洁的微信 Markdown 编辑器。 | |
| 7 | technical-books | 值得一看的技术书籍列表。 |
如何加入
要走得快,就一个人走;要走得远,就一起走。
参与开源项目,对于个人的成长是有很大帮助的,相信很多小伙伴都有体会。
如果你热爱开源,或者抱着一颗好奇的心,不妨加入 Doocs 组织,和我们一起维护项目,共同成长。可以通过以下两种方式加入:
- 直接在此项目的 issue 上评论,告知我们你想加入 Doocs。
- 发送邮件到 [email protected],写明你的 GitHub ID,如 yanglbme。
默认情况下,在你加入我们之后,你作为 GitHub Doocs 组织成员的信息是处于隐藏状态的。如果你希望在你的个人 GitHub 资料页上展示 Doocs 组织,你可以在此链接 https://github.com/orgs/doocs/people 处将你的信息从 private “私有”改为 public “公开”。当然,我们推荐设置为公开。
注意,刚开始加入,你暂时不会有项目的直接 write 权限,因为我们不确定你的提交是否符合项目规范。
你可以 fork 任何一个感兴趣的项目到你的个人 GitHub 帐户下,对项目作出修改后,提交你的 PR。Doocs 维护者会对你的提交内容进行 review。或许你最初的提交并不规范,这也没关系,改进之后,慢慢的,整个规范性就有了。当你的提交一直很符合项目的规范性,Doocs 维护者会将你添加到对应项目的 Collaborators 列表中,共同维护好项目。
如果你不熟悉 GitHub 操作流程,可以参考这篇文章。
Doocs 期待你的加入。
贡献者列表
感谢以下所有为此开源组织做出贡献的开发者朋友们。
联系我
GitHub 技术社区 Doocs 旗下唯一公众号「Doocs」,欢迎扫码关注,专注分享技术领域相关知识及行业最新资讯。当然,也可以加我个人微信(备注:GitHub),拉你进技术交流群。
File: README_CN.md
介绍
你好呀,欢迎来到 Doocs 开源社区。我们是一个非常友好的技术社区,目前专注于分享技术领域相关知识。
站点
发起人
杨立滨,专注于互联网后端领域。
- 微信: YLB0109
- 邮箱:[email protected]
- GitCode: https://gitcode.com/yanglbme
- Gitee:https://gitee.com/yanglbme
- GitHub: https://github.com/yanglbme
项目
目前 Doocs 有以下多个热门项目,欢迎 Star ⭐ 关注~
| # | 项目 | 描述 | 热度 |
|---|---|---|---|
| 1 | advanced-java | 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识。 | |
| 2 | leetcode | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解。 | |
| 3 | source-code-hunter | 互联网常用组件框架源码分析。 | |
| 4 | jvm | Java 虚拟机底层原理知识总结。 | |
| 5 | coding-interview | 代码面试题集,包括《剑指 Offer》、《编程之美》等。 | |
| 6 | md | 一款高度简洁的微信 Markdown 编辑器。 | |
| 7 | technical-books | 值得一看的技术书籍列表。 |
如何加入
要走得快,就一个人走;要走得远,就一起走。
参与开源项目,对于个人的成长是有很大帮助的,相信很多小伙伴都有体会。
如果你热爱开源,或者抱着一颗好奇的心,不妨加入 Doocs 组织,和我们一起维护项目,共同成长。可以通过以下两种方式加入:
- 直接在此项目的 discussion 上评论,告知我们你想加入 Doocs。
- 发送邮件到 [email protected],写明你的 GitHub ID,如 yanglbme。
默认情况下,在你加入我们之后,你作为 GitHub Doocs 组织成员的信息是处于隐藏状态的。如果你希望在你的个人 GitHub 资料页上展示 Doocs 组织,你可以在此链接 https://github.com/orgs/doocs/people 处将你的信息从 private “私有”改为 public “公开”。当然,我们推荐设置为公开。
注意,刚开始加入,你暂时不会有项目的直接 write 权限,因为我们不确定你的提交是否符合项目规范。
你可以 fork 任何一个感兴趣的项目到你的个人 GitHub 帐户下,对项目作出修改后,提交你的 PR。Doocs 维护者会对你的提交内容进行 review。或许你最初的提交并不规范,这也没关系,改进之后,慢慢的,整个规范性就有了。当你的提交一直很符合项目的规范性,Doocs 维护者会将你添加到对应项目的 Collaborators 列表中,共同维护好项目。
如果你不熟悉 GitHub 操作流程,可以参考这篇文章。
Doocs 期待你的加入。
贡献者列表
感谢以下所有为此开源组织做出贡献的开发者朋友们。
联系我
GitHub 技术社区 Doocs 旗下唯一公众号「Doocs」,欢迎扫码关注,专注分享技术领域相关知识及行业最新资讯。当然,也可以加我个人微信(备注:GitHub),拉你进技术交流群。