spring mvc 中的 DeferredResult

DeferredResult in spring mvc

我有一个 class 扩展了 DeferredResults 并扩展了 Runnable,如下所示

public class EventDeferredObject<T> extends DeferredResult<Boolean> implements Runnable {

    private Long customerId;

    private String email;

    @Override
    public void run() {

        RestTemplate restTemplate=new RestTemplate();

        EmailMessageDTO emailMessageDTO=new EmailMessageDTO("dineshshe@gmail.com", "Hi There");

        Boolean result=restTemplate.postForObject("http://localhost:9080/asycn/sendEmail", emailMessageDTO, Boolean.class);

        this.setResult(result);
    }

//Constructor and getter and setters
}

现在我有控制器 return 上面的对象 class,每当新请求到达控制器时,我们检查该请求是否存在于 HashMap 中(在该实例中存储未处理的请求) .如果不存在,那么我们正在创建 EventDeferredObject class 的对象,可以将其存储在 HashMap 中,并在 it.If 上调用 start() 方法,这种类型的请求已经存在,那么我们将 return 来自 HashMap.On 根据请求完成,我们将从 HashMap 中删除该请求。

@RequestMapping(value="/sendVerificationDetails")

public class SendVerificationDetailsController {


private ConcurrentMap<String , EventDeferredObject<Boolean>> requestMap=new  ConcurrentHashMap<String , EventDeferredObject<Boolean>>(); 

    @RequestMapping(value="/sendEmail",method=RequestMethod.POST)
    public EventDeferredObject<Boolean> sendEmail(@RequestBody EmailDTO emailDTO)
    {
        EventDeferredObject<Boolean> eventDeferredObject = null;

        System.out.println("Size:"+requestMap.size());

        if(!requestMap.containsKey(emailDTO.getEmail()))
        {
            eventDeferredObject=new EventDeferredObject<Boolean>(emailDTO.getCustomerId(), emailDTO.getEmail());
            requestMap.put(emailDTO.getEmail(), eventDeferredObject);

            Thread t1=new Thread(eventDeferredObject);
            t1.start();

        }
        else
        {
            eventDeferredObject=requestMap.get(emailDTO.getEmail());

        }
        eventDeferredObject.onCompletion(new Runnable() {

            @Override
            public void run() {
                if(requestMap.containsKey(emailDTO.getEmail()))
                {   
                    requestMap.remove(emailDTO.getEmail());
                }
            }
        });

        return eventDeferredObject;
    }

}

现在,如果没有相同的请求到达存储在 HashMap 中的请求,则此代码可以正常工作。如果我们同时给出多个不同的请求,代码就可以正常工作。

好吧,我不知道我是否理解正确,但我认为您的代码中可能存在竞争条件,例如此处:

        if(!requestMap.containsKey(emailDTO.getEmail()))
        {
            eventDeferredObject=new EventDeferredObject<Boolean>(emailDTO.getCustomerId(), emailDTO.getEmail());
            requestMap.put(emailDTO.getEmail(), eventDeferredObject);

            Thread t1=new Thread(eventDeferredObject);
            t1.start();

        }
        else
        {
            eventDeferredObject=requestMap.get(emailDTO.getEmail());

        }

想一想您有两个使用相同密钥 emailDTO.getEmail() 的请求的场景。 请求1检查map中是否有key,没有找到就放入里面。 稍后请求 2 来了,检查地图中是否有键,找到它,然后 去拿它;然而就在这之前,由请求 1 启动的线程结束,另一个由 onComplete 事件启动的线程从映射中删除键。此时,

requestMap.get(emailDTO.getEmail())

将 return 为 null,因此您将遇到 NullPointerException。 现在,这看起来确实是一种罕见的情况,所以我不知道这是否是您遇到的问题。

我会尝试修改代码如下(我自己没有运行,所以可能会出错):

public class EventDeferredObject<T> extends DeferredResult<Boolean> implements Runnable {

    private Long customerId;

    private String email;

    private ConcurrentMap ourConcurrentMap;

    @Override
    public void run() {
        ...
        this.setResult(result);
        ourConcurrentMap.remove(this.email);
    }

//Constructor and getter and setters
}

所以 DeferredResult 实现有责任将自己从并发映射中删除。此外,我不使用 onComplete 来设置回调线程,因为在我看来这是一个不必要的复杂化。为了避免我之前谈到的竞争条件,需要以某种方式将条目存在的验证与其获取结合到一个原子操作中;这是由 ConcurrentMap 的 putIfAbsent 方法完成的。因此我把控制器改成了

@RequestMapping(value="/sendVerificationDetails")
public class SendVerificationDetailsController {

    private ConcurrentMap<String , EventDeferredObject<Boolean>> requestMap=new  ConcurrentHashMap<String , EventDeferredObject<Boolean>>(); 

    @RequestMapping(value="/sendEmail",method=RequestMethod.POST)
    public EventDeferredObject<Boolean> sendEmail(@RequestBody EmailDTO emailDTO)
    {
        EventDeferredObject<Boolean> eventDeferredObject = new EventDeferredObject<Boolean>(emailDTO.getCustomerId(), emailDTO.getEmail(), requestMap);
        EventDeferredObject<Boolean> oldEventDeferredObject = requestMap.putIfAbsent(emailDTO.getEmail(), eventDeferredObject );

        if(oldEventDeferredObject == null)
        {
            //if no value was present before
            Thread t1=new Thread(eventDeferredObject);
            t1.start();
            return eventDeferredObject;
        }
        else
        {
            return oldEventDeferredObject; 
        }
    }
}

如果这不能解决你的问题,我希望至少能给点思路。