Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 多节点部署中的ArrayBlockingQueue同步_Java - Fatal编程技术网

Java 多节点部署中的ArrayBlockingQueue同步

Java 多节点部署中的ArrayBlockingQueue同步,java,Java,在简单的描述中,我有一个servlet,响应时间很长,所以我决定将其分为两部分,一部分只是对客户机的响应,另一部分执行一些业务逻辑并将结果存储在数据库中。为了减少响应时间,我使用ThreadPoolExecutor结合ArrayBlockingQueue异步执行业务逻辑。使用ArrayBlockingQueueI可以确保在同一客户机的请求是连续的情况下原始的FIFO顺序。这是重要的前提 以下是一个片段: Servlet public class HelloServlet extends Http

在简单的描述中,我有一个servlet,响应时间很长,所以我决定将其分为两部分,一部分只是对客户机的响应,另一部分执行一些业务逻辑并将结果存储在数据库中。为了减少响应时间,我使用
ThreadPoolExecutor
结合
ArrayBlockingQueue
异步执行业务逻辑。使用
ArrayBlockingQueue
I可以确保在同一客户机的请求是连续的情况下原始的FIFO顺序。这是重要的前提

以下是一个片段:

Servlet

public class HelloServlet extends HttpServlet { 
    AsyncExecutor exe = new AsyncExecutor();       
    protected void doGet(HttpServletRequest req,
            HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter w = resp.getWriter();       
        exe.executeAsync(exe.new Task(this));   
        w.print("HELLO TO CLIENT");
    }   
    protected void someBusinessMethod(){
        // long time execution here
    }
}
执行人

public class AsyncExecutor {
    static final BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(10, true);    
    static final Executor executor = new ThreadPoolExecutor(3, 5, 20L, TimeUnit.SECONDS, queue);    
    public void executeAsync(Task t){
        boolean isTaskAccepted = false;
        while(!isTaskAccepted){
            try {                               
                executor.execute(t);
                isTaskAccepted = true;
            } catch (RejectedExecutionException e){             
            }
        }   
    }   
    class Task implements Runnable{
        private HelloServlet servlet;       
        Task(HelloServlet servlet){
            this.servlet = servlet;                 
        }
        @Override
        public void run() {
            // just call back to servlet's business method      
            servlet.someBusinessMethod();           
        }       
    }   
}
与第一篇文章中的实现一样,如果请求到达同一个Tomcat节点,我可以保证executeOldModelLogic方法的FIFO顺序。但由于前面有多节点部署和循环LB,我最终会遇到这样一种情况,即对于同一个调用方,“旧模型中的更新订阅”比“旧模型中的创建订阅”先到达ArrayBlockingQueue,这当然是一个糟糕的逻辑错误

至于你建议的几点:

p1、p2和p4:我可能无法将其用作解决方案,因为我没有对象的状态。您可以看到,我将Aspect和JoinPoint的引用传递给Runnable任务,以便从Runnable回调executeldmodellogic到Aspect

p3不知道这可能值得调查


p5这是我想进一步调查的方向,我直觉认为这是在给定条件下解决问题的唯一方法。

有一些解决方案会马上浮现在脑海中

  • 使用数据库:将要运行的作业发布到数据库表中,让辅助服务器进程运行这些作业,并将结果放入输出表中。然后,当用户回拨网页时,它可以从输出表中提取等待他们的任何结果

  • 使用JMS服务:这是一个非常轻量级的消息传递服务,可以很好地与Tomcat应用程序集成。这里的缺点是,您必须运行另一个服务器端组件,并用您的应用程序构建集成层。但这不是一个很大的缺点

  • 切换到完整的J2EE容器(Java App Server):并使用EJB单例。我必须承认,我没有在不同的服务器实例上运行单例的经验,但我相信其中一些实例可能能够处理它

  • 使用EHCache或其他一些分布式缓存:我围绕EHCache构建了一个队列包装器,使其能够像FIFO队列一样使用,并且它还具有RMI(或JMS)复制,因此多个节点将看到相同的数据

  • 使用负载平衡器:如果负载平衡器支持会话级平衡,那么对单个用户会话的所有请求都可以定向到同一节点。在我工作的大型web环境中,我们无法跨多个服务器共享用户会话状态,因此我们设置了负载平衡,以确保用户的会话始终指向同一个web服务器

  • 希望这些想法能有所帮助

    @Aspect @Component public class MyAspect {
    
    @Autowired
    private ApplicationContext ctx; 
    @Autowired
    private AsyncExecutor asyncExecutor;
    
    @Around("@annotation(executeOpi)")
    public Object around(ProceedingJoinPoint jp, ExecuteOpi executeOpi) throws Throwable {
    
        LegacyMapper newModelExecutor = ctx.getBean(executeOpi.legacyMapper());
    
                // executes a new  model and then return result in the format of old model      
                Object result = newModelExecutor.executeNewModelLogic(joinPoint.getArgs());
        // executes old model logic asynchronously 
        asyncExecutor.executeAsync(asyncExecutor.new Task(this, jp)     
    
    
        return object
    }
    
    
    public void executeOldModelLogic(ProceedingJoinPoint jp) throws Throwable{
        // long time execution here
        jp.proceed();       
     }
    }