Skip to content

Commit ed48afb

Browse files
committed
feat: add native agent monomer mode
1 parent 2858f6e commit ed48afb

26 files changed

+531
-91
lines changed

labs/cluster-management/README.md

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,65 @@
11

22
## Arthas Native Agent - 集群管理
3-
4-
![](images/cluster_management.png)
53

6-
# 快速开始
4+
5+
6+
# 快速开始 - 单体模式(适用快速体验)
7+
8+
![](asserts/cluster_management_single.png)
9+
## 单体模式启动native-agent-management-web
10+
native-agent的管理页面
11+
启动参数
12+
13+
| 参数 | 必填 | 解释 |
14+
|----------------------|-----|-------------------------------------|
15+
| port | N | http端口 ,默认3939 |
16+
17+
example:
18+
```shell
19+
java -jar native-agent-management-web.jar
20+
```
21+
22+
## 单体模式启动native-agent-proxy
23+
proxy会向native-agent-management-web注册自己
24+
25+
| 参数 | 必填 | 解释 |
26+
|--------------------|-----|--------------------------------------|
27+
| port | N | http/ws端口 ,默认2233 |
28+
| ip | Y | proxy的ip |
29+
| management-address | Y | native-agent-manangement-web的地址,用于注册 |
30+
```shell
31+
java -jar native-agent-proxy.jar --ip 127.0.0.1 --management-address 127.0.0.1:3939
32+
```
33+
34+
## 单体模式启动native-agent
35+
native-agent,启动在需要动态attach的服务器上。同时需要依赖arthas-agent。请把arthas-agent、arthas-core、arthas-spy放在和native-agent同一个目录下。
36+
37+
| 参数 | 必填 | 解释 |
38+
|---------------|-----|----------------------------|
39+
| http-port | N | http端口 ,默认2671 |
40+
| ws-port | N | ws端口,默认2672 |
41+
| ip | Y | native-agent的ip |
42+
| proxy-address | Y | native-agent-proxy的地址,用于注册 |
43+
```shell
44+
java -jar native-agent.jar --ip 127.0.0.1 --proxy-address 127.0.0.1 :2233
45+
```
46+
47+
48+
49+
50+
# 集群模式
51+
![](asserts/cluster_management.png)
752

853
## 启动native-agent
9-
native-agent,启动在需要动态attach的服务器上
54+
native-agent,启动在需要动态attach的服务器上。同时需要依赖arthas-agent。请把arthas-agent、arthas-core、arthas-spy放在和native-agent同一个目录下。
55+
1056
启动参数
1157

1258
| 参数 | 必填 | 解释 |
1359
|----------------------|-----|-------------------------------------|
1460
| http-port | N | http端口 ,默认2671 |
1561
| ws-port | N | ws端口,默认2672 |
62+
| ip | Y | native-agent的ip地址 |
1663
| registration-typ | Y | 注册中心类型(目前实现的有etcd和zookeeper,推荐etcd) |
1764
| registration-address | Y | 注册中心的地址 |
1865

@@ -57,11 +104,11 @@ java -jar native-agent-management-web.jar --registration-type etcd --registrati
57104

58105
## 监控指定JVM
59106
进入native-agent-server管理页面,点击VIEW JAVA PROCESS INFO 按钮,可以查看到当前服务器上的Java进程
60-
![](images/native_agent_list.png)
107+
![](asserts/native_agent_list.png)
61108
进入到Java进程页后,我们可以点击Monitor按钮,Monitor目标Java进程
62-
![](images/native_agent_java_process_page.png)
109+
![](asserts/native_agent_java_process_page.png)
63110
之后点击MONITOR按钮就可以进入到监控界面了
64-
![](images/native_agent_moniotr_page.png)
111+
![](asserts/native_agent_moniotr_page.png)
65112

66113
# 扩展注册中心
67114
目前实现的有zookeeper和etcd,如果想要扩展注册中心,可以看看下面的实现。下面演示的是native-agent-management-web的扩展,其他也是同样的道理。
Loading

labs/cluster-management/native-agent-common/src/main/java/com/alibaba/arthas/nat/agent/common/utils/OkHttpUtil.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,18 @@ public static String post(String url, String json) throws IOException {
4343
}
4444
}
4545

46+
47+
public static Response postAndResponse(String url, String json) throws IOException {
48+
RequestBody body = RequestBody.create(json, JSON);
49+
Request request = new Request.Builder()
50+
.url(url)
51+
.post(body)
52+
.header("Content-Type", "application/json")
53+
.build();
54+
55+
try (Response response = client.newCall(request).execute()) {
56+
return response;
57+
}
58+
}
59+
4660
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.alibaba.arthas.nat.agent.management.web.discovery.impl;
2+
3+
import com.alibaba.arthas.nat.agent.management.web.discovery.NativeAgentProxyDiscovery;
4+
5+
import java.time.LocalDateTime;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.concurrent.ConcurrentHashMap;
10+
import java.util.concurrent.Executors;
11+
import java.util.concurrent.ScheduledExecutorService;
12+
import java.util.concurrent.TimeUnit;
13+
14+
/**
15+
* @description: NativeAgentManagementNativeAgentProxyDiscovery(Σ(っ °Д °;)っ 好长的类名)
16+
* @author:flzjkl
17+
* @date: 2024-10-29 20:59
18+
*/
19+
public class NativeAgentManagementNativeAgentProxyDiscovery implements NativeAgentProxyDiscovery {
20+
21+
/**
22+
* key: native agent ip : http port : ws port , value: expiration time
23+
*/
24+
private static Map<String, LocalDateTime> nativeAgentProxyMap = new ConcurrentHashMap<>();
25+
private final static int INITIAL_DELAY_SECONDS = 5;
26+
private final static int PERIOD_SECONDS = 5;
27+
28+
public static void storageNativeAgent(String address, LocalDateTime expirationTime) {
29+
nativeAgentProxyMap.put(address, expirationTime);
30+
}
31+
32+
public static void nativeAgentProxyCheckScheduled () {
33+
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
34+
Runnable task = () -> {
35+
LocalDateTime now = LocalDateTime.now();
36+
nativeAgentProxyMap.forEach((key, expirationTime) ->{
37+
if (now.isAfter(expirationTime)) {
38+
nativeAgentProxyMap.remove(key);
39+
}
40+
});
41+
};
42+
scheduler.scheduleAtFixedRate(task, INITIAL_DELAY_SECONDS, PERIOD_SECONDS, TimeUnit.SECONDS);
43+
}
44+
45+
@Override
46+
public List<String> listNativeAgentProxy(String address) {
47+
return new ArrayList<>(nativeAgentProxyMap.keySet());
48+
}
49+
}

labs/cluster-management/native-agent-management-web/src/main/java/com/alibaba/arthas/nat/agent/management/web/server/NativeAgentManagementWebBootstrap.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.alibaba.arthas.nat.agent.common.constants.NativeAgentConstants;
44
import com.alibaba.arthas.nat.agent.common.utils.WelcomeUtil;
5+
import com.alibaba.arthas.nat.agent.management.web.discovery.impl.NativeAgentManagementNativeAgentProxyDiscovery;
56
import com.alibaba.arthas.nat.agent.management.web.server.http.HttpRequestHandler;
67

78
import com.taobao.middleware.cli.CLI;
@@ -23,14 +24,14 @@
2324
import java.util.Arrays;
2425

2526
/**
26-
* @description: native agent server
27+
* @description: native agent management web
2728
* @author:flzjkl
2829
* @date: 2024-07-20 9:23
2930
*/
30-
3131
@Name("arthas-native-agent-management-web")
3232
@Summary("Bootstrap Arthas Native Management Web")
33-
@Description("EXAMPLES:\n" + " java -jar native-agent-management-web.jar --registration-type etcd --registration-address 161.169.97.114:2379\n"
33+
@Description("EXAMPLES:\n" + "java -jar native-agent-management-web.jar\n"
34+
+ "java -jar native-agent-management-web.jar --registration-type etcd --registration-address 161.169.97.114:2379\n"
3435
+ "java -jar native-agent-management-web.jar --port 3939 --registration-type etcd --registration-address 161.169.97.114:2379\n"
3536
+ "https://arthas.aliyun.com/doc\n")
3637
public class NativeAgentManagementWebBootstrap {
@@ -46,13 +47,13 @@ public void setPort(Integer port) {
4647
this.port = port;
4748
}
4849

49-
@Option(longName = "registration-type", required = true)
50+
@Option(longName = "registration-type")
5051
@Description("registration type")
5152
public void setRegistrationType(String registrationType) {
5253
this.registrationType = registrationType;
5354
}
5455

55-
@Option(longName = "registration-address", required = true)
56+
@Option(longName = "registration-address")
5657
@Description("registration address")
5758
public void setRegistrationAddress(String registrationAddress) {
5859
this.registrationAddress = registrationAddress;
@@ -75,6 +76,17 @@ public static void main(String[] args) {
7576
}
7677
logger.info("read input success!");
7778

79+
logger.info("check bootstrap params ...");
80+
boolean checkBootstrapParamsRes = checkBootstrapParams(nativeAgentManagementWebBootstrap);
81+
if (!checkBootstrapParamsRes) {
82+
throw new RuntimeException("Failed to verify the bootstrap parameters. " +
83+
"Please read the documentation and check the parameters you entered");
84+
}
85+
if (nativeAgentManagementWebBootstrap.getRegistrationType() == null && nativeAgentManagementWebBootstrap.getRegistrationAddress() == null) {
86+
nativeAgentManagementWebBootstrap.setRegistrationType("native-agent-management");
87+
nativeAgentManagementWebBootstrap.setRegistrationAddress("127.0.0,1:" + nativeAgentManagementWebBootstrap.getPortOrDefault());
88+
NativeAgentManagementNativeAgentProxyDiscovery.nativeAgentProxyCheckScheduled();
89+
}
7890
// Start the http server
7991
logger.info("start the http server... httPort:{}", nativeAgentManagementWebBootstrap.getPortOrDefault());
8092
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
@@ -106,6 +118,20 @@ protected void initChannel(SocketChannel ch) {
106118
}
107119
}
108120

121+
private static boolean checkBootstrapParams(NativeAgentManagementWebBootstrap managementBootstrap) {
122+
String address = managementBootstrap.getRegistrationAddress();
123+
String type = managementBootstrap.getRegistrationType();
124+
// single
125+
if (address == null && type == null) {
126+
return true;
127+
}
128+
// cluster
129+
if (address != null && type != null) {
130+
return true;
131+
}
132+
return false;
133+
}
134+
109135
public int getPortOrDefault() {
110136
if (this.port == null) {
111137
return DEFAULT_NATIVE_AGENT_MANAGEMENT_WEB_PORT;

labs/cluster-management/native-agent-management-web/src/main/java/com/alibaba/arthas/nat/agent/management/web/server/http/HttpNativeAgentProxyHandler.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package com.alibaba.arthas.nat.agent.management.web.server.http;
22

33
import com.alibaba.arthas.nat.agent.management.web.discovery.NativeAgentProxyDiscovery;
4+
import com.alibaba.arthas.nat.agent.management.web.discovery.impl.NativeAgentManagementNativeAgentProxyDiscovery;
45
import com.alibaba.arthas.nat.agent.management.web.factory.NativeAgentProxyDiscoveryFactory;
56
import com.alibaba.arthas.nat.agent.management.web.server.NativeAgentManagementWebBootstrap;
67
import com.alibaba.fastjson2.JSON;
78
import com.alibaba.fastjson2.TypeReference;
89
import io.netty.buffer.Unpooled;
910
import io.netty.channel.ChannelHandlerContext;
10-
import io.netty.handler.codec.http.DefaultFullHttpResponse;
11-
import io.netty.handler.codec.http.FullHttpRequest;
12-
import io.netty.handler.codec.http.FullHttpResponse;
13-
import io.netty.handler.codec.http.HttpResponseStatus;
11+
import io.netty.handler.codec.http.*;
1412

1513
import java.nio.charset.StandardCharsets;
14+
import java.time.LocalDateTime;
15+
import java.time.format.DateTimeFormatter;
1616
import java.util.List;
1717
import java.util.Map;
1818
import java.util.Random;
@@ -34,9 +34,27 @@ public FullHttpResponse handle(ChannelHandlerContext ctx, FullHttpRequest reques
3434
return responseFindAvailableProxyAddress(ctx, request);
3535
}
3636

37+
if ("register".equals(operation)) {
38+
String addressInfo = (String) bodyMap.get("nativeAgentProxyAddress");
39+
String expirationTimeStr = (String) bodyMap.get("expirationTime");
40+
return doRegisterNativeAgentProxy(request, addressInfo, expirationTimeStr);
41+
}
42+
3743
return null;
3844
}
3945

46+
private FullHttpResponse doRegisterNativeAgentProxy(FullHttpRequest request, String addressInfo, String expirationTimeStr) {
47+
LocalDateTime expirationTime = LocalDateTime.parse(expirationTimeStr, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"));
48+
NativeAgentManagementNativeAgentProxyDiscovery.storageNativeAgent(addressInfo, expirationTime);
49+
DefaultFullHttpResponse response = new DefaultFullHttpResponse(
50+
request.getProtocolVersion(),
51+
HttpResponseStatus.OK,
52+
Unpooled.copiedBuffer("success", StandardCharsets.UTF_8)
53+
);
54+
fillCorsHead(response);
55+
return response;
56+
}
57+
4058

4159
public FullHttpResponse responseFindAvailableProxyAddress(ChannelHandlerContext ctx, FullHttpRequest request) {
4260
String availableProxyAddress = findAvailableProxyAddress();
@@ -48,6 +66,7 @@ public FullHttpResponse responseFindAvailableProxyAddress(ChannelHandlerContext
4866
HttpResponseStatus.OK,
4967
Unpooled.copiedBuffer(availableProxyAddress, StandardCharsets.UTF_8)
5068
);
69+
fillCorsHead(response);
5170
return response;
5271
}
5372

@@ -67,4 +86,15 @@ public String findAvailableProxyAddress() {
6786
}
6887

6988

89+
private void fillCorsHead(FullHttpResponse fullHttpResponse) {
90+
// 设置跨域响应头
91+
fullHttpResponse.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
92+
fullHttpResponse.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, DELETE, OPTIONS");
93+
fullHttpResponse.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, "X-Requested-With, Content-Type, Authorization");
94+
95+
// 设置其他必要的头部
96+
fullHttpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json");
97+
fullHttpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH, fullHttpResponse.content().readableBytes());
98+
}
99+
70100
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
native-agent-management=com.alibaba.arthas.nat.agent.management.web.discovery.impl.NativeAgentManagementNativeAgentProxyDiscovery
12
zookeeper=com.alibaba.arthas.nat.agent.management.web.discovery.impl.ZookeeperNativeAgentProxyDiscovery
23
etcd=com.alibaba.arthas.nat.agent.management.web.discovery.impl.EtcdNativeAgentProxyDiscovery
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.alibaba.arthas.nat.agent.proxy.discovery.impl;
2+
3+
import com.alibaba.arthas.nat.agent.proxy.discovery.NativeAgentDiscovery;
4+
5+
import java.time.LocalDateTime;
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
import java.util.concurrent.ConcurrentHashMap;
9+
import java.util.concurrent.Executors;
10+
import java.util.concurrent.ScheduledExecutorService;
11+
import java.util.concurrent.TimeUnit;
12+
13+
/**
14+
* @description: NativeAgentProxyNativeAgentDiscovery
15+
* @author:flzjkl
16+
* @date: 2024-10-28 21:07
17+
*/
18+
public class NativeAgentProxyNativeAgentDiscovery implements NativeAgentDiscovery {
19+
20+
/**
21+
* key: native agent ip : http port : ws port , value: expiration time
22+
*/
23+
private static Map<String, LocalDateTime> nativeAgentMap = new ConcurrentHashMap<>();
24+
private final static int INITIAL_DELAY_SECONDS = 5;
25+
private final static int PERIOD_SECONDS = 5;
26+
27+
public static void storageNativeAgent(String address, LocalDateTime expirationTime) {
28+
nativeAgentMap.put(address, expirationTime);
29+
}
30+
31+
public static void nativeAgentCheckScheduled () {
32+
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
33+
Runnable task = () -> {
34+
LocalDateTime now = LocalDateTime.now();
35+
nativeAgentMap.forEach((key, expirationTime) ->{
36+
if (now.isAfter(expirationTime)) {
37+
nativeAgentMap.remove(key);
38+
}
39+
});
40+
};
41+
scheduler.scheduleAtFixedRate(task, INITIAL_DELAY_SECONDS, PERIOD_SECONDS, TimeUnit.SECONDS);
42+
}
43+
44+
45+
@Override
46+
public Map<String, String> findNativeAgent(String address) {
47+
Map<String, String> res = new HashMap<>();
48+
for (String key : nativeAgentMap.keySet()) {
49+
String[] split = key.split("@");
50+
res.put(split[0], split[1]);
51+
}
52+
return res;
53+
}
54+
}

0 commit comments

Comments
 (0)