首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
V2EX  ›  问与答

请教一个 Spring 并发请求的问题(如果有一个耗时操作,首次请求会影响其他并发请求)# Java #SpringBoot

  •  
  •   hiw2016 · 56 天前 · 775 次点击
    这是一个创建于 56 天前的主题,其中的信息可能已经有所发展或是发生改变。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.UUID;
    
    @RestController
    @RequestMapping("/")
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
        @GetMapping("/hello")
        private String sayHello() throws InterruptedException {
            String id = UUID.randomUUID().toString();
            System.out.println("进入 1: " + id + " 时间: " + System.currentTimeMillis() + " 线程 id: " + Thread.currentThread().getId());
            // 模拟耗时操作
            Thread.sleep(5000);
            System.out.println("进入 2: " + id + " 时间: " + System.currentTimeMillis() + " 线程 id: " + Thread.currentThread().getId());
            return "hello2";
        }
    }
    
    
    • 使用 ab -c 5 -n 5 http://localhost:8080/hello 模拟 5 个并发请求,输出如下:
    进入 1: 26ae2ea9-7e5c-4bd7-bc2d-8c4d0907d18a 时间: 1566302327095 线程 id: 28
    进入 2: 26ae2ea9-7e5c-4bd7-bc2d-8c4d0907d18a 时间: 1566302332099 线程 id: 28
    进入 1: 82768b68-3a05-4768-8290-8b43a176a57e 时间: 1566302332125 线程 id: 30
    进入 1: 05337b19-9a5a-41e9-8c9b-944d8ceeb06c 时间: 1566302332125 线程 id: 29
    进入 1: 3b7eea0a-4d8c-4878-9e21-741f1e6d2e76 时间: 1566302332125 线程 id: 32
    进入 1: 587305cd-89fc-441d-a575-ab436a63ad40 时间: 1566302332125 线程 id: 31
    进入 2: 3b7eea0a-4d8c-4878-9e21-741f1e6d2e76 时间: 1566302337127 线程 id: 32
    进入 2: 05337b19-9a5a-41e9-8c9b-944d8ceeb06c 时间: 1566302337127 线程 id: 29
    进入 2: 587305cd-89fc-441d-a575-ab436a63ad40 时间: 1566302337127 线程 id: 31
    进入 2: 82768b68-3a05-4768-8290-8b43a176a57e 时间: 1566302337127 线程 id: 30
    
    • 但是我的预期应该是:
    进入 1
    进入 1
    进入 1
    进入 1
    进入 1
    进入 2
    进入 2
    进入 2
    进入 2
    进入 2
    
    21 回复  |  直到 2019-08-21 18:57:03 +08:00
        1
    hiw2016   56 天前
    我应该换到 Java 节点么 🙈
        2
    hiw2016   56 天前
    @hiw2016 超时了,换不了了
        3
    HuasLeung   56 天前
    spring 中的 controller 是非线程安全的,多个线程访问会导致意料不到的结果
        4
    hiw2016   56 天前
    @HuasLeung 感谢你的回复~ 我重复尝试过多次,总是第一次的请求完整处理完,才开始处理后续请求,而后续请求是可以多线程进入方法并打印出「进入 1 」。疑问在于为什么第一次不能多线程执行方法呢?
        5
    HuasLeung   56 天前   ♥ 1
    @hiw2016
    ````
    // 模拟耗时操作
    Thread.sleep(5000);
    ````
    sleep()方法是不会释放锁的。在设置的 5 秒钟内不清楚你的请求的时间间隔是怎么样的,只要你的第 2 次请求在 5 秒后请求都能得到“总是第一次的请求完整处理完”
        6
    HuasLeung   56 天前
    @HuasLeung 打错,5 秒钟内
        7
    limuyan44   56 天前   ♥ 1
    不应该出现这种情况的,把 UUID.randomUUID()去掉试看看呢。
        8
    HuasLeung   56 天前
    ````
    public class DemoApplication {
    volatile String id = UUID.randomUUID().toString();


    }
    ````
        9
    HuasLeung   56 天前
    @HuasLeung
    ````
    public class DemoApplication {
    private volatile String id = UUID.randomUUID().toString();
    //省略
    @GetMapping("/hello")
    private String sayHello() throws InterruptedException {
    System.out.println("进入 1: " + this.id + " 时间: " + System.currentTimeMillis() + " 线程 id: " + Thread.currentThread().getId());
    // 模拟耗时操作
    Thread.sleep(5000);
    System.out.println("进入 2: " + this.id + " 时间: " + System.currentTimeMillis() + " 线程 id: " + Thread.currentThread().getId());
    return "hello2";
    }

    }
    ````
    手机写代码真是一点不方便,没编辑完就发出去了……试试看,这样写应该能解决单例模式的线程不安全问题
        10
    gIrl1990   56 天前
    ab 是啥工具
        11
    iffi   56 天前
    没明白你想问什么
        12
    Farigen   56 天前
    @gIrl1990 ab 是 Apache 的网站压力测试工具
        13
    notreami   56 天前
    问题的关键是,你以为设置 500ms 休眠就真的精准 500ms ???
        14
    notreami   56 天前   ♥ 1
    额,不对。看错了。你这是认为,为啥第一次请求结束了才有第二次请求。
        15
    anzu   56 天前
    为什么会有这种预期?如果一直每秒都有一个请求进来,那不是永远都无法进入 2 阶段了?
        16
    hiw2016   55 天前
    @HuasLeung 感谢,我换了一种测试方式,手动在五个不同的终端中 curl,输出是我的预期了。那可能是我理解错了 ab 的机制。
        17
    hiw2016   55 天前
    @limuyan44 也是一样的,但是我换了一种测试方法,手动在五个不同的终端中 curl,输出是我的预期了。可能是我理解错了 ab 的机制。
        18
    hiw2016   55 天前
    @notreami 对的,但是换成手动在五个不同的终端中 curl,输出是我的预期了。可能是我理解错了 ab 的机制。
        19
    hiw2016   55 天前
    @anzu 因为 Spring 每一个请求是一个单独的线程,所以会每个单独的处理。
        20
    notreami   55 天前   ♥ 1
    这跟 spring boot 没啥关系,ab 测试会先请求一次,并将返回值当作基准数据,比如 Document Length 参数。然后才会马力全开的测试。
        21
    hiw2016   55 天前
    @notreami 好的,谢谢,找到了一个替代品 siege
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1310 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 23ms · UTC 23:24 · PVG 07:24 · LAX 16:24 · JFK 19:24
    ♥ Do have faith in what you're doing.