前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于Servlet3.0异步特性实现请求鉴权与转发

基于Servlet3.0异步特性实现请求鉴权与转发

作者头像
春哥大魔王
发布2018-04-17 17:44:39
1.2K0
发布2018-04-17 17:44:39
举报

项目背景

在多个内网系统之上,增加一个网关服务,统一对第三方应用进行鉴权与认证,方可对内部资源服务进行访问,网关服务主要起到鉴权认证,请求转发主要借助Servlet3.0的异步特性实现,结合springboot进行开发。

将请求异步化的好处

同步请求会将整个请求链路的发起,解析,响应在一个同步逻辑中进行。

采用异步处化可以将请求中耗时操作交给线程池做异步处理,在高并发场景下,通过调用一个非web服务线程处理耗时逻辑,提高系统并发性。

由于线程池是隔离的,可以对线程池做业务隔离分组,进行请求分级,监控等。

思路

之前有几篇文章介绍了认证和鉴权的实现思路,可参考系统鉴权流程及签名生成规则,公网API安全--OAuth认证,互联网通用架构技术----公网API安全规范。

转发的思路主要希望可以将客户端请求直接转发到业务系统,网关系统对于请求api,通过识别入参的条件进行不同业务系统的路由,请求api不做干扰直接转发。

基于SpringBoot实现

  1. 在@SrpingBootApplication之上增加@EnableAsync注解。
  2. 如果项目中有自定义Filter,需要增加asyncSupported=true,@WebFilter(asyncSupported = true)。
  3. 通过ContextListener对Context进行监听,context初始化时进行线程池创建,context销毁时进行线程池销毁。
代码语言:javascript
复制
/**
 * Description
 *
 * @author Mr. Chun.
 */@WebListenerpublic class AppContextListener implements ServletContextListener {    /**
     * 通过ContextListener进行线程池初始化
     *
     * @param servletContextEvent
     */
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(                100,                200,                50000L,
                TimeUnit.MILLISECONDS,                new ArrayBlockingQueue<Runnable>(100));

        servletContextEvent.getServletContext().setAttribute("executor", executor);
    }    /**
     * 通过ContextListener进行线程池销毁
     * @param servletContextEvent
     */
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) servletContextEvent.getServletContext().getAttribute("executor");
        executor.shutdown();
    }
}
  1. 创建自定义Servlet,增加asyncSupported=true,@WebServlet(urlPatterns = "/qbs/route", asyncSupported = true)
代码语言:javascript
复制
/**
 * Description
 * ...
 * @author Mr. Chun.
 */@WebServlet(urlPatterns = "/qbs/route", asyncSupported = true)
public class AsyncLongRunningServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private static final Logger logger = LoggerFactory.getLogger(AsyncLongRunningServlet.class);    @Autowired
    private RestTemplate restTemplate;    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        logger.info("==== 进入Servlet的时间:" + new Date() + " ====");

        long startTime = System.currentTimeMillis();
        logger.info("AsyncLongRunningServlet Start::Name=" + Thread.currentThread().getName() + "::ID=" + Thread.currentThread().getId());
        req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);        //在子线程中执行业务调用,并由其负责输出响应,主线程退出
        AsyncContext ctx = req.startAsync();
        ctx.setTimeout(9000);

        ThreadPoolExecutor executor = (ThreadPoolExecutor) req.getServletContext().getAttribute("executor");
        executor.execute(new AsyncRequestProcessor(restTemplate, ctx, req.getMethod(), req.getParameter("api"))); // 任务提交线程池

        long endTime = System.currentTimeMillis();
        logger.info("AsyncLongRunningServlet End::Name=" + Thread.currentThread().getName() + "::ID=" + Thread.currentThread().getId() + "::Time Taken=" + (endTime - startTime) + " ms.");

        logger.info("==== 结束Servlet的时间:" + new Date() + " ====");
    }
}
  1. 将耗时任务交由独立线程进行处理,通过实现Runable的run()方法实现。
代码语言:javascript
复制
/**
 * Description
 * ...
 *
 * @author Mr. Chun.
 */public class AsyncRequestProcessor implements Runnable {    private static final Logger logger = LoggerFactory.getLogger(AsyncRequestProcessor.class);    private String url = "http://localhost:8080/";    private RestTemplate restTemplate;    private AsyncContext ctx = null;    private String requestMethod = "";    public AsyncRequestProcessor(RestTemplate restTemplate, AsyncContext ctx, String requestMethod, String api) {        this.restTemplate = restTemplate;        this.ctx = ctx;        this.requestMethod = requestMethod;
        url = url + api;
    }    public void run() {        try {            // 业务请求转发在这里处理
            // 请求入参
            Map map = ctx.getRequest().getParameterMap();            // 最终请求参数
            MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
            Iterator iterator = map.keySet().iterator();            while (iterator.hasNext()) {                String key = (String) iterator.next();                if (!ResponseBuilder.isSysParam(key)) {
                    paramMap.add(key, map.get(key).toString());
                }
            }            String json = "";            if ("GET".equals(requestMethod)) {
                json = restTemplate.getForObject(url, String.class, paramMap);
            } else if ("POST".equals(requestMethod)) {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(paramMap, headers);
                ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
                json = response.getBody();
            }
            ResponseBuilder.responseWrite((HttpServletResponse) ctx.getResponse(), json);
            logger.info("==== 业务处理完毕的时间:" + new Date() + " ====");
            ctx.complete(); // 通知容器,异步处理完成
        } catch (Exception e) {
            logger.error("AsyncExecutor e: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

© 著作权归作者所有

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-12-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 春哥talk 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 项目背景
  • 将请求异步化的好处
  • 思路
  • 基于SpringBoot实现
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档