欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识
原文出处: 编程迷思

概述

在利用Spring MVC开拓Web系统时,常常需要在处理惩罚请求时利用request工具,好比获取客户端ip地点、请求的url、header中的属性(如cookie、授权信息)、body中的数据等。由于在Spring MVC中,处理惩罚请求的Controller、Service等工具都是单例的,因此获取request工具时最需要留意的问题,即是request工具是否是线程安详的:当有大量并发请求时,可否担保差异请求/线程中利用差异的request工具。

这里尚有一个问题需要留意:前面所说的“在处理惩罚请求时”利用request工具,毕竟是在那边利用呢?思量到获取request工具的要领有微小的差异,概略可以分为两类:

  1.  在Spring的Bean中利用request工具:既包罗Controller、Service、Repository等MVC的Bean,也包罗了Component等普通的Spring Bean。为了利便说明,后文中Spring中的Bean一律简称为Bean。
  2. 在非Bean中利用request工具:如普通的Java工具的要领中利用,或在类的静态要领中利用。

另外,本文接头是环绕代表请求的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。