关于并发请求后台的研究

下面以简要的代码说明:

创建用于测试的对象

包括:Controller,Service,Repo,Entity。

Entity: User

User里有大量随机字段(超过50个)

@Entity
public class User implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    private String sasdfex;

    private String Seasddfgfx;

    private String Se23asfx;

//省略的还有许多随机字段,目的让表结构变大。
//省略getter和setter

Repo: UserRepo

Repo故意用慢速的方法去查询,延长时间。

public interface UserRepo extends JpaRepository<User, Long> {

    List<User> findBySeasddfgfxLikeAndSe23asfxLike(String Seasddfgfx, String se23asfx);//重点:用like加上多个"%"号,提高搜索难度。

}

Service: UserService

public class UserService {

    @Autowired
    private UserRepo userRepo;

    public void concurrentVoid() {
        this.doConcurrent();
    }

    private void doConcurrent() {
        User user = null;
        List<User> users = userRepo.findBySeasddfgfxLikeAndSe23asfxLike("%3df%gdsfg%", "%2%asfdasf%3a%");
        if (users.isEmpty()) {
            user = new User();
            user.setName("zz");
            user.setSeasddfgfx("3df%gdsfg");
            user.setSe23asfx("%2%asfdasf%3a%");
        }
        userRepo.save(user);
    }
}

Serivce做的事情,我们可以看到。一个public,一个private。

doConcurrent:先like查出所有满足条件的值。这个list没什么用,只是需要这个去延长方法执行时间。

如果查到的结果为empty,那么插入一条数据。数据的要求是:可以被Like搜索到。

Controller: UserController

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    UserService userService;

    @RequestMapping("/concurrentReq")
    public String concurrentReq() {
        userService.concurrentVoid();
        return "success";
    }
}

到这里骨架已经完成。

想办法,对user的数据表插入大量数据,这部分我就不详述了。

我采用的是mysql,数据量是5927009(接近600万)条。点用的空间大概是2G多。

select count(*) from user;
count(*)
5927009

这时候,就可以调用后端接口了:

server-port:8080

http://localhost:8080/user/concurrentReq

调用方法:

  1. 在浏览器里输入网址或者相关工具(PostMan?SoapUI?),手工点击。想要并发,你的手速可以吗?^-^

  2. 采用相关工具PostMan或者SoapUI,和浏览器类似。达不到我们想要的效果。

  3. 写java代码,模拟http请求。想要模拟并发,我们需要使用多线程。这里我采用线程池加CountDownLatcher。

编写测试用例

Test: UserTests


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleWebUiApplication.class)
@WebAppConfiguration
@IntegrationTest("server.port:8080")
@DirtiesContext
public class UserTests {

    @Test
    public void testUserConcurrentReq() throws Exception {
        ExecutorService service = Executors.newCachedThreadPool(); // 创建一个线程池
        final CountDownLatch cdlForNotification = new CountDownLatch(1);// 当cdlForNotification变为0,则唤醒所有等待唤醒的并发线程。
        final CountDownLatch cdlForConThread = new CountDownLatch(10);// 准备用10个并发线程,所以设置为10。每个线程运行完成则countDown一次,当10个都运行完成,变为0。则cdlForNotification停止等待。
        for (int i = 0; i < 10; i++) {
            Runnable runnable = new Runnable() {

                public void run() {
                    try {
                        System.out.println("线程" + Thread.currentThread().getName() + "准备完成");
                        cdlForNotification.await(); // 各线程都处于等待状态
                        System.out.println("线程" + Thread.currentThread().getName() + "开始运行");
                        new TestRestTemplate().getForEntity("http://localhost:" + 8080 + "/user/concurrentReq", String.class);
                        System.out.println("线程" + Thread.currentThread().getName() + "运行完成");
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        cdlForConThread.countDown(); // 任务运行完成,cdlForConThread减1。
                    }
                }
            };
            service.execute(runnable);// 为线程池加入任务
        }
        try {
            Thread.sleep((long) (Math.random() * 10000));

            System.out.println("线程" + Thread.currentThread().getName() + "即将唤醒所有并发线程");
            cdlForNotification.countDown(); // 发送命令,cdlForNotification减1,处于等待的线程停止等待转去运行任务。
            System.out.println("线程" + Thread.currentThread().getName() + "唤醒所有并发线程,正在等待结果");
            cdlForConThread.await(); // 唤醒并发线程后处于等待状态。一旦cdlForConThread为0时停止等待,继续往下运行
            System.out.println("线程" + Thread.currentThread().getName() + "已收到全部响应结果");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
        service.shutdown(); // 任务结束。停止线程池的全部线程
    }

}

这是一个并发请求的测试用例。还是要注意这里的用法。

其用意是,创建10个线程,用cdlForConThread(CountDownLatch)来控制线程的启动,阻塞,继续执行。

主线程用cdlForNotification控制他们的继续执行。

启动测试用例

步骤:

  1. 直接运行
  2. public 加上synchronized
  3. private 加上synchronized
  4. public和private 同时加上 synchronized
  5. public 加上 @Transactional
  6. private 加上 @Transactional
  7. public和private 同时加上 @Transactional

问题:
1. 只用synchronized行不行?
2. 为什么@Transactional在私有方法上不起作用?

如果有人看到这里,可以讨论下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值