Optimizing Batch Commodity Data Query Interface Based on request cache Request Caching Technology One Point Classroom (Multi-shore College)

Keywords: network Nginx REST SpringBoot

Optimization of batch commodity data query interface based on request cache request caching technology

The third step in the eight steps of Hystrix command execution is to check whether the Request cache has a cache.

First, there is a concept called Request Context request context. Generally speaking, in a web application, if we use Hystrix, we will impose a request context on each request in a filter. That is to say, every request is a request context. Then in the context of this request, we will execute N multi-code, call N Multi-Dependent services, and some dependent services may be invoked several times.

In the context of a request, if there are multiple commands, the parameters are the same, and the interface invoked is the same, and the result can be considered the same. At this point, we can have the results returned by the first command execution cached in memory, and then all subsequent calls to this dependency from the request context will be taken out of memory.

In this way, the advantage is that the same command does not need to be executed repeatedly in the context of a request, avoiding repeated execution of network requests and improving the performance of the entire request.

Take a chestnut. For example, in the context of a request, we request data with productId of 1, which is not available in the first cache. Then we will retrieve data from the commodity service, return the latest data results, and cache the data in memory. In the context of the same subsequent request, if there is a request for data with productId of 1, it would be better to fetch it directly from the cache.

Both HystrixCommand and Hystrix Observable Command can specify a cache key, and then Hystrix automatically caches, and then in the same request context, if accessed again, the cache will be directly fetched.

Next, let's look at how to use request cache request caching technology in a specific business scenario. Of course, the following code is just a basic Demo.

Now, suppose we want to make an interface to query commodity data in batches. In this interface, we use HystrixCommand to query the data of multiple commodity IDS in batches at one time. But there is a problem here. If Nginx fails to cache locally and retrieves a batch of caches, the delivered product Ids are not de-duplicated, such as product Ids = 1,1,1,2,2, then it may be said that the product id has been duplicated. If we follow our previous business logic, we may duplicate the product id = 1. Three commodity queries and two commodity queries with productId=2.

We can use request cache to optimize the interface for batch query of commodity data, that is to say, one request context, which only executes once for the same commodity query, and the rest of the duplicates go to request cache.

Implement Hystrix request context filter and register

Define the HystrixRequestContextFilter class to implement the Filter interface.

/**
 * Hystrix Request context filter
 */
public class HystrixRequestContextFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try {
            filterChain.doFilter(servletRequest, servletResponse);
        } catch (IOException | ServletException e) {
            e.printStackTrace();
        } finally {
            context.shutdown();
        }
    }

    @Override
    public void destroy() {

    }
}

The filter object is then registered in SpringBoot Application.

@SpringBootApplication
public class EshopApplication {

    public static void main(String[] args) {
        SpringApplication.run(EshopApplication.class, args);
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new HystrixRequestContextFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }
}

command overrides getCacheKey() method

In GetProduct InfoCommand, override the getCacheKey() method so that the result of each request is placed in the Hystrix request context. The next data request for the same productId is to fetch the cache directly without calling the run() method.

public class GetProductInfoCommand extends HystrixCommand<ProductInfo> {

    private Long productId;

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

    public GetProductInfoCommand(Long productId) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"))
                .andCommandKey(KEY));
        this.productId = productId;
    }

    @Override
    protected ProductInfo run() {
        String url = "http://localhost:8081/getProductInfo?productId=" + productId;
        String response = HttpClientUtils.sendGetRequest(url);
        System.out.println("Invoke interface to query commodity data. productId=" + productId);
        return JSONObject.parseObject(response, ProductInfo.class);
    }

    /**
     * The result of each request is placed in the request context of the Hystrix binding
     *
     * @return cacheKey Cache key
     */
    @Override
    public String getCacheKey() {
        return "product_info_" + productId;
    }

    /**
     * Empty the cache of a commodity id
     *
     * @param productId Commodity id
     */
    public static void flushCache(Long productId) {
        HystrixRequestCache.getInstance(KEY,
                HystrixConcurrencyStrategyDefault.getInstance()).clear("product_info_" + productId);
    }
}

Here we write a flushCache() method for developing a manual deletion cache.

controller calls command to query commodity information

In the context of a web request, a list of commodity IDS is passed in and multiple commodity data information is queried. For each productId, create a command.

If the id list is not duplicated, the duplicated id will be cached directly in the second query.

@Controller
public class CacheController {

    /**
     * Request for one-time batch query of multiple commodity data
     *
     * @param productIds List of commodity IDS separated by
     * @return Response state
     */
    @RequestMapping("/getProductInfos")
    @ResponseBody
    public String getProductInfos(String productIds) {
        for (String productId : productIds.split(",")) {
            // For each productId, create a command
            GetProductInfoCommand getProductInfoCommand = new GetProductInfoCommand(Long.valueOf(productId));
            ProductInfo productInfo = getProductInfoCommand.execute();
            System.out.println("Is the result taken from the cache:" + getProductInfoCommand.isResponseFromCache());
        }

        return "success";
    }
}

Initiation of requests

Invoke the interface to query the information of multiple commodities.

http://localhost:8080/getProductInfos?productIds=1,1,1,2,2,5

In the console, we can see the following results.

Invoke interface to query commodity data, productId=1
 Is the result taken from the cache false?
Is the result taken from the cache true?
Is the result taken from the cache true?
Invoke interface to query commodity data, productId=2
 Is the result taken from the cache false?
Is the result taken from the cache true?
Invoke interface to query commodity data, productId=5
 Is the result taken from the cache false?

The first time you query the data of productId=1, you call the interface to query, not to retrieve the results from the cache. Then a query for productId=1 appears, and the cache is fetched directly. In this way, the efficiency is much higher.

Delete Cache

We write an Update Product InfoCommand, after updating the commodity information, manually call the flushCache() written before, and manually delete the cache.

public class UpdateProductInfoCommand extends HystrixCommand<Boolean> {

    private Long productId;

    public UpdateProductInfoCommand(Long productId) {
        super(HystrixCommandGroupKey.Factory.asKey("UpdateProductInfoGroup"));
        this.productId = productId;
    }

    @Override
    protected Boolean run() throws Exception {
        // Perform an update of merchandise information here
        // ...

        // Then empty the cache
        GetProductInfoCommand.flushCache(productId);
        return true;
    }
}

In this way, after the request for the commodity is queried, the first time the interface call is used to query the latest commodity information.

Thank you for seeing it. If you can help me, please give me a compliment.

More experience and technology are welcome to come and learn together.
A little Classroom - Online Learning Platform for Dreams http://www.yidiankt.com/

[Pay attention to the public number and get it free of charge -[java Core Knowledge Points]

QQ discussion group: 616683098
QQ: 3184402434
Students who want to study in depth can study and discuss with QQ ~and have a full set of resources to share, experience to explore, wait for you!

Posted by ericwright17 on Wed, 04 Sep 2019 21:58:59 -0700