美團面試官:生成訂單後一段時間不支付訂單會自動關閉的功能該如何實現?越詳細越好~

業務場景

咱們以訂單功能爲例說明下:生成訂單後一段時間不支付訂單會自動關閉。最簡單的想法是設置定時任務輪詢,可是每一個訂單的建立時間不同,定時任務的規則沒法設定,若是將定時任務執行的間隔設置的太短,太影響效率。還有一種想法,在用戶進入訂單界面的時候,判斷時間執行相關操做。方式可能有不少,在這裏介紹一種監聽 Redis 鍵值對過時時間來實現訂單自動關閉。整理了一份Java面試寶典完整版PDF面試

實現思路

在生成訂單時,向 Redis 中增長一個 KV 鍵值對,K 爲訂單號,保證經過 K 能定位到數據庫中的某個訂單便可,V 可爲任意值。假設,生成訂單時向 Redis 中存放 K 爲訂單號,V 也爲訂單號的鍵值對,並設置過時時間爲 30 分鐘,若是該鍵值對在 30 分鐘過時後可以發送給程序一個通知,或者執行一個方法,那麼便可解決訂單關閉問題。實現:經過監聽 Redis 提供的過時隊列來實現,監聽過時隊列後,若是 Redis 中某一個 KV 鍵值對過時了,那麼將向監聽者發送消息,監聽者能夠獲取到該鍵值對的 K,注意,是獲取不到 V 的,由於已通過期了,這就是上面所提到的,爲何要保證能經過 K 來定位到訂單,而 V 爲任意值便可。拿到 K 後,經過 K 定位訂單,並判斷其狀態,若是是未支付,更新爲關閉,或者取消狀態便可。redis

開啓 Redis key 過時提醒

修改 redis 相關事件配置。找到 redis 配置文件 redis.conf,查看 notify-keyspace-events 配置項,若是沒有,添加 notify-keyspace-events Ex,若是有值,則追加 Ex,相關參數說明以下:spring

  • K:keyspace 事件,事件以 keyspace@ 爲前綴進行發佈數據庫

  • E:keyevent 事件,事件以 keyevent@ 爲前綴進行發佈ide

  • g:通常性的,非特定類型的命令,好比del,expire,rename等spring-boot

  • $:字符串特定命令spa

  • l:列表特定命令code

  • s:集合特定命令xml

  • h:哈希特定命令token

  • z:有序集合特定命令

  • x:過時事件,當某個鍵過時並刪除時會產生該事件

  • e:驅逐事件,當某個鍵因 maxmemore 策略而被刪除時,產生該事件

  • A:g$lshzxe的別名,所以」AKE」意味着全部事件

引入依賴

在 pom.xml 中添加 org.springframework.boot:spring-boot-starter-data-redis 依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

相關配置

定義配置 RedisListenerConfig 實現監聽 Redis key 過時時間

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

@Configuration
public class RedisListenerConfig {

    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {

        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
}

定義監聽器 RedisKeyExpirationListener,實現KeyExpirationEventMessageListener 接口,查看源碼發現,該接口監聽全部 db 的過時事件 keyevent@*:expired"整理了一份Java面試寶典完整版PDF

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;

/**
 * 監聽全部db的過時事件__keyevent@*__:expired"
 */
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {

    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    /**
     * 針對 redis 數據失效事件,進行數據處理
     * @param message
     * @param pattern
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {

        // 獲取到失效的 key,進行取消訂單業務處理
        String expiredKey = message.toString();
        System.out.println(expiredKey);
    }
}
相關文章
相關標籤/搜索