【问题标题】:How to synchronize concurring Web Service calls in Java如何在 Java 中同步并发 Web 服务调用
【发布时间】:2013-03-21 15:41:54
【问题描述】:

我目前正在开发一些由 SAP 系统触发的 Java 网络服务(以及带有 MySQL 连接的 JPA)。 为了简化我的问题,我将两个关键实体称为BlogEntryComment。一个BlogEntry 可以有多个CommentsComment 始终只属于一个 BlogEntry

所以我有三个服务(我不能也不想重新定义它们,因为它们是由我从 SAP 导出并使用并行与其他系统通信的 WSDL 定义的):CreateBlogEntry、@987654328 @,CreateCommentForUpcomingBlogEntry

它们被正确触发,CreateBlogEntryCreateComment 在单独调用时绝对没有问题。

但是:服务CreateCommentForUpcomingBlogEntry 发送Comment 和一个“外键”来识别“即将到来的”BlogEntry。在内部,它还调用CreateBlogEntry 来创建实际的BlogEntry由于它们的异步特性,这两个服务是并发的。

所以我有两个选择:

  1. 创建一个虚拟BlogEntry 并将Comment 连接到它并更新BlogEntry,一旦CreateBlogEntry“到达”
  2. 等待CreateBlogEntry,然后将Comment 连接到新的BlogEntry

目前我正在尝试前者,但是一旦两个服务都完全执行,我最终会得到两个 BlogEntries。其中一个只有CreateCommentForUpcomingBlogEntry 提供的ID,但它已正确连接到Comment(反之亦然)。另一个BlogEntry 拥有所有其他信息(例如postDatebody),但Comment 没有与之关联。


这里是服务实现CreateCommentForUpcomingBlogEntry的代码sn-p:

@EJB
private BlogEntryFacade blogEntryFacade;
@EJB
private CommentFacade commentFacade;
...
List<BlogEntry> blogEntries = blogEntryFacade.findById(request.getComment().getBlogEntryId().getValue());
BlogEntry persistBlogEntry;
if (blogEntries.isEmpty()) {
    persistBlogEntry = new BlogEntry();
    persistBlogEntry.setId(request.getComment().getBlogEntryId().getValue());
    blogEntryFacade.create(persistBlogEntry);
} else {
    persistBlogEntry = blogEntries.get(0);
}

Comment persistComment = new Comment();
persistComment.setId(request.getComment().getID().getValue());
persistComment.setBody(request.getComment().getBody().getValue());
/*
    set other properties
*/
persistComment.setBlogEntry(persistBlogEntry);
commentFacade.create(persistComment);
...

这是实现CreateBlogEntry的代码sn-p:

@EJB
private BlogEntryFacade blogEntryFacade;
...
List<BlogEntry> blogEntries = blogEntryFacade.findById(request.getBlogEntry().getId().getValue());
BlogEntry persistBlogEntry;
Boolean update = false;
if (blogEntries.isEmpty()) {
    persistBlogEntry = new BlogEntry();
} else {
    persistBlogEntry = blogEntries.get(0);
    update = true;
}
persistBlogEntry.setId(request.getBlogEntry().getId().getValue());
persistBlogEntry.setBody(request.getBlogEntry().getBody().getValue());
/*
    set other properties
*/
if (update) {
    blogEntryFacade.edit(persistBlogEntry);
} else {
    blogEntryFacade.create(persistBlogEntry);
}
...

这是一些无法使事情按预期发生的摆弄。

遗憾的是,我还没有找到同步这些同时服务调用的方法。我可以让CreateCommentForUpcomingBlogEntry sleep 几秒钟,但我认为这不是正确的做法。

我可以强制我的facades 和它们各自的EntityManagers 的每个实例重新加载它们的数据集吗?我可以将我的请求放入某种队列中,该队列会根据某些条件被清空吗?

那么:让它等待BlogEntry 存在的最佳做法是什么?

提前致谢, 大卫

信息:

  • GlassFish 服务器 3.1.2
  • EclipseLink,版本:Eclipse Persistence Services - 2.3.2.v20111125-r10461

【问题讨论】:

  • 如果已经存在具有相同 id(由CreateCommentForUpcomingBlogEntry 创建)的博客条目,您为什么不签入CreateBlogEntry?如果是,更新而不是创建。
  • 这正是我目前正在尝试做的(参见第二个代码 sn-p)。
  • 过失。奇怪的是你得到两个条目(具有相同的唯一 ID?)
  • 我可能不得不说,ID 是指由发布 SAP 系统生成的值,并不是真正的 ID,数据库有自己的一组主键(真正的唯一 ID)。现在,我通过在我的facade 中调用getEntityManager().getEntityManagerFactory().getCache().evictAll(); 告诉CreateCommentForUpcomingBlogEntry-Thread 休眠10 秒并清除EntityManger 的缓存,设法使我的代码正常运行。我仍然拒绝相信这是最好的解决方案:(
  • 换句话说blogEntryFacade.edit(persistBlogEntry);fails 更新当前记录bu创建一个新记录?听起来很牛逼。你确定你的持久化策略没问题吗?

标签: java web-services jpa persistence


【解决方案1】:

如果您确定收到了CreateBlogEntry 呼叫,请将CreateCommentForUpcomingBlogEntry 呼叫排队并在收到CreateBlogEntry 呼叫后将其出列并处理。

由于您在应用程序服务器上,对于队列,您可能可以使用自动刷新到存储的 JMS 队列或使用数据库缓存引擎(Ehcache ?),以防您收到大量调用或想要提供恢复机制跨重启。

【讨论】:

    猜你喜欢
    • 2011-04-28
    • 2010-09-22
    • 2012-11-21
    • 2017-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多