背景

某url性能测试表明,qps单机最高只有4000多,虽然靠堆机器可以解决问题,但是显然不是什么优雅的方案。

试着把controller里的所有的逻辑都屏蔽,只是简单的返回hello world,发现并没有什么用,略略提高了一点,但还是不到5000。显然是什么地方有个坑。

问题查找过程略去不表,坑有两个:

  1. 用了log4j,1.x的版本,这货的性能。。。嗯,不说了,都是泪
  2. spring url mapping也有点坑,在开trace日志的前提下(要知道此时是log也消耗性能),大概消耗了30%的性能。

google & 代码分析

google来google去,看了看spring的源代码,4.2.x。发现这篇文章写的不错。基本说明白了问题。

spring url

盗一张图来描述一下,spring处理url的过程



解释一下什么叫 完全匹配呢?

@RequestMapping(path = "/list/cityId/123", method = RequestMethod.GET) 是完全匹配

@RequestMapping(path = "/list/cityId/{cityId}", method = RequestMethod.GET) 不是完全匹配

看出问题来了吗?

如果path不是一个完全匹配的url,那么就需要遍历所有的path,依次做正则匹配。这个。。。不慢也没有道理吧。慢也就算了,随着一个项目里的url越来越多,那么匹配速度就会越来越慢。。。。

达达的方案

服务端:

1. 在每个@RequestMapping中添加接口对应服务名的信息。

2. 实现自己定义的HandlerMethod查询逻辑,在HandlerMethod注册时记录与之对应的服务名,在查询时通过HTTP请求头中的服务名查表获得HandlerMethod。

客户端:

1. 调用服务时将服务名加入到HTTP请求头中

分析:

* 这样的查询时间复杂度是O(1)的,典型的空间换时间。理论上使用这样的查找逻辑的效率和非RESTful接口的效率是一样的。

* 由于HandlerMethod的注册是在服务启动阶段完成的,且在运行时不会发生改变,所以不用考虑注册的效率以及并发问题。

* SpringMVC提供了一系列的方法可以让我们替换它的组件,所以该方案的可行性很高。

改进

这样做的话,虽然可以解决问题,但是客户端需要配合做修改,不是一个优雅的方案。

实际上,在一个具体的项目中,一般来说path参数一般也就是两种,int和string,比如

/city/id/{id:[\d]+}

/city/shared/{sharedStr:.*}

实际上,上面的path,可以用正则表达式:

@RequestMapping(path = "/list/cityId/{cityId}", method = RequestMethod.GET)

也就是说上面的path pattern可以表示成$ /city/id/{variable} $ 的形式。

那么一个用户的url请求呢? 比如/city/id/123?那就更简单了,/\d{2, }即可。也可以表示成$ /city/id/{variable} $ 的形式。

即用户的请求这可以映射到VariableLikeUrl,通过VariableLikeUrl可以直接找到具体的处理函数。

public class OwnUrlMapping extends RequestMappingHandlerMapping {

    private static final Pattern VARIABLE_PATTERN = Pattern.compile("/\\{[^/]+:[^/]+\\}");
protected Map<String, Pair<RequestMappingInfo, HandlerMethod>> url2method; @Override
protected void handlerMethodsInitialized(Map<RequestMappingInfo, HandlerMethod> handlerMethods) {
url2method = new HashMap<>();
handlerMethods.forEach((info, method) -> { Info2key(info).forEach(s -> {
RequestMappingInfo to variable like url
s = VariableLikeUrl
if (url2method.containsKey(s)) {
throw new IllegalStateException(
"Ambiguous mapping. " + s + " --> " +
method + " alread " + url2method.get(s)); }
url2method.put(s, new Pair<>(info, method));
}
);
});
} @Override
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
request to variableLikeUrl
String url = request2case(request.getRequestURI()) + ":" + request.getMethod();
Pair<RequestMappingInfo, HandlerMethod> method = url2method.get(url);
if (method != null) {
// If find it, call it directly
handleMatch(method.getFirst(), lookupPath, request);
return method.getSecond();
} else {
// Could not find, call super matching method
LOG.warn("no matching for " + url);
return super.lookupHandlerMethod(lookupPath, request);
}
}
}

总结

好处

  1. 性能可以得到有效的提升。
  2. 客户端完全没有修改
  3. 服务器controller的代码也不需要什么修改。

问题

  1. 项目by项目,如果出现不按格式写的url,就废了。。。
  2. 实际上spring考虑的东西还是比较多的,处理url,method 貌似还有很多别的东东,上述方法。
  3. 和spring的版本绑定,反正4.x版本都适用。

基本上为项目定制了一个url mapping规则,不能替代spring原有的mapping函数,但是依然有推广意义。毕竟没有几个项目把RequstMapping的所有参数都用全了不是。

Spring urlMapping的更多相关文章

  1. spring mvc 快速入门

    ---------- 转自尚学堂 高淇 --------- Spring  MVC 背景介绍 Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块.使用 Spring 可插入的 MVC ...

  2. 【转载】Spring MVC 整合 Freemarker

    前言 1.为什么要使用Spring MVC呢? 2.为什么要使用Freemarker呢? 3.为什么不使用Struts2呢? 此示例出现的原因就是发现了struts2的性能太差,所以学习Spring ...

  3. dwr和spring的整合

    1.dwr在spring配置文件的配置: <!-- 注意这里新增加的dwr tag, 为使其生效,文件头中要声明namespace --> <dwr:configuration /& ...

  4. spring filter拦截器

    实现的功能:判断用户是否已登录,未登录用户禁止访问任何页面或action,自动跳转到登录页面.比较好的做法是不管什么人都不能直接访问jsp页面,要访问就通过action,这样就变成了一个实实在在的权限 ...

  5. spring MVC学习笔记

    为开发团队选择一款优秀的MVC框架是件难事儿,在众多可行的方案中决择需要很高的经验和水平.你的一个决定会影响团队未来的几年.要考虑方面太多: 1.简单易用,以提高开发效率.使小部分的精力在框架上,大部 ...

  6. spring+hibernate常见异常集合

    spring+hibernate出错小结: (1)java.lang.NoClassDefFoundError: org/hibernate/context/CurrentSessionContext ...

  7. 搭建spring+mybatis+struts2环境的配置文件

    1.web.xml配置 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=& ...

  8. Spring mvc源码url路由-我们到底能走多远系列(38)

    我们到底能走多远系列38 扯淡: 马航的事,挺震惊的.还是多多珍惜身边的人吧. 主题: Spring mvc 作为表现层的框架,整个流程是比较好理解的,毕竟我们做web开发的,最早也经常接触的就是一个 ...

  9. Spring使用拦截器支持国际化(转)

    Spring使用拦截器支持国际化很方便,使用时只需要两个步骤: 一.spring配置 具体配置方式如下: <!-- 资源文件绑定器,文件名称:messages.properties(没有找到时的 ...

随机推荐

  1. android 获取SD卡相关信息

    Object localOb; String str1 = null; try { localOb = new FileReader("/sys/block/mmcblk0/device/t ...

  2. 利用C语言获得网页编码

    #include <stdio.h> #include <winsock.h> #include <string.h> #pragma comment(lib, & ...

  3. golang,liteide设置 windows7(64)

    1.安转go的环境,exe安装包 2.下载liteide27.2.1 3.打开liteide开始开发,在里面添加gopath,无法读取windows里面的gopath设置,不知道什么原因,以管理员运行 ...

  4. 【转】怎么让VS2015编写的程序在XP中顺利运行

    1. 概述 默认配置下VS2015编写的应用程序只能在Win8/Win10上运行.但幸好还保留了生成XP程序的设置项.XP和Win2003的用户还是大量存在的,我们程序软件的发布不能不考虑他们.另外X ...

  5. 在Mac上使用vim的几个命令

    Mac上面编辑文件的命令总是记不住,留一个参考!!! 如果是vim,则:Esc 退出编辑模式,输入以下命令: :wq  保存后退出vi,若为 :wq! 则为强制储存后退出(常用) :w    保存但不 ...

  6. Memcached 数据缓存系统

    Memcached 数据缓存系统 常用命令及使用:http://www.cnblogs.com/wayne173/p/5652034.html Memcached是一个自由开源的,高性能,分布式内存对 ...

  7. .NET微信开发通过Access Token和OpenID获取用户信息

    本文介绍如何获得微信公众平台关注用户的基本信息,包括昵称.头像.性别.国家.省份.城市.语言. 本文的方法将囊括订阅号和服务号以及自定义菜单各种场景,无论是否有高级接口权限,都有办法来获得用户基本信息 ...

  8. Maven间接依赖冲突解决办法

    如果项目中maven依赖太多,由于还有jar之间的间接依赖,所以可能会存在依赖冲突.依赖冲突大部分都是由于版本冲突引起的,查看maven的依赖关系,可以找到引起冲突的间接依赖 如上图,通过Depend ...

  9. Linux:加载硬盘

    在腾讯云购买了一个云服务器,送了20G的系统盘,购买了一个50G的数据盘. 第一次进入系统后,通过镜像中自带的脚本,挂载数据盘. 后来重装了系统,进入系统后查看硬盘信息.

  10. AJAX怎么用POST 传参数

    //注册回调函数.注意注册回调函数是不能加括号,加了会把函数的值返回给onreadystatechange xmlhttp.onreadystatechange = callback; //设置连接信 ...