目录
微服务之间的调用
使用RestTemplate
配置RestTemplate
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
80端口的controller方法直接调用
@RestController
@Slf4j
public class OrderController {
public static final String PAYMENT_URL = "http://localhost:8001";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment){
return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}
注: RestTemplate发送的为json字符串,8001接收端口要加@RequestBody注解解析
@PostMapping("/payment/create")
public CommonResult<Payment> create(@RequestBody Payment payment) {
int result = paymentService.create(payment);
log.info("*****插入结果:{}",result);
if(result>0){
return new CommonResult(200,"插入数据库成功",result);
}else{
return new CommonResult(444,"插入数据库失败",null);
}
}
工程重构
将所有微服务中相同的部分集中到一起,用引用的的方式使各个微服务代码简洁
1.新建Model 如:cloud-api-commons
2.修改cloud-api-commons POM文件 (最好添加hutool包)
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.1.0</version> </dependency> </dependencies>
3.cloud-api-commons中放入各个微服务重复的文件
4.执行Maven命令clean install
5.改造之前微服务端口,删除重复的部分,在报错的Controller文件位置Alt+Enter导入新添加依赖
6.完成
Eureak服务注册与发现
单机Eureak构建
创建eureakServer端注册服务中心
1.创建项目 cloud-eureka-server7001
2.改pom,主要添加下面配置
<!--eureka-server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
3.写yml
server: port: 7001 eureka: instance: hostname: eureka7001.com #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4.配置主启动main 添加注释@EnableEurekaServer
@SpringBootApplication @EnableEurekaServer public class EurekaMain7001 { public static void main(String[] args) { SpringApplication.run(EurekaMain7001.class, args); } }
5.进入http://localhost:7001 查看配置是否成功
eureakClient端8001注册进eureakServer成为服务提供者provider
1.pom添加
<!--eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
2.yml文件添加
eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: defaultZone: http://localhost:7001/eureka
3.8001 主启main动添加注释 @EnableEureakClient
4.先启动7001,后启动8001查看添加是否成功
eureakClient端80注册进eureakServer成为服务消费者consumer
1.80端口添加pom(同上)
2.修改yml文件
server: port: 80 spring: application: name: cloud-order-service eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: defaultZone: http://localhost:7001/eureka
3.80 主启动main添加注释 @EnableEureakClient
4.测试(同上)
集群Eureak构建
集群环境构建
参照单机方式创建 cloud-eureka-server7002 ,改pom,主启动添加注释
修改yml 7002与7001相互绑定
server: port: 7002 eureka: instance: hostname: eureka7002.com #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: defaultZone: http://eureka7001.com:7001/eureka/
修改电脑C:\Windows\System32\drivers\etc下的hosts文件,添加以下内容
127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com
将服务发布到集群配置中
参照8001构建8002等
修改8001,8002,80等所有服务yml文件中eurker的配置信息
spring: application: name: cloud-payment-service datasource: type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型 driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包 com.mysql.jdbc.Driver url: jdbc:mysql://localhost:13306/springcloud?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: abc123 eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: defaultZone: http://localhost:7001/eureka,http://eureka7002.com:7002/eureka instance: #修改7001页面显示的本服务信息 instance-id: payment8002 #开启访问信息有IP信息提示 prefer-ip-address: true mybatis: mapperLocations: classpath:mapper/*.xml type-aliases-package: com.gr.springcloud.entities # 所有Entity别名类所在包
修改8001,8002等的controller类 添加 String servePort 属性接收端口号信息
@RestController @Slf4j public class PaymentController { @Value("${server.port}") private String serverPort; @Resource private PaymentService paymentService; @PostMapping(value = "/payment/create") public CommonResult create(@RequestBody Payment payment) { int result = paymentService.create(payment); log.info("*****插入操作返回结果:" + result); if(result > 0) { return new CommonResult(200,"插入成功,返回结果"+result+"\t 服务端口:"+serverPort,payment); }else{ return new CommonResult(444,"插入失败",null); } } @GetMapping(value = "/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) { Payment payment = paymentService.getPaymentById(id); log.info("*****查询结果:{}",payment); if (payment != null) { return new CommonResult(200,"查询成功"+"\t 服务端口:"+serverPort,payment); }else{ return new CommonResult(444,"没有对应记录,查询ID: "+id,null); } } }
80消费者端口controller文件中静态变量修改为 CLOUD-PAYMENT-SERVICE 7001页面显示的端口名称
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
80端口config配置的RestTemplate微服务调用业务上添加@LoadBalanced注解赋予负载均衡的能力
@Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); }
测试:先启动7001,7002. 再启动8001,8002,80查看7001页面显示是否正常
Ribbon负载均衡服务调用
负载均衡:
集中式:即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
进程内:将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
一句话总结:负载均衡+RestTemplate调用
之前Eureak的spring-cloud-starter-netflix-eureka-client jar包默认已经引入了Ribbon的依赖
Ribbon单独的依赖为
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
Ribbon核心组件IRule接口下有7个常用的算法:
RoundRobinRule: 轮询
RandomRule: 随机
RetryRule 先按照轮询的策略获取服务,若获取服务失败则在指定时间内重试,获取可用的服务
WeightedResponseTimeRule 对 RoundRobinRule的扩展,对响应速度越快的实例选择权重越大,越容易被选择
BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
ZoneAvoidanceRule 默认规则,复合判断Server所在区域的性能和server的可用性选择服务器
如何替换
新建配置类Config,这个文件不能够和主启动类在同一个文件夹(也就是com.gr.springboot路径)下,要在com.gr路径下新建文件夹,配置config
@Configuration
public class MySelfRule {
@Bean
public IRule myRule(){
return new RandomRule();//定义为随机
}
}
当前微服务主启动类上添加注释 @RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration= MySelfRule.class)
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration= MySelfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}
OpenFeign服务接口调用
Feign是一个声明式的WEB服务客户端,让编写WEB服务客户端变得非常容易,只需要创建一个接口,并在接口上添加注解即可
Feign是在微服务消费者端(80)使用的: 服务调用接口+@FeignClient
Feign自带负载均衡配置项 Ribbon
重新构造80端口: cloud-consumer-feign-order80
1.写pom
<!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
2.改yml
3.写主启动类 @EnableDiscoveryClients ---> @EnableFeignClients(开启Feign)
@SpringBootApplication @EnableFeignClients public class OrderFeignMain80 { public static void main(String[] args){ SpringApplication.run(OrderFeignMain80.class,args); } }
4.业务类 新建Service接口并添加注解@FeignClient (使用Feign)
@Service @FeignClient("CLOUD-PAYMENT-SERVICE") //与服务提供类建立连接 public interface PaymentFeignService { //以服务提供类Controller层的方法建立接口 @GetMapping("/payment/get/{id}") CommonResult<Payment> getPaymentById(@PathVariable("id") Long id); }
5.控制层Controller 调用Service层组件
@RestController @Slf4j public class OrderFeignController { @Resource private PaymentFeignService paymentFeignService; @GetMapping("/consumer/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){ return paymentFeignService.getPaymentById(id); } }
6.测试 7001-->7002-->8001-->8002-->80
OpenFeign超时控制
默认Feign客户端只等待一秒钟,但是服务端处理需要超过1秒钟,导致Feign客户端不想等待了,直接返回报错。为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。
Feign80端口微服务yml文件中开启配置 (时间单位毫秒)
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
OpenFeign日志打印
Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节.说白了就是对Feign接口的调用情况进行监控和输出
日志级别:
NONE:默认的,不显示任何日志;
BASIC:仅记录请求方法、URL、响应状态码及执行时间;
HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;
FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。
配置Bean
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;//设置日志级别
}
}
yml文件开启功能
logging:
level:
# feign日志以什么级别监控哪个接口
com.gr.springcloud.service.PaymentFeignService: debug
Hystrix断路器
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
提供三个功能
服务降级: 程序运行异常; 超时; 服务熔断; 线程池/信号量打满 等情况下不让客户端等待,并返回一个友好提示
服务熔断: 达到最大服务访问后,直接拒绝访问,然后调用服务降级的方法返回友好提示
服务限流: 高能并发场景下,限定一秒钟放行N个
服务降级
服务提供者类启用
业务类中在可能出问题的方法上加 @HystrixCommand 注解,fallbackMethod属性内选定兜底的方法名.
@Service public class PaymentService { @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000")//设定等待时间5秒 }) public String paymentInfo_TimeOut(Integer id){ try { TimeUnit.MILLISECONDS.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return "线程池:"+Thread.currentThread().getName()+"id: "+id+"\t"+"O(∩_∩)O,耗费:"+"秒"; } public String paymentInfo_TimeOutHandler(Integer id){ return "线程池:"+Thread.currentThread().getName()+"8001系统繁忙或运行错误,请稍后再试,id: "+id+"\t"+"┭┮﹏┭┮"; } }
主启动类添加 @EnableCircuitBreaker 注解
常用方式: 服务消费者类启用
配置的热部署方式对JAVA代码的改动明显,但是对@HystrixCommand 内属性的修改建议重启微服务
yml配置开启 enabled
server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/ feign: hystrix: enabled: true
主启动类加@EnableHystrix注解
业务类中在可能出问题的方法上加 @HystrixCommand 注解,fallbackMethod属性内选定兜底的方法名.
@RestController @Slf4j public class PaymentHystirxController{ @GetMapping("/consumer/payment/hystrix/timeout/{id}") @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500") }) public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_TimeOut(id); return result; }
优化
代码膨胀:
每个方法都写一个兜底方法代码太过膨胀,所以最好集中起来统一管理
在业务类上加注解@DefaultProperties,其中 defaultFallback 属性指定兜底方法
在可能需要服务降级的方法上添加 @HystrixCommand 注解.如果注解内有指定的方法名就用此注解内指定的.否则就用通用的方法
@RestController @Slf4j @DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") public class OrderHystirxController { @GetMapping("/consumer/payment/hystrix/timeout/{id}") @HystrixCommand //加了@DefaultProperties属性注解,并且没有写具体方法名字,就用统一全局的 public String paymentInfo_TimeOut(@PathVariable("id") Integer id) { int age = 10/0; String result = paymentHystrixService.paymentInfo_TimeOut(id); return result; } //下面是全局fallback方法 public String payment_Global_FallbackMethod() { return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~"; } }
代码混乱
在80端口改动实现解耦
根据已有的80端口Feign的业务接口 PaymentHystrixService 新建一个实现类,用以为接口里的方法进行异常处理
@Component //一定要加 public class PaymentFallbackService implements PaymentHystrixService { @Override public String paymentInfo_OK(Integer id) { return "进程发生错误:paymentInfo_OK,/(ㄒoㄒ)/~~"; } @Override public String paymentInfo_TimeOut(Integer id) { return "进程发生错误:paymentInfo_TimeOut,/(ㄒoㄒ)/~~"; } }
tml文件开启Hystrix
feign: hystrix: enabled: true #在Feign中开启Hystrix
修改80端口Feign的业务接口 PaymentHystrixService 的@FeignClient注解,指定异常处理类
@Service @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class) public interface PaymentHystrixService { @GetMapping("/payment/hystrix/ok/{id}") String paymentInfo_OK(@PathVariable("id") Integer id); @GetMapping("/payment/hystrix/timeout/{id}") String paymentInfo_TimeOut(@PathVariable("id") Integer id); }
测试
服务熔断
熔断机制概述
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
当检测到该节点微服务调用响应正常后,恢复调用链路。
在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。
熔断机制的注解是@HystrixCommand。
熔断类型
熔断打开 再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback。通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。
熔断关闭 业务正常运行
熔断半开 对于这一问题,hystrix也为我们实现了自动恢复功能。当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑,当休眠时间窗到期,断路器将进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。
8001服务提供端口 Service层配置熔断器及其降级逻辑
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), //请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), //时间范围
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"), //失败率达到多少后跳闸
})
public String paymentCircuitBreaker(Integer id) {
if (id < 0) {
throw new RuntimeException("******id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(Integer id) {
return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " + id;
}
}
熔断器所有配置属性
//========================All
@HystrixCommand(fallbackMethod = "str_fallbackMethod",
groupKey = "strGroupCommand",
commandKey = "strCommand",
threadPoolKey = "strThreadPool",
commandProperties = {
// 设置隔离策略,THREAD 表示线程池 SEMAPHORE:信号池隔离
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
// 当隔离策略选择信号池隔离的时候,用来设置信号池的大小(最大并发数)
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 配置命令执行的超时时间
@HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
// 是否启用超时时间
@HystrixProperty(name = "execution.timeout.enabled", value = "true"),
// 执行超时的时候是否中断
@HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
// 执行被取消的时候是否中断
@HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),
// 允许回调方法执行的最大并发数
@HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 服务降级是否启用,是否执行回调函数
@HystrixProperty(name = "fallback.enabled", value = "true"),
// 是否启用断路器
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
// 该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为 20 的时候,
// 如果滚动时间窗(默认10秒)内仅收到了19个请求, 即使这19个请求都失败了,断路器也不会打开。
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
// 该属性用来设置在滚动时间窗中,表示在滚动时间窗中,在请求数量超过
// circuitBreaker.requestVolumeThreshold 的情况下,如果错误请求数的百分比超过50,
// 就把断路器设置为 "打开" 状态,否则就设置为 "关闭" 状态。
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
// 该属性用来设置当断路器打开之后的休眠时间窗。 休眠时间窗结束之后,
// 会将断路器置为 "半开" 状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为 "打开" 状态,
// 如果成功就设置为 "关闭" 状态。
@HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),
// 断路器强制打开
@HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
// 断路器强制关闭
@HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),
// 滚动时间窗设置,该时间用于断路器判断健康度时需要收集信息的持续时间
@HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
// 该属性用来设置滚动时间窗统计指标信息时划分"桶"的数量,断路器在收集指标信息的时候会根据
// 设置的时间窗长度拆分成多个 "桶" 来累计各度量值,每个"桶"记录了一段时间内的采集指标。
// 比如 10 秒内拆分成 10 个"桶"收集这样,所以 timeinMilliseconds 必须能被 numBuckets 整除。否则会抛异常
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
// 该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false, 那么所有的概要统计都将返回 -1。
@HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
// 该属性用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
@HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
// 该属性用来设置百分位统计滚动窗口中使用 “ 桶 ”的数量。
@HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
// 该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设定值的执行次数,
// 就从最初的位置开始重写。例如,将该值设置为100, 滚动窗口为10秒,若在10秒内一个 “桶 ”中发生了500次执行,
// 那么该 “桶” 中只保留 最后的100次执行的统计。另外,增加该值的大小将会增加内存量的消耗,并增加排序百分位数所需的计算时间。
@HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
// 该属性用来设置采集影响断路器状态的健康快照(请求的成功、 错误百分比)的间隔等待时间。
@HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),
// 是否开启请求缓存
@HystrixProperty(name = "requestCache.enabled", value = "true"),
// HystrixCommand的执行和事件是否打印日志到 HystrixRequestLog 中
@HystrixProperty(name = "requestLog.enabled", value = "true"),
},
threadPoolProperties = {
// 该参数用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量
@HystrixProperty(name = "coreSize", value = "10"),
// 该参数用来设置线程池的最大队列大小。当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列,
// 否则将使用 LinkedBlockingQueue 实现的队列。
@HystrixProperty(name = "maxQueueSize", value = "-1"),
// 该参数用来为队列设置拒绝阈值。 通过该参数, 即使队列没有达到最大值也能拒绝请求。
// 该参数主要是对 LinkedBlockingQueue 队列的补充,因为 LinkedBlockingQueue
// 队列不能动态修改它的对象大小,而通过该属性就可以调整拒绝请求的队列大小了。
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
}
)
public String strConsumer() {
return "hello 2020";
}
public String str_fallbackMethod()
{
return "*****fall back str_fallbackMethod";
}
GateWay网关
三大核心概念
路由(Route) 构建网关的基本模块,它由ID,目标URL,一系列的断言和过滤组成,如果断言为true则匹配该路由
断言(Predicate) 匹配HTTP请求中的所有内容,如果请求与断言相匹配则进行路由
过滤(Filter) 指的是Spring框架中的GatewayFilter的实例,使用过滤器,可以在请求被路由前或者后对请求进行修改
总体: web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。
predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了
基本配置
新建9527端口微服务,将8001;8002服务提供端口套起来
pom
<!--gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
yml
server: port: 9527 spring: application: name: cloud-gateway cloud: gateway: routes: - id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/get/** # 断言,路径相匹配的进行路由 - id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/lb/** # 断言,路径相匹配的进行路由 eureka: instance: hostname: cloud-gateway-service client: #服务提供者provider注册进eureka服务列表内 service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka
9527端口没有业务类
主启动类
@SpringBootApplication @EnableEurekaClient public class GateWayMain9527{ public static void main(String[] args){ SpringApplication.run(GateWayMain9527.class,args); } }
测试
7001-->8001-->9527
添加网关前: http://localhost:8001/payment/get/2
添加网关后: http://localhost:9527/payment/get/2
通过微服务实现动态路由
pom
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
yml uri的协议为lb,表示其用Gateway的负载均衡功能
server: port: 9527 spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由 routes: - id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 # uri: http://localhost:8001 #匹配后提供服务的路由地址 uri: lb://cloud-payment-service #匹配后提供服务的路由地址 predicates: - Path=/payment/get/** # 断言,路径相匹配的进行路由 - id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 # uri: http://localhost:8001 #匹配后提供服务的路由地址 uri: lb://cloud-payment-service #匹配后提供服务的路由地址 predicates: - Path=/payment/lb/** # 断言,路径相匹配的进行路由 eureka: instance: hostname: cloud-gateway-service client: #服务提供者provider注册进eureka服务列表内 service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka
测试 7001-->8001-->8002-->9527 访问业务提供类的lb方法 http://localhost:9527/payment/lb 返回8001,8002来回切换
Predicate(断言)的使用
说白了Predicate就是实现一组匹配规则,让请求过来找对应的Route进行处理
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
- After=2020-02-05T15:10:03.685+08:00[Asia/Shanghai] # 断言,路径相匹配的进行路由
#- Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai] # 断言,路径相匹配的进行路由
#- Between=2020-02-02T17:45:06.206+08:00[Asia/Shanghai],2020-03-25T18:59:06.206+08:00[Asia/Shanghai]
#- Cookie=username,zzyy
#- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
#- Host=**.gr.com
- Method=GET #请求方式为GET
- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
#id:我们自定义的路由 ID,保持唯一
##uri:目标服务地址
##predicates:路由条件,Predicate接受一个输入参数返回一个布尔值。
## 该属性包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)
Filter的使用
自定义过滤器
新建一个类,实现 implements GlobalFilter, Ordered 两个接口
@Component @Slf4j public class MyLogGateWayFilter implements GlobalFilter, Ordered { //重写接口内两个方法 @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("*************come in MyLogGateWayFilter"+new Date()); //判断传输来的值是否带有key:uname String uname = exchange.getRequest().getQueryParams().getFirst("uname"); if(uname == null){ log.info("*************用户名为null,非法用户,/(ㄒoㄒ)/~~"); exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } //设置优先级,数字越小优先级越高 @Override public int getOrder() { return 0; } }
测试: 7001-->8001-->8002-->9527
访问http://localhost:9527/payment/lb?uname=23 成功访问http://localhost:9527/payment/lb失败
Config分布式配置中心
与GitHub整合实现一处修改处处生效
首先配置好GitHub
新建3344 Cloud的配置中心模块
pom
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
yml
server: port: 3344 spring: application: name: cloud-config-center #注册进Eureka服务器的微服务名 cloud: config: server: git: uri: git@github.com:GR/springcloud-config.git #GitHub上面的git仓库名字 ####搜索目录 search-paths: - springcloud-config ####读取分支 label: master #服务注册到eureka地址 eureka: client: service-url: defaultZone: http://localhost:7001/eureka
主启动类 添加注解为@EnableConfigServer
@SpringBootApplication @EnableConfigServer public class ConfigCenterMain3344{ public static void main(String[] args){ SpringApplication.run(ConfigCenterMain3344.class, args); } }
启动3344测试是否可以从GitHub上获取配置内容 http://localhost:3344/master/config-dev.yml
/{name}-{profiles}.yml
/{label}-{name}-{profiles}.yml
label:分支(branch)
name :服务名
profiles:环境(dev/test/prod)
Config客户端配置
新建3355客户端
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
新建 bootstrap.yml yml文件 要将Client模块下的application.yml文件改为bootstrap.yml,这是很关键的,因为bootstrap.yml是比application.yml先加载的。bootstrap.yml优先级高于application.yml
server: port: 3355 spring: application: name: config-client cloud: #Config客户端配置 config: label: master #分支名称 name: config #配置文件名称 profile: dev #读取后缀名称 上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml uri: http://localhost:3344 #配置中心地址k #服务注册到eureka地址 eureka: client: service-url: defaultZone: http://localhost:7001/eureka
主启动,添加到Eureak中
编写简单业务类,测试功能
@RestController public class ConfigClientController{ @Value("${config.info}") private String configInfo; @GetMapping("/configInfo") public String getConfigInfo(){ return configInfo; } }
测试: 7001-->3344-->3355 http://localhost:3355/configinfo
Config客户端动态刷新
如上配置之后每次GitHub修改配置文件,3344可以立即同步.而3355必须重启服务才能同步.解决:
确保pom有以下模块
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
yml文件下设置暴露监控断点 (添加)
# 暴露监控端点 management: endpoints: web: exposure: include: "*"
业务类Controller类上添加自动刷新注释: @RefreshScope
此时运维发送POST请求刷新3355即可同步