SpringBoot
- 约定优于配置(简单理解就是遵循约定、规范编程)
解决问题
- 起步依赖
- 自动配置
- 将一些配置的类自动加入IOC容器中,可以直接使用
启动
<!--所用的springBoot项目都会直接或者间接的继承spring-boot-starter-parent
1.指定项目的编码格式为UTF-8
2.指定JDK版本为1.8
3.对项目依赖的版本进行管理,当前项目再引入其他常用的依赖时就不需要再指定版本号,避免版本冲突 的问题
4.默认的资源过滤和插件管理-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<dependencies>
<!--引入Spring Web及Spring MVC相关的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!--可以将project打包为一个可以执行的jar-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
/**
* Application启动类通常放在二级包下
* 因为Springboot在启动时会默认扫描启动类所在的包及其子包下面的内容
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@RestController
@RequestMapping("/boot")
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "Hello world";
}
}
配置注入
- 使用@Value("${}")方式注入
- 无法注入List、Map等,会报错
- 只能注入String类型
@Configuration
public class Person {
@Value("${person.id}")
private int id;
@Value("${person.name}")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#person
person.id=188
person.name=苗珣
- 使用@ConfigurationProperties(prefix = “person”)方式注入
- 需要声明为Spring组件
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private int id;
private String name;
private List hobby;
private String[] family;
private Map map;
private Pet pet;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getHobby() {
return hobby;
}
public void setHobby(List hobby) {
this.hobby = hobby;
}
public String[] getFamily() {
return family;
}
public void setFamily(String[] family) {
this.family = family;
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", hobby=" + hobby +
", family=" + Arrays.toString(family) +
", map=" + map +
", pet=" + pet +
'}';
}
}
#person
person.id=188
person.name=苗珣
person.hobby=read,write
person.family=father,mather
person.map.key1=value1
person.map.key2=value2
person.pet.type=dog
person.pet.name=哈士奇
- 配置文件加载顺序
- properties>yaml>yml
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGq8SHhx-1621235126568)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507134733013.png)]
源码剖析
依赖管理
- 为什么引入dependency不需要指定版本?
- 我们自己的工程 --> spring-boot-boot-parent.pom --> spring-boot-dependencies.pom
- spring-boot-dependencies.pom定义好了依赖的版本号,且经过spring官方测试不存在兼容性问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gyjX5gxp-1621235126571)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507134355730.png)]
- spring-boot-parent.pom指定了java的版本和编码格式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D7SnU2Ec-1621235126573)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507134618861.png)]
- Spring-boot-starter-web
- 集成json、tomcat、spring-web、spring-webmvc
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rx33mJQ6-1621235126576)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507135154713.png)]
自动配置
SpringBootApplication
- @SpringBootApplication 内部原理
- @SpringBootApplication --> @EnableAutoConfiguration
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rUB5awzX-1621235126579)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507202109006.png)]
EnableAutoConfiguration
- @EnableAutoConfiguration注解
- @EnableAutoConfiguration --> @AutoConfigurationPackage
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JTTqdCNC-1621235126580)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507202137471.png)]
AutoConfigurationPackage
- @AutoConfigurationPackage注解
- @AutoConfigurationPackage --> @Import(AutoConfigurationPackages.Registrar.class)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X6zrkB6J-1621235126581)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507202319980.png)]
AutoConfigurationPackages.Registrar.class
- AutoConfigurationPackages.Registrar.class
- 通过打断点启动项目可知,启动项目一定会加载AutoConfigurationPackages.Registrar.class这个类
- AutoConfigurationPackages.Registrar.class 内部调用registerBeanDefinitions方法,传入元数据metadata
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EqlniYNL-1621235126582)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507202404916.png)]
- 传入metadata加载启动类,扫描二级包下的类注册装配到IOC容器中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ibPjC2RI-1621235126583)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507202636141.png)]
AutoConfigurationImportSelector.class
- @SpringBootApplication --> @EnableAutoConfiguration -->AutoConfigurationImportSelector.class
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sDQrMeju-1621235126584)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507210243093.png)]
selectImports
- AutoConfigurationImportSelector.class --> selectImports方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rv301DTt-1621235126585)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507210340487.png)]
getAutoConfigurationEntry
- selectImports方法 --> getAutoConfigurationEntry 方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NJaHcJsK-1621235126585)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507210423043.png)]
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
//判断EnableAutoConfiguration注解有没有开启
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//获得注解的属性信息
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获得候选配置列表(查询META-INF/spring.factories文件内容)
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//去重(ArrayList转换为LinkedHashSet)
configurations = removeDuplicates(configurations);
//去除一些多余的配置类,根据EnabledAutoConfiguratio的exclusions属性进行排除
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//根据pom文件中加入的依赖文件筛选中最终符合当前项目运行环境对应的自动配置类
configurations = getConfigurationClassFilter().filter(configurations);
//触发自动配置导入监听事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-StimEMvc-1621235126586)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507210942114.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hUp1DFf5-1621235126587)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507211029278.png)]
getCandidateConfigurations
- getAutoConfigurationEntry --> getCandidateConfigurations
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fkg6V67w-1621235126588)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507212105186.png)]
- getCandidateConfigurations --> loadFactoryNames
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W4SMOOjo-1621235126588)(/Users/miaoxun3/Library/Application Support/typora-user-images/image-20210507212153219.png)]
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//获取出入的键
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
//加载类路径下spring.factories文件,将其中设置的 配置类的全路径信息封装 为Enumeration类对象
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
//循环Enumeration类对象,根据相应的节点信息生成Properties对象,通过传入的 键获取值,在将值切割为一个个小的字符串转化为Array,方法result集合中
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
ComponentScan
|- @SpringBootConfiguration |- @Configuration //通过javaConfig的方式来添加组件到IOC容器中 |- @EnableAutoConfiguration |- @AutoConfigurationPackage //自动配置包,与@ComponentScan扫描到的添加到IOC |- @Import(AutoConfigurationImportSelector.class) //到META- INF/spring.factories中定义的bean添加到IOC容器中|- @ComponentScan //包扫描
整合Mybatis
整合Redis
Thymeleaf
SpringCloud
垂直应用架构
SOA应用架构
微服务应用架构
Eureka注册中心
- Eureka Server
server:
port: 9200
spring:
application:
name: lagou-cloud-eureka-server # 应用名称,会在Eureka中作为服务的id标识(serviceId)
eureka:
instance:
hostname: localhost
client:
service-url: # 客户端与EurekaServer交互的地址,如果是集群,也需要写其它Server的地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
register-with-eureka: false # 自己就是服务不需要注册自己
fetch-registry: false # 自己就是服务不需要从Eureka Server获取服务信息,默认为true,置为false
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
- Eureka Client
eureka: client: serviceUrl: # eureka server的路径 defaultZone: http://localhost:9200/eureka/ instance: #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip) prefer-ip-address: true #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
@SpringBootApplication@EnableEurekaClientpublic class PageApplication { public static void main(String[] args) { SpringApplication.run(PageApplication.class, args); } @Bean public RestTemplate restTemplate(){ return new RestTemplate(); }}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yIWooiPn-1621235126589)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210511124243857.png)]
Ribbon负载均衡
- 基于客户端的负载均衡:Ribbon
- 基于服务端的负载均衡:Nginx、F5等
1、copy两个商品服务提供者
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vNCrLys4-1621235126590)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210511125030765.png)]
2、修改page客户端
- 启动类
@SpringBootApplication//@EnableEurekaClient@EnableDiscoveryClientpublic class PageApplication { public static void main(String[] args) { SpringApplication.run(PageApplication.class, args); } @Bean @LoadBalanced //开启客户端负载均衡 public RestTemplate restTemplate(){ return new RestTemplate(); }}
- Controller
@RestController@RequestMapping("/page")public class PageController { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @GetMapping("/getData/{id}") public Product findDataById(@PathVariable Integer id) { String url = "http://lagou-service-product/product/query/" + id; Product product = restTemplate.getForObject(url, Product.class); System.out.println("从lagou-service-product获得product对象:" + product); return product; }}
- 高级应用
- IRule.class 负载均衡策略接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MJ9GuKDK-1621235126590)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210511131300012.png)]
负载均衡策略
负载均衡策略 | 描述 |
---|---|
RoundRobinRule:轮询策略 | 默认超过10次获取到的server都不可用,会返回一个空的server |
RandomRule:随机策略 | 如果随机到的server为null或者不可用的话,会while不停的循环选取 |
RetryRule:重试策略 | 一定时限内循环重试。默认继承RoundRobinRule,也支持自定义注入,RetryRule会在每次选取之后,对选举的server进行判断,是否为null,是否alive,并且在500ms内会不停的选取判断。而RoundRobinRule失效的策略是超过10次,RandomRule是没有失效时间的概念,只要serverList没都挂。 |
BestAvailableRule:最小连接数策略 | 遍历serverList,选取出可用的且连接数最小的一个server。该算法里面有一个LoadBalancerStats的成员变量,会存储所有server的运行状况和连接数。如果选取到的server为null,那么会调用RoundRobinRule重新选取。 |
AvailabilityFilteringRule:可用过滤策略 | 扩展了轮询策略,会先通过默认的轮询选取一个server,再去判断该server是否超时可用,当前连接数是否超限,都成功再返回。 |
ZoneAvoidanceRule:区域权衡策略**(默认策略)** | 扩展了轮询策略,继承了2个过滤器:ZoneAvoidancePredicate和AvailabilityPredicate,除了过滤超时和链接数过多的server,还会过滤掉不符合要求的zone区域里面的所有节点, 在一个区域/机房内的服务实例中轮询。先过滤再轮询 |
- 工作原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TDFn0Fpu-1621235126591)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210511175047456.png)]
/* * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.cloud.client.loadbalancer;import org.springframework.beans.factory.ObjectProvider;import org.springframework.beans.factory.SmartInitializingSingleton;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.client.ClientHttpRequestInterceptor;import org.springframework.retry.backoff.BackOffPolicy;import org.springframework.retry.support.RetryTemplate;import org.springframework.web.client.RestTemplate;import java.util.ArrayList;import java.util.Collections;import java.util.List;/** * Auto-configuration for Ribbon (client-side load balancing). * * @author Spencer Gibb * @author Dave Syer * @author Will Tran * @author Gang Li */@Configuration@ConditionalOnClass(RestTemplate.class)@ConditionalOnBean(LoadBalancerClient.class)@EnableConfigurationProperties(LoadBalancerRetryProperties.class)public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) { return () -> restTemplateCustomizers.ifAvailable(customizers -> { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } }); } @Autowired(required = false) private List<LoadBalancerRequestTransformer> 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); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; } } @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryAutoConfiguration { @Bean @ConditionalOnMissingBean public LoadBalancedRetryFactory loadBalancedRetryFactory() { return new LoadBalancedRetryFactory() {}; } } @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryInterceptorAutoConfiguration { @Bean @ConditionalOnMissingBean public RetryLoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory loadBalancedRetryFactory) { return new RetryLoadBalancerInterceptor(loadBalancerClient, properties, requestFactory, loadBalancedRetryFactory); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final RetryLoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; } }}
Hystrix熔断器
保险丝
调用链路
服务雪崩效应
- 服务的提供者不可用,导致服务的调用者也不可用了
服务熔断
@HystrixCommand( // 线程池标识,要保持唯一,不唯一的话就共用了 threadPoolKey = "getProductServerPort3TimeoutFallback", // 线程池细节属性配置 threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "2"), // 线程数 @HystrixProperty(name = "maxQueueSize", value = "20") // 等待队列长度 }, // commandProperties熔断的一些细节属性配置 commandProperties = { // 每一个属性都是一个HystrixProperty @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000"), // hystrix高级配置,定制工作过程细节 // 统计时间窗口定义 @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "8000"), // 统计时间窗口内的最小请求数 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2"), // 统计时间窗口内的错误数量百分比阈值 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"), // 自我修复时的活动窗口长度 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "3000") }, fallbackMethod = "myFallBack" // 回退方法 )
舱壁模式
- 线程池隔离策略:每一个控制方法都有一个单独的线程池
- 如果服务A把公用的线程池占满,那么会导致服务B也不可用,因此Hystrix为了避免问题服务请求过多导致正常服务不可用,单独为每一个控制方法创建一个专属的线程池,以线程池名称为唯一标识,这种模式叫做舱壁模式
工作流程
1)当调用出现问题时,开启一个时间窗(10s)
2)在这个时间窗内,统计调用次数是否达到最小请求数?
如果没有达到,则重置统计信息,回到第1步
如果达到了,统计失败的请求数占所有请求数的百分比,是否达到阈值?
如果达到,则发生熔断(不再请求对应服务)
如果没有达到,则重置统计信息,回到第1步
3)如果跳闸,则会开启一个活动窗口(默认5s),每隔5s,Hystrix会让一个请求通过,到达那个问题服
务,看是否调用成功,如果成功,重置断路器回到第1步,如果失败,回到第3步
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FD3b00yh-1621235126592)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210512100506252.png)]
# 配置熔断策略:hystrix: command: default: circuitBreaker: # 强制打开熔断器,如果该属性设置为true,强制断路器进入打开状态,将会拒绝所有的 请求。默认false关闭的 forceOpen: false # 触发熔断错误比例阈值,默认值50% errorThresholdPercentage: 50 # 熔断后休眠时长,默认值5秒 sleepWindowInMilliseconds: 3000 # 熔断触发最小请求次数,默认值是20 requestVolumeThreshold: 2 execution: isolation: thread: # 熔断超时设置,默认为1秒 timeoutInMilliseconds: 2000
- 基于springboot的健康检查观察跳闸状态(自动投递微服务暴露健康检查细节)
- 健康检查接口http://localhost:9100/actuator/health
# springboot中暴露健康检查等断点接口management: endpoints: web: exposure: include: "*"# 暴露健康接口的细节 endpoint: health: show-details: always
Hystrix 线程池队列配置案例:
有一次在生产环境,突然出现了很多笔还款单被挂起,后来排查原因,发现是内部系统调用时出现了
Hystrix调用异常。在开发过程中,因为核心线程数设置的比较大,没有出现这种异常。放到了测试环
境,偶尔有出现这种情况。
后来调整maxQueueSize属性,确实有所改善。可没想到在生产环境跑了一段时间后却又出现这种了
情况,此时我第一想法就是去查看maxQueueSize属性,可是maxQueueSize属性是设置值了。
当时就比较纳闷了,为什么maxQueueSize属性不起作用,后来通过查看官方文档发现Hystrix还有一
个queueSizeRejectionThreshold属性,这个属性是控制队列最大阈值的,而Hystrix默认只配置了5个,
因此就算我们把maxQueueSize的值设置再大,也是不起作用的。两个属性必须同时配置
hystrix: threadpool: default: coreSize: 10 #并发执行的最大线程数,默认10 maxQueueSize: 1000 #BlockingQueue的最大队列数,默认值-1 queueSizeRejectionThreshold: 800 #即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝,默认值5
正确的配置案例:
- 将核心线程数调低,最大队列数和队列拒绝阈值的值都设置大一点:
hystrix: threadpool: default: coreSize: 10 maxQueueSize: 1500 queueSizeRejectionThreshold: 1000
Feign远程调用
- 消费方定义Feign接口
/** * 自定义Feign接口,调用Product商品微服务的接口都在此定义 */@FeignClient(name = "lagou-service-product") //提供方namepublic interface ProductFeign { @GetMapping("/product/query/{id}") //提供方完整路径 public Product selectById(@PathVariable Integer id);}
- Controller实现
@RestController@RequestMapping("/page")public class PageController { @Autowired private ProductFeign productFeign; //引入自定义Feign接口 @GetMapping("/getData/{id}") public Product findDataById(@PathVariable Integer id) { return productFeign.selectById(id); //直接返回自定义Feign接口 }}
Gateway网关
- 启动类
@SpringBootApplication@EnableDiscoveryClient //注册到Eureka注册中心public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); }}
- 配置路由
server: port: 9300eureka: client: serviceUrl: # eureka server的路径 defaultZone: http://localhost:9200/eureka instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@spring: application: name: lagou-cloud-gateway cloud: gateway: # 路由 routes: - id: service-page-router uri: http://localhost:9100 predicates: - Path=/page/** - id: service-product-router uri: http://localhost:9000 predicates: - Path=/product/**
Spring Cloud Config
- 启动类
@SpringBootApplication@EnableDiscoveryClient //注册到Eureka注册中心@EnableConfigServer //启用配置服务public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); }}
- 配置文件
server: port: 9400 # 后期该微服务多实例,端口从9100递增(10个以内)Spring: application: name: lagou-cloud-config cloud: config: server: # git配置 git: uri: https://gitee.com/miaoo/config.git username: miaoo password: miaoxun123 search-paths: - config # 分支 label: mastereureka: client: serviceUrl: # eureka server的路径 defaultZone: http://localhost:9200/eureka/ instance: #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip) prefer-ip-address: true #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
- 通过配置中心找到配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9FMUhQAw-1621235126592)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210513203941095.png)]
- 配置服务消费者
- 修改application.yml --> bootstrap.yml
- 配置config server
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PvT1SrNx-1621235126593)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210513205235245.png)]
- 远程配置中心配置文件内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NxN62TAp-1621235126594)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210513205923579.png)]
-
启动服务访问配置中心内容
@RestController@RequestMapping("/config")public class ConfigController { @Value("${user.name}") private String name; @Value("${user.age}") private Integer age; @RequestMapping("/getConfig") public String getPort(){ return name + age; }}
-
成功访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lAjP4NmQ-1621235126595)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210513210022239.png)]
手动刷新
- Client添加refresh端口
management: endpoints: web: exposure: include: "*"
- Controller添加@RefreshScope注解
@RestController@RequestMapping("/config")@RefreshScopepublic class ConfigController { @Value("${user.name}") private String name; @Value("${user.age}") private Integer age; @RequestMapping("/getConfig") public String getPort(){ return name + age; }}
- 发送post请求http://localhost:9100/actuator/refresh
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bqmbwTfj-1621235126595)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210513214135818.png)]
- 再次请求,最新数据已刷新
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Iro7S5m-1621235126596)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210513214323265.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-redLCqrR-1621235126597)(C:\Users\miaoxun3\AppData\Roaming\Typora\typora-user-images\image-20210513214155429.png)]
自动刷新
-
消息总线BUS(MQ中间件)使用广播机制,通知消息消费者配置已变更
- MQ的广播消息会被所有注册在注册中心的服务监听、消费
-
配置rabbitmq(Config Server 和 Client 都需要配置)
Spring: rabbitmq: host:# port: 5672(默认值)# username: guest(默认值)# password: guest(默认值)
Spring Cloud Alibaba
Nacos
- Nacos = Eureka+Config+Bus 注册中心+配置中心+消息总线