# my-SpringCloud **Repository Path**: panleiming/my-SpringCloud ## Basic Information - **Project Name**: my-SpringCloud - **Description**: Spring Cloud组件的学习 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-07-01 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README Spring Could生态的学习 === # 目录 1. springboot 1. 特性 2. EnableAutoConfiguration自动装配 3. Starter启动依赖 4. Actuator监控 5. 实战 * 实现自定义的条件装配 * 实现批量动态导入配置 * 实现自己的starter组件 * 实现自己的actuator菜单 * springboot集成Prometheus和Grafana 2. Netflix Ribbon 1. 基础使用 2. LoadBalanced注解原理 3. 实战 1. 基础使用 3. OpenFeign(声明式的伪RPC) 1. 基础使用 2. 高级使用 1. 将底层httpclient替换成okhttp 3. 实战 1. 基础使用 # springboot ## 特性 * EnableAutoConfiguration自动装配 * Starter启动依赖 * Actuator监控 * Spring Boot CLI命令行操作功能 ## EnableAutoConfiguration自动装配 ### Spring IOC 装配bean(静态装配bean) * xml配置 * @Configuration注解 * @Enable开头的模块化注解 ### Spring动态装配Bean * 实现ImportSelector接口 * 实现ImportBeanDefinitionRegistrar接口 ### SpringFactoryLoader(SPI机制) 实现spring.factories ### 条件装配 Conditional开头的注解 ## Starter启动依赖 ### 命名方式 * 官方包:spring-boot-starter-xxx * 第三包:xxx-spring-boot-starter ### 原理 * 官方实现自动装配主要是通过`@EnableAutoConfiguration`,自动装配的主入口类为`AutoConfigurationImportSelector` * 非官方实现自动装配主要通过`SpingFactoryLoader机制`实现 ## Actuator监控 ​ [官方文档]() ​ 使用Actuator监控需要引入如下的jar包,然后在配置文件中进行配置,默认展示的是["info", "health"],最后启动服务进行访问:**http://ip:port/actuator/{env}** ```java org.springframework.boot spring-boot-starter-actuator ``` ​ 配置信息如下: ```java management.endpoints.web.exposure.include=* // 开启全部 management.endpoint.shutdown.enable=true // 开启指定的选项 ``` ### health ​ 健康检查 ### metrics * JVM(垃圾收集器/内存/堆) * 系统(运行时间/平均负载/处理器信息) * 线程池信息 * tomcat会话信息 ## 实战 ### 实现自定义的条件装配 步骤: 1. 声明自己的条件类实现`Condition`接口 2. 在需要加上条件的bean上加上`@Conditional({你自己实现条件类})注解` 对应项目:[mycondition-demo](https://gitee.com/panleiming/my-SpringCloud/tree/master/mycondition-demo) ### 实现批量动态导入配置 步骤: 1. 实现`ImportSelector`接口(MyImportSelector) 2. 实现自己的`EnableAutoConfigration注解`,需要在注解上加上`import注解`(EnableAutoConfiguration) 3. 在主类上添加自己的`EnableAutoConfigration注解` 对应项目:[myselector-demo]() ### 实现自己的starter组件 步骤: 1. 声明自己的自动装配类(MyRedissonAutoConfiguration) 2. 声明自己的配置绑定类(MyRedissonProperties) 3. 在resources目录下添加META-INF目录,并在目录下创建spring.properties文件 4. 引入`spring-boot-configuration-processor`jar包,在META-INF下新增`additional-spring-configuration-metadata.json`,进行属性说明的编写 对应项目:[myredisssons-spring-boot-starter]() 对应测试项目:[starter-test]() ### 实现自己的actuator菜单 步骤: 1. 设置自己的菜单类,最好面向接口编程。接口命名规则最好遵循**菜单名+Indicator**,实现类命名规则最好遵循**功能+菜单名+Indicator**,例如:MailHealthIndicator 2. 在菜单栏的实现类上加上@EndPoint注解 3. 在需要调用的方法上加上@ReadOperation注解 4. 将该bean添加到到容器中 5. 在全局配置文件中开启菜单`management.endpoints.web.exposure.include=health,info,currentTime` 6. 访问菜单:http://ip:port/actuator/currentTime 对应项目:[myactuator-demo]()下的[indicator包]() ### springboot集成Prometheus和Grafana 步骤: 1. 引入以下依赖,springboot会自动装配一个`PrometheusMeterRegistry`和`CollectorRegistry`来收集和输出格式化的metrics数据,使得Prometheus服务器可以爬取 ```java io.micrometer micrometer-registry-prometheus ``` 2. 在全局配置文件中开启菜单`management.endpoints.web.exposure.include=health,info,currentTime,prometheus` 3. 访问菜单:http://ip:port/actuator/prometheus 4. [安装Prometheus]() 5. [安装Grafana]() # Netflix Ribbon ## 基础使用 ### 基础步骤 1. 引入依赖包 ```java org.springframework.cloud spring-cloud-starter-netflix-ribbon 2.2.3.RELEASE ``` 2. 在配置文件中配置提供者的地址列表 ```java # 配置指定服务提供者的地址列表,其中spring-cloud-order-service是serviceId spring-cloud-order-service.ribbon.listOfServers=localhost:8081,localhost:8082 ``` ### 第一种方式,直接使用LoadBalancerClient类 LoadBalancerClient类来对配置的服务器列表进行负载均衡算法返回当前可以请求的服务器地址 代码如下: ```java @RestController public class UserController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadbalanceClient; @GetMapping("/user/{orderId}") public String user(@PathVariable("orderId") Integer orderId) { // 让loadbalanceClient进行负载均衡算法,返回服务提供者的地址 ServiceInstance serviceInstance = loadbalanceClient.choose("spring-cloud-order-service"); String url = String.format("http://%s:%s/order/%d", serviceInstance.getHost(), serviceInstance.getPort(), orderId); return restTemplate.getForObject(url, String.class); } } ``` ### 第二种方式,使用@LoadBalanced注解类 利用@LoadBalanced对RestTemplate类进行拦截处理 代码如下: 配置类: ```java @Configuration public class RibbonUserConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } /** * 添加LoadBalanced注解,告诉LoadBalancerInterceptor对该类进行拦截 */ @Bean("loadBalanced") @LoadBalanced public RestTemplate loadBalancedRestTemplate() { return new RestTemplate(); } } ``` 调用类: ```java @RestController public class UserAnnotationController { @Autowired @Qualifier("loadBalanced") private RestTemplate restTemplate; @GetMapping("/userAnno/{orderId}") public String user(@PathVariable("orderId") Integer orderId) { // 直接调用服务id return restTemplate.getForObject("http://spring-cloud-order-service/order/" + orderId, String.class); } } ``` ## LoadBalanced注解原理 调用流程图: ![LoadBalanced注解调用过程](http://assets.processon.com/chart_image/5f0836720791290be029a87d.png) 首先查看@LoadBalanced注解的源码,可以发现主要是用到了`@Qualifier`注解,用于标记 ```java @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Qualifier public @interface LoadBalanced { } ``` 由于这是个starter组件,查询`RibbonAutoConfiguration`发现有这个自动配置类,源代码如下。其中`@AutoConfigureAfter`注解表示RibbonAutoConfiguration配置类在EurekaClientAutoConfiguration类之后装配;`@AutoConfigureBefore`注解表示RibbonAutoConfiguration配置类在LoadBalancerAutoConfiguration和AsyncLoadBalancerAutoConfiguration类之前装配。 ```java @Configuration @Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class) @RibbonClients @AutoConfigureAfter( name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration") @AutoConfigureBefore({ LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class }) @EnableConfigurationProperties({ RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class }) public class RibbonAutoConfiguration { ... } ``` 由于这个配置类中没有找到@LoadBalanced注解,继续查找,发现LoadBalancerAutoConfiguration和AsyncLoadBalancerAutoConfiguration这两个类都有@LoadBalanced,LoadBalancerAutoConfiguration类代码如下: ```java public class LoadBalancerAutoConfiguration { // 这里存储了所有被@LoadBalanced标记过的RestTemplate类 @LoadBalanced @Autowired(required = false) private List restTemplates = Collections.emptyList(); ... } ``` 对标记的原始的RestTemplate类进行处理,代码如下: ```java @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider> restTemplateCustomizers) { return () -> restTemplateCustomizers.ifAvailable(customizers -> { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } }); } ``` 继续往下找,发现RestTemplateCustomizer接口的实现类,,主要就是将**LoadBalancerInterceptor类**添加到RestTemplate类中。 ```java @Configuration(proxyBeanMethods = false) @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate") static class LoadBalancerInterceptorConfig { @Bean public LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; } } ``` 查看LoadBalancerInterceptor类的源码,查看拦截的逻辑: ```java /** * 实现了ClientHttpRequestInterceptor接口,就可以成为RestTemplate的拦截器 */ 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)); } @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); // loadBalancer对服务列表进行了解析 return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution)); } } ``` ## 实战 ### 基础使用 [多节点服务的提供者Order]() [客户端调用者User]() # OpenFeign(声明式的伪RPC) 面向接口开发 ## 基础使用 1. 引入依赖包: ```java org.springframework.cloud spring-cloud-starter-netflix-ribbon 2.2.3.RELEASE org.springframework.cloud spring-cloud-starter-openfeign 2.2.3.RELEASE ``` 2. 在application.properties中配置服务地址信息 ```java spring-cloud-provider.ribbon.listOfServers=localhost:8081 ``` 3. 声明调用接口类 ```java @FeignClient("spring-cloud-provider") public interface IProviderService { @GetMapping("/provide") String provide(); } ``` 4. 在启动类上添加@EnableFeignClients注解 ## 高级使用 ### 将底层httpclient替换成okhttp 1. 改写application.properties配置文件 ```java feign.okhttp.enabled=true feign.httpclient.enabled=false ``` 2. 添加okhttp的依赖 ```java io.github.openfeign feign-okhttp ``` ## 实战 ### 基础使用 [openfeign提供者]() [openfeign消费者]()