互聯網高並發之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

  

 

 

 

效果:

 

 

 可以看到(開啟超時),不同的線程池,服務隔離,服務降級。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM