ejb能调用另一个ejb吗_异步EJB只是一个Gi头吗?

ejb能调用另一个ejb吗

在之前的文章( 此处和此处 )中,我展示了当服务器负载沉重时,创建非阻塞异步应用程序可以提高性能。 EJB 3.1引入了@Asynchronous批注,用于指定方法将在将来的某个时间返回其结果。 Javadocs声明必须返回voidFuture 。 下面的清单显示了使用此注释的服务示例:

Service2.java

@Stateless
public class Service2 {@Asynchronouspublic Future<String> foo(String s) {// simulate some long running processThread.sleep(5000);s += "<br>Service2: threadId=" + Thread.currentThread().getId();return new AsyncResult<String>(s);}
}

注释在第4行上。该方法返回String类型的Future ,并在第10行上通过将输出包装在AsyncResult 。 在客户端代码调用EJB方法时,容器拦截了该调用并创建了一个任务,它将在另一个线程上运行,以便它可以立即返回Future 。 当容器然后使用其他线程运行任务时,它将调用EJB的方法并使用AsyncResult来完成给定调用者的Future 。 即使看起来与Internet上所有示例中的代码完全一样,此代码也存在一些问题。 例如, Future类仅包含用于获取Future结果的阻塞方法,而不包含用于在回调完成时注册回调的任何方法。 这将导致如下所示的代码,当容器处于加载状态时,这是很糟糕的:

客户端程序

//type 1
Future<String> f = service.foo(s);
String s = f.get(); //blocks the thread, but at least others can run
//... do something useful with the string...//type 2
Future<String> f = service.foo(s);
while(!f.isDone()){try {Thread.sleep(100);} catch (InterruptedException e) {...}
}
String s = f.get();
//... do something useful with the string...

这种代码是不好的,因为它导致线程阻塞,这意味着它们在这段时间内无法做任何有用的事情。 当其他线程可以运行时,需要进行上下文切换,这会浪费时间和精力(有关成本或我以前的文章的结果,请参见这篇好文章)。 像这样的代码会使已经处于负载状态的服务器承受更大的负载,并停止运行。

那么是否有可能使容器异步执行方法,而编写不需要阻塞线程的客户端呢? 它是。 以下清单显示了一个servlet。

AsyncServlet2.java

@WebServlet(urlPatterns = { "/AsyncServlet2" }, asyncSupported = true)
public class AsyncServlet2 extends HttpServlet {@EJB private Service3 service;protected void doGet(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {final PrintWriter pw = response.getWriter();pw.write("<html><body>Started publishing with thread " + Thread.currentThread().getId() + "<br>");response.flushBuffer(); // send back to the browser NOWCompletableFuture<String> cf = new CompletableFuture<>();service.foo(cf);// since we need to keep the response open, we need to start an async contextfinal AsyncContext ctx = request.startAsync(request, response);cf.whenCompleteAsync((s, t)->{try {if(t!=null) throw t;pw.write("written in the future using thread " + Thread.currentThread().getId()+ "... service response is:");pw.write(s);pw.write("</body></html>");response.flushBuffer();ctx.complete(); // all done, free resources} catch (Throwable t2) {
...

第1行声明Servlet支持异步运行-不要忘记这一点! 第8-10行开始将数据写入响应,但是有趣的是第13行中的调用异步服务方法的行。 我们没有将Future用作返回类型,而是向其传递了CompletableFuture ,它用于将结果返回给我们。 怎么样? 第16行会启动异步servlet上下文,因此我们仍然可以在doGet方法返回后写入响应。 从第17行开始,然后有效地在CompletableFuture上注册了一个回调,一旦CompletableFuture完成并返回结果,该回调将被调用。 这里没有阻塞代码–没有线程被阻塞,没有线程被轮询,等待结果! 在负载下,服务器中的线程数可以保持最少,从而确保服务器可以高效运行,因为需要较少的上下文切换。

服务实现如下所示:

Service3.java

@Stateless
public class Service3 {@Asynchronouspublic void foo(CompletableFuture<String> cf) {// simulate some long running processThread.sleep(5000);cf.complete("bar");}
}

第7行确实很丑陋,因为它会阻塞,但假装这是代码调用使用大多数Web服务客户端和JDBC驱动程序会阻塞的API调用在Internet或较慢的数据库中远程部署的Web服务的代码。 或者,使用异步驱动程序 ,当结果可用时,完成第9行所示的将来。然后向CompletableFuture发出信号,可以调用在先前清单中注册的回调。

这不只是使用简单的回调吗? 这肯定是相似的,下面的两个清单显示了使用自定义回调接口的解决方案。

AsyncServlet3.java

@WebServlet(urlPatterns = { "/AsyncServlet3" }, asyncSupported = true)
public class AsyncServlet3 extends HttpServlet {@EJB private Service4 service;protected void doGet(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
...final AsyncContext ctx = request.startAsync(request, response);service.foo(s -> {
...pw.write("</body></html>");response.flushBuffer();ctx.complete(); // all done, free resources
...

Service4.java

@Stateless
public class Service4 {@Asynchronouspublic void foo(Callback<String> c) {// simulate some long running processThread.sleep(5000);c.apply("bar");}public static interface Callback<T> {void apply(T t);}
}

同样,在客户端中,绝对没有任何阻塞。 但是,由于以下原因,使用CompletableFutureAsyncServlet2Service3类的早期示例更好一些:

  • CompletableFuture的API允许出现异常/失败,
  • CompletableFuture类提供用于异步执行回调和相关任务的方法,即在fork-join池中,以便整个系统使用尽可能少的线程运行,从而可以更有效地处理并发性,
  • 可将CompletableFuture与其他对象结合使用,以便您可以注册一个回调,仅在多个CompletableFuture完成时才能调用该回调,
  • 回调不会立即被调用,而是池中有限数量的线程按它们应运行的顺序为CompletableFuture的执行提供服务。

在第一个清单之后,我提到异步EJB方法的实现存在一些问题。 除了阻塞客户端之外,另一个问题是,根据EJB 3.1 Spec的 4.5.3章,客户端事务上下文不会通过异步方法调用传播。 如果您想使用@Asynchronous批注创建两个可以并行运行并在单个事务中更新数据库的方法,那么它将不起作用。 这在某种程度上限制了@Asynchronous注释的使用。

使用CompletableFuture ,您可能会认为可以在同一个事务上下文中并行运行多个任务,方法是先在EJB中启动一个事务,然后创建多个可运行对象,并使用runAsync方法运行它们,该方法在执行中运行它们池,然后注册一个回调以使用allOf方法完成所有操作后allOf 。 但是您可能会因为多种原因而失败:

  • 如果您使用容器管理的事务,那么一旦导致事务开始的EJB方法将控制权返回给容器,事务将被提交-如果那时您的期货还没有完成,您将不得不阻止运行EJB方法的线程这样它就等待并行执行的结果,而阻塞正是我们要避免的,
  • 如果运行任务的单个执行池中的所有线程都被阻塞,等待它们的数据库调用应答,那么您将有可能创建性能不佳的解决方案–在这种情况下,您可以尝试使用非阻塞的异步驱动程序 ,但不能每个数据库都有这样的驱动程序,
  • 一旦任务在不同的线程(例如执行池中的线程)上运行,线程本地存储(TLS)就不再可用,因为正在运行的线程与将工作提交到执行池并进行设置的线程不同在提交工作之前将值存入TLS,
  • 诸如EntityManager 类的资源不是线程安全的 。 这意味着你无法通过EntityManager成提交给池的任务,而每个任务需要得到它自己的保持EntityManager实例,而是创建EntityManager取决于TLS(见下文)。

让我们用以下代码更详细地考虑TLS,该代码显示了一种异步服务方法,该服务方法试图做几件事以测试允许的操作。

Service5.java

@Stateless
public class Service5 {@Resource ManagedExecutorService mes;@Resource EJBContext ctx;@PersistenceContext(name="asdf") EntityManager em;@Asynchronouspublic void foo(CompletableFuture<String> cf, final PrintWriter pw) {//pw.write("<br>inside the service we can rollback, i.e. we have access to the transaction");//ctx.setRollbackOnly();//in EJB we can use EMKeyValuePair kvp = new KeyValuePair("asdf");em.persist(kvp);Future<String> f = mes.submit(new Callable<String>() {@Overridepublic String call() throws Exception {try{ctx.setRollbackOnly();pw.write("<br/>inside executor service, we can rollback the transaction");}catch(Exception e){pw.write("<br/>inside executor service, we CANNOT rollback the transaction: " + e.getMessage());}try{//in task inside executor service we CANNOT use EMKeyValuePair kvp = new KeyValuePair("asdf");em.persist(kvp);pw.write("...inside executor service, we can use the EM");}catch(TransactionRequiredException e){pw.write("...inside executor service, we CANNOT use the EM: " + e.getMessage());}
...

第12行没有问题,您可以回滚在容器调用EJB方法时在第9行自动启动的事务。 但是该事务将不是可能由调用第9行的代码启动的全局事务。第16行也没有问题,您可以使用EntityManager写入由第9行开始的事务内部的数据库。显示了在不同线程上运行代码的另一种方式,即使用Java EE 7中引入的ManagedExecutorService 。但是,这在任何时候都依赖TLS时也会失败,例如,第22行和第31行会导致异常,因为在第9行启动的事务无法定位,因为使用TLS来定位,并且第21-35行中的代码使用与第19行之前的代码不同的线程运行。

下一个清单显示,第11-14行在CompletableFuture上注册的完成回调也与第4-10行运行在不同的线程中,因为在第6行的回调之外启动提交事务的调用将在第6行失败再次参考图13,因为第13行的调用在TLS中搜索当前事务,并且因为运行第13行的线程与运行第6行的线程不同,所以找不到事务。 实际上,下面的清单实际上有一个不同的问题:处理对Web服务器的GET请求的线程运行第JBAS010152: APPLICATION ERROR: transaction still active in request with status 0和11行,然后返回,此时JBoss日志JBAS010152: APPLICATION ERROR: transaction still active in request with status 0 –即使线程运行第13行可以找到该事务,它是否仍处于活动状态或容器是否已关闭它也值得怀疑。

AsyncServlet5.java

@Resource UserTransaction ut;@Override
protected void doGet(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {ut.begin();	
...CompletableFuture<String> cf = new CompletableFuture<>();service.foo(cf, pw);
...cf.whenCompleteAsync((s, t)->{...ut.commit(); // => exception: "BaseTransaction.commit - ARJUNA016074: no transaction!"});
}

事务显然依赖于线程和TLS。 但这不仅仅是依赖TLS的事务。 以JPA为例,该JPA被配置为直接在TLS中存储会话(即与数据库的连接) ,或者被配置为将该会话的范围限定为当前的JTA事务 ,而该事务又依赖于TLS。 或以使用从EJBContextImpl.getCallerPrincipal提取的Principal进行安全性检查为例,该Principal对AllowedMethodsInformation.checkAllowed进行调用,然后再调用使用TLS的CurrentInvocationContext并仅返回(如果在TLS中未找到任何上下文),而不是进行适当的权限检查如第112行所示。

这些对TLS的依赖意味着,在使用CompletableFuture或Java SE fork-join池或其他线程池(无论是否由容器管理)时,许多标准Java EE功能将不再起作用。

公平起见,对于Java EE,我在这里所做的事情都是按设计的! 规范实际上禁止在EJB容器中启动新线程。 我记得十多年前我曾经使用过旧版本的Websphere进行过一次测试–启动线程会引发异常,因为容器确实严格遵守规范。 这是有道理的:不仅因为线程数应由容器管理,还因为Java EE对TLS的依赖意味着使用新线程会导致问题。 从某种意义上讲,这意味着使用CompletableFuture是非法的,因为它使用了不受容器管理的线程池(该池由JVM管理)。 使用Java SE的ExecutorService也是如此。 Java EE 7的ManagedExecutorService是一个特例-它是规范的一部分,因此您可以使用它,但是您必须了解这样做的含义。 EJB上的@Asynchronous批注也是如此。

结果是可以在Java EE容器中编写异步非阻塞应用程序,但是您确实必须知道自己在做什么,并且可能必须手动处理安全性和事务之类的事情,这确实是个问题。首先使用Java EE容器的原因。

那么是否有可能编写一个容器来消除对TLS的依赖以克服这些限制? 的确如此,但是解决方案不仅仅取决于Java EE。 该解决方案可能需要更改Java语言。 许多年前,在依赖注入之前,我曾经写过POJO服务,它在方法之间传递了JDBC连接,即作为服务方法的参数。 我这样做是为了可以在同一事务内(即在同一连接上)创建新的JDBC语句。 我所做的与JPA或EJB容器所需要做的事情并没有什么不同。 但是,现代框架没有使用TLS作为显式传递连接或用户之类的东西的方式,而是使用TLS作为集中存储“上下文”的位置,例如,连接,事务,安全信息等。 只要您在同一线程上运行,TLS就是隐藏此类样板代码的好方法。 让我们假装TLS从未被发明过。 我们如何在不强制每个方法都将其作为参数的情况下传递上下文? Scala的implicit关键字是一种解决方案。 您可以声明参数可以隐式定位,这使编译器将其添加到方法调用中成为问题。 因此,如果Java SE引入了这种机制,则Java EE不需要依赖TLS,我们可以构建真正的异步应用程序,在该应用程序中,容器可以像今天一样通过检查注释来自动处理事务和安全性! 也就是说,当使用同步Java EE时,容器会知道何时提交事务-在启动事务的方法调用结束时。 如果您异步运行,则需要显式关闭事务,因为容器不再知道何时执行此操作。

当然,保持不阻塞的需要以及因此不依赖TLS的需求在很大程度上取决于当前的方案。 我不相信我今天在这里描述的问题是当今的普遍问题,而是它们是处理市场细分市场的应用程序所面临的问题。 只需看一下似乎为优秀的Java EE工程师提供的工作数量,而同步编程就是其中的标准。 但是,我确实相信,规模更大的IT软件系统会变得越来越多,它们处理的数据越多,阻塞API就会成为一个问题。 我还认为,当前硬件增长速度的下降使这个问题更加复杂。 有趣的是,Java是否a)是否需要跟上异步处理的趋势,以及b)Java平台是否会采取行动来固定对TLS的依赖。

翻译自: https://www.javacodegeeks.com/2015/08/is-asynchronous-ejb-just-a-gimmick.html

ejb能调用另一个ejb吗

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337494.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为啥电脑从C盘开始?A、B盘去哪了?

点击蓝字关注我们前些天硬盘坏了&#xff0c;幸好不是系统盘&#xff0c;不然那些软件安装配置会把我折腾坏&#xff0c;或许这也是在暗示我该换电脑了。重要的数据部分没有遭到损坏&#xff0c;数据是无价的&#xff0c;还是要勤备份。于是换上了一张新的硬盘&#xff0c;当时…

Python3求最后一个单词长度

Python3求最后一个单词长度原题&#xff1a;https://leetcode-cn.com/problems/length-of-last-word/ 给定一个仅包含大小写字母和空格 ’ ’ 的字符串 s&#xff0c;返回其最后一个单词的长度。如果字符串从左向右滚动显示&#xff0c;那么最后一个单词就是最后出现的单词。 …

java 锁_Java之线程并发的各种锁、锁、锁

因为两周没更新了...也不是懒&#xff0c;这两周确实有些忙&#xff0c;赶项目进度赶的不亦乐乎...终于赶在工期前&#xff0c;可以进入内测了&#xff0c;我也有了些时间&#xff0c;可以更新啦...线程并发锁是很常见的问题&#xff0c;而且在Java中锁的类型、概念、使用场景等…

threadsafe_Agrona的Threadsafe堆缓冲区

threadsafe这篇博客文章通过说明我们如何轻松访问线程内存来进行线程安全操作&#xff0c;继续了我在Agrona库上进行的系列文章 。 在继续讨论这是一个相当高级的主题之前&#xff0c;我可能应该警告一下&#xff0c;并且我不尝试解释诸如内存屏障之类的概念&#xff0c;仅概述…

C语言编程中错误异常该如何统一处理?1.8万字总结

点击蓝字关注我们本文主要总结嵌入式系统C语言编程中&#xff0c;主要的错误处理方式。一、错误概念1.1 错误分类从严重性而言&#xff0c;程序错误可分为致命性和非致命性两类。对于致命性错误&#xff0c;无法执行恢复动作&#xff0c;最多只能在用户屏幕上打印出错消息或将其…

Python3实现最小栈

Python3实现最小栈原题 https://leetcode-cn.com/problems/min-stack/ 设计一个支持 push&#xff0c;pop&#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 push(x) – 将元素 x 推入栈中。 pop() – 删除栈顶的元素。 top() – 获取栈顶元素。 getMin…

pycharm remote 远程项目 同步 本地_利器:PyCharm本地连接服务器搭建深度学习实验环境的三重境界...

作为实验室社畜&#xff0c;常需要在本地使用Pycharm写代码&#xff0c;然后将代码同步到服务器上&#xff0c;使用远程登录服务器并运行代码做实验。这其中有很多事情如果没有好的工具&#xff0c;做起来会非常麻烦。比如如何快速同步本地与服务器的代码&#xff1f;如何优雅地…

防御性编程技巧

点击蓝字关注我们在防御性编程的大框架之下&#xff0c;有许多常识性的规则。人们在想到防御性编程的时候&#xff0c;通常都会想到“断言”&#xff0c;这没有错。我们将在后面对此进行讨论。但是&#xff0c;还是有一些简单的编程习惯可以极大地提高代码的安全性。尽管看上去…

unity创建和销毁对象_如何创建和销毁对象

unity创建和销毁对象本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的旅程&#xff01; 在这里查看 &…

Python3实现打家劫舍问题

Python3实现打家劫舍问题原题 https://leetcode-cn.com/problems/house-robber/ 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同…

cookie无法读取bdstoken_第二章(第12节):cookie操作

有时候我们需要验证浏览器中 cookie 是否正确&#xff0c;因为基于真实 cookie 的测试是无法通过白盒和集成测试进行的。WebDriver 提供了操作 cookie 的相关方法&#xff0c;可以读取、添加和删除 cookie信息。WebDriver 操作 cookie 的方法&#xff1a;get_cookies()&#xf…

js 序列化内置对象_内置序列化技术

js 序列化内置对象本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的旅程&#xff01; 在这里查看 &#x…

C++ 基本的输入输出

点击蓝字关注我们C 标准库虽然提供了一组丰富的输入/输出功能&#xff0c;但是本章只讨论 C 编程中最基本和最常见的 I/O 操作。C 的 I/O 发生在流中&#xff0c;流是字节序列。如果字节流是从设备&#xff08;如键盘、磁盘驱动器、网络连接等&#xff09;流向内存&#xff0c;…

Python3有效括号问题

Python3有效括号问题原题 https://leetcode-cn.com/problems/valid-parentheses/ 给定一个只包括 ‘(’&#xff0c;’)’&#xff0c;’{’&#xff0c;’}’&#xff0c;’[’&#xff0c;’]’ 的字符串&#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左…

http 和 https_HTTPS与HTTP区别

HTTPS与HTTP的认识&#xff1a;HTTP 加密 认证 完整性保护 HTTPSHTTP的全称是 Hypertext Transfer Protocol Vertion (超文本传输协议)HTTPS&#xff1a; HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议。HTTPS和HTTP的区别&#xff1a;HTTPS协议需要到ca申…

C语言指针的自我介绍(你了解我吗?了解多少?)

点击蓝字关注我们hey! Ladies and Gentlemen.&#x1f601;欢迎大家来看望我&#xff0c;对&#xff0c;我就是指针(pointer)&#xff0c;被很多人吐槽&#x1f614;&#xff0c;也被人说好。我希望大家了解过我以后&#xff0c;能够爱上我&#x1f618;。大家在了解我之前 &am…

Python3最长连续递增序列问题

Python3最长连续递增序列问题原题 https://leetcode-cn.com/problems/longest-continuous-increasing-subsequence/ 题目&#xff1a; 给定一个未经排序的整数数组&#xff0c;找到最长且连续的的递增序列。 示例 1: 输入: [1,3,5,4,7] 输出: 3 解释: 最长连续递增序列是 [1…

访问nfs_通过NFS访问编年引擎

访问nfs总览 编年史引擎是数据虚拟化层。 它抽象化了访问&#xff0c;操作和订阅各种数据源的复杂性&#xff0c;因此该数据的用户无需知道实际存储数据的方式或位置。 这意味着该数据可以在系统之间迁移或以更有效的方式存储&#xff0c;但对于开发人员来说使用起来很复杂。 …

flex 解析json文件_使用 Python 处理 JSON 格式的数据 | Linux 中国

如果你不希望从头开始创造一种数据格式来存放数据&#xff0c;JSON 是一个很好的选择。如果你对 Python 有所了解&#xff0c;就更加事半功倍了。下面就来介绍一下如何使用 Python 处理 JSON 数据。-- Seth KenlonJSON 的全称是 JavaScript 对象表示法JavaScript Object Notati…

【C语言】指针进阶第一站:字符指针 typedef关键字!

点击蓝字关注我们简单回顾一下指针的概念内存会划分以字节为单位的空间&#xff0c;每一个字节都有一个编号&#xff08;地址/指针&#xff09;指针变量可以存放这个地址/指针注&#xff1a;我们日常所说的指针&#xff0c;一般是指针变量下面让我们坐上指针进阶的直通车&#…