헤르메스 LIFE

[Spring Boot] Multi FileUpload Test 본문

Spring Boot Framework

[Spring Boot] Multi FileUpload Test

헤르메스의날개 2022. 2. 27. 13:04
728x90

테스트 환경

https://hermeslog.tistory.com/577?category=1078420 

 

[Spring Boot] Multi FileUpload

개발환경 1. STS 버전 : 4.13.1 2. JDK 버전 : OpenJDK 11.0.14_9_x64 3. Tomcat 버전 : 9.0.56 4. Maven 버전 : 3.8.4 5. Spring 버전 : Spring Boot 2.6.3 6. Database : Docker 에 DB 설치 - primary - Postgr..

hermeslog.tistory.com


동시 업로드를 어디까지 받을 수 있는지 확인하고 싶었습니다.

로컬테스트에서 이정도 성능이면, 서버에서는 더더욱 좋지.. 싶습니다.

HttpClient 를 Thread 로 이용했습니다.

        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
        </dependency>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.study</groupId>
    <artifactId>springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>springboot</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>11</java.version>
    </properties>
    <dependencies>

        <!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> 
            <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mustache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>


        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

FileUploadControllerTest.java

package com.study.springboot.system.controller;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.nio.charset.Charset;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
public class FileUploadControllerTest {

    @Autowired(required = true)
    FileUploadController fileUpload;

    @Test
    public void upload() throws Exception {
        System.out.println("main start .......");

        for (int i = 0; i < 10; i++) {
            Runnable task = new Task1();

            Thread thread = new Thread(task);
            thread.setName("thread-A" + i);

            thread.start();
        }

        for (int i = 0; i < 10; i++) {
            Runnable task = new Task2();

            Thread thread = new Thread(task);
            thread.setName("thread-B" + i);

            thread.start();
        }

        for (int i = 0; i < 10; i++) {
            Runnable task = new Task3();

            Thread thread = new Thread(task);
            thread.setName("thread-C" + i);

            thread.start();
        }

        for (int i = 0; i < 10; i++) {
            Runnable task = new Task4();

            Thread thread = new Thread(task);
            thread.setName("thread-D" + i);

            thread.start();
        }

        for (int i = 0; i < 10; i++) {
            Runnable task = new Task5();

            Thread thread = new Thread(task);
            thread.setName("thread-E" + i);

            thread.start();
        }

        for (int i = 0; i < 10; i++) {
            Runnable task = new Task6();

            Thread thread = new Thread(task);
            thread.setName("thread-F" + i);

            thread.start();
        }

        for (int i = 0; i < 10; i++) {
            Runnable task = new Task7();

            Thread thread = new Thread(task);
            thread.setName("thread-G" + i);

            thread.start();
        }

        for (int i = 0; i < 10; i++) {
            Runnable task = new Task8();

            Thread thread = new Thread(task);
            thread.setName("thread-H" + i);

            thread.start();
        }

        for (int i = 0; i < 10; i++) {
            Runnable task = new Task9();

            Thread thread = new Thread(task);
            thread.setName("thread-I" + i);

            thread.start();
        }

        for (int i = 0; i < 10; i++) {
            Runnable task = new Task10();

            Thread thread = new Thread(task);
            thread.setName("thread-J" + i);

            thread.start();
        }
        
        Thread.sleep(120000);  // 재우지 않으면 Connection 이 Close 되어버림. ( 1000: 1초 )

        System.out.println("main exit .......");
    }

    class Task1 implements Runnable {

        @Override
        public void run() {
            File imgFile = new File("c:/Temp/pickpick/이미지 004.jpg");
            excuteClient(Thread.currentThread(), imgFile);
        }

    }

    class Task2 implements Runnable {

        @Override
        public void run() {
            File imgFile = new File("c:/Temp/pickpick/이미지 005.jpg");
            excuteClient(Thread.currentThread(), imgFile);
        }

    }

    class Task3 implements Runnable {

        @Override
        public void run() {
            File imgFile = new File("c:/Temp/pickpick/이미지 006.jpg");
            excuteClient(Thread.currentThread(), imgFile);
        }

    }

    class Task4 implements Runnable {

        @Override
        public void run() {
            File imgFile = new File("c:/Temp/pickpick/이미지 007.jpg");
            excuteClient(Thread.currentThread(), imgFile);
        }

    }

    class Task5 implements Runnable {

        @Override
        public void run() {
            File imgFile = new File("c:/Temp/pickpick/이미지 008.jpg");
            excuteClient(Thread.currentThread(), imgFile);
        }

    }

    class Task6 implements Runnable {

        @Override
        public void run() {
            File imgFile = new File("c:/Temp/pickpick/이미지 009.jpg");
            excuteClient(Thread.currentThread(), imgFile);
        }

    }

    class Task7 implements Runnable {

        @Override
        public void run() {
            File imgFile = new File("c:/Temp/pickpick/이미지 010.jpg");
            excuteClient(Thread.currentThread(), imgFile);
        }

    }

    class Task8 implements Runnable {

        @Override
        public void run() {
            File imgFile = new File("c:/Temp/pickpick/이미지 001.jpg");
            excuteClient(Thread.currentThread(), imgFile);
        }

    }

    class Task9 implements Runnable {

        @Override
        public void run() {
            File imgFile = new File("c:/Temp/pickpick/이미지 002.jpg");
            excuteClient(Thread.currentThread(), imgFile);
        }

    }

    class Task10 implements Runnable {

        @Override
        public void run() {
            File imgFile = new File("c:/Temp/pickpick/이미지 003.jpg");
            excuteClient(Thread.currentThread(), imgFile);
        }

    }
    
    private void excuteClient(Thread threadId, File imgFile) {
        MultipartEntityBuilder params = MultipartEntityBuilder.create();

        CloseableHttpClient http   = HttpClients.createDefault();
        StringBuffer        result = new StringBuffer();

        try {
            params.addPart("files",
                    new FileBody(imgFile, ContentType.create("application/octet-stream"), imgFile.getName()));

            HttpPost post = new HttpPost("http://localhost:9090/upload");
            post.setHeader("Connection", "Keep-Alive");
            post.setHeader("Accept-Charset", "UTF-8");
            post.setEntity(params.build());

            CloseableHttpResponse response = http.execute(post);

            try {
                HttpEntity     res = response.getEntity();
                BufferedReader br  = new BufferedReader(
                        new InputStreamReader(res.getContent(), Charset.forName("UTF-8")));

                String buffer = null;
                while ((buffer = br.readLine()) != null) {
                    result.append(buffer).append("\r\n");
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                http.close();
            } catch (Exception e) {
            }
        }

        // Printing the status and the contents of the response
        System.out.println("threadId >> " + threadId + " >> " + result.toString());
    }
}

 


결과

300개 파일이 저장된 모습입니다.


여러번 테스트 한 중에...

아래의 설정을 추가하고, 좀 더 빨라졌음을 느꼈습니다.

아래 참고 Site를 참조하였습니다.

server:
  port: 9090
  servlet:
    context-path: /
    encoding:
      charset: UTF-8
      enabled: true
      force: true
  tomcat:
    accept-count: 100                        # Spring Default : 100
    max-connections: 100                     # Spring Default : 8192
    connection-timeout: 20000                # Spring Default : 60000 (i.e. 60 seconds)
    threads:
      max: 200                               # Spring Default : 200
      min-spare: 10                          # Spring Default : 10
    keep-alive-timeout: 15                   # Spring Default : 15
    max-keep-alive-requests: 100             # Spring Default : 100
    max-http-form-post-size: 100
    uri-encoding: UTF-8                      # Spring Default : UTF-8

application.xml

server:
  port: 9090
  servlet:
    context-path: /
    encoding:
      charset: UTF-8
      enabled: true
      force: true
  tomcat:
    accept-count: 100                        # Spring Default : 100
    max-connections: 100                     # Spring Default : 8192
    connection-timeout: 20000                # Spring Default : 60000 (i.e. 60 seconds)
    threads:
      max: 200                               # Spring Default : 200
      min-spare: 10                          # Spring Default : 10
    keep-alive-timeout: 15                   # Spring Default : 15
    max-keep-alive-requests: 100             # Spring Default : 100
    max-http-form-post-size: 100
    uri-encoding: UTF-8                      # Spring Default : UTF-8

spring:
  mvc:
   view:
    prefix: /WEB-INF/jsp/
    suffix: .jsp

  h2:
    console:
      enabled: true
      path: /h2-console

  datasource:
      url: jdbc:postgresql://localhost:5432/springboot
      username: hermeswing
      password: pass
      hikari:
        idle-timeout: 10000
        maximum-pool-size: 10
        minimum-idle: 5
        pool-name: BaseHikariPool
              
  jpa:
    generate-ddl: true
    hibernate:
      ddl-auto: update         # Hibernate ddl auto (create, create-drop, validate, update)
    show-sql: false
    properties:
      hibernatte:
        format_sql: true
  profiles:
    active: local


logging:
  level:
    org:
      hibernate:
        SQL: debug
        type:
          descriptor:
            SQL: trace

https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html

 

Common Application Properties

 

docs.spring.io

https://www.codetd.com/en/article/12548952

 

spring boot Tomcat maxConnections、maxThreads、acceptCount - Code World

table of Contents 1. maxConnections: 2. acceptCount  3. maxThreads: 4.connectionTimeout Question 1: Question 2: Appendix 1: Appendix 2: reference: Picture 1: 1. maxConnections: Attribute Description maxConnections The maximum number of connections t

www.codetd.com

 

728x90