• 基于kafka实现异步消息请求响应


    我们知道单体架构中的HTTP是同步请求响应,微服务架构中的消息时异步请求,无响应。

    但如果实际需求中,我们需要获得这个消息的请求结果怎么办?

    理论上也是可以实现的!

    一、基于SettableFuture实现

    首先,需要对请求的消息体进行升级,增加一个msgID,用于在接收返回消息时进行识别。

    第二,如果发送和接收消息的双方未约定请求通道和响应通道,发送消息时,消息体还需要携带响应通道信息。

    为了简化需求,我们假定请求通道和响应通道双方已经约定好。

    因为是异步请求,所以发送完消息后,需要返回一个future对象。而且这个future还是可以set的。

    我们想到了guava中的SettableFuture对象。

    好,下面进入实战环节,重新定义kafka发送消息的方法。新增一个异步send,跟普通send方法不同,发送完消息之后,我们有构造了一个future对象。

     //发送异步响应消息,需要写的唯一msgID,初步考虑使用UUID实现
        public ResponseFuture sendAsync(String msgId,String msg) {
            send(TASK_TOPIC, msg);
            SettableFuture<String> future = SettableFuture.create();
            ResponseFuture responseFuture = new ResponseFuture(future);
            ResponseFuture.put(msgId, responseFuture);
            return responseFuture;
        }

    核心的实现在ResponseFuture中。它相当于一个缓冲池,用于存放请求和未返回的响应。

    @Data
    public class ResponseFuture {
        public static final Map<String, ResponseFuture> map = new ConcurrentHashMap<>(256);
    
    
        public static void remove(String msgId) {
            map.remove(msgId);
        }
    
    
    
        public static void put(String msgId, ResponseFuture future) {
            map.put(msgId, future);
        }
    
    
    
        public static void setResponse(String msgId, ResponseBody result) {
            ResponseFuture response = map.get(msgId);
            response.setCurrentTime(System.currentTimeMillis());
            response.getFuture().set(result);
            map.put(msgId, response);
        }
    
        private Long currentTime;
        private SettableFuture<ResponseBody> future;
    
        public ResponseFuture( SettableFuture<ResponseBody> future) {
            this.currentTime = System.currentTimeMillis();
            this.future = future;
        }
    
        //todo:周期性调度删除过期数据
        public static void clear() {
            for (Map.Entry<String, ResponseFuture> entry : map.entrySet()) {
                ResponseFuture value = entry.getValue();
                Long currentTime = value.getCurrentTime();
                if (System.currentTimeMillis() - currentTime > 5000) {
                    map.remove(entry.getKey());
                }
            }
        }
    
    
    }

    kafka接收消息

    /**
         * 监听回复消息
         * @param record
         */
        public void listenResponse(ConsumerRecord<?, ?> record) {
            Optional<?> kafkaMessage = Optional.ofNullable(record.value());
            if (kafkaMessage.isPresent()) {
                Object message = kafkaMessage.get();
                log.info(message.toString());
                String msgId = message.toString().substring(1);
                ResponseBody responseBody = JSON.parseObject(message.toString(), ResponseBody.class);
                ResponseFuture.setResponse(msgId,responseBody);
            }
        }

    发送和接收的主函数

    public String testAsync() throws ExecutionException, InterruptedException {
            UUID uuid = UUID.randomUUID();
            String msgId = uuid.toString();
            String msg = "";
            ResponseFuture responseFuture = kafkaSender.sendAsync(msgId, msg);
            SettableFuture<ResponseBody> future = responseFuture.getFuture();
            try {
                ResponseBody responseBody = future.get(1, TimeUnit.SECONDS);
                return responseBody.getMsg();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
            ResponseFuture.remove(msgId);
            return null;
    
        }

     二、基于CountDownLatch实现

    经同事提醒,还有另外一种实现方式,那就是基于CountDownLatch

    发送消息的时候, 创建一个latch,存入到ResponseFuture的map中,然后latch开始await(),或者等待一定时间。

    然后收到消息之后,收到消息的线程根据msgId取出对应的latch,进行countDown()操作。通知发送进程已经拿到返回值。

    这个时候发送进程就可以去一个专门的地方拿返回值了。

  • 相关阅读:
    IDEA mybatis no data source 警告
    Flex 本地化(多语言) 语言文件夹设置
    Flex中 将字符串转化为Datetime类
    Disabling Clang Compiler warnings
    didEndEditingRowAtIndexPath with nil indexPath
    万达的商业模式有什么独到之处
    微信公众平台入门到精通合集
    BeautifulSoup解析非标准HTML的问题
    《人件》与软件研发项目管理
    足球3v3心得
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/16541074.html
Copyright © 2020-2023  润新知