资讯专栏INFORMATION COLUMN

Java高并发及测试代码

waruqi / 2990人阅读

摘要:公司的妹子不会做并发测试。并发测试是多个人同时访问一个服务,这不就是多线程吗于是灵光一现使用多线程来写并发测试代码。测试得出如下代码的执行效率最高,个并发耗时毫秒

公司的妹子不会做并发测试。做完一名程序猿看着有点干捉急。并发测试是多个人同时访问一个服务,这不就是多线程吗!于是灵光一现使用多线程来写并发测试代码。想想心理都有点小激动咧。效果比工具还好,废话不多说贴代码

添加Maven依赖

</>复制代码

  1. com.squareup.okhttp3
  2. okhttp
  3. 3.8.1


</>复制代码

  1. com.squareup.okio
  2. okio
  3. 1.11.0

</>复制代码

  1. com.google.code.gson
  2. gson
  3. 2.8.0

先封装OKHTTP(使用CallBack思想做的封装),这个很早之前就封装了,公司移动端也是使用OKHTTP做的服务请求调用。经常遇到图片上传不了的问题,报的错是Socket连接超时的问题。解决这个问题so easy,把连接时间(KEEP_ALIVE)时间设置长一点就行了嘛!
OkHttp底层是用socket做的通信,现在很多应该的底层通信都用的Socket,例子不多说,全靠经验。

public abstract class HttpCommon {

</>复制代码

  1. /**
  2. * 设置连接超时时间为30000秒
  3. */
  4. private final static int CONNECT_TIMT_OUT = 30000;
  5. /**
  6. * 设置写超时时间为30000秒
  7. */
  8. private final static int WRITE_TIME_OUT = 30000;
  9. static {
  10. final OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
  11. okHttpClient = httpBuilder.connectTimeout(CONNECT_TIMT_OUT, TimeUnit.SECONDS)
  12. .writeTimeout(WRITE_TIME_OUT, TimeUnit.SECONDS).build();
  13. }
  14. public abstract void callBack(String responseString);
  15. /**
  16. * get请求
  17. *
  18. * @param url url地址
  19. * @param map 请求参数
  20. * @return 返回结果。如果为“”表示失败
  21. */
  22. public void get(String url, Map map) {
  23. url = wrapUrl(url, map);
  24. // 创建请求参数
  25. Request request = new Request.Builder().url(url).build();
  26. //创建请求对象
  27. Call call = okHttpClient.newCall(request);
  28. try {
  29. Response response = call.execute();
  30. if (response.isSuccessful()) {
  31. callBack(response.body().string());
  32. }
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. /**
  38. * post请求
  39. *
  40. * @param url post请求的url
  41. * @param t post请求的表单实体
  42. * @return 返回结果。如果为“”表示失败
  43. */
  44. public void post(String url, Map map, T t) {
  45. url = wrapUrl(url, map);
  46. String json = new Gson().toJson(t);
  47. RequestBody body = RequestBody.create(JSON, json);
  48. Request request = new Request.Builder().url(url).post(body).build();
  49. Response response = null;
  50. try {
  51. response = okHttpClient.newCall(request).execute();
  52. if (response.isSuccessful()) {
  53. callBack(response.body().string());
  54. }
  55. } catch (IOException e) {
  56. e.printStackTrace();
  57. }
  58. }
  59. /**
  60. * post请求
  61. *
  62. * @param url post请求的url
  63. * @param t post请求的表单实体
  64. * @return 返回结果。如果为“”表示失败
  65. */
  66. public void post(String url, T t) {
  67. String json = new Gson().toJson(t);
  68. RequestBody body = RequestBody.create(JSON, json);
  69. Request request = new Request.Builder().url(url).post(body).build();
  70. Response response = null;
  71. try {
  72. response = okHttpClient.newCall(request).execute();
  73. if (response.isSuccessful()) {
  74. callBack(response.body().string());
  75. }
  76. } catch (IOException e) {
  77. e.printStackTrace();
  78. }
  79. }
  80. /**
  81. * 上传文件请求
  82. *
  83. * @param url 请求url
  84. * @param map 请求参数
  85. * @param filePath 文件路径
  86. * @return 返回结果。结果为""表示失败
  87. */
  88. private void uploadFile(String url, Map map, String filePath) {
  89. url = wrapUrl(url, map);
  90. File file = new File(filePath);
  91. RequestBody fileBody = RequestBody.create(OCTET, file);
  92. RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
  93. .addFormDataPart("image", file.getName(), fileBody).build();
  94. Request request = new Request.Builder().url(url).post(requestBody).build();
  95. execute(request);
  96. }
  97. /**
  98. * 上传多个文件请求
  99. *
  100. * @param url 请求url
  101. * @param map 请求参数
  102. * @param filePaths 文件路径
  103. * @return 返回结果。结果为""表示失败
  104. */
  105. private void uploadFiles(String url, Map map, List filePaths) {
  106. url = wrapUrl(url, map);
  107. MultipartBody.Builder builder = new MultipartBody.Builder();
  108. builder.setType(MultipartBody.FORM);
  109. for (String str : filePaths) {
  110. File file = new File(str);
  111. RequestBody fileBody = RequestBody.create(OCTET, file);
  112. builder.addFormDataPart("image", file.getName(), fileBody);
  113. }
  114. RequestBody requestBody = builder.build();
  115. Request request = new Request.Builder().url(url).post(requestBody).build();
  116. execute(request);
  117. }
  118. /**
  119. * 执行文件上传操作
  120. *
  121. * @param request
  122. */
  123. private void execute(Request request) {
  124. try {
  125. Response response = okHttpClient.newCall(request).execute();
  126. if (response.isSuccessful()) {
  127. callBack(response.body().string());
  128. }
  129. } catch (IOException e) {
  130. e.printStackTrace();
  131. }
  132. }
  133. /**
  134. * 拼接get请求url
  135. *
  136. * @param url 请求url
  137. * @param map 参数
  138. * @return 返回拼接完的url地址
  139. */
  140. private String wrapUrl(String url, Map map) {
  141. if (null == map) {
  142. return url;
  143. }
  144. url += "?";
  145. for (Map.Entry entry : map.entrySet()) {
  146. url += entry.getKey() + "=" + entry.getValue() + "&";
  147. }
  148. if (url.endsWith("&")) {
  149. url = url.substring(0, url.length() - 1);
  150. }
  151. return url;
  152. }
  153. /**
  154. * 请求客户端
  155. */
  156. private static OkHttpClient okHttpClient;
  157. /**
  158. * Json媒体类型
  159. */
  160. private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
  161. /**
  162. * 二进制流的媒体类型
  163. */
  164. private static final MediaType OCTET = MediaType.parse("application/octet-stream");

}

public class RunThread {

</>复制代码

  1. private final String URL;
  2. private HttpCommon httpCommon;
  3. private int num;
  4. private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 100, 1000000L, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
  5. private CountDownLatch countDownLatch;
  6. /**
  7. * @param url 服务URL地址,
  8. * @param num 并发访问次数,一般配置50+
  9. */
  10. public RunThread(String url, int num) {
  11. this.URL = url;
  12. this.num = num;
  13. this.countDownLatch = new CountDownLatch(num);
  14. httpCommon = new HttpCommon() {
  15. @Override
  16. public void callBack(String responseString) {
  17. System.out.println(responseString);
  18. }
  19. };
  20. }
  21. public void testGet(Map map) {
  22. long startTime = System.currentTimeMillis();
  23. for (int i = 0; i < num; i++) {
  24. executor.execute(new Runnable() {
  25. @Override
  26. public void run() {
  27. httpCommon.get(URL, map);
  28. countDownLatch.countDown();
  29. }
  30. });
  31. }
  32. try {
  33. countDownLatch.await();
  34. long executeTime = System.currentTimeMillis() - startTime;
  35. System.out.println("一共消耗:" + executeTime +"毫秒");
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. public void testPost(Map map, T t) {
  41. long startTime = System.currentTimeMillis();
  42. for (int i = 0; i < num; i++) {
  43. executor.execute(new Runnable() {
  44. @Override
  45. public void run() {
  46. httpCommon.post(URL, map, t);
  47. countDownLatch.countDown();
  48. }
  49. });
  50. }
  51. try {
  52. countDownLatch.wait();
  53. long executeTime = System.currentTimeMillis() - startTime;
  54. System.out.println("一共消耗:" + executeTime +"毫秒");
  55. } catch (InterruptedException e) {
  56. e.printStackTrace();
  57. }
  58. }

}

</>复制代码

  1. public static void main(String[] args) {
  2. String Url = "http://localhost:8085/test/add";
  3. RunThread testMain = new RunThread(Url, 1000);
  4. // 测试Get请求
  5. testMain.testGet(new HashMap<>());

// // 测试POST请求、PUT请求、DELETE请求
// testMain.testPost(new HashMap<>(), null);

</>复制代码

  1. }

上面是并发测试代码,那么如何写高并发测试代码呢!想到两点:一个锁、一个事务。先用Oracle做实验。

</>复制代码

  1. insert into testa
  2. (aaaa, bbbb)
  3. values
  4. (#{aaa}, #{aaa})


Service层代码,设置事务的隔离级别为不可重复读
Isolation.REPEATABLE_READ,结果报错“Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: 仅 READ_COMMITTED 和 SERIALIZABLE 是有效的事务处理级”。卧槽!还能不能一起愉快地玩耍了,Oracle居然只支持可重复读和可系列化两种事务级别,真是让人大跌眼镜。

贴一下高并发代码吧,经过实验,通过1000个并发请求,使用Durid + Lock成功1百个不到(在这里还是得喷一下阿里的技术),使用dbcp2 + Lock成功2百多个,使用dbcp2 + synchronized 竟然成功了940个。
@Autowired
private TestMapper testMapper;

//private Lock lock = new ReentrantLock();

@Transactional(isolation = Isolation.SERIALIZABLE)
public synchronized Integer test(Integer a, Integer b) {

</>复制代码

  1. int c = testMapper.select();
  2. c += 1;
  3. testMapper.insert(c);
  4. return c;

}

代码有问题,找找错误原因吧。Spring AOP执行事务,会在Service方法执行之前就开始事务,再执行Synchronized同步方法。这样会导致查询数据并没有做同步,修改成如下代码,能完美解决问题。测试得出如下代码的执行效率最高,1000个并发耗时9018毫秒
@Autowired
private TestMapper testMapper;

//private Lock lock = new ReentrantLock();

public synchronized Integer test(Integer a, Integer b) {

</>复制代码

  1. int c = testMapper.select();
  2. c += 1;
  3. update(c);
  4. return c;

}

@Transactional(isolation = Isolation.SERIALIZABLE)
public void update(int c) {

</>复制代码

  1. testMapper.insert(c);

}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71321.html

相关文章

  • 墙裂推荐:搜云库技术团队,面试必备的技术干货

    摘要:今天整理了一下近大半年以来的一些文章,和我的预期一样,很多文章我都忘记自己曾经写过了,这个记录的过程让我也有了新的理解。希望大家,收藏,点赞,加转发。 今天整理了一下近大半年以来的一些文章,和我的预期一样,很多文章我都忘记自己曾经写过了,这个记录的过程让我也有了新的理解。希望大家,收藏,点赞,加转发。 面试必备 面试必备:深入Spring MVC DispatchServlet 源码...

    SegmentFault 评论0 收藏0
  • 墙裂推荐:搜云库技术团队,面试必备的技术干货

    摘要:今天整理了一下近大半年以来的一些文章,和我的预期一样,很多文章我都忘记自己曾经写过了,这个记录的过程让我也有了新的理解。希望大家,收藏,点赞,加转发。 今天整理了一下近大半年以来的一些文章,和我的预期一样,很多文章我都忘记自己曾经写过了,这个记录的过程让我也有了新的理解。希望大家,收藏,点赞,加转发。 面试必备 面试必备:深入Spring MVC DispatchServlet 源码...

    Neilyo 评论0 收藏0
  • Node.js运行原理、并发性能测试对比生态圈汇总

    摘要:模式,单实例多进程,常用于多语言混编,比如等,不支持端口复用,需要自己做应用的端口分配和负载均衡的子进程业务代码。就是我们需要一个调度者,保证所有后端服务器都将性能充分发挥,从而保持服务器集群的整体性能最优,这就是负载均衡。 showImg(https://segmentfault.com/img/remote/1460000019425391?w=1440&h=1080); Nod...

    kamushin233 评论0 收藏0
  • Node.js运行原理、并发性能测试对比生态圈汇总

    摘要:模式,单实例多进程,常用于多语言混编,比如等,不支持端口复用,需要自己做应用的端口分配和负载均衡的子进程业务代码。就是我们需要一个调度者,保证所有后端服务器都将性能充分发挥,从而保持服务器集群的整体性能最优,这就是负载均衡。 showImg(https://segmentfault.com/img/remote/1460000019425391?w=1440&h=1080); Nod...

    BDEEFE 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<