• Hystrix


    1,Hyxtrix 是什么?

     Netflix的一套开源框架,可以理解为高并发的一套解决方案。可以提供服务隔离,服务熔断,服务降级。

    2,服务隔离

      介绍服务隔离前,需要先了解下,服务雪崩效应。

      服务雪崩效应指的是:因为一个服务产生了阻塞堆积(可能有大量的访问请求),而导致其他的服务不可用。

      

    雪崩效应产生的原因:在一个服务中,Tomcat 默认的线程池是1个,比如最大线程数目是50 个,阻塞队列里面的线程数据是100个,当访问A 

    的请求比如来了1000个,那么线程池中的线程就会去处理请求A,并且还有大量的请求在阻塞队列中进行等待。。。。

    若这时候有了请求B,因为线程池中的已经没有可用线程,所以请求B 就会访问不了,因为A 影响到了请求B。这就是雪崩效应。

    怎么解决?

    Hyxtrix中提供了解决方案-------服务隔离

    什么是服务隔离?

    就是服务与服务接口之间互不影响,服务隔离的实现方法,原子计数器隔离(当服务接口A 访问到了多少之后,不可访问,这个处理不是很好),线程池隔离,每个服务接口有自己的线程池,这样就不共用一个线程池了。

    例子:服务隔离之前

    本地有两个服务,member(8081) order(8080),order 里面的接口通过rpc 远程调用member 里面的接口,实现了两个服务间的通信。

    rpc 其实就是通过http 协议,底层是通过socket 技术完成的通信。代码如下:

    member 代码:

    service:

    package com.aiyuesheng.service;
    
    import java.util.concurrent.ConcurrentHashMap;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class MemberService {
        
        public ConcurrentHashMap<String, String> searchBankInfo() throws InterruptedException{
            ConcurrentHashMap<String, String> bankInfo = new ConcurrentHashMap<String, String>();
            bankInfo.put("NAME", "China Bank");
            bankInfo.put("LOCATION", "SHANGHAI");
            Thread.sleep(1500); //为了效果更加明显
            return bankInfo;
        }
        
        public ConcurrentHashMap<String, String> searchCustomerInfo(){
            ConcurrentHashMap<String, String> cusInfo = new ConcurrentHashMap<String, String>();
            cusInfo.put("NAME", "CHRIS");
            cusInfo.put("SEX", "MALE");
            return cusInfo;
        } 
    }

    controller:

    @RestController
    public class Index {
        
        @Autowired
        private MemberService memberService;
    
        @RequestMapping("/searchBankInfo")
        public Object searchBankInfo() throws InterruptedException {
    
            return memberService.searchBankInfo();
        }
        
        @RequestMapping("/searchCustomerInfo")
        public Object searchCustomerInfo() {
            return memberService.searchCustomerInfo();
        }
    }

    启动类:

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

    order 的代码:

    service: rpc远程调用member 的接口

    package com.aiyuesheng.service;
    
    import org.springframework.stereotype.Service;
    
    import com.aiyuesheng.utils.HttpClientUtils;
    import com.alibaba.fastjson.JSONObject;
    
    @Service
    public class OrderService {
    
        public JSONObject searchBankInfo() {
            JSONObject result = HttpClientUtils.httpGet("http://127.0.0.1:8081/searchBankInfo");
            return result;
        }
        
        public JSONObject searchCustomerInfo() {
            JSONObject result = HttpClientUtils.httpGet("http://127.0.0.1:8081/searchCustomerInfo");
            return result;
        }
    
    }

    controller:

    package com.aiyuesheng.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.aiyuesheng.service.OrderService;
    
    @RestController
    public class Index {
    
        @Autowired
        private OrderService orderService;
    
        @RequestMapping("/searchBankInfo")
        public Object searchBankInfo() {
            System.out.println("searchBankInfo");
            return orderService.searchBankInfo();
        }
        
        @RequestMapping("/searchCustomerInfo")
        public Object searchCustomerInfo() {
            System.out.println("searchCustomerInfo");
            return orderService.searchCustomerInfo();
        }
    
    
    }

    rpc:工具类

    package com.aiyuesheng.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工具类
     */
    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参数
         */
        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
         */
        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;
        }
    
    
        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;
        }
    
    }

    启动类:

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

    测试:两个服务同时启动

    通过jmeter 模拟请求:

    600 个线程同时跑http://127.0.0.1:8080/searchBankInfo 的时候,有些线程放入了阻塞队列里面

    当我们访问http://127.0.0.1:8080/searchCustomerInfo 的时候速度会变的很慢

    加入hystrix 服务隔离:代码实现如下:

    package com.aiyuesheng.hystrix;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.aiyuesheng.service.OrderService;
    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;
    
    public class OrderHystrixCommand extends HystrixCommand<JSONObject> {
    
        @Autowired
        private OrderService orderService;
    
        // 通过构造方法传参
        public OrderHystrixCommand(OrderService orderService) {
            super(setter());
            this.orderService = orderService;
        }
    
        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);
        }
    
        @Override
        protected JSONObject run() throws Exception {
            JSONObject customerInfo = orderService.searchCustomerInfo();
            System.out.println("当前线程名称:" + Thread.currentThread().getName());
            return customerInfo;
        }
    
        @Override
        protected JSONObject getFallback() {
            // 如果Hystrix发生熔断,当前服务不可用,直接执行Fallback方法
            System.out.println("系统错误!");
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code", 500);
            jsonObject.put("msg", "系统错误!");
            return jsonObject;
        }
    }

    原来的接口改为:

        @RequestMapping("/searchCustomerInfo")
        public Object searchCustomerInfo() throws Exception {
            return orderService.searchCustomerInfo();
        }
    
        @RequestMapping("/hystrixSearchCustomerInfo")
        public Object hystrixSearchCustomerInfo() throws Exception {
            OrderHystrixCommand orderHystrixCommand = new OrderHystrixCommand(orderService);
            return orderHystrixCommand.execute();
        }

    3,服务降级

      当服务不可用的时候,为了避免让客户端一直等待,可以直接返回个页面提示用户,例如服务器忙,请稍后重试。可以提供用户体验。

      避免服务雪崩的情况发生。使用fallback 方法返回

    4,服务熔断

       服务熔断的目的是为了保护服务器,为了不让其宕机。

       当高并发的情况下,大量的请求过来的时候,可以设置一个零界点,超过这个这个临界点之后的请求,直接拒绝掉,返回服务降级的提示,从而保护服务器。

       服务熔断一般和服务降级一块使用。

    5,应用场景

    线程池隔离:

    1、 第三方应用或者接口

    2、 并发量大

    信号量隔离:

    1、 内部应用或者中间件(redis)

    2、 并发需求不大(突发的高并发,难以预计多少并发量,难设置临界点,所以不太适合高并发量)

     
  • 相关阅读:
    待测试
    js中substring和substr的用法
    JavaScript lastIndexOf() 方法
    CSS3 :nth-child() 选择器
    jQuery :has() 选择器
    jquery tr:even,tr:eq(),tr:nth-child()区别
    JS模块化工具requirejs教程(二):基本知识
    JS模块化工具requirejs教程(一):初识requirejs
    HTML基础js操作
    HTML基础dom操作
  • 原文地址:https://www.cnblogs.com/pickKnow/p/11250374.html
Copyright © 2020-2023  润新知