背景:
公司基础设施部门推出了自己的微服务框架 (以下简称 M), 要求所有业务应用都要接入进去, 但坑爹的是 M 只提供了 SpringMVC 工程的 support, 对于采用 Stripes 作为 MVC 框架的应用并不支持, 所以就必须重构这个 Stripes 应用.
M 虽然代替 Tomcat 自己实现了 IO, 工作线程池, 服务注册发现等等, 但还是提供了对 Servlet 规范的简单支持 (移除了 web.xml,filter,listener, 打包方式也改成了 tar.gz, 保留了 ServletContext,request,response 等等).
思路与分析:
由于现有应用的业务逻辑还是比较复杂 (沉淀了 3,4 年), 且涉及到多个 App 端和其他第三方的交互, 所以如果想对应用做完全的重构是一件非常头痛的事情, 不仅要投入大量的后端, 测试等人力成本, 而且整个重构周期也会比较长, 影响到对其他需求的响应.
于是在想: 既然 SpringMVC 和 Stripes 都遵循 Servlet 规范, 而 M 又能支持 SpringMVC, 是否可以通过 SpringMVC 拦截到所有请求后, 把之前的老接口的请求转发给 Stripes 处理, 以后新开发的接口就直接走 SpringMVC, 这样既兼容了公司的 M 框架也对原有应用的改动较小, 开发周期短, 测试人员只需要验证一些系统边界和主要流程, 不用再挨个挨个验证已有的业务逻辑, 这样可以节省大量人力成本, 缩短开发周期.
SpringMVC 是目前最流行的 MVC 框架, 是 Spring 庞大家族的一员, 而 Stripes 也是一个非常轻量的 MVC 框架, 但是 SpringMVC 的 Controller 跟 Stripes 的 ActionBean 有个很明显的区别就是前者是单例的, 跟状态无关, 所有的请求参数都是封装在 ServletRequest 中的, 而后者是 prototype, 每次请求都会重新生成一个 ActionBean 对象.
动手实现原型:
新建桥接工程 XXX-bridge, 所有新增代码均放在新工程, 对老工程零侵入, 仅新增了 pom 文件里的两个 plugin, 可以实现 Stripes 和 SpringMVC 两种框架模式下的并行开发,(原来的老工程持续有需求进来).
- /**
- * 拦截所有请求, 如果能映射到 Stripes 则直接派发给 Stripes 并 return false;
- * 如果不能映射则回退给 SpringMVC
- */
- @Override
- public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
- if (handlerMapping.isEmpty()) {
- return true;
- }
- String url = httpServletRequest.getRequestURI();
- String contextPath = httpServletRequest.getContextPath();
- url = url.replace(contextPath, "").replaceAll("/+","/");
- LOGGER.info("当前请求路径: {}", url);
- if (!StripesContextHandlerInterceptor.handlerMapping.containsKey(url)) {
- LOGGER.info("请求由 SpringMVC 处理: {}", url);
- return true;
- }
- try {
- LOGGER.info("请求由 Stripes 处理: {}", url);
- StripesContextHolder.doService(httpServletRequest.getServletContext(), getApplicationContext(),
- httpServletRequest, httpServletResponse);
- return false;
- } catch (Exception e) {
- LOGGER.error("处理请求异常:", e);
- return false;
- }
- }
关键点在于要解析到所有的老接口地址, 然后通过拦截器来判断当前请求是否是老接口, 是的话就转发给 Stripes 来处理. 其他详细的代码托管在 GitHub(已排除掉公司相关信息)
问题与验证:
1, 因为初始化 Stripes 需要 ServletContext, 但是应用启动时却没办法获取到 ServletContext 对象, 所以在 StripesContextHolder 里做了懒加载, 只有当第一个请求进来后才初始化 Stripes.
2,Stripes 是支持 queryString 为空的查询的, 类似: http://127.0.0.1/server/test?list 这种, 而 M 会在解析请求参数的时候对这种情况直接抛异常, 经过与基础设施同事协商, 他们做出让步, 对这种请求的情况直接放行给应用, 从而避免了前端和第三方做改造.
今晚上线成功, 目前跟 Tomcat 并行发布, 5% 的流量灰度, 观察日志一切正常. 前期的技术验证加上近一月的开发测试到今天的上线, 算是向着架构的方向迈出了小小一步.
未完待续...
来源: https://www.cnblogs.com/ocean234/p/9663255.html