概述
在利用Spring MVC开拓Web系统时,常常需要在处理惩罚请求时利用request工具,好比获取客户端ip地点、请求的url、header中的属性(如cookie、授权信息)、body中的数据等。由于在Spring MVC中,处理惩罚请求的Controller、Service等工具都是单例的,因此获取request工具时最需要留意的问题,即是request工具是否是线程安详的:当有大量并发请求时,可否担保差异请求/线程中利用差异的request工具。
这里尚有一个问题需要留意:前面所说的“在处理惩罚请求时”利用request工具,毕竟是在那边利用呢?思量到获取request工具的要领有微小的差异,概略可以分为两类:
另外,本文接头是环绕代表请求的request工具展开的,但所用要领同样合用于response工具、InputStream/Reader、OutputStream/ Writer等;个中InputStream/Reader可以读取请求中的数据,OutputStream/ Writer可以向响应写入数据。
最后,获取request工具的要领与Spring及MVC的版本也有干系;本文基于Spring4举办接头,且所做的尝试都是利用4.1.1版本。
如何测试线程安详性
既然request工具的线程安详问题需要出格存眷,为了便于后头的接头,下面先说明如何测试request工具是否是线程安详的。
测试的根基思路,是模仿客户端大量并发请求,然后在处事器判定这些请求是否利用了沟通的request工具。
判定request工具是否沟通,最直观的方法是打印出request工具的地点,假如沟通则说明利用了沟通的工具。然而,在险些所有web处事器的实现中,都利用了线程池,这样就导致先后达到的两个请求,大概由同一个线程处理惩罚:在前一个请求处理惩罚完成后,昆山软件开发,线程池收回该线程,并将该线程从头分派给了后头的请求。而在同一线程中,利用的request工具很大概是同一个(地点沟通,属性差异)。因此即即是对付线程安详的要领,差异的请求利用的request工具地点也大概沟通。
为了制止这个问题,一种要领是在请求处理惩罚进程中使线程休眠几秒,这样可以让每个线程事情的时间足够长,从而制止同一个线程分派给差异的请求;另一种要领,是利用request的其他属性(如参数、header、body等)作为request是否线程安详的依据,因为即便差异的请求先后利用了同一个线程(request工具地点也沟通),只要利用差异的属性别离结构了两次request工具,那么request工具的利用就是线程安详的。本文利用第二种要领举办测试。
客户端测试代码如下(建设1000个线程别离发送请求):
public class Test { public static void main(String[] args) throws Exception { String prefix = UUID.randomUUID().toString().replaceAll("-", "") + "::"; for (int i = 0; i < 1000; i++) { final String value = prefix + i; new Thread() { @Override public void run() { try { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet httpGet = new HttpGet("http://localhost:8080/test?key=" + value); httpClient.execute(httpGet); httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } }.start(); } } }
处事器中Controller代码如下(临时省略了获取request工具的代码):
@Controller public class TestController { // 存储已有参数,用于判定参数是否反复,从而判定线程是否安详 public static Set<String> set = new ConcurrentSkipListSet<>(); @RequestMapping("/test") public void test() throws InterruptedException { // …………………………通过某种方法得到了request工具……………………………… // 判定线程安详 String value = request.getParameter("key"); if (set.contains(value)) { System.out.println(value + "\t反复呈现,request并发不安详!"); } else { System.out.println(value); set.add(value); } // 模仿措施执行了一段时间 Thread.sleep(1000); } }
增补:上述代码原利用HashSet来判定value是否反复,经网友品评指正,利用线程不安详的荟萃类验证线程安详性是欠妥的,现已改为ConcurrentSkipListSet。