hystrix降级预热问题

1、问题描述

  模拟hystrix在高并发条件下流量降级处理。设计使用多级降级策略。但是在使用测试时发现第一次直接就不断的降级一直到最低级策略。当使用httpclient客户端模拟请求时,一直在降级。

2、问题分析

  这是由于java程序在初次服务时效率总是很低。对用户发送的请求需要花很长时间处理。所以服务就自动降级了。

3、问题解决

  如果是使用浏览器访问,则再访问一次就可以了。在java分布式开发过程中,有很多时候明明代码写的都是正确的,但是实验的结果总是和预期不符。这里很大一部分是这个原因,尤其是在使用httpclient客户端模拟请求时。这里我们还是需要使用浏览器测试。

首先模拟一个服务。返回产品信息。

package com.njust.eshop.product.ha.controller;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.njust.eshop.product.ha.utils.HttpClientUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * 缓存服务的接口
 *
 * @author Administrator
 */
@RestController
public class ProductController {

    @RequestMapping("/getProductInfo")
    public String getProductInfo(Long productId) {
        return "{\"id\": " + productId + ", \"name\": \"iphone7手机1\", \"price\": 5599, \"pictureList\":\"a.jpg,b.jpg\", \"specification\": \"iphone7的规格\", \"service\": \"iphone7的售后服务\", \"color\": \"红色,白色,黑色\", \"size\": \"5.5\", \"shopId\": 1, \"modifiedTime\": \"2017-01-01 12:00:00\", \"cityId\": 1, \"brandId\": 1}";
    }


}

接着定义hystrix降级策略。这里假设用户的productId为-1的时候采用一级降级,当productId为-2的时候采用二级降级。

package com.njust.eshop.cache.ha.hystrix.command;


import com.alibaba.fastjson.JSONObject;
import com.netflix.hystrix.*;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault;
import com.njust.eshop.cache.ha.cache.local.BrandCache;
import com.njust.eshop.cache.ha.cache.local.LocationCache;
import com.njust.eshop.cache.ha.entity.ProductInfo;
import com.njust.eshop.cache.ha.utils.HttpClientUtils;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 获取商品信息
 *
 * @author Administrator
 */
public class GetProductInfoCommand extends HystrixCommand<ProductInfo> {

    public static final HystrixCommandKey KEY = HystrixCommandKey.Factory.asKey("GetProductInfoCommand");

    private Long productId;

    public GetProductInfoCommand(Long productId) {
		super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"))
				.andCommandKey(KEY)
				.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("GetProductInfoPool"))
				.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
						.withCoreSize(10)
						.withMaxQueueSize(12)
						.withQueueSizeRejectionThreshold(15))
				.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
						.withCircuitBreakerRequestVolumeThreshold(30)
						.withCircuitBreakerErrorThresholdPercentage(40)
						.withCircuitBreakerSleepWindowInMilliseconds(3000)
						.withExecutionTimeoutInMilliseconds(500)
						.withFallbackIsolationSemaphoreMaxConcurrentRequests(30))
				);
//        super(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"));
        this.productId = productId;
    }

    @Override
    protected ProductInfo run() throws Exception {
//		System.out.println("调用接口,查询商品数据,productId=" + productId);
//
        if (productId.equals(-1L)) {
            throw new Exception();
        }
        if (productId.equals(-2L)) {
            throw new Exception();
        }
//
//		if(productId.equals(-2L)) {
//			Thread.sleep(3000);
//		}
//
//		if(productId.equals(-3L)) {
////			Thread.sleep(250);
//		}

        String url = "http://127.0.0.1:8084/getProductInfo?productId=" + productId;
        String response = HttpClientUtils.sendGetRequest(url);
        return JSONObject.parseObject(response, ProductInfo.class);
    }

//	@Override
//	protected String getCacheKey() {
//		return "product_info_" + productId;
//	}

    @Override
    protected ProductInfo getFallback() {
        System.out.println("--------getFallback-------------------");
        return new FirstLevelFallbackCommand(productId).execute();
    }

    private static class FirstLevelFallbackCommand extends HystrixCommand<ProductInfo> {

        private Long productId;

        public FirstLevelFallbackCommand(Long productId) {
            // 第一级的降级策略,因为这个command是运行在fallback中的
            // 所以至关重要的一点是,在做多级降级的时候,要将降级command的线程池单独做一个出来
            // 如果主流程的command都失败了,可能线程池都已经被占满了
            // 降级command必须用自己的独立的线程池
            super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"))
                    .andCommandKey(HystrixCommandKey.Factory.asKey("FirstLevelFallbackCommand"))
                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("FirstLevelFallbackPool")));
            this.productId = productId;
        }

        @Override
        protected ProductInfo run() throws Exception {
            // 这里,因为是第一级降级的策略,所以说呢,其实是要从备用机房的机器去调用接口
            // 但是,我们这里没有所谓的备用机房,所以说还是调用同一个服务来模拟
            System.out.println("-------------------response-------------------: ");
            if (productId.equals(-2L)) {
                throw new Exception();
            }
            System.out.println("-------------------response-------------------: ");
            String url = "http://127.0.0.1:8084/getProductInfo?productId=" + productId;
            String response = HttpClientUtils.sendGetRequest(url);
            System.out.println("response: " + response);
            return JSONObject.parseObject(response, ProductInfo.class);
        }

        @Override
        protected ProductInfo getFallback() {
            System.out.println("-------------------response---getFallback----------------: ");
            // 第二级降级策略,第一级降级策略,都失败了
            ProductInfo productInfo = new ProductInfo();
            // 从请求参数中获取到的唯一条数据
            productInfo.setId(productId);
            // 从本地缓存中获取一些数据
            productInfo.setBrandId(BrandCache.getBrandId(productId));
            productInfo.setBrandName(BrandCache.getBrandName(productInfo.getBrandId()));
            productInfo.setCityId(LocationCache.getCityId(productId));
            productInfo.setCityName(LocationCache.getCityName(productInfo.getCityId()));
            // 手动填充一些默认的数据
            productInfo.setColor("默认颜色 第二级降级策略");
            productInfo.setModifiedTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            productInfo.setName("默认商品");
            productInfo.setPictureList("default.jpg");
            productInfo.setPrice(0.0);
            productInfo.setService("默认售后服务");
            productInfo.setShopId(-1L);
            productInfo.setSize("默认大小");
            productInfo.setSpecification("默认规格");
            return productInfo;
        }

    }

    public static void flushCache(Long productId) {
        HystrixRequestCache.getInstance(KEY,
                HystrixConcurrencyStrategyDefault.getInstance()).clear("product_info_" + productId);
    }

}

定义浏览器测试类,测试该降级策略。这里模拟使用一级降级策略。

package com.njust.eshop.cache.ha.controller;

import com.njust.eshop.cache.ha.hystrix.command.GetProductInfoCommand;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Chen
 * @version 1.0
 * @date 2020/4/12 16:52
 * @description:
 */
@RestController
public class TestController {

    @RequestMapping("testMutil")
    public String testMutil() {
        GetProductInfoCommand getProductInfoCommand1 = new GetProductInfoCommand(-1L);
//        Thread.sleep(3000);
//        System.out.println(getProductInfoCommand1.execute());
//        GetProductInfoCommand getProductInfoCommand2 = new GetProductInfoCommand(-2L);
//        System.out.println(getProductInfoCommand2.execute());
//        Thread.sleep(5000);
        return getProductInfoCommand1.execute().toString();
    }
}

运行两个服务器,输出结果为:

在这里插入图片描述
第一次直接输出二级降级数据,明显不符合预期。再次刷新浏览器。我们发现可以获取到正常结果。

在这里插入图片描述

控制台输出结果如下;

--------getFallback-------------------
-------------------response-------------------: 
-------------------response-------------------: 
-------------------response---getFallback----------------: 
response: {"id": -1, "name": "iphone7手机", "price": 5599, "pictureList":"a.jpg,b.jpg", "specification": "iphone7的规格", "service": "iphone7的售后服务", "color": "红色,白色,黑色", "size": "5.5", "shopId": 1, "modifiedTime": "2017-01-01 12:00:00", "cityId": 1, "brandId": 1}

--------getFallback-------------------
-------------------response-------------------: 
-------------------response-------------------: 
response: {"id": -1, "name": "iphone7手机", "price": 5599, "pictureList":"a.jpg,b.jpg", "specification": "iphone7的规格", "service": "iphone7的售后服务", "color": "红色,白色,黑色", "size": "5.5", "shopId": 1, "modifiedTime": "2017-01-01 12:00:00", "cityId": 1, "brandId": 1}

--------getFallback-------------------
-------------------response-------------------: 
-------------------response-------------------: 
response: {"id": -1, "name": "iphone7手机1", "price": 5599, "pictureList":"a.jpg,b.jpg", "specification": "iphone7的规格", "service": "iphone7的售后服务", "color": "红色,白色,黑色", "size": "5.5", "shopId": 1, "modifiedTime": "2017-01-01 12:00:00", "cityId": 1, "brandId": 1}


其实我们发现第一次请求的时候已经获取到http的请求结果,但是由于超时的原因直接降级了。

下面来直接编写测试类。测试类的好处就是不需要启动服务器,测试便捷。

package com.njust.eshop.cache.ha;

import com.njust.eshop.cache.ha.hystrix.command.GetProductInfoCommand;

/**
 * @author Chen
 * @version 1.0
 * @date 2020/4/11 19:44
 * @description:
 */
public class MultiLevelFallbackTest {
    public static void main(String[] args) throws Exception {
        GetProductInfoCommand getProductInfoCommand1 = new GetProductInfoCommand(-1L);
//        Thread.sleep(3000);
        System.out.println(getProductInfoCommand1.execute());
//        GetProductInfoCommand getProductInfoCommand2 = new GetProductInfoCommand(-2L);
//        System.out.println(getProductInfoCommand2.execute());
//        Thread.sleep(5000);
    }
}

这里的逻辑和上面的一样。直接运行测试类。输出结果如下:

-------------------response-------------------: 
-------------------response-------------------: 
-------------------response---getFallback----------------: 
ProductInfo(id=-1, name=默认商品, price=0.0, pictureList=default.jpg, specification=默认规格, service=默认售后服务, color=默认颜色 第二级降级策略, size=默认大小, shopId=-1, modifiedTime=2020-04-12 17:14:15, cityId=1, cityName=北京, brandId=1, brandName=iphone)

这里我们发现无论我们运行多少次,都是直接使用的二级降级的策略。因为每次我们测试只是相当于第一次。控制台输出结果:

-------------------response-------------------: 
-------------------response-------------------: 
-------------------response---getFallback----------------: 
ProductInfo(id=-1, name=默认商品, price=0.0, pictureList=default.jpg, specification=默认规格, service=默认售后服务, color=默认颜色 第二级降级策略, size=默认大小, shopId=-1, modifiedTime=2020-04-12 17:30:26, cityId=1, cityName=北京, brandId=1, brandName=iphone)

4、总结

  由于java有编译优化的功能,所以当测试类不行的时候,直接走正常测试吧。

点个赞再走呗!欢迎留言哦!

发布了25 篇原创文章 · 获赞 4 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/qq_32510597/article/details/105472788
今日推荐