@Async is not a silver bullet (@Async는 은 탄환이 아니다)
- private 메서드에는 적용이 안됨 (public만)
- self-invocation(자가 호출)해서는 안됨 (같은 클래스 내부의 메서드를 호출하는 것은 안됨)
- 리턴값에 대해서 void나 CompletableFuture<> 여야 함
참고
- https://jeong-pro.tistory.com/187
@Async is not a silver bullet (@Async는 은 탄환이 아니다)
백업
1. Spring Boot?
- 스프링 프레임워크를 사용하는 프로젝트를 쉽게 설정할 수 있도록 해주는 서브 프로젝트
2. Spring Boot의 특징
- 단독으로 스프링 애플리케이션을 생성
- Tomcat, Jetty, Undertow 를 내장
- 빌드 구성을 단순화하기 위해 'starter' 종속성 제공
- 스프링과 3th party 라이브러리를 가능한 자동적으로 설정
- 상용화에 필요한 metrics, health checks, externalized configuration 같은 기능 제공
- XML 설정을 생성하지 않고 요구하지 않음
1. Spring Initializr 로 Spring Boot 프로젝트 생성
1.1. Generator
- Spring Initializr 사이트 접속 ( https://start.spring.io/ )
![]() |
( 2021년 1월 설정 화면 ) |
plugins { // 스프링부트 의존성 관리 및 애플리케이션 패키징을 위한 Gradle 플러그인 id "org.springframework.boot" version "2.4.1" // 스프링부트 버전에 대해 의존성을 자동으로 가져옴 id "io.spring.dependency-management" version "1.0.10.RELEASE" id "java" } group = "com.chakans" version = "0.0.1-SNAPSHOT" // 자바 버전 선언 sourceCompatibility = "11" repositories { mavenCentral() } // 프로젝트와 의존 관계에 있는 라이브러리 정의 dependencies { implementation "org.springframework.boot:spring-boot-starter" implementation "org.springframework.boot:spring-boot-starter-web" testImplementation ("org.springframework.boot:spring-boot-starter-test") { exclude group: "org.junit.vintage", module: "junit-vintage-engine" } } test { useJUnitPlatform() }
3. Application.java
3.1. @SpringBootApplication
- @EnableAutoConfiguration, @ComponentScan, @Configuration을 하나로 묶은 것이라 볼 수 있음
- 해당 어노테이션이 있는 package위치를 기준으로 ComponentScan을 수행함 ( 기준 package 위치 변경 가능 )
- main 메소드 위치하는 곳임
4. index.html
- resources > static 하위에 index.html 파일 생성
![]() |
A 서버 -------------------------------------- B 서버 |
$ sudo apt update && sudo apt install tinc
$ sudo mkdir -p /etc/tinc/sample/hosts
$ sudo vim /etc/tinc/sample/tinc.conf
-- A 서버
-------------------------
Name = tinc_a Device = /dev/net/tun AddressFamily = ipv4 Interface = tun0 -------------------------
-- B 서버
Name = tinc_b Device = /dev/net/tun AddressFamily = ipv4 Interface = tun0-------------------------ConnectTo = tinc_a-------------------------
-- A 서버
$ sudo vim /etc/tinc/sample/hosts/tinc_a
------------------------- Address = 172.17.0.2 Subnet = 100.100.100.100 -------------------------
-- B 서버 $ sudo vim /etc/tinc/sample/hosts/tinc_b ------------------------- Address = 172.17.0.3 Subnet = 100.100.100.200 -------------------------
$ sudo tincd -n sample -K 4096
-- A 서버
$ sudo vim /etc/tinc/sample/tinc-up-------------------------#!/bin/sh ip link set $INTERFACE up ip addr add 100.100.100.100 dev $INTERFACE ip route add 100.100.100.0/24 dev $INTERFACE-------------------------$ sudo vim /etc/tinc/sample/tinc-down-------------------------#!/bin/sh ip route del 100.100.100.0/24 dev $INTERFACE ip addr del 100.100.100.100 dev $INTERFACE ip link set $INTERFACE down--------------------------- B 서버$ sudo vim /etc/tinc/sample/tinc-up-------------------------#!/bin/sh ip link set $INTERFACE up ip addr add 100.100.100.200 dev $INTERFACE ip route add 100.100.100.0/24 dev $INTERFACE-------------------------$ sudo vim /etc/tinc/sample/tinc-down-------------------------#!/bin/sh ip route del 100.100.100.0/24 dev $INTERFACE ip addr del 100.100.100.200 dev $INTERFACE ip link set $INTERFACE down-------------------------
$ sudo chmod -v +x /etc/tinc/sample/tinc-{up,down}
$ sudo vim /etc/systemd/system/tinc.service ------------------------- [Unit] Description=Tinc net sample After=network.target [Service] Type=simple WorkingDirectory=/etc/tinc/sample ExecStart=/sbin/tincd -n sample -D -d3 ExecReload=/sbin/tincd -n sample -kHUP TimeoutStopSec=5 Restart=always RestartSec=60 [Install] WantedBy=multi-user.target -------------------------
$ sudo systemctl enable tinc.service
$ sudo systemctl status tinc.service
-- A 서버
$ scp /etc/tinc/sample/hosts/tinc_a user@172.17.0.2:/tmp/ $ ssh -t user@172.17.0.2 sudo mv -v /tmp/tinc_a /etc/tinc/sample/hosts/
-- B 서버
$ scp /etc/tinc/sample/hosts/tinc_b user@172.17.0.3:/tmp/
$ ssh -t user@172.17.0.3 sudo mv -v /tmp/tinc_b /etc/tinc/sample/hosts/
-- A 서버 & B 서버
$ sudo tincd -n sample -D -d3
$ sudo ip a
$ ping 100.100.100.***
1 2 3 | angular.config(['$locationProvider'], function ($locationProvider) { $locationProvider.html5Mode(true); }); |
1 2 3 4 5 6 | @NgModule({ imports: [ RouterModule.forRoot(LAYOUT_ROUTES, { useHash: false }) ], ... }) |
compile("org.tuckey:urlrewritefilter:${urlrewritefilter_version}")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tuckey.web.filters.urlrewrite.Conf; import org.tuckey.web.filters.urlrewrite.utils.StringUtils; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import java.io.InputStream; import java.net.URL; /** * Subclass of {@link org.tuckey.web.filters.urlrewrite.UrlRewriteFilter} * that overrides the configuration file loading mechanism. */ public class UrlRewriteFilter extends org.tuckey.web.filters.urlrewrite.UrlRewriteFilter { private final Logger log = LoggerFactory.getLogger(UrlRewriteFilter.class); @Override protected void loadUrlRewriter(FilterConfig filterConfig) throws ServletException { ServletContext context = filterConfig.getServletContext(); String confPath = filterConfig.getInitParameter("confPath"); boolean modRewriteStyleConf = getModRewriteStyleConf(filterConfig.getInitParameter("modRewriteConf")); try { final InputStream inputStream = getClass().getClassLoader().getResourceAsStream(confPath); final URL confUrl = getClass().getClassLoader().getResource(confPath); if (inputStream == null) { log.error("unable to find urlrewrite conf file at " + confPath); // set the writer back to null if (isLoaded()) { log.error("unloading existing conf"); destroyUrlRewriter(); } } else { Conf conf = new Conf(context, inputStream, confPath, confUrl.toString(), modRewriteStyleConf); checkConf(conf); } } catch (Throwable e) { log.error("unloading urlrewrite conf file at " + confPath, e); throw new ServletException(e); } } private boolean getModRewriteStyleConf(String modRewriteConf) { boolean modRewriteStyleConf = false; if (!StringUtils.isBlank(modRewriteConf)) { modRewriteStyleConf = "true".equals(StringUtils.trim(modRewriteConf).toLowerCase()); } return modRewriteStyleConf; } } |
1 2 3 4 5 6 7 8 9 10 11 | private void initUrlRewriteFilter(ServletContext servletContext, EnumSet<DispatcherType> disps) { FilterRegistration.Dynamic urlRewriteFilter = servletContext.addFilter("urlRewriteFilter", new UrlRewriteFilter()); urlRewriteFilter.setInitParameter("confPath", "url-rewrite.xml"); urlRewriteFilter.setInitParameter("confReloadLastCheck", "-1"); urlRewriteFilter.setInitParameter("logLevel", "WARM"); urlRewriteFilter.addMappingForUrlPatterns(disps, true, "/*"); urlRewriteFilter.setAsyncSupported(true); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN" "https://www.tuckey.org/res/dtds/urlrewrite4.0.dtd"> <urlrewrite > <!-- default --> <rule match-type="regex"> <condition type="request-uri" operator="notequal"> ^.*\.(html?|htc|js|css|xml|map|json|ico|bmp|gif|jpe?g|png|svg|eot|woff|woff2|ttf|pdf|swf|txt)$ </condition> <from>^/(.*)$</from> <to last="true">/index.html</to> </rule> </urlrewrite> |
![]() |
그림1) Nginx로 Reverse-Proxy 환경 구성도 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # cat /etc/nginx/sites-available/default
upstream sample_proxy {
server 192.168.56.2:8080;
server 192.168.56.3:8080; } server { listen 80 default_server; listen [::]:80 default_server; root /var/www/html; index index.html index.htm index.nginx-debian.html; server_name _; location / { proxy_pass http://sample_proxy; } } |
1 2 3 4 | upstream sample_proxy { server 192.168.56.2:8080; server 192.168.56.3:8080; } |
1 2 3 4 5 6 | upstream sample_proxy { least_conn; server 192.168.56.2:8080; server 192.168.56.3:8080; } |
1 2 3 4 5 6 | upstream sample_proxy { ip_hash; server 192.168.56.2:8080; server 192.168.56.3:8080; } |
1 2 3 4 5 6 | upstream sample_proxy { hash $request_uri consistent; server 192.168.56.2:8080; server 192.168.56.3:8080; } |
1 2 3 4 5 6 | upstream sample_proxy { least_time header; server 192.168.56.2:8080; server 192.168.56.3:8080; } |
구분 | 제어자 | 설명 |
---|---|---|
접근권한 | public | 모든 클래스에서 접근이 가능함 |
protected | 동일 패키지에 속하는 클래스와 하위 클래스 관계의 클래스에 의해 접근이 가능함 | |
private | 클래스 내에서만 접근이 가능함 |
종류 | 클래스 | 하위 클래스 | 동일 클래스 | 모든 클래스 |
---|---|---|---|---|
(default) | O | X | O | X |
public | O | O | O | O |
protected | O | O | O | X |
private | O | X | X | X |