# spring-cloud-source **Repository Path**: zhangjuntao/spring-cloud-source ## Basic Information - **Project Name**: spring-cloud-source - **Description**: spring-cloud-source - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2018-01-21 - **Last Updated**: 2025-06-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring Cloud: ## 1、ConfigurableEnvironment 配置 ### 1.1、加载顺序 > 1、`CommandLinePropertySource ` 命令行加载 > > 2、`bootstrap` 配置文件加载 > > 3、`application` 配置文件加载 #### 1.2、bootstrap配置加载 > `BootstrapApplicationListener`是加载`boostrap.yml`配置文件的类。(bootstrapProperties.addFirst) ```java org.springframework.cloud.bootstrap.BootstrapApplicationListener#onApplicationEvent
org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext
StandardEnvironment bootstrapEnvironment = new StandardEnvironment(); MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources(); bootstrapProperties.addFirst() mergeDefaultProperties() 合并properties配置 ``` #### 1.3、application配置加载 #### 1.4、spring boot 加载配置 ```java spring boot 构建配置: org.springframework.boot.SpringApplication#prepareEnvironment 构建命令行配置: configureEnvironment(environment, applicationArguments.getSourceArgs()); 通过事件驱动配置: bootstrap[spring-cloud]、application listeners.environmentPrepared(environment); ``` #### 1.5、配置bootstrap、application、logging ```java ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (final ApplicationListener listener : getApplicationListeners(event, type)) ``` ## 2、Endpoint 实现 ### 2.1、healthEndPoint: > 1、`EndpointAutoConfiguration#EndpointAutoConfiguration`: > > `ObjectProvider `通过构造依赖得到Bean的一个引用。方法`getIfAvailable()`得到bean的实例。`healthIndicators` 中包括了实例化出的各种`HealthIndicator` 子类,该子类的 `health`方式实现接口的健康检查。 > > ```java > // `ObjectProvider` 通过构造依赖得到Bean的一个引用。将healthIndicators类加载到 > public EndpointAutoConfiguration( > ObjectProvider healthAggregator, ObjectProvider HealthIndicator>> healthIndicators, ObjectProvider> infoContributors, > ObjectProvider> publicMetrics, ObjectProvider traceRepository) { > this.healthAggregator = (HealthAggregator)healthAggregator.getIfAvailable(); > this.healthIndicators = (Map)healthIndicators.getIfAvailable(); > this.infoContributors = (List)infoContributors.getIfAvailable(); > this.publicMetrics = (Collection)publicMetrics.getIfAvailable(); > this.traceRepository = (TraceRepository)traceRepository.getIfAvailable(); > } > ``` > > > 2、``healthIndicators`使用初始化`HealthEndpoint` 实例: > > ```JAVA > @Bean > @ConditionalOnMissingBean > public HealthEndpoint healthEndpoint() { > return new HealthEndpoint((HealthAggregator)(this.healthAggregator == null ? > new OrderedHealthAggregator() : this.healthAggregator), this.healthIndicators == null ? Collections.emptyMap() : this.healthIndicators); > } > ``` > 3、将`HealthEndpoint`交给`HealthMvcEndpoint`,并初始化该还实例。request请求`org.springframework.boot.actuate.endpoint.HealthEndpoint#invoke`并委派给`HealthEndpoint` 并将处理结果返回restful给前端。 > > ```java > // EndpointWebMvcManagementContextConfiguration # healthMvcEndpoint > @Bean > @ConditionalOnBean(HealthEndpoint.class) > @ConditionalOnMissingBean > @ConditionalOnEnabledEndpoint("health") > public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate, > ManagementServerProperties managementServerProperties) { > HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate, > this.managementServerProperties.getSecurity().isEnabled(), > managementServerProperties.getSecurity().getRoles()); > if (this.healthMvcEndpointProperties.getMapping() != null) { > healthMvcEndpoint > .addStatusMapping(this.healthMvcEndpointProperties.getMapping()); > } > return healthMvcEndpoint; > } > ``` > > **PS : 继承该接口`org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointMvcAdapterl` 来实现spring boot EndPoint的增加。** ## 3、Spring Config 动态配置 > 1、`@RefreshScope` 注解来实现标记`@EnableConfigurationProperties`Bean中的属性刷新。 > > 2、 `context.stop()` 、`context.refresh()` 、`context.pause()` 来实现对spring bean的暂停、刷新功能。 ## 4、Spring 内省机制 ### 4.1、bean 参数绑定: ​ `org.springframework.boot.bind.RelaxedDataBinder` 、 `org.springframework.boot.bind.RelaxedNames` 实现参数绑定。 **残缺 ** ## 5、Eureka 分析 ### 5.1、原理实现 > 1、Eureka client 定时续约。 > > 2、Eureka server 之间相互热备。 ### 5.2、源码分析 在该json文件中,来确定`bean`和`bean properties` 之间的关系映射。 **PS : spring-configuration-metadata.json 是 properties和 bean 的映射关系 [配置信息从这个文件查询]** `**spring-cloud-netflix-eureka-client-1.2.3.RELEASE**.jar!/META-INF/spring-configuration-metadata.json` ```json ## sourceType preperty name 为关联的Bean class。 ## 例如:"eureka.client"的配置是作为于 org.springframework.cloud.netflix.eureka.EurekaClientConfigBean的Bean 属性中的。 { "groups": [ { "name": "eureka.client", "type": "org.springframework.cloud.netflix.eureka.EurekaClientConfigBean", "sourceType": "org.springframework.cloud.netflix.eureka.EurekaClientConfigBean" }, { "name": "eureka.instance", "type": "org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean", "sourceType": "org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean" } ], "properties": [ { "name": "eureka.client.allow-redirects", "type": "java.lang.Boolean", "description": "Indicates whether server can redirect a client request to a backup server/cluster.\n If set to false, the server will handle the request directly, If set to true, it\n may send HTTP redirect to the client, with a new server location.", "sourceType": "org.springframework.cloud.netflix.eureka.EurekaClientConfigBean", "defaultValue": false } ] } ``` ## 6、JDK Closeable ### 6.1、closeable 自动释放资源 `try()-closeable` 实现自动释放资源。 ```java /** * 资源访问客户端。类似jdbc client、httpclient等等 */ public class ResourceClient implements Closeable{ public void doInvoke(String args) { System.out.println(" 执行资源访 "+args.toUpperCase()); } @Override public void close() throws IOException { System.err.println("================关闭资源链接================"); } } ``` ```java public class ResourceManage { /** * closeable try() 自动释放资源 * @param args */ public static void main(String[] args) { try (ResourceClient resourceClient = new ResourceClient(); ResourceClient resourceClient2 = new ResourceClient();) { resourceClient.doInvoke("zhang"); resourceClient2.doInvoke("zhang2"); } catch (IOException e) { e.printStackTrace(); } } } ``` ## 7、http断点续传 [http调用流程博客](http://blog.sina.com.cn/s/blog_ec8c9eae0102x3uu.html) ### 7.1、Range、Content-Range > 所谓断点续传,也就是要从文件已经下载的地方开始继续下载。在以前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了。一般断点下载时才用到 Range 和 Content-Range 实体头。 > `Range ` > 用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式: > > **Range:(unit=first byte pos)-[last byte pos] ** > > `Content-Range` > 用于响应头,指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式: > > **Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth] ** > > `Accept-Ranges` > > 用于响应,标识服务器支持断点续传功能。 > > `Content-Length : (1900 - 580) ` > > 用于响应,标识本次http的响应内容字节数。并不是文件总的大小。(长度则不是总长度了,而580到1900共有多少字节。) **客户端请求下载文件:** ```http 请求下载整个文件: GET /test.rar HTTP/1.1 Connection: close Host: 116.1.219.219 Range: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头 ``` **服务端响应:** ```http HTTP/1.1 200 OK 或者 HTTP/1.1 206 Partial Content(使用断点续传方式) Content-Length: 801 Content-Type: application/octet-stream Content-Range: bytes 0-800/801 //801:文件总大小 Accept-Ranges : bytes //告诉客户端可以断点续传 ``` ### 7.2、`Range`多样化: ```http 表示头500个字节:Range: bytes=0-499 表示第二个500字节:Range: bytes=500-999 表示最后500个字节:Range: bytes=-500 表示500字节以后的范围:Range: bytes=500- 第一个和最后一个字节:Range: bytes=0-0,-1 同时指定几个范围:Range: bytes=500-600,601-999 ``` ### 7.3、Tomcat 文件断点续传: > **`DefaultServlet`实现流程** > > 1、`org.apache.catalina.servlets.DefaultServlet#executePartialPut`方法将大的,并支持的断点续传的文件,利用`RandomAccessFile`按照断点续传的请求字节`Range` 拆分出字节,并将该端字节数组存放到一个临时的文件中,返回临时文件。 **文件上传功能:** ```java protected File executePartialPut(HttpServletRequest req, Range range, String path)throws IOException { // Append data specified in ranges to existing content for this // resource - create a temp. file on the local filesystem to // perform this operation File tempDir = (File) getServletContext().getAttribute (ServletContext.TEMPDIR); // Convert all '/' characters to '.' in resourcePath String convertedResourcePath = path.replace('/', '.'); File contentFile = new File(tempDir, convertedResourcePath); if (contentFile.createNewFile()) { // Clean up contentFile when Tomcat is terminated contentFile.deleteOnExit(); } try (RandomAccessFile randAccessContentFile = new RandomAccessFile(contentFile, "rw");){ WebResource oldResource = resources.getResource(path); //文件源,服务器上的文件路径 // Copy data in oldRevisionContent to contentFile if (oldResource.isFile()) { try (BufferedInputStream bufOldRevStream = new BufferedInputStream(oldResource.getInputStream(), BUFFER_SIZE);) { int numBytesRead; byte[] copyBuffer = new byte[BUFFER_SIZE]; while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) { randAccessContentFile.write(copyBuffer, 0, numBytesRead); } } } randAccessContentFile.setLength(range.length); // Append data in request input stream to contentFile randAccessContentFile.seek(range.start); int numBytesRead; byte[] transferBuffer = new byte[BUFFER_SIZE]; try (BufferedInputStream requestBufInStream = new BufferedInputStream(req.getInputStream(), BUFFER_SIZE);) { while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) { randAccessContentFile.write(transferBuffer, 0, numBytesRead); } } } return contentFile; } ``` **GET文件下载功能:** ``` java // 根据 `Range` 头来确定断点续传是否支持 1、org.apache.catalina.servlets.DefaultServlet#serveResource // 975 // 文件流的拷贝 WebResource -> servletOutStream 输出断点续传文件 2、org.apache.catalina.servlets.DefaultServlet#copy(org.apache.catalina.WebResource, javax.servlet.ServletOutputStream, org.apache.catalina.servlets.DefaultServlet.Range) // 文件流的拷贝功能实现 3、org.apache.catalina.servlets.DefaultServlet#copyRange(java.io.InputStream, javax.servlet.ServletOutputStream, long, long) ``` ### 7.4、请求记录:[http content_length 无关] First: ​ 客户端请求。服务端返回`Accept-Ranges:bytes` 头即可。`HTTP status code 200`. ```http Request URL:http://172.29.80.111:8080/12345.mp4?SSSS Request Method:GET Status Code:200 Remote Address:172.29.80.111:8080 Referrer Policy:no-referrer-when-downgrade Response Headers view source **Accept-Ranges:bytes** Content-Length:12356 Content-Type:video/mp4 Date:Fri, 26 Jan 2018 02:49:02 GMT ETag:W/"14179933-1516786518000" Last-Modified:Wed, 24 Jan 2018 09:35:18 GMT ``` Second: ​ 客户端请求`Range:bytes=0-`,服务端`Accept-Ranges:bytes` 和 `Content-Range:bytes 0-14179932/14179933` 。返回状态码 `206` 。 ```http Request URL:http://172.29.80.111:8080/12345.mp4?SSSS Request Method:GET Status Code:206 Remote Address:172.29.80.111:8080 Referrer Policy:no-referrer-when-downgrade Response Headers Accept-Ranges:bytes Content-Length:14179933 Content-Range:bytes 0-14179932/14179933 Content-Type:video/mp4 Date:Fri, 26 Jan 2018 02:49:02 GMT ETag:W/"14179933-1516786518000" Last-Modified:Wed, 24 Jan 2018 09:35:18 GMT Request Headers Range:bytes=0- ``` ## 8、大佬博客 1、**[峰云就她了](http://xiaorui.cc/wp-admin/)** ## 9、MQ 使用注意点: ### 9.1、消息的重复消费 > `场景`:由于网络抖动,`MQ ack`失败。会出现消息的重复消费。 > > `解决`: 消费业务的幂等性 或者利用 `redis eventID event (expire 1 Hour)`保证业务不被重复消费 > > `要领` : **幂等 或者 记录保存。(redis)** ### 9.2、消息的绝对顺序 > ` 场景`:支付成功后,将先会用户增加余额,再发送商家通知短信。(业务并行再MQ中,并必须保证顺序)增加余额消费者先得到`event`。但是,系统夯住了。通知商家消费者,得到`event`并立即通知。此时,该场景没有保证`event `的顺序性。 > > `解决` :根据不同的`eventId hash`到不能的队列中,然后。该队列由一个消费者消费,从而避免了多消费者消费同一个队列的先后性问题。 > > `要领`:**顺序`EVENT` 同一个队列,并单一消费者。(hash sharding)** ## 10、高并发架构 #### 10.1、cache 缓存 1、采用`SQL/mongodb`和`redis/memcache`结合的 ,MongoDB 用来保存DB数据库缓存。Redis 用来保存热点数据。大value的数据存放在`memcache` 中。多级缓存,防止缓存穿透。 **回源顺序: Redis [memcache] - > `monfodb` -> DB** ## 11、Ribbon 负载均衡 ### 11.1、基本组件 > 1、`ILoadBalencer` 负载均衡器 > > 2、`IPing` 判断server instance 的状态。更新serverlist 路由表内容。 > > 3、`LRule` 负载均衡策略。根据负载均衡策略choose 相应的server instance。 > > 4、`ServerList` 发现、保存server instance 实例的路由信息。【路由表】 > > 5、`ServerListFilter` 发现、保存server instance 时候的的filter。 ![ribbon](https://gitee.com/zhangjuntao/spring-cloud-source/raw/master/image/Ribbon%20%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AD%96%E7%95%A5.png "Ribbon 组件") ### 11.2、Ribbon的配置: 分析`RibbonClientConfiguration` 源码: ```Java public class RibbonClientConfiguration { @Autowired private PropertiesFactory propertiesFactory; /**记录Ribbon的默认配置信息**/ @Bean @ConditionalOnMissingBean public IClientConfig ribbonClientConfig() { DefaultClientConfigImpl config = new DefaultClientConfigImpl(); config.loadProperties(this.name); return config; } /**IRule 实现 loadBalancer的负载均衡策略**/ @Bean @ConditionalOnMissingBean public IRule ribbonRule(IClientConfig config) { if (this.propertiesFactory.isSet(IRule.class, name)) { return this.propertiesFactory.get(IRule.class, config, name); } ZoneAvoidanceRule rule = new ZoneAvoidanceRule(); rule.initWithNiwsConfig(config); return rule; } /**加载IPing bean,用在load balancer中,定时判断服务的可用性**/ @Bean @ConditionalOnMissingBean public IPing ribbonPing(IClientConfig config) { if (this.propertiesFactory.isSet(IPing.class, name)) { return this.propertiesFactory.get(IPing.class, config, name); } return new DummyPing(); } /**得到配置的服务实例列表信息**/ @Bean @ConditionalOnMissingBean @SuppressWarnings("unchecked") public ServerList ribbonServerList(IClientConfig config) { if (this.propertiesFactory.isSet(ServerList.class, name)) { return this.propertiesFactory.get(ServerList.class, config, name); } ConfigurationBasedServerList serverList = new ConfigurationBasedServerList(); serverList.initWithNiwsConfig(config); return serverList; } /**服务更新实例**/ @Bean @ConditionalOnMissingBean public ServerListUpdater ribbonServerListUpdater(IClientConfig config) { return new PollingServerListUpdater(config); } /**将各个组件【IRule、IPing、ServerList、ServerListUpdater】交给LoadBalancer管理。实现服务定时IPing续约。IRule的负载均衡、ServerList的服务列表保存、ServerListUpdater服务轮训更新**/ @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); } /**请求的重试策略**/ @Bean @ConditionalOnMissingBean public RetryHandler retryHandler(IClientConfig config) { return new DefaultLoadBalancerRetryHandler(config); } /** `RibbonLoadBalancerContext` 持有ILoadBalancer 负载均衡器。并且,支持默认重试原则。**/ @Bean @ConditionalOnMissingBean public RibbonLoadBalancerContext ribbonLoadBalancerContext( ILoadBalancer loadBalancer, IClientConfig config, RetryHandler retryHandler) { return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler); } } ``` ### 11.2、@LoadBalancer 的实现 `LoadBalancer` 的自动装配: > 1、spring.factories 中的 `EnableAutoConfiguration` 自动装配。 > > 2、将负载均衡 `LoadBalancer` 和 `Retry` 加载到 多个`RestTemplateCustomizer` 中。 > > 3、`LoadBalancerAutoConfiguration` 中,通过`RestTemplateCustomizer` 对 `RestTemplate` 增加`LoadBalancerInterceptor` 实现客户端的请求的负载均衡。给所有 `RestTemplate` 增加负载均衡、重试拦截器。 源码分析: ```java @Configuration @ConditionalOnClass(RestTemplate.class) @ConditionalOnBean(LoadBalancerClient.class) @EnableConfigurationProperties(LoadBalancerRetryProperties.class) public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List restTemplates = Collections.emptyList(); /** 将RestTemplateCustomizer 用来初始化 所有的List **/ @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializer( final List customizers) { return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } } }; } @Autowired(required = false) private List transformers = Collections.emptyList(); @Bean @ConditionalOnMissingBean public LoadBalancerRequestFactory loadBalancerRequestFactory( LoadBalancerClient loadBalancerClient) { return new LoadBalancerRequestFactory(loadBalancerClient, transformers); } @Configuration @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate") static class LoadBalancerInterceptorConfig { @Bean public LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); } //加载负载均衡策略到RestTemplateCustomizer中。 @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } } /** 初始化 Retry 重试策略 **/ @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryAutoConfiguration { @Bean public RetryTemplate retryTemplate() { RetryTemplate template = new RetryTemplate(); template.setThrowLastExceptionOnExhausted(true); return template; } @Bean @ConditionalOnMissingBean public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() { return new LoadBalancedRetryPolicyFactory.NeverRetryFactory(); } } /** RetryLoadBalancerInterceptor:重试负载均衡策略 RetryTemplate 的class存在的话,RestTemplateCustomizer加载上 RetryLoadBalancerInterceptor **/ @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryInterceptorAutoConfiguration { @Bean @ConditionalOnMissingBean public RetryLoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancedRetryPolicyFactory lbRetryPolicyFactory, LoadBalancerRequestFactory requestFactory) { return new RetryLoadBalancerInterceptor(loadBalancerClient, properties, lbRetryPolicyFactory, requestFactory); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final RetryLoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } } } ``` `LoadBalancerInterceptor`:自动实现`RestTemplate` 负载均衡。 ```java public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { private LoadBalancerClient loadBalancer; private LoadBalancerRequestFactory requestFactory; public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) { this.loadBalancer = loadBalancer; this.requestFactory = requestFactory; } public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) { // for backwards compatibility this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer)); } /** 拦截并实现server instance name -> url更换。达到负载均衡的策略。 **/ @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution)); } } ``` Ribbon 自动装配,并实现restTemplate的负载均衡: ![Ribbon 自动装配,并实现restTemplate的负载均衡](https://gitee.com/zhangjuntao/spring-cloud-source/raw/master/image/Resttemplate%20loadbalancerInterceptor%E5%AE%9E%E7%8E%B0%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1.png "Ribbon 自动装配") ## 12、Hystrix 熔断器 ### 12.1、@EnableCircuitBreaker实现 1、`@Import(EnableCircuitBreakerImportSelector.class)` 导入(import)、并选择(select) 配置类。 ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(EnableCircuitBreakerImportSelector.class) public @interface EnableCircuitBreaker { } ``` 2、得到相应的 `Configuration` 配置信息。 `ImportSelector` 中的`selectImports` 函数,来返回一个 `Configuration` 配置信息。 ```java public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. */ String[] selectImports(AnnotationMetadata importingClassMetadata); } ``` 3、`EnableCircuitBreakerImportSelector` 中返回 `spring.factories` 的配置类信息。 `spring-cloud-netflix-core` 中的` spring.factories` 配置信息: ``` properties org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\ org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration ``` `SpringFactoryImportSelector `来实现对 select configuration classes的动态选择配置。[spring.factories -> configuration classes name] ```java @CommonsLog public abstract class SpringFactoryImportSelector implements DeferredImportSelector, BeanClassLoaderAware, EnvironmentAware { private ClassLoader beanClassLoader; private Class annotationClass; private Environment environment; @SuppressWarnings("unchecked") protected SpringFactoryImportSelector() { this.annotationClass = (Class) GenericTypeResolver .resolveTypeArgument(this.getClass(), SpringFactoryImportSelector.class); } @Override public String[] selectImports(AnnotationMetadata metadata) { if (!isEnabled()) { return new String[0]; } // 得到注解的元数据信息 【AnnotationAttributes】 AnnotationAttributes attributes = AnnotationAttributes.fromMap( metadata.getAnnotationAttributes(this.annotationClass.getName(), true)); Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is " + metadata.getClassName() + " annotated with @" + getSimpleName() + "?"); // 从spring.factories中的得到 configuration classes 配置类。 List factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader .loadFactoryNames(this.annotationClass, this.beanClassLoader))); if (factories.isEmpty() && !hasDefaultFactory()) { throw new IllegalStateException("Annotation @" + getSimpleName() + " found, but there are no implementations. Did you forget to include a starter?"); } if (factories.size() > 1) { // there should only ever be one DiscoveryClient, but there might be more than // one factory log.warn("More than one implementation " + "of @" + getSimpleName() + " (now relying on @Conditionals to pick one): " + factories); } return factories.toArray(new String[factories.size()]); } } ``` 4、`HystrixCircuitBreakerConfiguration` 配置 `Hystrix` 熔断配置。 * `HystrixCommandAspect` 拦截rest请求,并包装成为 `HystrixConmand` 。 * `HystrixShutdownHook` 利用spring的`DisposableBean` 来实现Hystrix shutdown 的资源回收 * `HystrixStreamEndpoint` 实现hystrix servlet Dashboard 数据提供 * `HystrixMetricsPollerConfiguration` 来实现 Metrics 健康指数的监控。 ```java @Configuration public class HystrixCircuitBreakerConfiguration { /**利用 aop 拦截请求,并包装成 hystrix Command**/ @Bean public HystrixCommandAspect hystrixCommandAspect() { return new HystrixCommandAspect(); } @Bean public HystrixShutdownHook hystrixShutdownHook() { return new HystrixShutdownHook(); } @Bean public HasFeatures hystrixFeature() { return HasFeatures.namedFeatures(new NamedFeature("Hystrix", HystrixCommandAspect.class)); } @Configuration @ConditionalOnProperty(value = "hystrix.stream.endpoint.enabled", matchIfMissing = true) @ConditionalOnWebApplication @ConditionalOnClass({ Endpoint.class, HystrixMetricsStreamServlet.class }) protected static class HystrixWebConfiguration { @Bean public HystrixStreamEndpoint hystrixStreamEndpoint() { return new HystrixStreamEndpoint(); } @Bean public HasFeatures hystrixStreamFeature() { return HasFeatures.namedFeature("Hystrix Stream Servlet", HystrixStreamEndpoint.class); } } @Configuration @ConditionalOnProperty(value = "hystrix.metrics.enabled", matchIfMissing = true) @ConditionalOnClass({ HystrixMetricsPoller.class, GaugeService.class }) @EnableConfigurationProperties(HystrixMetricsProperties.class) protected static class HystrixMetricsPollerConfiguration implements SmartLifecycle { private static Log logger = LogFactory .getLog(HystrixMetricsPollerConfiguration.class); @Autowired(required = false) private GaugeService gauges; @Autowired private HystrixMetricsProperties metricsProperties; private ObjectMapper mapper = new ObjectMapper(); private HystrixMetricsPoller poller; private Set reserved = new HashSet(Arrays.asList("group", "name", "type", "currentTime")); @Override public void start() { if (this.gauges == null) { return; } MetricsAsJsonPollerListener listener = new MetricsAsJsonPollerListener() { @Override public void handleJsonMetric(String json) { try { @SuppressWarnings("unchecked") Map map = HystrixMetricsPollerConfiguration.this.mapper .readValue(json, Map.class); if (map != null && map.containsKey("type")) { addMetrics(map, "hystrix."); } } catch (IOException ex) { // ignore } } }; this.poller = new HystrixMetricsPoller(listener, metricsProperties.getPollingIntervalMs()); // start polling and it will write directly to the listener this.poller.start(); logger.info("Starting poller"); } private void addMetrics(Map map, String root) { StringBuilder prefixBuilder = new StringBuilder(root); if (map.containsKey("type")) { prefixBuilder.append((String) map.get("type")); if (map.containsKey("group")) { prefixBuilder.append(".").append(map.get("group")); } prefixBuilder.append(".").append(map.get("name")); } String prefix = prefixBuilder.toString(); for (String key : map.keySet()) { Object value = map.get(key); if (!this.reserved.contains(key)) { if (value instanceof Number) { String name = prefix + "." + key; this.gauges.submit(name, ((Number) value).doubleValue()); } else if (value instanceof Map) { @SuppressWarnings("unchecked") Map sub = (Map) value; addMetrics(sub, prefix); } } } } @Override public void stop() { if (this.poller != null) { this.poller.shutdown(); } } @Override public boolean isRunning() { return this.poller != null ? this.poller.isRunning() : false; } @Override public int getPhase() { return Ordered.LOWEST_PRECEDENCE; } @Override public boolean isAutoStartup() { return true; } @Override public void stop(Runnable callback) { if (this.poller != null) { this.poller.shutdown(); } callback.run(); } } /** * 释放hystrix资源 * {@link DisposableBean} that makes sure that Hystrix internal state is cleared when * {@link ApplicationContext} shuts down. */ private class HystrixShutdownHook implements DisposableBean { @Override public void destroy() throws Exception { // Just call Hystrix to reset thread pool etc. Hystrix.reset(); } } } ``` 5、`HystrixCommandAspect` 利用AOP来实现hystrixCommand的请求和熔断。 ```java @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(); } @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)") public void hystrixCommandAnnotationPointcut() { } @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)") public void hystrixCollapserAnnotationPointcut() { } /** aroud 实现请求拦截和包装成hystrixCommand **/ @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 metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method)); MetaHolder metaHolder = metaHolderFactory.create(joinPoint); //HystrixCommandFactory 创建一个 hystrixCommand,并被HystrixInvokable 包装成实例。 HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder); ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ? metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType(); Object result; try { if (!metaHolder.isObservable()) { 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; } } ``` ## 13、alibaba fastjson采坑: ```java package com.alibaba.fastjson.util; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONAware; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; import com.alibaba.fastjson.JSONPathException; import com.alibaba.fastjson.JSONReader; import com.alibaba.fastjson.JSONStreamAware; import com.alibaba.fastjson.JSONWriter; import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.parser.DefaultJSONParser; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.parser.JSONLexer; import com.alibaba.fastjson.parser.JSONLexerBase; import com.alibaba.fastjson.parser.JSONReaderScanner; import com.alibaba.fastjson.parser.JSONScanner; import com.alibaba.fastjson.parser.JSONToken; import com.alibaba.fastjson.parser.ParseContext; import com.alibaba.fastjson.parser.ParserConfig; import com.alibaba.fastjson.parser.SymbolTable; import com.alibaba.fastjson.parser.deserializer.AutowiredObjectDeserializer; import com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer; import com.alibaba.fastjson.parser.deserializer.ExtraProcessable; import com.alibaba.fastjson.parser.deserializer.ExtraProcessor; import com.alibaba.fastjson.parser.deserializer.ExtraTypeProvider; import com.alibaba.fastjson.parser.deserializer.FieldDeserializer; import com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer; import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; import com.alibaba.fastjson.serializer.AfterFilter; import com.alibaba.fastjson.serializer.BeanContext; import com.alibaba.fastjson.serializer.BeforeFilter; import com.alibaba.fastjson.serializer.ContextObjectSerializer; import com.alibaba.fastjson.serializer.ContextValueFilter; import com.alibaba.fastjson.serializer.JSONSerializer; import com.alibaba.fastjson.serializer.JavaBeanSerializer; import com.alibaba.fastjson.serializer.LabelFilter; import com.alibaba.fastjson.serializer.Labels; import com.alibaba.fastjson.serializer.NameFilter; import com.alibaba.fastjson.serializer.ObjectSerializer; import com.alibaba.fastjson.serializer.PropertyFilter; import com.alibaba.fastjson.serializer.PropertyPreFilter; import com.alibaba.fastjson.serializer.SerialContext; import com.alibaba.fastjson.serializer.SerializeBeanInfo; import com.alibaba.fastjson.serializer.SerializeConfig; import com.alibaba.fastjson.serializer.SerializeFilter; import com.alibaba.fastjson.serializer.SerializeFilterable; import com.alibaba.fastjson.serializer.SerializeWriter; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.ValueFilter; public class ASMClassLoader extends ClassLoader { private static java.security.ProtectionDomain DOMAIN; private static Map> classMapping = new HashMap>(); static { DOMAIN = (java.security.ProtectionDomain) java.security.AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return ASMClassLoader.class.getProtectionDomain(); } }); Class[] jsonClasses = new Class[] {JSON.class, JSONObject.class, JSONArray.class, JSONPath.class, JSONAware.class, JSONException.class, JSONPathException.class, JSONReader.class, JSONStreamAware.class, JSONWriter.class, TypeReference.class, FieldInfo.class, TypeUtils.class, IOUtils.class, IdentityHashMap.class, ParameterizedTypeImpl.class, JavaBeanInfo.class, ObjectSerializer.class, JavaBeanSerializer.class, SerializeFilterable.class, SerializeBeanInfo.class, JSONSerializer.class, SerializeWriter.class, SerializeFilter.class, Labels.class, LabelFilter.class, ContextValueFilter.class, AfterFilter.class, BeforeFilter.class, NameFilter.class, PropertyFilter.class, PropertyPreFilter.class, ValueFilter.class, SerializerFeature.class, ContextObjectSerializer.class, SerialContext.class, SerializeConfig.class, JavaBeanDeserializer.class, ParserConfig.class, DefaultJSONParser.class, JSONLexer.class, JSONLexerBase.class, ParseContext.class, JSONToken.class, SymbolTable.class, Feature.class, JSONScanner.class, JSONReaderScanner.class, AutowiredObjectDeserializer.class, ObjectDeserializer.class, ExtraProcessor.class, ExtraProcessable.class, ExtraTypeProvider.class, BeanContext.class, FieldDeserializer.class, DefaultFieldDeserializer.class, }; for (Class clazz : jsonClasses) { classMapping.put(clazz.getName(), clazz); } } public ASMClassLoader(){ super(getParentClassLoader()); } public ASMClassLoader(ClassLoader parent){ super (parent); } static ClassLoader getParentClassLoader() { ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if (contextClassLoader != null) { try { contextClassLoader.loadClass(JSON.class.getName()); return contextClassLoader; } catch (ClassNotFoundException e) { // skip } } return JSON.class.getClassLoader(); } protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class mappingClass = classMapping.get(name); if (mappingClass != null) { return mappingClass; } try { return super.loadClass(name, resolve); } catch (ClassNotFoundException e) { throw e; } } public Class defineClassPublic(String name, byte[] b, int off, int len) throws ClassFormatError { Class clazz = defineClass(name, b, off, len, DOMAIN); return clazz; } public boolean isExternalClass(Class clazz) { ClassLoader classLoader = clazz.getClassLoader(); if (classLoader == null) { return false; } ClassLoader current = this; while (current != null) { if (current == classLoader) { return false; } current = current.getParent(); } return true; } } ``` **getParentClassLoader()函数设置classload** ** 利用asm字节码技术,反序列化parseObject 时候,会实例化出类加载器,当前线程所持有的的classload是该ASMClassLoader的父类,打破了双亲委托机制。所以,需要重新设置该classload。**