# 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注解的源码,可以发现主要是用到了`@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消费者]()