• 互联网高并发之Hystrix实现服务隔离和降级


    当大多数人在使用Tomcat时,多个HTTP服务会共享一个线程池,假设其中一个HTTP服务访问的数据库响应非常慢,这将造成服务响应时间延迟增加,大多数线程阻塞等待数据响应返回,导致整个Tomcat线程池都被该服务占用,甚至拖垮整个Tomcat。因此,如果我们能把不同HTTP服务隔离到不同的线程池,则某个HTTP服务的线程池满了也不会对其他服务造成灾难性故障。这就需要线程隔离或者信号量隔离来实现了。

    使用线程隔离或信号隔离的目的是为不同的服务分配一定的资源,当自己的资源用完,直接返回失败而不是占用别人的资源。

    Hystrix实现服务隔离两种方案

    Hystrix的资源隔离策略有两种,分别为:线程池和信号量。

    线程池方式

    1、 使用线程池隔离可以完全隔离第三方应用,请求线程可以快速放回。 2、 请求线程可以继续接受新的请求,如果出现问题线程池隔离是独立的不会影响其他应用。 
    3、 当失败的应用再次变得可用时,线程池将清理并可立即恢复,而不需要一个长时间的恢复。 
    4、 独立的线程池提高了并发性

    缺点: 
    线程池隔离的主要缺点是它们增加计算开销(CPU)。每个命令的执行涉及到排队、调度和上 下文切换都是在一个单独的线程上运行的。

    每个服务接口都有自己独立的线程池,管理运行当前自己的接口。但是cpu开销比较大

    在同一个线程池中,素有请求全部到一个服务进行访问,这时候会导致其他服服务没有线程接收请求访问,所以就会产生服务雪崩效应

     Member:

    pom:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>com.itmayeidu</groupId>
    	<artifactId>member</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    
    
    
    	<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>
    
    	</dependencies>
    </project>
    

      controller

    package com.toov5.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    
    @RestController
    @RequestMapping("/member")
    public class MemberController {
    
        @RequestMapping("/memberIndex")
        public Object memberIndex() throws InterruptedException {
            Map<String, Object> hashMap = new HashMap<String, Object>();
            hashMap.put("code", 200);
            hashMap.put("msg", "memberIndex");
            Thread.sleep(1500);
            return hashMap;
        }
    
    }

    启动类

    package com.toov5.controller;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class AppMember {
    
        public static void main(String[] args) {
            SpringApplication.run(AppMember.class, args);
        }
    
    }

    yml:

    server:
      port: 8081
    

      

    Order

     pom

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>com.itmayeidu</groupId>
    	<artifactId>order</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    
    	<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>
    
    </project>
    

     controller

     

    package com.toov5.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.alibaba.fastjson.JSONObject;
    import com.toov5.hystrix.OrderHystrixCommand;
    import com.toov5.hystrix.OrderHystrixCommand2;
    import com.toov5.service.MemberService;
    
    
    @RestController
    @RequestMapping("/order")
    public class OrderController {
        @Autowired
        private MemberService memberService;
    
        @RequestMapping("/orderIndex")
        public Object orderIndex() throws InterruptedException {
            JSONObject member = memberService.getMember(); //返回值与OrderHystrixCommand中的泛型对应
            System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
            return member;
        }
    
        // 已经理解
        @RequestMapping("/orderIndexHystrix")
        public Object orderIndexHystrix() throws InterruptedException {
            return new OrderHystrixCommand(memberService).execute();
        }
    
    
        @RequestMapping("/findOrderIndex")
        public Object findIndex() {
            System.out.println("当前线程:" + Thread.currentThread().getName() + ",findOrderIndex");
            return "findOrderIndex";
        }
    }

    service

    package com.toov5.service;
    
    import org.springframework.stereotype.Service;
    
    import com.alibaba.fastjson.JSONObject;
    import com.toov5.utils.HttpClientUtils;
    
    
    @Service
    public class MemberService {
    
        public JSONObject getMember() {
    
            JSONObject result = HttpClientUtils.httpGet("http://127.0.0.1:8081/member/memberIndex");
            return result;
        }
    
    }

    utils

    package com.toov5.utils;
    
    import com.alibaba.fastjson.JSONObject;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpStatus;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.io.IOException;
    
    /**
     * HttpClient4.3工具类
     * 
     * @author hang.luo
     */
    public class HttpClientUtils {
        private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class); // 日志记录
    
        private static RequestConfig requestConfig = null;
    
        static {
            // 设置请求和传输超时时间
            requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build();
        }
    
        /**
         * post请求传输json参数
         * 
         * @param url
         *            url地址
         * @param json
         *            参数
         * @return
         */
        public static JSONObject httpPost(String url, JSONObject jsonParam) {
            // post请求返回结果
            CloseableHttpClient httpClient = HttpClients.createDefault();
            JSONObject jsonResult = null;
            HttpPost httpPost = new HttpPost(url);
            // 设置请求和传输超时时间
            httpPost.setConfig(requestConfig);
            try {
                if (null != jsonParam) {
                    // 解决中文乱码问题
                    StringEntity entity = new StringEntity(jsonParam.toString(), "utf-8");
                    entity.setContentEncoding("UTF-8");
                    entity.setContentType("application/json");
                    httpPost.setEntity(entity);
                }
                CloseableHttpResponse result = httpClient.execute(httpPost);
                // 请求发送成功,并得到响应
                if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    String str = "";
                    try {
                        // 读取服务器返回过来的json字符串数据
                        str = EntityUtils.toString(result.getEntity(), "utf-8");
                        // 把json字符串转换成json对象
                        jsonResult = JSONObject.parseObject(str);
                    } catch (Exception e) {
                        logger.error("post请求提交失败:" + url, e);
                    }
                }
            } catch (IOException e) {
                logger.error("post请求提交失败:" + url, e);
            } finally {
                httpPost.releaseConnection();
            }
            return jsonResult;
        }
    
        /**
         * post请求传输String参数 例如:name=Jack&sex=1&type=2
         * Content-type:application/x-www-form-urlencoded
         * 
         * @param url
         *            url地址
         * @param strParam
         *            参数
         * @return
         */
        public static JSONObject httpPost(String url, String strParam) {
            // post请求返回结果
            CloseableHttpClient httpClient = HttpClients.createDefault();
            JSONObject jsonResult = null;
            HttpPost httpPost = new HttpPost(url);
            httpPost.setConfig(requestConfig);
            try {
                if (null != strParam) {
                    // 解决中文乱码问题
                    StringEntity entity = new StringEntity(strParam, "utf-8");
                    entity.setContentEncoding("UTF-8");
                    entity.setContentType("application/x-www-form-urlencoded");
                    httpPost.setEntity(entity);
                }
                CloseableHttpResponse result = httpClient.execute(httpPost);
                // 请求发送成功,并得到响应
                if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    String str = "";
                    try {
                        // 读取服务器返回过来的json字符串数据
                        str = EntityUtils.toString(result.getEntity(), "utf-8");
                        // 把json字符串转换成json对象
                        jsonResult = JSONObject.parseObject(str);
                    } catch (Exception e) {
                        logger.error("post请求提交失败:" + url, e);
                    }
                }
            } catch (IOException e) {
                logger.error("post请求提交失败:" + url, e);
            } finally {
                httpPost.releaseConnection();
            }
            return jsonResult;
        }
    
        /**
         * 发送get请求
         * 
         * @param url
         *            路径
         * @return
         */
        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;
        }
    
    }

    Hystrix:

    package com.toov5.hystrix;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.alibaba.fastjson.JSONObject;
    import com.netflix.hystrix.HystrixCommand;
    import com.netflix.hystrix.HystrixCommandGroupKey;
    import com.netflix.hystrix.HystrixCommandKey;
    import com.netflix.hystrix.HystrixCommandProperties;
    import com.netflix.hystrix.HystrixThreadPoolKey;
    import com.netflix.hystrix.HystrixThreadPoolProperties;
    import com.toov5.service.MemberService;
    
    
    @SuppressWarnings("rawtypes")
    public class OrderHystrixCommand extends HystrixCommand<JSONObject> {
        @Autowired
        private MemberService memberService;
    
        /**
         * @param group
         */
        public OrderHystrixCommand(MemberService memberService) {
            super(setter());  //隔离
            this.memberService = memberService;
        }
     
        //表示服务执行的代码
        protected JSONObject run() throws Exception {
            JSONObject member = memberService.getMember();
            System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
            return member;
        }
    
        private static Setter setter() {
    
            // 服务分组  相同的会员是一组
            HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("members");
            // 服务标识
            HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("member");
            // 线程池名称
            HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("member-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(true);
            return HystrixCommand.Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey)
                    .andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);
    
        }
    
        @Override    //降级
        protected JSONObject getFallback() {
            // 如果Hystrix发生熔断,当前服务不可用,直接执行Fallback方法
            System.out.println("系统错误!");
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code", 500);
            jsonObject.put("msg", "系统错误!");
            return jsonObject;
        }
    }

    启动类:

    package com.toov5;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class AppOrder {
    
        public static void main(String[] args) {
            SpringApplication.run(AppOrder.class, args);
        }
    
    }

    yml:

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

      

    效果:

     可以看到(开启超时),不同的线程池,服务隔离,服务降级。

  • 相关阅读:
    矩阵乘法与邻接矩阵
    矩阵加速 学习笔记
    P5596 【XR-4】题
    P1842 奶牛玩杂技
    CF449B Jzzhu and Cities
    小球与盒子
    [JZOJ5279]香港记者题解--最短路图
    [学习笔记]二分图匹配与匈牙利算法
    [NOIP2018模拟赛10.25]瞎搞报告
    luogu2657-Windy数题解--数位DP
  • 原文地址:https://www.cnblogs.com/toov5/p/9986857.html
Copyright © 2020-2023  润新知