高可用后端架构设计实战-利用request cache请求缓存优化批量查询接口

简介: 高可用后端架构设计实战-利用request cache请求缓存优化批量查询接口

回顾执行流程

1、创建command,2种command类型

2、执行command,4种执行方式

3、查找是否开启了request cache,是否有请求缓存,如果有缓存,直接取用缓存,返回结果


reqeust context(请求上下文)

一般在一个web应用中,Hystrix会在一个filter里面,对每个请求都添加一个请求上下文。即Tomcat容器内,每一次请求,就是一次请求上下文。

然后在这次请求上下文中,我们会去执行N多代码,调用N多依赖服务,有的依赖服务可能还会调用好几次。


在一次请求上下文中,如果有多个command,参数及调用的接口也是一样的,其实结果也可以认为是一样的

那么就可以让第一次command执行返回的结果缓存在内存,然后这个请求上下文中,后续的其他对这个依赖的调用全部从内存中取用缓存结果即可


这样避免在一次请求上下文中多次执行一样的command,避免重复执行网络请求,从而提升整个请求的性能


request cache的原理图

11.png

对于请求缓存(request caching),请求合并(request collapsing),请求日志(request log),等等技术,都必须自己管理HystrixReuqestContext的声明周期


在一个请求执行之前,都必须先初始化一个request context

HystrixRequestContext context = HystrixRequestContext.initializeContext();

然后在请求结束之后,需要关闭request context

context.shutdown();

一般来说,在java web的应用中,都是通过filter过滤器来实现的

public class HystrixRequestContextServletFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
     throws IOException, ServletException {
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try {
            chain.doFilter(request, response);
        } finally {
            context.shutdown();
        }
    }
}
@Bean
public FilterRegistrationBean indexFilterRegistration() {
    FilterRegistrationBean registration = new FilterRegistrationBean(new IndexFilter());
    registration.addUrlPatterns("/");
    return registration;
}

结合业务背景,我们做了一个批量查询商品数据的接口,在这个里面,我们其实通过HystrixObservableCommand一次性批量查询多个商品id的数据


但是这里有个问题,如果说nginx在本地缓存失效了,重新获取一批缓存,传递过来的productId都没有进行去重,1,1,2,2,5,6,7


那么可能说,商品id出现了重复,如果按照我们之前的业务逻辑,可能就会重复对productId=1的商品查询两次,productId=2的商品查询两次


我们对批量查询商品数据的接口,可以用request cache做一个优化,就是说一次请求,就是一次request context,对相同的商品查询只能执行一次,其余的都走request cache

public class CommandUsingRequestCache extends HystrixCommand<Boolean> {
    private final int value;
    protected CommandUsingRequestCache(int value) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.value = value;
    }
    @Override
    protected Boolean run() {
        return value == 0 || value % 2 == 0;
    }
    @Override
    protected String getCacheKey() {
        return String.valueOf(value);
    }
}
@Test
public void testWithCacheHits() {
    HystrixRequestContext context = HystrixRequestContext.initializeContext();
    try {
        CommandUsingRequestCache command2a = new CommandUsingRequestCache(2);
        CommandUsingRequestCache command2b = new CommandUsingRequestCache(2);
        assertTrue(command2a.execute());
        // this is the first time we've executed this command with
        // the value of "2" so it should not be from cache
        assertFalse(command2a.isResponseFromCache());
        assertTrue(command2b.execute());
        // this is the second time we've executed this command with
        // the same value so it should return from cache
        assertTrue(command2b.isResponseFromCache());
    } finally {
        context.shutdown();
    }
    // start a new request context
    context = HystrixRequestContext.initializeContext();
    try {
        CommandUsingRequestCache command3b = new CommandUsingRequestCache(2);
        assertTrue(command3b.execute());
        // this is a new request context so this 
        // should not come from cache
        assertFalse(command3b.isResponseFromCache());
    } finally {
        context.shutdown();
    }
}

缓存的手动清理

public static class GetterCommand extends HystrixCommand<String> {
    private static final HystrixCommandKey GETTER_KEY = HystrixCommandKey.Factory.asKey("GetterCommand");
    private final int id;
    public GetterCommand(int id) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetSetGet"))
                .andCommandKey(GETTER_KEY));
        this.id = id;
    }
    @Override
    protected String run() {
        return prefixStoredOnRemoteDataStore + id;
    }
    @Override
    protected String getCacheKey() {
        return String.valueOf(id);
    }
    /**
     * Allow the cache to be flushed for this object.
     * 
     * @param id
     *            argument that would normally be passed to the command
     */
    public static void flushCache(int id) {
        HystrixRequestCache.getInstance(GETTER_KEY,
                HystrixConcurrencyStrategyDefault.getInstance()).clear(String.valueOf(id));
    }
}
public static class SetterCommand extends HystrixCommand<Void> {
    private final int id;
    private final String prefix;
    public SetterCommand(int id, String prefix) {
        super(HystrixCommandGroupKey.Factory.asKey("GetSetGet"));
        this.id = id;
        this.prefix = prefix;
    }
    @Override
    protected Void run() {
        // persist the value against the datastore
        prefixStoredOnRemoteDataStore = prefix;
        // flush the cache
        GetterCommand.flushCache(id);
        // no return value
        return null;
    }
}
目录
相关文章
|
10天前
|
消息中间件 缓存 监控
优化微服务架构中的数据库访问:策略与最佳实践
在微服务架构中,数据库访问的效率直接影响到系统的性能和可扩展性。本文探讨了优化微服务架构中数据库访问的策略与最佳实践,包括数据分片、缓存策略、异步处理和服务间通信优化。通过具体的技术方案和实例分析,提供了一系列实用的建议,以帮助开发团队提升微服务系统的响应速度和稳定性。
|
7天前
|
缓存 NoSQL 数据库
构建高效后端服务:从架构设计到性能优化的实践之路
本文旨在探讨如何通过合理的架构设计和性能优化策略,构建一个既稳定又高效的后端服务。文章首先概述了后端服务开发中常见的挑战和误区,随后详细介绍了微服务架构、缓存机制、数据库优化、服务器配置以及代码审查等关键技术和方法。通过深入浅出的案例分析和实用建议,本文将为后端开发者提供一套系统化的指导方案,助力其打造出高性能的后端服务体系。
|
3天前
|
消息中间件 弹性计算 运维
云消息队列RabbitMQ 版架构优化评测
云消息队列RabbitMQ 版架构优化评测
19 6
|
8天前
|
Kubernetes Java Android开发
用 Quarkus 框架优化 Java 微服务架构的设计与实现
Quarkus 是专为 GraalVM 和 OpenJDK HotSpot 设计的 Kubernetes Native Java 框架,提供快速启动、低内存占用及高效开发体验,显著优化了 Java 在微服务架构中的表现。它采用提前编译和懒加载技术实现毫秒级启动,通过优化类加载机制降低内存消耗,并支持多种技术和框架集成,如 Kubernetes、Docker 及 Eclipse MicroProfile,助力开发者轻松构建强大微服务应用。例如,在电商场景中,可利用 Quarkus 快速搭建商品管理和订单管理等微服务,提升系统响应速度与稳定性。
28 5
|
10天前
|
消息中间件 缓存 监控
优化微服务架构中的数据库访问:策略与实践
随着微服务架构的普及,如何高效管理和优化数据库访问成为了关键挑战。本文探讨了在微服务环境中优化数据库访问的策略,包括数据库分片、缓存机制、异步处理等技术手段。通过深入分析实际案例和最佳实践,本文旨在为开发者提供实际可行的解决方案,以提升系统性能和可扩展性。
|
13天前
|
缓存 算法 安全
深入探索现代操作系统架构及其优化策略
本文旨在探讨现代操作系统的架构设计及其在性能优化上的关键技术。通过对操作系统的核心组件和调度机制的分析,揭示其背后的设计理念与技术挑战,同时提出针对性的优化措施,以提升系统效率和用户体验。
|
3天前
|
设计模式 算法 搜索推荐
后端开发中的设计模式应用
在软件开发的浩瀚海洋中,设计模式犹如一座座灯塔,为后端开发者指引方向。本文将深入探讨后端开发中常见的设计模式,并通过实例展示如何在实际项目中巧妙应用这些模式,以提升代码的可维护性、扩展性和复用性。通过阅读本文,您将能够更加自信地应对复杂后端系统的设计与实现挑战。
21 3
|
3天前
|
JavaScript 前端开发 API
深入浅出Node.js后端开发
【9月更文挑战第23天】在这篇文章中,我们将探索Node.js的世界,了解它如何改变后端开发的面貌。通过实际案例和代码示例,我们不仅学习Node.js的核心概念,还会深入探讨它的高级特性,如异步编程、事件驱动模型以及微服务架构的应用。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角和实用技能,帮助你构建更高效、可扩展的后端系统。
33 19
|
1天前
|
JavaScript 开发者
深入理解Node.js事件循环及其在后端开发中的应用
【8月更文挑战第57天】本文将带你走进Node.js的事件循环机制,通过浅显易懂的语言和实例代码,揭示其背后的工作原理。我们将一起探索如何高效利用事件循环进行异步编程,提升后端应用的性能和响应速度。无论你是Node.js新手还是有一定经验的开发者,这篇文章都能给你带来新的启发和思考。
|
1天前
|
存储 运维 负载均衡
后端开发中的微服务架构实践与思考
本文旨在探讨后端开发中微服务架构的应用及其带来的优势与挑战。通过分析实际案例,揭示如何有效地实施微服务架构以提高系统的可维护性和扩展性。同时,文章也讨论了在采用微服务过程中需要注意的问题和解决方案。