背景:
公司基础设施部门推出了自己的微服务框架(以下简称 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(已排除掉公司相关信息)
https://github.com/hiccup234/web-advance/blob/master/bridge.rar
问题与验证:
1、因为初始化Stripes需要 ServletContext,但是应用启动时却没办法获取到 ServletContext 对象,所以在 StripesContextHolder 里做了懒加载,只有当第一个请求进来后才初始化Stripes。
2、Stripes 是支持 queryString 为空的查询的,类似:http://127.0.0.1/server/test?list 这种,而 M 会在解析请求参数的时候对这种情况直接抛异常,经过与基础设施同事协商,他们做出让步,对这种请求的情况直接放行给应用,从而避免了前端和第三方做改造。
今晚上线成功,目前跟 Tomcat 并行发布,5% 的流量灰度,观察日志一切正常。前期的技术验证加上近一月的开发测试到今天的上线,算是向着架构的方向迈出了小小一步。
未完待续...
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于