目录

  • 异步执行
  • 增加内嵌 Tomcat 的最大连接数
  • 使用 @ComponentScan()
  • 默认 Tomcat 容器改为 Undertow
  • 使用 BufferedWriter 进行缓冲
  • Deferred 方式实现异步调用
  • 异步调用可以使用 AsyncHandlerInterceptor 进行拦截
异步执行
实现方式二种:
  • 使用异步注解 @aysnc、启动类:添加 @EnableAsync 注解
  • JDK 8 本身有一个非常好用的 Future 类——CompletableFuture
@AllArgsConstructor
publicclassAskThreadimplementsRunnable
{

private
 CompletableFuture<Integer> re = 
null
;


publicvoidrun()
{

int
 myRe = 
0
;

try
 {

            myRe = re.get() * re.get();

        } 
catch
 (Exception e) {

            e.printStackTrace();

        }

        System.out.println(myRe);

    }


publicstaticvoidmain(String[] args)throws InterruptedException 
{

final
 CompletableFuture<Integer> future = 
new
 CompletableFuture<>();

new
 Thread(
new
 AskThread(future)).start();

//模拟长时间的计算过程
        Thread.sleep(
1000
);

//告知完成结果
        future.complete(
60
);

    }

}

在该示例中,启动一个线程,此时 AskThread 对象还没有拿到它需要的数据,执行到  myRe = re.get() * re.get() 会阻塞。
我们用休眠 1 秒来模拟一个长时间的计算过程,并将计算结果告诉 future 执行结果,AskThread 线程将会继续执行。
publicclassCalc
{

publicstatic Integer calc(Integer para)
{

try
 {

//模拟一个长时间的执行
            Thread.sleep(
1000
);

        } 
catch
 (InterruptedException e) {

            e.printStackTrace();

        }

return
 para * para;

    }


publicstaticvoidmain(String[] args)throws ExecutionException, InterruptedException 
{

final
 CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> calc(
50
))

                .thenApply((i) -> Integer.toString(i))

                .thenApply((str) -> 
"\""
 + str + 
"\""
)

                .thenAccept(System.out::println);

        future.get();

    }

}

CompletableFuture.supplyAsync 方法构造一个 CompletableFuture 实例,在 supplyAsync() 方法中,它会在一个新线程中,执行传入的参数。
在这里它会执行 calc() 方法,这个方法可能是比较慢的,但这并不影响 CompletableFuture 实例的构造速度,supplyAsync() 会立即返回。
而返回的 CompletableFuture 实例就可以作为这次调用的契约,在将来任何场合,用于获得最终的计算结果。
supplyAsync 用于提供返回值的情况,CompletableFuture 还有一个不需要返回值的异步调用方法 runAsync(Runnable runnable),一般我们在优化 Controller 时,使用这个方法比较多。
这两个方法如果在不指定线程池的情况下,都是在 ForkJoinPool.common 线程池中执行,而这个线程池中的所有线程都是 Daemon(守护)线程,所以,当主线程结束时,这些线程无论执行完毕都会退出系统。
核心代码:
CompletableFuture.runAsync(
() ->
this
.afterBetProcessor(betRequest,betDetailResult,appUser,id)

);

异步调用使用 Callable 来实现:
@RestController
publicclassHelloController
{


privatestaticfinal
 Logger logger = LoggerFactory.getLogger(HelloController.class);


@Autowired
private
 HelloService hello;


@GetMapping
(
"/helloworld"
)

public String helloWorldController()
{

return
 hello.sayHello();

    }


/**

     * 异步调用restful

     * 当controller返回值是Callable的时候,springmvc就会启动一个线程将Callable交给TaskExecutor去处理

     * 然后DispatcherServlet还有所有的spring拦截器都退出主线程,然后把response保持打开的状态

     * 当Callable执行结束之后,springmvc就会重新启动分配一个request请求,然后DispatcherServlet就重新

     * 调用和处理Callable异步执行的返回结果, 然后返回视图

     *

     * 
@return
     */

@GetMapping
(
"/hello"
)

public Callable<String> helloController()
{

        logger.info(Thread.currentThread().getName() + 
" 进入helloController方法"
);

        Callable<String> callable = 
new
 Callable<String>() {


@Override
public String call()throws Exception 
{

                logger.info(Thread.currentThread().getName() + 
" 进入call方法"
);

                String say = hello.sayHello();

                logger.info(Thread.currentThread().getName() + 
" 从helloService方法返回"
);

return
 say;

            }

        };

        logger.info(Thread.currentThread().getName() + 
" 从helloController方法返回"
);

return
 callable;

    }

}

异步调用的方式 WebAsyncTask:
@RestController
publicclassHelloController
{


privatestaticfinal
 Logger logger = LoggerFactory.getLogger(HelloController.class);


@Autowired
private
 HelloService hello;


/**

     * 带超时时间的异步请求 通过WebAsyncTask自定义客户端超时间

     *

     * 
@return
     */

@GetMapping
(
"/world"
)

public WebAsyncTask<String> worldController()
{

        logger.info(Thread.currentThread().getName() + 
" 进入helloController方法"
);


// 3s钟没返回,则认为超时
        WebAsyncTask<String> webAsyncTask = 
new
 WebAsyncTask<>(
3000
new
 Callable<String>() {


@Override
public String call()throws Exception 
{

                logger.info(Thread.currentThread().getName() + 
" 进入call方法"
);

                String say = hello.sayHello();

                logger.info(Thread.currentThread().getName() + 
" 从helloService方法返回"
);

return
 say;

            }

        });

        logger.info(Thread.currentThread().getName() + 
" 从helloController方法返回"
);


        webAsyncTask.onCompletion(
new
 Runnable() {


@Override
publicvoidrun()
{

                logger.info(Thread.currentThread().getName() + 
" 执行完毕"
);

            }

        });


        webAsyncTask.onTimeout(
new
 Callable<String>() {


@Override
public String call()throws Exception 
{

                logger.info(Thread.currentThread().getName() + 
" onTimeout"
);

// 超时的时候,直接抛异常,让外层统一处理超时异常
thrownew
 TimeoutException(
"调用超时"
);

            }

        });

return
 webAsyncTask;

    }


/**

     * 异步调用,异常处理,详细的处理流程见MyExceptionHandler类

     *

     * 
@return
     */

@GetMapping
(
"/exception"
)

public WebAsyncTask<String> exceptionController()
{

        logger.info(Thread.currentThread().getName() + 
" 进入helloController方法"
);

        Callable<String> callable = 
new
 Callable<String>() {


@Override
public String call()throws Exception 
{

                logger.info(Thread.currentThread().getName() + 
" 进入call方法"
);

thrownew
 TimeoutException(
"调用超时!"
);

            }

        };

        logger.info(Thread.currentThread().getName() + 
" 从helloController方法返回"
);

returnnew
 WebAsyncTask<>(
20000
, callable);

    }


}

增加内嵌 Tomcat 的最大连接数
代码如下:
@Configuration
publicclassTomcatConfig
{

@Bean
public ConfigurableServletWebServerFactory webServerFactory()
{

        TomcatServletWebServerFactory tomcatFactory = 
new
 TomcatServletWebServerFactory();

        tomcatFactory.addConnectorCustomizers(
new
 MyTomcatConnectorCustomizer());

        tomcatFactory.setPort(
8005
);

        tomcatFactory.setContextPath(
"/api-g"
);

return
 tomcatFactory;

    }

classMyTomcatConnectorCustomizerimplementsTomcatConnectorCustomizer
{

publicvoidcustomize(Connector connector)
{

            Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();

//设置最大连接数
            protocol.setMaxConnections(
20000
);

//设置最大线程数
            protocol.setMaxThreads(
2000
);

            protocol.setConnectionTimeout(
30000
);

        }

    }


}

使用 @ComponentScan()
使用 @ComponentScan() 定位扫包比 @SpringBootApplication 扫包更快。
默认 Tomcat 容器改为 Undertow
默认 Tomcat 容器改为 Undertow(Jboss 下的服务器,Tomcat 吞吐量 5000,Undertow 吞吐量 8000)
<exclusions>
<exclusion>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-tomcat
</artifactId>
</exclusion>
</exclusions>
改为:
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-undertow
</artifactId>
</dependency>
使用 BufferedWriter 进行缓冲
这里不给大家举例,可自行尝试。
Deferred 方式实现异步调用
代码如下:
@RestController
publicclassAsyncDeferredController
{

privatefinal
 Logger logger = LoggerFactory.getLogger(
this
.getClass());

privatefinal
 LongTimeTask taskService;


@Autowired
publicAsyncDeferredController(LongTimeTask taskService)
{

this
.taskService = taskService;

    }


@GetMapping
(
"/deferred"
)

public DeferredResult<String> executeSlowTask()
{

        logger.info(Thread.currentThread().getName() + 
"进入executeSlowTask方法"
);

        DeferredResult<String> deferredResult = 
new
 DeferredResult<>();

// 调用长时间执行任务
        taskService.execute(deferredResult);

// 当长时间任务中使用deferred.setResult("world");这个方法时,会从长时间任务中返回,继续controller里面的流程
        logger.info(Thread.currentThread().getName() + 
"从executeSlowTask方法返回"
);

// 超时的回调方法
        deferredResult.onTimeout(
new
 Runnable(){


@Override
publicvoidrun()
{

    logger.info(Thread.currentThread().getName() + 
" onTimeout"
);

// 返回超时信息
    deferredResult.setErrorResult(
"time out!"
);

   }

  });


// 处理完成的回调方法,无论是超时还是处理成功,都会进入这个回调方法
        deferredResult.onCompletion(
new
 Runnable(){


@Override
publicvoidrun()
{

    logger.info(Thread.currentThread().getName() + 
" onCompletion"
);

   }

  });


return
 deferredResult;

    }

}

异步调用可以使用 AsyncHandlerInterceptor 进行拦截
代码如下:
@Component
publicclassMyAsyncHandlerInterceptorimplementsAsyncHandlerInterceptor
{


privatestaticfinal
 Logger logger = LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);


@Override
publicbooleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws
 Exception 
{

returntrue
;

 }


@Override
publicvoidpostHandle
(HttpServletRequest request, HttpServletResponse response, Object handler,

   ModelAndView modelAndView)
throws Exception 
{

// HandlerMethod handlerMethod = (HandlerMethod) handler;
  logger.info(Thread.currentThread().getName()+ 
"服务调用完成,返回结果给客户端"
);

 }


@Override
publicvoidafterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws
 Exception 
{

if
(
null
 != ex){

   System.out.println(
"发生异常:"
+ex.getMessage());

  }

 }


@Override
publicvoidafterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
throws
 Exception 
{


// 拦截之后,重新写回数据,将原来的hello world换成如下字符串
  String resp = 
"my name is chhliu!"
;

  response.setContentLength(resp.length());

  response.getOutputStream().write(resp.getBytes());


  logger.info(Thread.currentThread().getName() + 
" 进入afterConcurrentHandlingStarted方法"
);

 }


}

  • —————END—————
PS:如果觉得我的分享不错,欢迎大家随手点赞、在看。
 关注公众号:Java后端编程,回复下面关键字 
要Java学习完整路线,回复  路线 
缺Java入门视频,回复 视频 
要Java面试经验,回复  面试 
缺Java项目,回复: 项目 
进Java粉丝群: 加群 
PS:如果觉得我的分享不错,欢迎大家随手点赞、在看。
(完)
加我"微信获取一份 最新Java面试题资料
请备注:666不然不通过~
最近好文
最近面试BAT,整理一份面试资料Java面试BAT通关手册,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:关注公众号并回复 java 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡
继续阅读
阅读原文