From 57e233b2223876f9237215a2e4028ec4acfe8494 Mon Sep 17 00:00:00 2001 From: Kim JinSeon Date: Tue, 5 Nov 2024 10:25:38 +0900 Subject: [PATCH 1/8] =?UTF-8?q?chore:=20logback=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gateway, auth, user 모듈 수정 --- service/auth/server/build.gradle | 2 +- .../src/main/resources/logback-spring.xml | 55 +++++++++++-------- service/gateway/server/build.gradle | 2 +- .../src/main/resources/logback-spring.xml | 55 +++++++++++-------- service/user/server/build.gradle | 2 +- .../src/main/resources/logback-spring.xml | 55 +++++++++++-------- 6 files changed, 96 insertions(+), 75 deletions(-) diff --git a/service/auth/server/build.gradle b/service/auth/server/build.gradle index b414f64..ad7c1d8 100644 --- a/service/auth/server/build.gradle +++ b/service/auth/server/build.gradle @@ -39,7 +39,7 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - implementation 'net.logstash.logback:logstash-logback-encoder:8.0' +// implementation 'net.logstash.logback:logstash-logback-encoder:8.0' } diff --git a/service/auth/server/src/main/resources/logback-spring.xml b/service/auth/server/src/main/resources/logback-spring.xml index be5d76e..b23bb78 100644 --- a/service/auth/server/src/main/resources/logback-spring.xml +++ b/service/auth/server/src/main/resources/logback-spring.xml @@ -1,5 +1,5 @@ - - + + %d{yyyy-MM-dd HH:mm:ss.SSS} springboot-elk [%thread] %-5level %logger{36} - %msg%n @@ -7,28 +7,35 @@ - - - - logstash01:50000 - - - - - UTC - - - - - - - - - + + + + + 127.0.0.1:50000 + + + + UTC + + + + + + + + + + + + + + - - - - + + + + + + diff --git a/service/gateway/server/build.gradle b/service/gateway/server/build.gradle index 8a75e30..c58443a 100644 --- a/service/gateway/server/build.gradle +++ b/service/gateway/server/build.gradle @@ -33,7 +33,7 @@ dependencies { implementation project(':common:domain') implementation project(':service:auth:auth_dto') - implementation 'net.logstash.logback:logstash-logback-encoder:8.0' +// implementation 'net.logstash.logback:logstash-logback-encoder:8.0' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.cloud:spring-cloud-starter-gateway' diff --git a/service/gateway/server/src/main/resources/logback-spring.xml b/service/gateway/server/src/main/resources/logback-spring.xml index be5d76e..b23bb78 100644 --- a/service/gateway/server/src/main/resources/logback-spring.xml +++ b/service/gateway/server/src/main/resources/logback-spring.xml @@ -1,5 +1,5 @@ - - + + %d{yyyy-MM-dd HH:mm:ss.SSS} springboot-elk [%thread] %-5level %logger{36} - %msg%n @@ -7,28 +7,35 @@ - - - - logstash01:50000 - - - - - UTC - - - - - - - - - + + + + + 127.0.0.1:50000 + + + + UTC + + + + + + + + + + + + + + - - - - + + + + + + diff --git a/service/user/server/build.gradle b/service/user/server/build.gradle index ec5a5ae..34b03db 100644 --- a/service/user/server/build.gradle +++ b/service/user/server/build.gradle @@ -45,7 +45,7 @@ dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - implementation 'net.logstash.logback:logstash-logback-encoder:8.0' +// implementation 'net.logstash.logback:logstash-logback-encoder:8.0' } diff --git a/service/user/server/src/main/resources/logback-spring.xml b/service/user/server/src/main/resources/logback-spring.xml index be5d76e..b23bb78 100644 --- a/service/user/server/src/main/resources/logback-spring.xml +++ b/service/user/server/src/main/resources/logback-spring.xml @@ -1,5 +1,5 @@ - - + + %d{yyyy-MM-dd HH:mm:ss.SSS} springboot-elk [%thread] %-5level %logger{36} - %msg%n @@ -7,28 +7,35 @@ - - - - logstash01:50000 - - - - - UTC - - - - - - - - - + + + + + 127.0.0.1:50000 + + + + UTC + + + + + + + + + + + + + + - - - - + + + + + + From 7d91515d1b5b375e9dc4f3b5f73c2dabf4846fb0 Mon Sep 17 00:00:00 2001 From: Kim JinSeon Date: Sun, 10 Nov 2024 21:30:49 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20prometheus,=20grafana=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gateway 사용량 추적 --- compose-monitoring.yml | 18 ++++++++++++++++++ prometheus.yml | 15 +++++++++++++++ service/eureka/server/build.gradle | 2 ++ .../server/src/main/resources/application.yml | 14 ++++++++++++++ service/gateway/server/build.gradle | 3 ++- .../server/src/main/resources/application.yml | 14 ++++++++++++++ 6 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 compose-monitoring.yml create mode 100644 prometheus.yml diff --git a/compose-monitoring.yml b/compose-monitoring.yml new file mode 100644 index 0000000..6ef27ce --- /dev/null +++ b/compose-monitoring.yml @@ -0,0 +1,18 @@ +version: '3.8' + +services: + prometheus: + image: prom/prometheus:latest + container_name: prometheus + ports: + - "9090:9090" + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + + grafana: + image: grafana/grafana:latest + container_name: grafana + ports: + - "3000:3000" + volumes: + - ./grafana:/var/lib/grafana diff --git a/prometheus.yml b/prometheus.yml new file mode 100644 index 0000000..c866220 --- /dev/null +++ b/prometheus.yml @@ -0,0 +1,15 @@ +global: + scrape_interval: 15s +scrape_configs: + - job_name: 'eureka-services' + eureka_sd_configs: + - server: 'http://host.docker.internal:19090/eureka' # Eureka 서버 주소 + metrics_path: '/actuator/prometheus' + relabel_configs: + - source_labels: [ '__meta_eureka_app_name' ] + target_label: 'job' + - source_labels: [ '__meta_eureka_instance_ip_addr' ] + target_label: 'instance' + - source_labels: [ '__meta_eureka_app_instance_status' ] + action: keep + regex: 'UP' # 서비스 상태가 "UP"인 인스턴스만 스크랩 \ No newline at end of file diff --git a/service/eureka/server/build.gradle b/service/eureka/server/build.gradle index 96a5074..db07a79 100644 --- a/service/eureka/server/build.gradle +++ b/service/eureka/server/build.gradle @@ -27,6 +27,8 @@ dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'io.micrometer:micrometer-registry-prometheus' } dependencyManagement { diff --git a/service/eureka/server/src/main/resources/application.yml b/service/eureka/server/src/main/resources/application.yml index 5a1e7ee..f8af099 100644 --- a/service/eureka/server/src/main/resources/application.yml +++ b/service/eureka/server/src/main/resources/application.yml @@ -6,3 +6,17 @@ spring: server: port: 19090 + +management: + endpoints: + web: + exposure: + include: prometheus + endpoint: + health: + show-details: always + prometheus: + enabled: true + metrics: + tags: + application: ${spring.application.name} diff --git a/service/gateway/server/build.gradle b/service/gateway/server/build.gradle index c58443a..4f78083 100644 --- a/service/gateway/server/build.gradle +++ b/service/gateway/server/build.gradle @@ -46,7 +46,8 @@ dependencies { testRuntimeOnly 'org.junit.platform:junit-platform-launcher' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.redisson:redisson:3.35.0' - + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'io.micrometer:micrometer-registry-prometheus' } dependencyManagement { diff --git a/service/gateway/server/src/main/resources/application.yml b/service/gateway/server/src/main/resources/application.yml index b165398..a797847 100644 --- a/service/gateway/server/src/main/resources/application.yml +++ b/service/gateway/server/src/main/resources/application.yml @@ -54,3 +54,17 @@ spring: discovery: locator: enabled: true + +management: + endpoints: + web: + exposure: + include: prometheus + endpoint: + health: + show-details: always + prometheus: + enabled: true + metrics: + tags: + application: ${spring.application.name} From ac8172d129e5dee23682094035c32cff94524f8c Mon Sep 17 00:00:00 2001 From: Kim JinSeon Date: Sun, 10 Nov 2024 21:31:20 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20redisson=20lock=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/application/UserQueueService.java | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java b/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java index d375899..525b1e7 100644 --- a/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java +++ b/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java @@ -27,29 +27,26 @@ public class UserQueueService { @Value("${MAX_ACTIVE_USERS}") private long MAX_ACTIVE_USERS; private final long INACTIVITY_THRESHOLD = 300; - private Long activeUsers; public Mono registerUser(String userId) { return reactiveRedisTemplate.opsForZSet() .rank(USER_QUEUE_PROCEED_KEY, userId) .defaultIfEmpty(-1L) - .flatMap(rank -> { - if (rank >= 0) { - return updateUserActivityTime(userId) - .thenReturn(new RegisterUserResponse(0L)); - } + .flatMap(rank -> rank >= 0 ? handleProceedUser(userId) : handleNewUser(userId)); + } - return reactiveRedisTemplate.opsForSet().size(USER_ACTIVE_SET_KEY) - .flatMap(activeUsers -> { - if (activeUsers < MAX_ACTIVE_USERS) { - return addToProceedQueue(userId); - } else { - return checkAndAddToQueue(userId); - } - }); - }); + private Mono handleProceedUser(String userId) { + return updateUserActivityTime(userId) + .thenReturn(new RegisterUserResponse(0L)); + } + + private Mono handleNewUser(String userId) { + return reactiveRedisTemplate.opsForSet().size(USER_ACTIVE_SET_KEY) + .flatMap(activeUsers -> activeUsers < MAX_ACTIVE_USERS ? addToProceedQueue(userId) + : checkAndAddToQueue(userId)); } + private Mono checkAndAddToQueue(String userId) { return reactiveRedisTemplate.opsForZSet().score(USER_QUEUE_WAIT_KEY, userId) .defaultIfEmpty(-1.0) @@ -73,22 +70,42 @@ private Mono updateWaitQueueScore(String userId) { ; } - private Mono addToProceedQueue(String userId) { + public Mono addToProceedQueue(String userId) { + return Mono.create(sink -> { + lockComponent.execute(userId, 1000, 1000, () -> { + try { + addUserToQueue(userId) + .doOnSuccess(sink::success) + .doOnError(sink::error) + .subscribe(); + } catch (Exception e) { + sink.error(e); + } + }); + }); + } + + private Mono addUserToQueue(String userId) { var unixTime = Instant.now().getEpochSecond(); return reactiveRedisTemplate.opsForZSet() .add(USER_QUEUE_PROCEED_KEY, userId, unixTime) - .filter(i -> i) + .filter(success -> success) .flatMap(success -> { if (success) { - return reactiveRedisTemplate.opsForSet() - .add(USER_ACTIVE_SET_KEY, userId) - .map(i -> new RegisterUserResponse(0L)); + return addToActiveSet(userId); } else { return checkAndAddToQueue(userId); } }); } + private Mono addToActiveSet(String userId) { + return reactiveRedisTemplate.opsForSet() + .add(USER_ACTIVE_SET_KEY, userId) + .map(i -> new RegisterUserResponse(0L)); + } + + private Mono addToWaitQueue(String userId) { var unixTime = Instant.now().getEpochSecond(); return reactiveRedisTemplate.opsForZSet() From 5bf3d5f01f4ed2e32da5b65e7b74774234b9c8bd Mon Sep 17 00:00:00 2001 From: Kim JinSeon Date: Sun, 10 Nov 2024 21:51:49 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20auth=20=EB=AA=A8=EB=93=88=20redis?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/auth/server/build.gradle | 3 ++- .../auth/server/src/main/resources/application-local.yml | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/service/auth/server/build.gradle b/service/auth/server/build.gradle index ad7c1d8..ebd598e 100644 --- a/service/auth/server/build.gradle +++ b/service/auth/server/build.gradle @@ -40,7 +40,8 @@ dependencies { testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' // implementation 'net.logstash.logback:logstash-logback-encoder:8.0' - + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + } dependencyManagement { diff --git a/service/auth/server/src/main/resources/application-local.yml b/service/auth/server/src/main/resources/application-local.yml index 5392185..5f8d694 100644 --- a/service/auth/server/src/main/resources/application-local.yml +++ b/service/auth/server/src/main/resources/application-local.yml @@ -6,3 +6,11 @@ eureka: jwt: secret-key: Zr2PMyKH4UheWy6zscq6Wc/nSLl6L/AJ6b5QvLzWXJg5wzQiGdJncTTOBCxvW8Rkl1T0N+1bqF52Mw2eZvwA0i5zyq+VVbIjPyz+b7DW0Xd2wTnXifwxM12LU2oyXpyLz access-token-expire-in: 3600000 + +spring: + data: + redis: + host: localhost + port: 6381 + username: default + password: systempass From c0a17ea53206cd028c1c2180c70995e0412ac3ff Mon Sep 17 00:00:00 2001 From: Kim JinSeon Date: Thu, 21 Nov 2024 17:15:41 +0900 Subject: [PATCH 5/8] =?UTF-8?q?refactor:=20active=20queue=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/application/UserQueueService.java | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java b/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java index 525b1e7..edbb8de 100644 --- a/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java +++ b/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java @@ -23,7 +23,6 @@ public class UserQueueService { private final DistributedLockComponent lockComponent; private final String USER_QUEUE_WAIT_KEY = "users:queue:wait"; private final String USER_QUEUE_PROCEED_KEY = "users:queue:proceed"; - private final String USER_ACTIVE_SET_KEY = "users:active"; @Value("${MAX_ACTIVE_USERS}") private long MAX_ACTIVE_USERS; private final long INACTIVITY_THRESHOLD = 300; @@ -41,7 +40,7 @@ private Mono handleProceedUser(String userId) { } private Mono handleNewUser(String userId) { - return reactiveRedisTemplate.opsForSet().size(USER_ACTIVE_SET_KEY) + return reactiveRedisTemplate.opsForSet().size(USER_QUEUE_PROCEED_KEY) .flatMap(activeUsers -> activeUsers < MAX_ACTIVE_USERS ? addToProceedQueue(userId) : checkAndAddToQueue(userId)); } @@ -84,25 +83,14 @@ public Mono addToProceedQueue(String userId) { }); }); } - + private Mono addUserToQueue(String userId) { var unixTime = Instant.now().getEpochSecond(); return reactiveRedisTemplate.opsForZSet() .add(USER_QUEUE_PROCEED_KEY, userId, unixTime) - .filter(success -> success) - .flatMap(success -> { - if (success) { - return addToActiveSet(userId); - } else { - return checkAndAddToQueue(userId); - } - }); - } - - private Mono addToActiveSet(String userId) { - return reactiveRedisTemplate.opsForSet() - .add(USER_ACTIVE_SET_KEY, userId) - .map(i -> new RegisterUserResponse(0L)); + .filter(i -> i) + .switchIfEmpty(Mono.error(new GatewayException(GatewayErrorCode.TOO_MANY_REQUESTS))) + .thenReturn(new RegisterUserResponse(0L)); } @@ -155,14 +143,13 @@ private Mono removeInactiveUsers() { .filter(userWithScore -> currentTime - userWithScore.getScore() > INACTIVITY_THRESHOLD) .flatMap(userWithScore -> { String userId = userWithScore.getValue(); - return reactiveRedisTemplate.opsForZSet().remove(USER_QUEUE_PROCEED_KEY, userId) - .then(reactiveRedisTemplate.opsForSet().remove(USER_ACTIVE_SET_KEY, userId)); + return reactiveRedisTemplate.opsForZSet().remove(USER_QUEUE_PROCEED_KEY, userId); }) .then(); } private Mono allowUserTask() { - return reactiveRedisTemplate.opsForSet().size(USER_ACTIVE_SET_KEY) + return reactiveRedisTemplate.opsForSet().size(USER_QUEUE_PROCEED_KEY) .flatMap(activeUsers -> { long slotsAvailable = MAX_ACTIVE_USERS - activeUsers; if (slotsAvailable <= 0) { @@ -178,7 +165,7 @@ private Mono moveUsersToProceeds(long count) { .flatMap(user -> { String userId = Objects.requireNonNull(user.getValue()); return updateUserActivityTime(userId) - .then(reactiveRedisTemplate.opsForSet().add(USER_ACTIVE_SET_KEY, userId)); + .then(reactiveRedisTemplate.opsForSet().add(USER_QUEUE_PROCEED_KEY, userId)); }) .count(); } From bfbbbcae052680f05c1b7d3a2f7c6835ced1377e Mon Sep 17 00:00:00 2001 From: Kim JinSeon Date: Thu, 21 Nov 2024 17:17:55 +0900 Subject: [PATCH 6/8] =?UTF-8?q?rename:=20queue=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit proceed -> active --- .../server/application/UserQueueService.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java b/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java index edbb8de..ca349f4 100644 --- a/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java +++ b/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java @@ -22,26 +22,26 @@ public class UserQueueService { private final ReactiveRedisTemplate reactiveRedisTemplate; private final DistributedLockComponent lockComponent; private final String USER_QUEUE_WAIT_KEY = "users:queue:wait"; - private final String USER_QUEUE_PROCEED_KEY = "users:queue:proceed"; + private final String USER_QUEUE_ACTIVE_KEY = "users:queue:active"; @Value("${MAX_ACTIVE_USERS}") private long MAX_ACTIVE_USERS; private final long INACTIVITY_THRESHOLD = 300; public Mono registerUser(String userId) { return reactiveRedisTemplate.opsForZSet() - .rank(USER_QUEUE_PROCEED_KEY, userId) + .rank(USER_QUEUE_ACTIVE_KEY, userId) .defaultIfEmpty(-1L) - .flatMap(rank -> rank >= 0 ? handleProceedUser(userId) : handleNewUser(userId)); + .flatMap(rank -> rank >= 0 ? handleActiveUser(userId) : handleNewUser(userId)); } - private Mono handleProceedUser(String userId) { + private Mono handleActiveUser(String userId) { return updateUserActivityTime(userId) .thenReturn(new RegisterUserResponse(0L)); } private Mono handleNewUser(String userId) { - return reactiveRedisTemplate.opsForSet().size(USER_QUEUE_PROCEED_KEY) - .flatMap(activeUsers -> activeUsers < MAX_ACTIVE_USERS ? addToProceedQueue(userId) + return reactiveRedisTemplate.opsForSet().size(USER_QUEUE_ACTIVE_KEY) + .flatMap(activeUsers -> activeUsers < MAX_ACTIVE_USERS ? addToActiveQueue(userId) : checkAndAddToQueue(userId)); } @@ -69,7 +69,7 @@ private Mono updateWaitQueueScore(String userId) { ; } - public Mono addToProceedQueue(String userId) { + public Mono addToActiveQueue(String userId) { return Mono.create(sink -> { lockComponent.execute(userId, 1000, 1000, () -> { try { @@ -87,7 +87,7 @@ public Mono addToProceedQueue(String userId) { private Mono addUserToQueue(String userId) { var unixTime = Instant.now().getEpochSecond(); return reactiveRedisTemplate.opsForZSet() - .add(USER_QUEUE_PROCEED_KEY, userId, unixTime) + .add(USER_QUEUE_ACTIVE_KEY, userId, unixTime) .filter(i -> i) .switchIfEmpty(Mono.error(new GatewayException(GatewayErrorCode.TOO_MANY_REQUESTS))) .thenReturn(new RegisterUserResponse(0L)); @@ -108,7 +108,7 @@ private Mono addToWaitQueue(String userId) { public Mono isAllowed(String userId) { return reactiveRedisTemplate.opsForZSet() - .rank(USER_QUEUE_PROCEED_KEY, userId) + .rank(USER_QUEUE_ACTIVE_KEY, userId) .defaultIfEmpty(-1L) .map(rank -> rank >= 0) .flatMap(isAllowed -> { @@ -139,40 +139,40 @@ public void scheduleAllowUser() { private Mono removeInactiveUsers() { long currentTime = Instant.now().getEpochSecond(); return reactiveRedisTemplate.opsForZSet() - .rangeWithScores(USER_QUEUE_PROCEED_KEY, Range.closed(0L, -1L)) + .rangeWithScores(USER_QUEUE_ACTIVE_KEY, Range.closed(0L, -1L)) .filter(userWithScore -> currentTime - userWithScore.getScore() > INACTIVITY_THRESHOLD) .flatMap(userWithScore -> { String userId = userWithScore.getValue(); - return reactiveRedisTemplate.opsForZSet().remove(USER_QUEUE_PROCEED_KEY, userId); + return reactiveRedisTemplate.opsForZSet().remove(USER_QUEUE_ACTIVE_KEY, userId); }) .then(); } private Mono allowUserTask() { - return reactiveRedisTemplate.opsForSet().size(USER_QUEUE_PROCEED_KEY) + return reactiveRedisTemplate.opsForSet().size(USER_QUEUE_ACTIVE_KEY) .flatMap(activeUsers -> { long slotsAvailable = MAX_ACTIVE_USERS - activeUsers; if (slotsAvailable <= 0) { return Mono.just(0L); } - return moveUsersToProceeds(slotsAvailable); + return moveUsersToActives(slotsAvailable); }); } - private Mono moveUsersToProceeds(long count) { + private Mono moveUsersToActives(long count) { return reactiveRedisTemplate.opsForZSet() .popMin(USER_QUEUE_WAIT_KEY, count) .flatMap(user -> { String userId = Objects.requireNonNull(user.getValue()); return updateUserActivityTime(userId) - .then(reactiveRedisTemplate.opsForSet().add(USER_QUEUE_PROCEED_KEY, userId)); + .then(reactiveRedisTemplate.opsForSet().add(USER_QUEUE_ACTIVE_KEY, userId)); }) .count(); } private Mono updateUserActivityTime(String userId) { long currentTime = Instant.now().getEpochSecond(); - return reactiveRedisTemplate.opsForZSet().add(USER_QUEUE_PROCEED_KEY, userId, currentTime); + return reactiveRedisTemplate.opsForZSet().add(USER_QUEUE_ACTIVE_KEY, userId, currentTime); } } From 90259461527b8bff07fd13f3eea6836883633c8f Mon Sep 17 00:00:00 2001 From: Kim JinSeon Date: Thu, 21 Nov 2024 17:21:43 +0900 Subject: [PATCH 7/8] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=ED=81=90=20=EB=93=B1=EB=A1=9D=20=EC=84=B1=EB=8A=A5=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용자 등록 시 대기 큐 즉시 추가로 처리 - 사용자 등록 로직 단순화 --- .../server/application/UserQueueService.java | 146 +++++------------- 1 file changed, 41 insertions(+), 105 deletions(-) diff --git a/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java b/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java index ca349f4..2bde683 100644 --- a/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java +++ b/service/gateway/server/src/main/java/com/sparta/gateway/server/application/UserQueueService.java @@ -19,91 +19,26 @@ @RequiredArgsConstructor public class UserQueueService { + private static final String USER_QUEUE_WAIT_KEY = "users:queue:wait"; + private static final String USER_QUEUE_ACTIVE_KEY = "users:queue:active"; + private static final long INACTIVITY_THRESHOLD = 300; + private final ReactiveRedisTemplate reactiveRedisTemplate; - private final DistributedLockComponent lockComponent; - private final String USER_QUEUE_WAIT_KEY = "users:queue:wait"; - private final String USER_QUEUE_ACTIVE_KEY = "users:queue:active"; + @Value("${MAX_ACTIVE_USERS}") private long MAX_ACTIVE_USERS; - private final long INACTIVITY_THRESHOLD = 300; - - public Mono registerUser(String userId) { - return reactiveRedisTemplate.opsForZSet() - .rank(USER_QUEUE_ACTIVE_KEY, userId) - .defaultIfEmpty(-1L) - .flatMap(rank -> rank >= 0 ? handleActiveUser(userId) : handleNewUser(userId)); - } - - private Mono handleActiveUser(String userId) { - return updateUserActivityTime(userId) - .thenReturn(new RegisterUserResponse(0L)); - } - - private Mono handleNewUser(String userId) { - return reactiveRedisTemplate.opsForSet().size(USER_QUEUE_ACTIVE_KEY) - .flatMap(activeUsers -> activeUsers < MAX_ACTIVE_USERS ? addToActiveQueue(userId) - : checkAndAddToQueue(userId)); - } - - - private Mono checkAndAddToQueue(String userId) { - return reactiveRedisTemplate.opsForZSet().score(USER_QUEUE_WAIT_KEY, userId) - .defaultIfEmpty(-1.0) - .flatMap(score -> { - if (score >= 0) { - return updateWaitQueueScore(userId); - } else { - return addToWaitQueue(userId); - } - }); - } - - private Mono updateWaitQueueScore(String userId) { - double newScore = Instant.now().getEpochSecond(); - return reactiveRedisTemplate.opsForZSet().score(USER_QUEUE_WAIT_KEY, userId) - .flatMap(oldScore -> - reactiveRedisTemplate.opsForZSet().add(USER_QUEUE_WAIT_KEY, userId, newScore) - .then(reactiveRedisTemplate.opsForZSet().rank(USER_QUEUE_WAIT_KEY, userId)) - ) - .map(rank -> new RegisterUserResponse(rank + 1)) - ; - } - public Mono addToActiveQueue(String userId) { - return Mono.create(sink -> { - lockComponent.execute(userId, 1000, 1000, () -> { - try { - addUserToQueue(userId) - .doOnSuccess(sink::success) - .doOnError(sink::error) - .subscribe(); - } catch (Exception e) { - sink.error(e); - } - }); - }); + private long getCurrentTime() { + return Instant.now().getEpochSecond(); } - private Mono addUserToQueue(String userId) { - var unixTime = Instant.now().getEpochSecond(); - return reactiveRedisTemplate.opsForZSet() - .add(USER_QUEUE_ACTIVE_KEY, userId, unixTime) - .filter(i -> i) - .switchIfEmpty(Mono.error(new GatewayException(GatewayErrorCode.TOO_MANY_REQUESTS))) - .thenReturn(new RegisterUserResponse(0L)); - } - - - private Mono addToWaitQueue(String userId) { - var unixTime = Instant.now().getEpochSecond(); + public Mono registerUser(String userId) { return reactiveRedisTemplate.opsForZSet() - .add(USER_QUEUE_WAIT_KEY, userId, unixTime) - .filter(i -> i) - .switchIfEmpty(Mono.error(new GatewayException(GatewayErrorCode.TOO_MANY_REQUESTS))) - .flatMap(i -> reactiveRedisTemplate.opsForZSet() - .rank(USER_QUEUE_WAIT_KEY, userId)) - .map(rank -> new RegisterUserResponse(rank + 1)) - ; + .add(USER_QUEUE_WAIT_KEY, userId, getCurrentTime()) + .flatMap(success -> + reactiveRedisTemplate.opsForZSet().rank(USER_QUEUE_WAIT_KEY, userId) + ) + .map(rank -> new RegisterUserResponse(rank + 1)); } public Mono isAllowed(String userId) { @@ -111,51 +46,49 @@ public Mono isAllowed(String userId) { .rank(USER_QUEUE_ACTIVE_KEY, userId) .defaultIfEmpty(-1L) .map(rank -> rank >= 0) - .flatMap(isAllowed -> { - if (isAllowed) { - return updateUserActivityTime(userId).thenReturn(true); - } - return Mono.just(false); - }); + .flatMap(isAllowed -> isAllowed ? + updateUserActivityTime(userId).thenReturn(true) : + Mono.just(false) + ); } public Mono getRank(String userId) { - return reactiveRedisTemplate.opsForZSet().rank(USER_QUEUE_WAIT_KEY, userId) + return reactiveRedisTemplate.opsForZSet() + .rank(USER_QUEUE_WAIT_KEY, userId) .defaultIfEmpty(-1L) .map(rank -> rank >= 0 ? rank + 1 : rank); } - @Scheduled(fixedRate = 30000) + @Scheduled(fixedRate = 10000, initialDelay = 500) public void scheduleAllowUser() { removeInactiveUsers() .then(allowUserTask()) - .subscribe( - movedUsers -> { - }, - error -> log.error(GatewayErrorCode.INTERNAL_SERVER_ERROR.getMessage(), error) - ); + .subscribe(); } private Mono removeInactiveUsers() { - long currentTime = Instant.now().getEpochSecond(); + long currentTime = getCurrentTime(); return reactiveRedisTemplate.opsForZSet() .rangeWithScores(USER_QUEUE_ACTIVE_KEY, Range.closed(0L, -1L)) .filter(userWithScore -> currentTime - userWithScore.getScore() > INACTIVITY_THRESHOLD) - .flatMap(userWithScore -> { - String userId = userWithScore.getValue(); - return reactiveRedisTemplate.opsForZSet().remove(USER_QUEUE_ACTIVE_KEY, userId); - }) + .flatMap(userWithScore -> removeUser(userWithScore.getValue())) + .then(); + } + + private Mono removeUser(String userId) { + return reactiveRedisTemplate.opsForZSet() + .remove(USER_QUEUE_ACTIVE_KEY, userId) .then(); } private Mono allowUserTask() { - return reactiveRedisTemplate.opsForSet().size(USER_QUEUE_ACTIVE_KEY) + return reactiveRedisTemplate.opsForZSet() + .size(USER_QUEUE_ACTIVE_KEY) .flatMap(activeUsers -> { long slotsAvailable = MAX_ACTIVE_USERS - activeUsers; - if (slotsAvailable <= 0) { - return Mono.just(0L); - } - return moveUsersToActives(slotsAvailable); + return slotsAvailable <= 0 ? + Mono.just(0L) : + moveUsersToActives(slotsAvailable); }); } @@ -164,15 +97,18 @@ private Mono moveUsersToActives(long count) { .popMin(USER_QUEUE_WAIT_KEY, count) .flatMap(user -> { String userId = Objects.requireNonNull(user.getValue()); - return updateUserActivityTime(userId) - .then(reactiveRedisTemplate.opsForSet().add(USER_QUEUE_ACTIVE_KEY, userId)); + return reactiveRedisTemplate.opsForZSet() + .add(USER_QUEUE_ACTIVE_KEY, userId, getCurrentTime()) + .filter(Boolean::booleanValue) + .switchIfEmpty(Mono.error(new GatewayException(GatewayErrorCode.TOO_MANY_REQUESTS))) + .thenReturn(1L); }) .count(); } private Mono updateUserActivityTime(String userId) { - long currentTime = Instant.now().getEpochSecond(); - return reactiveRedisTemplate.opsForZSet().add(USER_QUEUE_ACTIVE_KEY, userId, currentTime); + return reactiveRedisTemplate.opsForZSet() + .add(USER_QUEUE_ACTIVE_KEY, userId, getCurrentTime()); } } From 949efec32c5d4198c0656edd91440de6c0857f6c Mon Sep 17 00:00:00 2001 From: Kim JinSeon Date: Thu, 21 Nov 2024 17:24:45 +0900 Subject: [PATCH 8/8] =?UTF-8?q?delete:=20redisson=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/DistributedLockComponent.java | 47 ------------------- .../configuration/RedissonConfig.java | 30 ------------ 2 files changed, 77 deletions(-) delete mode 100644 service/gateway/server/src/main/java/com/sparta/gateway/server/application/DistributedLockComponent.java delete mode 100644 service/gateway/server/src/main/java/com/sparta/gateway/server/infrastructure/configuration/RedissonConfig.java diff --git a/service/gateway/server/src/main/java/com/sparta/gateway/server/application/DistributedLockComponent.java b/service/gateway/server/src/main/java/com/sparta/gateway/server/application/DistributedLockComponent.java deleted file mode 100644 index f9a72f3..0000000 --- a/service/gateway/server/src/main/java/com/sparta/gateway/server/application/DistributedLockComponent.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.sparta.gateway.server.application; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.annotation.Aspect; -import org.redisson.api.RLock; -import org.redisson.api.RedissonClient; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; - -@Aspect -@Component -@RequiredArgsConstructor -@Slf4j(topic = "DistributedLockComponent") -public class DistributedLockComponent { - - private final RedissonClient redissonClient; - - public void execute( - String lockName, long waitMilliSecond, long leaseMilliSecond, Runnable logic) { - RLock lock = redissonClient.getLock(lockName); - try { - boolean isLocked = lock.tryLock(waitMilliSecond, leaseMilliSecond, TimeUnit.MILLISECONDS); - if (!isLocked) { - throw new IllegalStateException("[" + lockName + "] lock 획득 실패"); - } - logic.run(); - } catch (InterruptedException e) { - log.error(e.getMessage(), e); - throw new RuntimeException(e); - } finally { - if (lock.isHeldByCurrentThread()) { - lock.unlock(); - } - } - } - - - @Bean - public ExecutorService customThreadPool() { - return Executors.newFixedThreadPool(10); - } - -} diff --git a/service/gateway/server/src/main/java/com/sparta/gateway/server/infrastructure/configuration/RedissonConfig.java b/service/gateway/server/src/main/java/com/sparta/gateway/server/infrastructure/configuration/RedissonConfig.java deleted file mode 100644 index 2c4fbb4..0000000 --- a/service/gateway/server/src/main/java/com/sparta/gateway/server/infrastructure/configuration/RedissonConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.sparta.gateway.server.infrastructure.configuration; - -import lombok.extern.slf4j.Slf4j; -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.config.Config; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@Slf4j -public class RedissonConfig { - - private static final String REDIS_URL_PREFIX = "redis://"; - - @Value("${spring.data.redis.host}") - private String host; - - @Value("${spring.data.redis.port}") - private int port; - - @Bean - RedissonClient redissonClient() { - Config config = new Config(); - config.useSingleServer().setAddress(REDIS_URL_PREFIX + host + ":" + port); - return Redisson.create(config); - } - -}