Skip to content

Commit d92817b

Browse files
authored
feat: Dynamic agent registeration & multiple stomp topic & scheduler feature (#60)
* dev: change static css/styles.css path in html * dev: temporary add Randomized TestResult SSE event * dev: css textarea style add * dev: json mapper method add * dev: CommonTestResult class name change * dev: CommonTestResult class name change * dev: invalid json string errorcode * dev: TemplateInfo getting service * dev: util for verify accessable template * dev: add all throws JsonParsing exception * dev: add prepareScript & header field * dev: jsonMapper pre-defining * dev: pre-check invalid json body * dev: headers & prepareScript entity field add * dev: remove unused test and add exception * dev: JsonProcessingException add * dev: remove random method and add vuser, maxRequest, maxDuration field * dev: TestResult class change * dev: pre-build empty template * dev: add front design and functional method * dev: http load sender with multi threads * dev: dns resolver apple silicon dependency * dev: change dto field * dev: additional method and comments * dev: apply changed field * chore: apply changed field * dev: health check endpoint * dev: RequestSpec setter * dev: child thread with parent thread dependency setting * dev: child thread managing method * dev: TPS, MTTFB percentile consts * dev: TestResult scheduled & async-nonblocking http sender & emitter update * test: add mockServer test * fix: java syntax error * dev: split service & controller * dev: pr comment merge & test remove * dev: test exclude setting * dev: add jacoco perfTest within gradle * dev: sse mocking response method ! * dev: test code with sse mock response & message event * dev: schedulerManager duplication/shutdown test * dev: requestHeadersSpec test * dev: HealthCheck controller test * dev: agent information dto * dev: agent status manager setup * dev: agent status manager testing * dev: agent system scheduler * dev: test with cpu, memory, url, etc. * dev: system scheduler constant * dev: encapsulate method * chore: remove system print * dev: maxRequest maxDuration validator * chore: const test exclude * test: sendRequest wrong url, duration null * test: AgentInfo endpoint * dev: eureka bm-agent discovery service * dev: netflix eureka and actuator setup * dev: eureka app-name change clarify * dev: discovery current bm-agent finder scheduler * dev: exit code * dev: connect bm-controller method * chore: logging error * dev: scheduler task * dev: token provider impl. and test * dev: header prefix * dev: regular system constant * dev: append agentStatusManager to constructor * test: performance, initial, sender test * dev: commandLineRunner for connect to bm-controller * dev: lock performance test agent when it is running * dev: pagination api * dev: agent status READY when task is done * chore: remove unused test * dev: reconfigure agent manager * test: restapi endpoint change and apply to it * dev: agent listener * dev: performance test lock * dev: adjust test code * dev: always on ready state * dev: version setup * dev: docker composing configure * chore: remove unused option * dev: th fragment header * dev: th fragment showing agent status with ws * dev: scheduler for send ws events * dev: add header&agentStatus fragments * dev: stomp connection with 3 topics * dev: styling agent status shower * dev: clarify path variable values * dev: agent status update, emitter completion * chore: apply meaningful field name
1 parent 8733708 commit d92817b

File tree

64 files changed

+1532
-291
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1532
-291
lines changed

bm-agent/build.gradle

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,32 @@ plugins {
77
}
88

99
group = 'org.benchmarker'
10-
version = '0.0.1-SNAPSHOT'
10+
version = '1.0.0'
1111

1212
def excludeJacocoTestCoverageReport = [
1313
// bmagent
1414
'org/benchmarker/bmagent/BmAgentApplication**',
1515
'org/benchmarker/bmagent/sse/**',
1616
'org/benchmarker/bmagent/consts/SystemSchedulerConst',
17+
'org/benchmarker/bmagent/status/**',
18+
'org/benchmarker/bmagent/initializer/**',
1719
// 'org/benchmarker/bmagent/**',
1820
]
1921

2022
java {
2123
sourceCompatibility = '17'
2224
}
2325

26+
ext {
27+
set('springCloudVersion', "2023.0.0")
28+
}
29+
30+
dependencyManagement {
31+
imports {
32+
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
33+
}
34+
}
35+
2436
jacoco {
2537
toolVersion = "0.8.8"
2638
}
@@ -41,16 +53,23 @@ dependencies {
4153
implementation 'org.projectlombok:lombok'
4254
annotationProcessor 'org.projectlombok:lombok'
4355
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
56+
implementation 'org.springframework.boot:spring-boot-starter-actuator'
4457
// add webflux
4558
implementation 'org.springframework.boot:spring-boot-starter-webflux'
4659
testImplementation 'org.springframework.boot:spring-boot-starter-test'
4760
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
4861
testImplementation 'org.testcontainers:junit-jupiter'
4962
testImplementation 'com.squareup.okhttp3:mockwebserver:4.9.1'
63+
64+
implementation "io.jsonwebtoken:jjwt-api:0.11.1"
65+
runtimeOnly "io.jsonwebtoken:jjwt-impl:0.11.1"
66+
runtimeOnly "io.jsonwebtoken:jjwt-jackson:0.11.1"
5067
if (isAppleSilicon()) {
5168
runtimeOnly("io.netty:netty-resolver-dns-native-macos:4.1.94.Final:osx-aarch_64")
5269
}
5370

71+
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
72+
5473
implementation project(':bm-common')
5574
}
5675

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
package org.benchmarker.bmagent;
22

3+
import org.springframework.boot.ExitCodeGenerator;
34
import org.springframework.boot.SpringApplication;
45
import org.springframework.boot.autoconfigure.SpringBootApplication;
6+
import org.springframework.context.annotation.Bean;
57

68
@SpringBootApplication
79
public class BmAgentApplication {
10+
@Bean
11+
public ExitCodeGenerator exitCodeGenerator() {
12+
return new ExitCodeGenerator() {
13+
@Override
14+
public int getExitCode() {
15+
return 42;
16+
}
17+
};
18+
}
819

920
public static void main(String[] args) {
21+
System.setProperty("spring.application.name","bm-agent");
1022
SpringApplication.run(BmAgentApplication.class, args);
1123
}
12-
1324
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.benchmarker.bmagent.consts;
2+
3+
public interface HeaderConst {
4+
String bearerPrefix = "Bearer ";
5+
String tokenKey = "Authorization";
6+
7+
}

bm-agent/src/main/java/org/benchmarker/bmagent/consts/SystemSchedulerConst.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ public interface SystemSchedulerConst {
77
*/
88
Long systemSchedulerId = -100L;
99
String systemUsageSchedulerName = "cpu-memory-usage-update";
10+
Integer connectControllerTimeout = 10; // seconds
11+
Integer connectionFailedLimit = 50;
12+
1013

1114
}

bm-agent/src/main/java/org/benchmarker/bmagent/controller/AgentApiController.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import jakarta.servlet.http.HttpServletRequest;
55
import java.util.Map;
6+
import java.util.Set;
67
import lombok.RequiredArgsConstructor;
78
import lombok.extern.slf4j.Slf4j;
89
import org.benchmarker.bmagent.AgentInfo;
@@ -42,15 +43,16 @@ public class AgentApiController {
4243
* @param action String
4344
* @return SseEmitter
4445
*/
45-
@PostMapping("/templates/{template_id}")
46+
@PostMapping("/groups/{group_id}/templates/{template_id}")
4647
public SseEmitter manageSSE(@PathVariable("template_id") Long templateId,
48+
@PathVariable("group_id") String groupId,
4749
@RequestParam("action") String action, @RequestBody TemplateInfo templateInfo) {
4850
log.info(templateInfo.toString());
49-
agentStatusManager.getAndUpdateStatusIfReady(
50-
AgentStatus.TESTING).orElseThrow(() -> new RuntimeException("agent is not ready"));
5151

5252
if (action.equals("start")) {
53-
return sseManageService.start(templateId, templateInfo);
53+
agentStatusManager.getAndUpdateStatusIfReady(
54+
AgentStatus.TESTING).orElseThrow(() -> new RuntimeException("agent is not ready"));
55+
return sseManageService.start(templateId, groupId, templateInfo);
5456
} else {
5557
sseManageService.stop(templateId);
5658
return null;
@@ -73,10 +75,12 @@ public AgentInfo getStatus() {
7375
String scheme = request.getScheme(); // http or https
7476
String serverName = request.getServerName();
7577
int serverPort = request.getServerPort();
78+
Set<Long> longs = scheduledTaskService.getStatus().keySet();
7679

7780
String agentServerUrl = scheme + "://" + serverName + ":" + serverPort;
7881

7982
return AgentInfo.builder()
83+
.templateId(longs)
8084
.cpuUsage(agentStatusManager.getCpuUsage())
8185
.memoryUsage(agentStatusManager.getMemoryUsage())
8286
.startedAt(agentStatusManager.getStartedAt())

bm-agent/src/main/java/org/benchmarker/bmagent/initializer/Initializer.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,39 @@
22

33
import java.util.concurrent.TimeUnit;
44
import lombok.RequiredArgsConstructor;
5+
import lombok.extern.slf4j.Slf4j;
56
import org.benchmarker.bmagent.consts.SystemSchedulerConst;
67
import org.benchmarker.bmagent.schedule.ScheduledTaskService;
78
import org.benchmarker.bmagent.status.AgentStatusManager;
9+
import org.springframework.beans.BeansException;
810
import org.springframework.boot.CommandLineRunner;
11+
import org.springframework.context.ApplicationContext;
12+
import org.springframework.context.ApplicationContextAware;
13+
import org.springframework.context.annotation.Profile;
914
import org.springframework.stereotype.Component;
1015

1116
@Component
17+
@Profile("!test")
18+
@Slf4j
1219
@RequiredArgsConstructor
13-
public class Initializer implements CommandLineRunner {
20+
public class Initializer implements CommandLineRunner, ApplicationContextAware {
1421

1522
private final ScheduledTaskService scheduledTaskService;
1623
private final AgentStatusManager agentStatusManager;
24+
private ApplicationContext applicationContext;
25+
1726

1827
@Override
1928
public void run(String... args) throws Exception {
29+
log.info("init");
2030
// cpu, memory usage checker
2131
scheduledTaskService.startChild(SystemSchedulerConst.systemSchedulerId,
2232
SystemSchedulerConst.systemUsageSchedulerName, agentStatusManager::updateStats, 0, 1,
2333
TimeUnit.SECONDS);
2434
}
35+
36+
@Override
37+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
38+
this.applicationContext = applicationContext;
39+
}
2540
}

bm-agent/src/main/java/org/benchmarker/bmagent/pref/HttpSender.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
import java.util.stream.IntStream;
1818
import lombok.Getter;
1919
import lombok.extern.slf4j.Slf4j;
20+
import org.benchmarker.bmagent.AgentStatus;
2021
import org.benchmarker.bmagent.service.IScheduledTaskService;
22+
import org.benchmarker.bmagent.status.AgentStatusManager;
2123
import org.benchmarker.bmagent.util.WebClientSupport;
2224
import org.benchmarker.bmcommon.dto.TemplateInfo;
2325
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
26+
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
2427
import reactor.core.publisher.Mono;
2528

2629
/**
@@ -35,14 +38,17 @@ public class HttpSender {
3538
private final ResultManagerService resultManagerService;
3639
private final IScheduledTaskService scheduledTaskService;
3740

41+
private final AgentStatusManager agentStatusManager;
42+
3843

3944
public HttpSender(ResultManagerService resultManagerService,
40-
IScheduledTaskService scheduledTaskService) {
45+
IScheduledTaskService scheduledTaskService, AgentStatusManager agentStatusManager) {
4146
this.resultManagerService = resultManagerService;
4247
this.scheduledTaskService = scheduledTaskService;
48+
this.agentStatusManager = agentStatusManager;
4349
}
4450

45-
private Integer defaultMaxRequestsPerUser = 100000000;
51+
private Integer defaultMaxRequestsPerUser = Integer.MAX_VALUE;
4652
private Integer defaultMaxDuration = 5; // 5 hours
4753
private AtomicInteger totalRequests = new AtomicInteger(0);
4854
private AtomicInteger totalSuccess = new AtomicInteger(0);
@@ -66,7 +72,8 @@ public HttpSender(ResultManagerService resultManagerService,
6672
*
6773
* @param templateInfo {@link TemplateInfo}
6874
*/
69-
public void sendRequests(TemplateInfo templateInfo) throws MalformedURLException {
75+
public void sendRequests(SseEmitter sseEmitter, TemplateInfo templateInfo) throws MalformedURLException {
76+
7077
URL url = new URL(templateInfo.getUrl());
7178
RequestHeadersSpec<?> req = WebClientSupport.create(templateInfo.getMethod(),
7279
templateInfo.getUrl(),
@@ -85,16 +92,20 @@ public void sendRequests(TemplateInfo templateInfo) throws MalformedURLException
8592
}
8693

8794
Duration duration = templateInfo.getMaxDuration();
95+
log.info("Now send multiple HTTP request to target server");
96+
log.info(templateInfo.toString());
8897

8998
// Future setup
9099
futures = IntStream.range(0, templateInfo.getVuser())
91100
.mapToObj(i -> CompletableFuture.runAsync(() -> {
92101
long startTime = System.currentTimeMillis(); // 시작 시간 기록
93102
long endTime = startTime + duration.toMillis();
103+
94104
for (int j = 0; j < templateInfo.getMaxRequest(); j++) {
95105
// 만약 running 이 아니거나 시간이 끝났다면,
96106
if (!isRunning || System.currentTimeMillis() > endTime) {
97-
return;
107+
agentStatusManager.updateAgentStatus(AgentStatus.READY);
108+
break;
98109
}
99110
long requestStartTime = System.currentTimeMillis(); // 요청 시작 시간 기록
100111
req.exchangeToMono(resp -> {
@@ -192,6 +203,7 @@ public Map<Double, Double> calculateMttfbPercentile(List<Double> percentile) {
192203
* downstream 에서만 제거 가능. 외부에서는 cancel 불가능...
193204
*/
194205
public void cancelRequests() {
206+
log.info("cancel requests");
195207
isRunning = false;
196208

197209
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.benchmarker.bmagent.security;
2+
3+
4+
import io.jsonwebtoken.Claims;
5+
import io.jsonwebtoken.Jwts;
6+
import io.jsonwebtoken.SignatureAlgorithm;
7+
import java.util.Date;
8+
import lombok.Setter;
9+
import lombok.extern.slf4j.Slf4j;
10+
import org.springframework.beans.factory.annotation.Value;
11+
import org.springframework.stereotype.Component;
12+
13+
/**
14+
* {@link JwtTokenProvider} class is used to create Json Web Token.
15+
*/
16+
@Slf4j
17+
@Component
18+
@Setter
19+
public class JwtTokenProvider {
20+
21+
/**
22+
* Access token's expiration time
23+
*/
24+
public static String expirationTime;
25+
26+
/**
27+
* Refresh token's expiration time
28+
*/
29+
public static String refreshExpirationTime;
30+
31+
/**
32+
* Secret key for signing the token
33+
*/
34+
public static String secret;
35+
36+
@Value("${token.expiration_time}")
37+
private void setExpirationTime(String expirationTime) {
38+
JwtTokenProvider.expirationTime = expirationTime;
39+
}
40+
41+
@Value("${token.refresh_expiration_time}")
42+
private void setRefreshExpirationTime(String refreshExpirationTime) {
43+
JwtTokenProvider.refreshExpirationTime = refreshExpirationTime;
44+
}
45+
46+
@Value("${token.secret}")
47+
private void setSecret(String secret) {
48+
JwtTokenProvider.secret = secret;
49+
}
50+
51+
52+
/**
53+
* Create Json Web Token with user's username and authorities
54+
*
55+
* <p> Here, this method generate accessToken </p>
56+
* <p> JWT's payload will looks like below</p>
57+
* <blockquote><pre>
58+
* {
59+
* "sub": "agent"
60+
* }
61+
* </pre></blockquote>
62+
*
63+
* @return Json-web-token
64+
*/
65+
public static String createAccessToken() {
66+
Claims claims = Jwts.claims().setSubject("agent");
67+
68+
Long expirationTimeLong = Long.parseLong(expirationTime);
69+
final Date createdDate = new Date();
70+
final Date expirationDate = new Date(createdDate.getTime() + expirationTimeLong);
71+
return Jwts.builder()
72+
.setClaims(claims)
73+
.setSubject("agent")
74+
.setIssuedAt(createdDate)
75+
.setExpiration(expirationDate)
76+
.signWith(SignatureAlgorithm.HS512, secret)
77+
.compact();
78+
}
79+
}

bm-agent/src/main/java/org/benchmarker/bmagent/service/ISseManageService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public interface ISseManageService extends SseManageConsts {
1919
* @param templateInfo TemplateInfo
2020
* @return SseEmitter
2121
*/
22-
SseEmitter start(Long id, TemplateInfo templateInfo);
22+
SseEmitter start(Long id, String groupId, TemplateInfo templateInfo);
2323

2424
/**
2525
* Stop the SSE emitter for the given id

0 commit comments

Comments
 (0)