SpringCloud微服务学习----------服务容错

如图 A调用B B调用C C服务挂了 然后B会不停的调用C B会重试同步等待会导致资源耗尽 B也挂了 同理A也挂了

服务降级 :有种弃车保帅的感觉,比如双11来了 , 一个服务器的资源就那么多 ,就会让 查询商品,购买商品等业务占用更多的资源,而其他非核心业务成为了弱可用业务

下面来敲敲代码,首先引入依赖

<!-- 		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
		</dependency> -->
		<dependency>
		   <groupId>org.springframework.cloud</groupId>
		   <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
		</dependency>

启动类加入注解

package com.sola;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootApplication
@EnableSwagger2
@EnableEurekaClient
//重点
@EnableCircuitBreaker
public class EurekaClientPostManApplication {

	public static void main(String[] args) {
		SpringApplication.run(EurekaClientPostManApplication.class, args);
	}
}

这是种省事的开启方法 先围观一下 下面来写一下controller

package com.sola.controller;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.sola.service.ResourcesService;

import io.swagger.annotations.ApiOperation;

@RestController
@RequestMapping("Hystrix")
public class HystrixController {

	@Autowired
	private ResourcesService resourcesservice;
	
	
	@GetMapping(value="test")
	@ApiOperation("小测试")
	//这里是指定的方法名要去实现
	@HystrixCommand(fallbackMethod = "fallback")
	public String oneTest(){
		
		RestTemplate resttemplate = new RestTemplate();
		
		String forObject = resttemplate.getForObject("http://192.168.66.63:30524/eurekaApi/findroadworkfor_feign", String.class);
		
		//int i = 1/0;
		
		return forObject;
	}
	private String fallback(){
		//上面出故障了来调用这个方法
		
		return "服务繁忙请稍后再试~~";
	}
	
}

这个就是一个调用接口错误,的一个服务降级去实现另一个服务,还可以去判断服务并发数太高,数据库连接数太多了,就可以抛一个异常手动降级(if else)

如果这样的话,那就得每一个都要设置一般,所以提供了一个默认的方法

package com.sola.controller;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.sola.service.ResourcesService;

import io.swagger.annotations.ApiOperation;

@RestController
@RequestMapping("Hystrix")
@DefaultProperties(defaultFallback = "defaultfallback")
public class HystrixController {

	@Autowired
	private ResourcesService resourcesservice;
	
	
	@GetMapping(value="test")
	@ApiOperation("小测试")
	//这里是指定的方法名要去实现
	/*@HystrixCommand(fallbackMethod = "fallback")*/
	@HystrixCommand
	public String oneTest(){
		
		RestTemplate resttemplate = new RestTemplate();
		
		String forObject = resttemplate.getForObject("http://192.168.66.63:30524/eurekaApi/findroadworkfor_feign", String.class);
		
		int i = 1/0;
		
		return forObject;
	}
	private String defaultfallback(){
		//上面出故障了来调用这个方法
		
		return "默认=====服务繁忙请稍后再试~~";
	}
	private String fallback(){
		//上面出故障了来调用这个方法
		
		return "服务繁忙请稍后再试~~";
	}
	
}

超时设置(比较常用)

比如在postman里调用的roadwork的接口里加入一个线程等待

	@GetMapping(value="findroadworkfor_feign")
	@ApiOperation("查询postman服务里的警员,通过feign")
	public Map<String, Object> getPostmanPolice(){
		
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return resourcesservice.findroadwork();
	}

然后看运行,postman会返回繁忙。

看流程,roadwork接口里的方法也执行了,但是postman返回的还是繁忙

原因是豪猪默认设置的超时等待是1秒,所以我们要去设置下超时时间

@GetMapping(value="test")
	@ApiOperation("小测试")
	//这里是指定的方法名要去实现
	/*@HystrixCommand(fallbackMethod = "fallback")*/
    //设置超时时间
	@HystrixCommand(commandProperties={
			
			@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
	})
	public String oneTest(){
		
		RestTemplate resttemplate = new RestTemplate();
		
		String forObject = resttemplate.getForObject("http://192.168.66.63:30524/eurekaApi/findroadworkfor_feign", String.class);
		
		//int i = 1/0;
		
		return forObject;
	}

这个value的参数时哪来的呢,可以去源码研究一下

可以看到默认就是一秒 ,然后我们去搜寻一下赋值方法,就能看到这里啦

        this.executionTimeoutInMilliseconds = getProperty(propertyPrefix, key, "execution.isolation.thread.timeoutInMilliseconds", builder.getExecutionIsolationThreadTimeoutInMilliseconds(), default_executionTimeoutInMilliseconds);

现在我们再去运行就算线程等待两秒也可以ok了

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

依赖隔离 

线程池隔离 会为每一个hystrixCommand创建一独立的线程池,就算某个包装下的依赖服务出现延迟过高的情况,也只是对该依赖的服务产生影响,并不会拖慢其他服务。

使用了@HystrixCommand框架就会自动的为这个函数实现了依赖隔离

服务的熔断(听着很牛逼,终于能看到是啥东西了)

熔断配置还是要用到Commandpropertis的配置 主要是这个几个参数

我们来配置一下,并且做一个判断偶数才执行调用其他服务

	@GetMapping(value="test")
	@ApiOperation("小测试")
	//这里是指定的方法名要去实现
	/*@HystrixCommand(fallbackMethod = "fallback")*/
	@HystrixCommand(commandProperties={
			//@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
			
			@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 oneTest(int num){
		
		if(num%2==0){
		
		RestTemplate resttemplate = new RestTemplate();
		
		String forObject = resttemplate.getForObject("http://192.168.66.63:30524/eurekaApi/findroadworkfor_feign", String.class);
		
		//int i = 1/0;
		
		return forObject;
		}
		return "空空如也";
	}

然后运行,去输入奇偶数结果

正常肯定是这个样子,但是不停的去刷新偶数,然后再去访问奇数,what the fuck?什么鬼~

 简述

断路器是一种自我保护机制,当某个服务发生故障,通过监控直接切断主逻辑调用

当故障达到一定的值,断路器将会跳闸,断路器对象返回错误

 Closed是熔断器的关闭状态

当服务错误达到了一定的值或者比例就会启动熔断机制。

Open是是熔断器打开状态,此时对服务都会返回错误(fallback)

但是设置了一个时钟,到了一定时间就会进入半熔断状态(Half Open)

允许定量的服务请求,

如果请求都成功了,或者成功了一定的比例

则认为恢复了,他就会关闭熔断器

否则就会又回到熔断器全开状态

第一个参数SleepWindowInMilliseconds就是时钟参数

10秒后版开启熔断器半熔断状态,假如有请求通过或者一定比例通过,将关闭熔断器,否则反之

第二个参数RequestVolumeThreshold

断路器的最小请求数(个人理解就是请求多少次算一次百分比)

最后一个参数ErrorThresholdPercentage设置错误过后打开熔断器的百分比条件例子是百分之六十

嗯,明白了吗这就是熔断器

在方法写入参数感觉有点写死了,还可以在yml上写入各种参数 例如

这个是全局的,还可以为方法单独设置,默认的key就是方法名

----------------------------------------------------------------------------------------------------------------------------------------------------------------

Feign-hystrix的使用

Feign已经已经引入了hystrix所以直接用就可以了

首先得先在配置文件中配置

feign.hystrix.enabled=true

然后再去feign接口去配置属性

package com.sola.feignclient;

import java.util.List;
import java.util.Map;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import io.swagger.annotations.ApiOperation;

@FeignClient(name = "EurekaClient-PostMan",fallback = PostManClientFallback.class)
public interface PostManClient {
	
	@GetMapping("/eurekaApi/test")
	public String getMsg();

	@GetMapping("/eurekaApi/findPolice")
	public List<Map<String, Object>> findPolice();
	
	@GetMapping("/eurekaApi/findPolicebyname")
	public List<Map<String, Object>> findPoliceByName(@RequestParam(value="name") String name);
	
	@PostMapping(value="/eurekaApi/findPoliceinanyname")
	public List<Map<String, Object>> findPoliceInAnyName(@RequestBody List<String> names);
	
}
	

再去建立一个fallback的实现类,实现此接口

假如产生服务降级就会走实现类中的方法,先设置默认为空

package com.sola.feignclient;

import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Component;

@Component
public class PostManClientFallback implements PostManClient{

	@Override
	public String getMsg() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Map<String, Object>> findPolice() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Map<String, Object>> findPoliceByName(String name) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Map<String, Object>> findPoliceInAnyName(List<String> names) {
		// TODO Auto-generated method stub
		return null;
	}

}

然后在方法中打个断点debug运行一下,假装线程等待

就会返回空

------------------------------------------------------------------------------------------------------------------------------------------------------------------ 

HyStrix-dashboard(可视化HyStrix的一个组件)

首先来配置依赖

		<dependency>
		   <groupId>org.springframework.cloud</groupId>
		   <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
		</dependency>

下面这个依赖是因为 之前stream 里已经引入相关依赖的 所以这里不用引入,假如没有引入过应该要引入

在启动类上加入开启注解

package com.sola;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootApplication
@EnableSwagger2
@EnableEurekaClient
@EnableCircuitBreaker
@EnableHystrixDashboard//这个
public class EurekaClientPostManApplication {

	public static void main(String[] args) {
		SpringApplication.run(EurekaClientPostManApplication.class, args);
	}
}

启动后访问

 

 红的是指的单个服务,其他是是集群,分布啥的 暂时不管,先来单个,运行然后报错

大概意思是无法连接,我们去看控制台

显示发送过,却无法连接 

暂时无法解决。。。。。。。。。。。。。。。。。。。。。。。。。。

又在网上查了一下 给跪给跪

-------------------------------------------------------------------------------------------------------------------------------------------------------------

Zuul的超时设置

通过zuul路由到某个微服务,请求成功,但是响应却总是报500错误,经过调试发现只有在后台处理超过1s的请求会这样,感觉应该是响应超时的问题,但是无论我将zuul.host.socket-timeout-millis或zuul.host.connect-timeout-millis设置多大,问题依然存在。 

server:
  port: 8080
  servlet:
    context-path: /
eureka:
  client:
    service-url:
      defaultZone:   http://localhost:8761/eureka/
  instance:
    status-page-url: http://localhost:8080/swagger-ui.html
zuul:
  ignoredServices: '*'
  ignoredPatterns: /**/admin/**
  host:
    max-per-route-connections: 60000
    socket-timeout-millis: 60000
    connect-timeout-millis: 60000
  routes:
    docdelivery:
      path: /doc-delivery/**
      serviceId: doc-delivery
    configserver:
      path: /config-server/**
      serviceId: config-server

查看官网 http://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#_zuul_timeouts 后发现,对于通过serviceId路由的服务,设置zuul.host是没用的,你需要设置另外两个参数:ribbon.ReadTimeout 和ribbon.SocketTimeout。
在application.yml中添加如下配置:

ribbon:
  ReadTimeout: 60000  # 单位毫秒数
  SocketTimeout: 60000

问题得到解决

猜你喜欢

转载自blog.csdn.net/jiulanhao/article/details/82789985