互联网高并发解决方案——Hystrix实现服务隔离和降级

一、雪崩效应

1.雪崩效应是什么?

在分布式的的环境下,服务之间相互依赖调用,一个服务往往会依赖于其他几个服务,所以,当一个服务不可用时,就会影响到其它服务的正常工作。例如,在抢购系统中,当有大量并发请求调用商品服务,订单服务可能会资源耗尽,无法对外提供服务,并且这种不可用还会影响到其他的服务,就像雪崩一样。

2.为什么会产生雪崩效应?

Tomcat的底层使用了线程池技术,并且默认是提供1个线程池的,所有的请求都是在一个线程池中被处理。假设线程池中只有最多创建20个线程,那么当第21个请求来的时候就必须等待,所以会产生阻塞,从而导致服务堆积,影响其他服务的正常使用。

二、服务隔离

服务隔离就是要减少服务与服务之间的依赖关系,当然这种依赖关系并不是指业务依赖。通过服务隔离,防止服务雪崩效应,最终以服务降级、熔断、限流等手段,提高系统的高可用。

服务隔离的两种方式:线程池、信号量

使用线程池是比较常用的一种方式,它为每一个请求都单独开辟一个线程池处理,与其他请求完全隔离,线程池内部的阻塞不会影响到其他线程池,但是对CPU的开销非常大。

三、服务熔断

服务熔断类似现实世界中的“保险丝“,当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时。 目的就是为了保护系统的可用。为不是因为一个异常的发生影响到整个系统的运行。例如当电压过大时,保险丝会烧掉,导致不能用电,但是可以防止火灾的发生。

四、服务降级

所谓降级,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。 这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强。目的就是提升用户的体验,防止雪崩效应。例如可以返回一个友好的提示,而不是暴露系统错误给用户。

五、Hystrix简介

Hystrix是由Netflix开源的一个延迟和容错库,是SOA/微服务架构中提供服务隔离、熔断、降级机制的工具/框架。Netflix Hystrix是断路器的一种实现,用于高微服务架构的可用性,是防止服务出现雪崩的利器。可以提升系统的可用性与容错性。

1.Hystrix具备的优点

  • 在网络依赖服务出现高延迟或者失败时,为系统提供保护和控制
  • 缩短延迟等待时间和快速恢复:当异常的依赖回复正常后,失败的请求所占用的线程会被快速清理,不需要额外等待
  • 提供失败回退(Fallback)和相对优雅的服务降级机制
  • 提供有效的服务容错监控、报警和运维控制手段

六、使用Hystrix实现服务隔离和降级

案例:搭建一套分布式rpc远程通讯,订单服务调用会员服务以线程池的方式实现服务隔离,防止雪崩效应案例

案例分析:搭建两个SpringBoot项目——hystrix_member(会员服务)、hystrix_order(订单服务)。在订单服务中调用会员服务,会员服务设置休眠时间模拟请求处理时间。order中有三个接口:“/order/orderIndex”(未解决雪崩效应)、“/order/orderIndexHystrix”(解决了雪崩效应)、“/order/findOrderIndex”(用于测试雪崩效应)。

项目结构:

互联网高并发解决方案——Hystrix实现服务隔离和降级_第1张图片

Member服务:

导入依赖


		org.springframework.boot
		spring-boot-starter-parent
		2.0.0.RELEASE
	
	
		
			org.springframework.boot
			spring-boot-starter-web
		
		
		
			org.apache.httpcomponents
			httpclient
		
		
		
			com.alibaba
			fastjson
			1.2.47
		
		
			com.netflix.hystrix
			hystrix-metrics-event-stream
			1.5.12
		
		
			com.netflix.hystrix
			hystrix-javanica
			1.5.12
		
	

MemberController.java

import java.util.HashMap;
import java.util.Map;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 会员控制层,供order调用
 * @author johson
 *
 */
@RestController
@RequestMapping("/member")
public class MemberController {

	@GetMapping("/getMember")
	public Object getMember() throws InterruptedException{
		
		Map map = new HashMap();
		
		map.put("name", "张三");
		map.put("age", 5);
		
		Thread.sleep(1500);
		
		return map;
	}
}

order服务

导入依赖


        org.springframework.boot
        spring-boot-starter-parent
        2.0.0.RELEASE
    
    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.apache.httpcomponents
            httpclient
        
        
        
            com.alibaba
            fastjson
            1.2.47
        
        
            com.netflix.hystrix
            hystrix-metrics-event-stream
            1.5.12
        
        
            com.netflix.hystrix
            hystrix-javanica
            1.5.12
        
    

application.yml,设置tomcat最大线程数为20

server:
  port: 8080
  tomcat:
    max-threads: 20

HttpClientUtils.java,发送http请求工具类,超时时间可自行设置

/**
 * HttpClient4.3工具类
 * @author johson
 */
public class HttpClientUtils {
    private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class); // 日志记录

    private static RequestConfig requestConfig = null;

    static {
        // 设置请求和传输超时时间
        requestConfig = RequestConfig.custom().setSocketTimeout(20000).setConnectTimeout(20000).build();
    }

    /**
     * 发送get请求
     */
    public static JSONObject httpGet(String url) {
        // get请求返回结果
        JSONObject jsonResult = null;
        CloseableHttpClient client = HttpClients.createDefault();
        // 发送get请求
        HttpGet request = new HttpGet(url);
        request.setConfig(requestConfig);
        try {
            CloseableHttpResponse response = client.execute(request);

            // 请求发送成功,并得到响应
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                // 读取服务器返回过来的json字符串数据
                HttpEntity entity = response.getEntity();
                String strResult = EntityUtils.toString(entity, "utf-8");
                // 把json字符串转换成json对象
                jsonResult = JSONObject.parseObject(strResult);
            } else {
                logger.error("get请求提交失败:" + url);
            }
        } catch (IOException e) {
            logger.error("get请求提交失败:" + url, e);
        } finally {
            request.releaseConnection();
        }
        return jsonResult;
    }

}

OrderService.java,在这里调用Member服务

/**
 * 在这里通过rpc通信调用会员服务
 * @author johson
 *
 */
@Service
public class OrderService {
	
	public JSONObject getMember(){
		JSONObject result = HttpClientUtils.httpGet("http://127.0.0.1:8081/member/getMember");
		return result;
	}

}

OrderHystrixCommand .java

/**
 * 使用Hystrix解决雪崩效应
 * @author johson
 *
 */
public class OrderHystrixCommand extends HystrixCommand {
	
	@Autowired
	private OrderService orderService;

	/**
	 * @param group
	 */
	public OrderHystrixCommand(OrderService orderService) {
		super(setter());
		this.orderService = orderService;
	}

	protected JSONObject run() throws Exception {
		JSONObject member = orderService.getMember();
		System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
		return member;
	}

	private static Setter setter() {

		// 服务分组
		HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("orders");
		// 服务标识
		HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("order");
		// 线程池名称
		HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("order-pool");
		
		// 线程池配置 线程池大小为10,线程存活时间15秒 队列等待的阈值为100,超过100执行拒绝策略
		HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter().withCoreSize(10)
				.withKeepAliveTimeMinutes(15).withQueueSizeRejectionThreshold(100);
		
		// 命令属性配置Hystrix 开启超时
		HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
				// 采用线程池方式实现服务隔离
				.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
				// 禁止超时错误
				.withExecutionTimeoutEnabled(false);
		return HystrixCommand.Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey)
				.andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);

	}

	/**
	 * 发生熔断,则fallback
	 */
	@Override
	protected JSONObject getFallback() {
		// 如果Hystrix发生熔断,当前服务不可用,直接执行Fallback方法
		System.out.println("系统错误!");
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("code", 500);
		jsonObject.put("msg", "系统错误!");
		return jsonObject;
	}
}

OrderController.java

/**
 * 订单控制层,用于测试Hystrix解决雪崩效应
 * @author johson
 *
 */
@RestController
@RequestMapping("/order")
public class OrderController {

	@Autowired
	private OrderService orderService;
	
	/**
	 * 未解决雪崩效应
	 */
	@RequestMapping("/orderIndex")
	public Object orderIndex(){
		JSONObject member = orderService.getMember();
		System.out.println("当前线程名称为:"+Thread.currentThread()
					.getName()+",订单服务调用会员服务:member="+member);
		
		return member;
	}
	
	/**
	 * 解决雪崩效应
	 */
	@RequestMapping("/orderIndexHystrix")
	public Object orderIndexHystrix(){
		return new OrderHystrixCommand(orderService).execute();
	}
	
	/**
	 * 用于测试雪崩效应
	 */
	@RequestMapping("/findOrderIndex")
	public Object findOrderIndex(){
		System.out.println("当前线程为:"+Thread.currentThread().getName()+",findOrderIndex");
		return "findOrderIndex";
	}
	
}

测试工具:jmeter

互联网高并发解决方案——Hystrix实现服务隔离和降级_第2张图片

测试方法:分别向“/order/orderIndex”(未解决雪崩效应)、“/order/orderIndexHystrix”(解决了雪崩效应)发起大量http请求,在浏览器中访问“/order/findOrderIndex”(用于测试雪崩效应)接口,观察响应时间。

互联网高并发解决方案——Hystrix实现服务隔离和降级_第3张图片

互联网高并发解决方案——Hystrix实现服务隔离和降级_第4张图片

测试结果:向“/order/orderIndex”发起大量请求时,在浏览器中访问“/order/findOrderIndex”响应时间很长;

                  向“/order/orderIndexHystrix”发起大量请求时,“/order/findOrderIndex”响应时间短,不受影响。

PS:分别在浏览器中访问这三个接口,对比控制台输出的线程名。可以发现“/order/orderIndex”与“/order/findOrderIndex”在同一个线程池处理,而“/order/orderIndexHystrix”在单独的线程池处理。

互联网高并发解决方案——Hystrix实现服务隔离和降级_第5张图片

你可能感兴趣的:(微服务,高并发,服务隔离)