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;
}
}
}
如果这不能解决你的问题,我希望至少能给点思路。
我有一个 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;
}
}
}
如果这不能解决你的问题,我希望至少能给点思路。