互联网高并发解决方案——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”(用于测试雪崩效应)。

项目结构:

Member服务:

导入依赖

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>
		<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-metrics-event-stream</artifactId>
			<version>1.5.12</version>
		</dependency>
		<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-javanica</artifactId>
			<version>1.5.12</version>
		</dependency>
	</dependencies>

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<String, Object> map = new HashMap<String, Object>();
		
		map.put("name", "张三");
		map.put("age", 5);
		
		Thread.sleep(1500);
		
		return map;
	}
}

order服务

导入依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-metrics-event-stream</artifactId>
            <version>1.5.12</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
            <version>1.5.12</version>
        </dependency>
    </dependencies>

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<JSONObject> {
	
	@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

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

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值