IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    优雅关闭与session draining

    hongjiang发表于 2016-10-08 16:12:22
    love 0

    最近看到nginx plus收费版里有个session draining的概念,在用nginx做反向代理,要优雅停止后端应用的话,需要先在nginx层控制请求不再发到相应的节点,但与此同时已经建立的链接则要继续保持,一直等这些链接都结束了再停止应用。

    其实在免费版的tengine就能实现这个特性,在以前的这篇nginx反向代理对后端某个节点优雅下线的问题 文章里测试过后端tomcat有一个耗时很长的请求,当tengine端健康监测已发现后端节点不可用的情况下,该请求并不会被tengine中止,而会等后端响应结束后再返回给客户端。

    优化关闭应用并不只是在负载均衡或反向代理层面就能完美解决的,后端的应用本身也要考虑很多细节。举一个例子,Actor怎么有序的关闭,可以参考之前分享的Governor模式,所有干活的Actor由Governor创建,在创建时就设定好优先级。

    在应用关闭的时候,对master发送PoisonPill,示例代码如下:

    // 在你的容器或微容器里监听到应用关闭事件时触发shutdown,最简单的情况下是在shutdownhook里
    def shutdown(): Unit = {
        gracefulStopActors
        system.terminate
        Await.result(system.whenTerminated, Duration.Inf)
        springContext.close
    }
    
    private def gracefulStopActors {
        master ! PoisonPill
        signal.acquire //wait
    }
    

    在governor角色里,当收到Terminated消息后顺序结束子actor

    ...
    case Terminated(actor) if actor == observable => {
      logger.info("===receive terminated message from: " + actor)
      // 先顺序的杀死所有托管的子actor,并将结果发给自己
      stopAll(managedActors.toList.sorted) pipeTo self
    }
    ...
    
    // 顺序的停止所有子actor
    protected def stopAll(kids: List[OrderedActorRef]): Future[Any] = {
        kids match {
          case first :: Nil =>
            logger.info("===graceful stop: " + first.actor)
            gracefulStop(first.actor, stopTimeout).flatMap { _ =>
              Future { AllDead }
          }
          case first :: rest =>
            logger.info("===graceful stop: " + first.actor)
            gracefulStop(first.actor, stopTimeout).flatMap { _ =>
            stopAll(rest)
          }
          case Nil =>
            Future { AllDead }
        }
    }
    

    再举一个例子,应用里有一个线程一直轮询从redis里获取数据,你在关闭应用的时候如何优雅终止这个线程呢?下面用一段代码简单模拟这个场景;假设这个应用没有任何容器,结束就是靠收到kill信号,那么在ShutdownHook里,要阻塞的等待(一段时间)worker线程结束后再退出才算安全

    public class Test {
      static volatile boolean running = true;
    
      public static void main(String[] args) {
      Thread worker = new Thread() {
          public void run() {
              while (running) {
                  try {
                      System.out.println("i'm running.1");
                      Thread.sleep(1000);
                      System.out.println("i'm running.2");
                      Thread.sleep(1000);
                      System.out.println("i'm running.3");
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      };
      worker.start();
      Runtime.getRuntime().addShutdownHook(new Thread() {
          public void run() {
              System.out.println("receive shutdown signal.");
              running = false;
    
              while(worker.isAlive()) {
                  System.out.println("waiting for worker thread finish.");
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                  }
              }
              System.out.println("wroker thread finished!");
          }
      });
     }
    }
    


沪ICP备19023445号-2号
友情链接