ExecutorService を使用して一連のスレッド化されたタスクを実行する EJB をサーブレットが呼び出すテスト EE7 アプリケーションがあります。同じ一連のタスクを実行しているボグ標準の Java コンソール アプリと比較すると、パフォーマンス (1 秒あたりのトランザクション数) は大きく異なります - コンソール アプリで 2.5k tps 対 EE アプリで 50 tps (どちらも、コードを 60 秒間繰り返し、EE アプリは SoapUI を介して実行されます)。
タスクは、100 ミリ秒スリープする単なるダミー実装です。テストでは、5 つの並列タスクが送信されます。タイミングは、これらすべてが予想どおり 100 ~ 101 ミリ秒で完了することを示しています。Glassfish + サーブレット + EJB は、実際にはオーバーヘッドをまったく追加していないようです。
Windows と Linux の両方で同じテストを実行しましたが、結果は同じでした。どちらの場合も、Glassfish 4.0 とデフォルトの ManagedExecutorService を使用しています。max connections/cache/bean/thread/etc の設定を 256 に増やしました。
注: EJB を呼び出すだけで、ExecutorService に何も送信しない場合、9k tps になります。タスクの待ち時間をなくすと、5,000 tps になります。私のマシンは決してリソースを使い果たしているようには見えません (CPU コアの平均は 10 ~ 25% で、システム メモリに余裕があります (jvm を調整していません))。
これは少し漠然としていますが、Glassfish の構成、または Java EE7 でのマネージド スレッドの実行に問題があると思いますか? 私は、Glassfish から少なくとも 1,000 tps を期待し、2,000 tps を期待していました。
更新: JBoss の Wildfly 8 ベータ版を試してみましたが、約 5k tps になりました。これは大きな違いです。
以下のコードから再作成するには、Netbeans 7.4 で Glassfish 4/EE7 をターゲットとする EE アプリを作成し、サーブレットと EJB を追加します。
EJB + Task クラス:
package ejb;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import javax.annotation.Resource;
import javax.ejb.*;
import javax.enterprise.concurrent.ManagedExecutorService;
@Stateless
@LocalBean
public class NewSessionBean {
@Resource(name="concurrent/__defaultManagedExecutorService")
ManagedExecutorService executor;
public String businessMethod() {
long start = System.currentTimeMillis();
String message = "<p>starting businessMethod()</p>";
List<MyTask> tasks = new ArrayList<MyTask>();
for(int i = 0; i < 5; i++) {
MyTask task = new MyTask(String.format("Task %d \r\n", i));
tasks.add(task);
}
List<Future<String>> futures = new ArrayList<Future<String>>();
int taskCount = tasks.size();
for(int i = 0; i < taskCount; i++) {
MyTask task = tasks.get(i);
if(task != null) {
futures.add(executor.submit(task));
}
}
int futureCount = futures.size();
for(int i = 0; i < futureCount; i++) {
Future<String> future = futures.get(i);
if(future != null) {
try {
message += future.get();
} catch (InterruptedException ex) {
message += ex.getMessage();
Logger.getLogger(NewSessionBean.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) {
message += ex.getMessage();
Logger.getLogger(NewSessionBean.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
long end = System.currentTimeMillis();
message += String.format("<p>took %d ms (started at %d)</p>", end-start, start);
return message;
}
}
class MyTask implements Callable<String> {
String message;
public MyTask(String message) {
this.message = message;
}
@Override
public String call() throws Exception {
if(message == null) message = "";
long start = System.currentTimeMillis();
message += String.format("<p>thread %s</p>", Thread.currentThread().getName());
message += String.format("<p>started at %d</p>", start);
Thread.sleep(100);
long end = System.currentTimeMillis();
message += String.format("<p>ended at %d (duration = %d ms)</p>", end, end-start);
return message;
}
}
サーブレット:
package servlet;
import java.io.*;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class NewServlet extends HttpServlet {
@EJB
ejb.NewSessionBean nsb;
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet NewServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Servlet NewServlet at " + request.getContextPath() + "</h1>");
out.println(nsb.businessMethod());
out.println("</body>");
out.println("</html>");
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletInfo() {
return "Short description";
}
}