Published on

ARTS 第21周

Authors
  • Algorithm: Delete Columns to Make Sorted.
  • Review: Graceful Shutdown Spring Boot Applications.
  • Tip: Web Server Graceful Shutdown.
  • Share: Spring Cloud Ribbon 负载均衡.

Algorithm

Delete Columns to Make Sorted

package org.nocoder.leetcode.solution;

/**
 * 944. Delete Columns to Make Sorted
 *
 * We are given an array A of N lowercase letter strings, all of the same length.
 *
 * Now, we may choose any set of deletion indices, and for each string, we delete all the characters in those indices.  The remaining rows of strings form columns when read north to south.
 *
 * For example, if we have an array A = ["abcdef","uvwxyz"] and deletion indices {0, 2, 3}, then the final array after deletions is ["bef","vyz"], and the remaining columns of A are ["b","v"], ["e","y"], and ["f","z"].  (Formally, the c-th column is [A[0][c], A[1][c], ..., A[A.length-1][c]].)
 *
 * Suppose we chose a set of deletion indices D such that after deletions, each remaining column in A is in non-decreasing sorted order.
 *
 * Return the minimum possible value of D.length.
 *
 *
 *
 * Example 1:
 *
 * Input: ["cba","daf","ghi"]
 * Output: 1
 * Explanation:
 * After choosing D = {1}, each column ["c","d","g"] and ["a","f","i"] are in non-decreasing sorted order.
 * If we chose D = {}, then a column ["b","a","h"] would not be in non-decreasing sorted order.
 * Example 2:
 *
 * Input: ["a","b"]
 * Output: 0
 * Explanation: D = {}
 * Example 3:
 *
 * Input: ["zyx","wvu","tsr"]
 * Output: 3
 * Explanation: D = {0, 1, 2}
 *
 *
 * Note:
 *
 * 1 <= A.length <= 100
 * 1 <= A[i].length <= 1000
 *
 * @author jason
 * @date 2018/11/24.
 */
public class DeleteColumnsToMakeSorted {

    public static int minDeletionSize(String[] A) {
        int count = 0;
        for (int i = 0; i < A[0].length(); i++) {
            for (int j = 1; j < A.length; j++) {
                if (A[j-1].charAt(i) > A[j].charAt(i)) {
                    count++;
                    break;
                }
            }
        }

        return count;
    }

    public static void main(String[] args) {
        String[] a1 = new String[]{"cba","daf","ghi"};
        System.out.println(minDeletionSize(a1));

        String[] a2 = new String[]{"a","b"};
        System.out.println(minDeletionSize(a2));

        String[] a3 = new String[]{"zyx","wvu","tsr"};
        System.out.println(minDeletionSize(a3));
    }
}

Review

Graceful Shutdown Spring Boot Applications

许多开发人员和架构师讨论设计,流量负载,框架,模式,但很少有人关心关闭阶段。 考虑一下这个场景,一个应用程序,其中有一个很长的阻塞过程,这个应用需要关闭以替换为更新的版本。如果不是杀死所有连接而是优雅等待然后在关闭之前完成,那不是很好吗?

作者在文中给出了springboot graceful shutdown的示例,但是如果我们的应用是跑在docker容器里的,还会生效吗?

我们使用 docker start container 和 docker stop container 来启动和关闭容器,docker stop 有个选项 -t ,默认会等10s,,我们应用里设置了graceful shutdown,但是响应时间超过了默认值,同样客户端会拿不到响应;但是如果把 -t 的时间设置长一点,比如60s, 当请求全部响应完成后,即使没有到 60s ,docker 容器也会 stop。所以关闭容器的时候,还是加上-t,并且时间设置长一些,保证客户端请求能够处理完成。

jason@mac  ~/local/git/arts/2018   master  docker stop --help

Usage:	docker stop [OPTIONS] CONTAINER [CONTAINER...]

Stop one or more running containers

Options:
  -t, --time int   Seconds to wait for stop before killing it (default 10)

Tip

Web Server Graceful Shutdown

当客户端请求已经到达服务端,并且服务端还没有处理完的时候,把 web server 停掉,这时候我们需要保证正在处理中的请求能够正常处理完毕并相应到客户端,同时不再接受新的请求,这种停服务的方式称为graceful shutdown

  • Tomcat
public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
    private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
    private volatile Connector connector;
    @Override
    public void customize(Connector connector) {
        this.connector = connector;
    }
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        this.connector.pause();
        Executor executor = this.connector.getProtocolHandler().getExecutor();
        if (executor instanceof ThreadPoolExecutor) {
            try {
                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                threadPoolExecutor.shutdown();
                if (!threadPoolExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
                    log.warn("Tomcat thread pool did not shut down gracefully within "
                            + "30 seconds. Proceeding with forceful shutdown");
                }
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
}
@Bean
public GracefulShutdown gracefulShutdown() {
    return new GracefulShutdown();
}
@Bean
public ConfigurableServletWebServerFactory webServerFactory(final GracefulShutdown gracefulShutdown) {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    factory.addConnectorCustomizers(gracefulShutdown);
    return factory;
}

  • Undertow
@Component
    public class GracefulShutdown implements ApplicationListener<ContextClosedEvent> {

        @Autowired
        private GracefulShutdownWrapper gracefulShutdownWrapper;

        @Autowired
        private ServletWebServerApplicationContext context;

        @Override
        public void onApplicationEvent(ContextClosedEvent contextClosedEvent){
            gracefulShutdownWrapper.getGracefulShutdownHandler().shutdown();
            try {
                UndertowServletWebServer webServer = (UndertowServletWebServer)context.getWebServer();
                Field field = webServer.getClass().getDeclaredField("undertow");
                field.setAccessible(true);
                Undertow undertow = (Undertow) field.get(webServer);
                List<Undertow.ListenerInfo> listenerInfo = undertow.getListenerInfo();
                Undertow.ListenerInfo listener = listenerInfo.get(0);
                ConnectorStatistics connectorStatistics = listener.getConnectorStatistics();
                while (connectorStatistics.getActiveConnections() > 0){}
                // 此处也可以使用 Thread.sleep(30000) 来等待响应完毕
            }catch (Exception e){
                // Application Shutdown
            }
        }
    }
@Component
public class GracefulShutdownWrapper implements HandlerWrapper{

    private GracefulShutdownHandler gracefulShutdownHandler;

    @Override
    public HttpHandler wrap(HttpHandler handler) {
        if(gracefulShutdownHandler == null) {
            this.gracefulShutdownHandler = new GracefulShutdownHandler(handler);
        }
        return gracefulShutdownHandler;
    }

    public GracefulShutdownHandler getGracefulShutdownHandler() {
        return gracefulShutdownHandler;
    }

}
@Component
@AllArgsConstructor
public class UndertowExtraConfiguration {

    private final GracefulShutdownWrapper gracefulShutdownWrapper;

    @Bean
    public UndertowServletWebServerFactory servletWebServerFactory() {
        UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
        factory.addDeploymentInfoCustomizers(deploymentInfo -> deploymentInfo.addOuterHandlerChainWrapper(gracefulShutdownWrapper));
        factory.addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.ENABLE_STATISTICS, true));
        return factory;
    }

}

Share

Spring Cloud Ribbon 负载均衡

使用 Ribbon 做负载均衡。

注册中心是 eureka,应用分为 web 和 core, web 为 consumer, core 为 service。目前有一个 web 服务, 两个 core 服务。

当core需要部署更新的时候,先停掉其中一台,更新后启动,再停另一台,更新启动。如果不做负载均衡,那么eureka50%的几率会将请求分发到以及停了服务的那个service上,这时候服务就不可用了。

这时候ribbon的负载均衡配置就可以上场了,实现重试本身,重试 next server,正在处理请求中的也可以转到另一台可用的server上。