mirror of https://github.com/alibaba/arthas.git
parent
9b56029152
commit
35aa6cf686
@ -0,0 +1,53 @@
|
||||
{
|
||||
"title": "Arthas mc-redefine命令",
|
||||
"description": "Arthas mc-redefine命令",
|
||||
"difficulty": "精通者",
|
||||
"time": "10-20 分钟",
|
||||
"details": {
|
||||
"steps": [
|
||||
{
|
||||
"title": "启动demo",
|
||||
"text": "start-demo.md"
|
||||
},
|
||||
{
|
||||
"title": "启动arthas-boot",
|
||||
"text": "arthas-boot.md"
|
||||
},
|
||||
{
|
||||
"title": "mc命令",
|
||||
"text": "mc.md"
|
||||
},
|
||||
{
|
||||
"title": "redefine命令",
|
||||
"text": "redefine.md"
|
||||
},
|
||||
{
|
||||
"title": "热更新代码",
|
||||
"text": "case-jad-mc-redefine.md"
|
||||
}
|
||||
],
|
||||
"intro": {
|
||||
"text": "intro.md"
|
||||
},
|
||||
"finish": {
|
||||
"text": "finish.md"
|
||||
},
|
||||
"assets": {
|
||||
"host01": []
|
||||
}
|
||||
},
|
||||
"environment": {
|
||||
"uilayout": "terminal",
|
||||
"showdashboard": true,
|
||||
"dashboards": [
|
||||
{
|
||||
"name": "Web Port 80",
|
||||
"port": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"backend": {
|
||||
"imageid": "java",
|
||||
"environmentsprotocol": "http"
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
This case introduces the ability to dynamically update code via the `jad`/`mc`/`redefine` command.
|
||||
|
||||
Currently, visiting http://localhost/user/0 will return a 500 error:
|
||||
|
||||
`curl http://localhost/user/0`{{execute T3}}
|
||||
|
||||
```
|
||||
{"timestamp":1550223186170,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"}
|
||||
```
|
||||
|
||||
This logic will be modified by `redefine` command below.
|
||||
|
||||
### Use jad command to decompile UserController
|
||||
|
||||
`jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java`{{execute T2}}
|
||||
|
||||
The result of jad command will be saved in the `/tmp/UserController.java` file.
|
||||
|
||||
|
||||
Then open `Terminal 3`, use `vim` to edit `/tmp/UserController.java`:
|
||||
|
||||
`vim /tmp/UserController.java`{{execute T3}}
|
||||
|
||||
For example, when the user id is less than 1, it also returns normally without throwing an exception:
|
||||
|
||||
```java
|
||||
@GetMapping(value={"/user/{id}"})
|
||||
public User findUserById(@PathVariable Integer id) {
|
||||
logger.info("id: {}", (Object)id);
|
||||
if (id != null && id < 1) {
|
||||
return new User(id, "name" + id);
|
||||
// throw new IllegalArgumentException("id < 1");
|
||||
}
|
||||
return new User(id.intValue(), "name" + id);
|
||||
}
|
||||
```
|
||||
|
||||
### Use sc command to find the ClassLoader that loads the UserController
|
||||
|
||||
`sc -d *UserController | grep classLoaderHash`{{execute T2}}
|
||||
|
||||
```bash
|
||||
$ sc -d *UserController | grep classLoaderHash
|
||||
classLoaderHash 1be6f5c3
|
||||
```
|
||||
|
||||
It can be found that it is loaded by spring boot `LaunchedURLClassLoader@1be6f5c3`.
|
||||
|
||||
Please write down your classLoaderHash here, in the case here, it's `1be6f5c3`. It will be used in the future steps.
|
||||
|
||||
Note: Please replace `<classLoaderHash>` with your classLoaderHash above, then execute the commands manually in the following steps:
|
||||
|
||||
### mc
|
||||
|
||||
After saving `/tmp/UserController.java`, compile with the `mc` (Memory Compiler) command and specify the ClassLoader with the `-c` option:
|
||||
|
||||
`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp`
|
||||
|
||||
```bash
|
||||
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
|
||||
Memory compiler output:
|
||||
/tmp/com/example/demo/arthas/user/UserController.class
|
||||
Affect(row-cnt:1) cost in 346 ms
|
||||
```
|
||||
|
||||
### redefine
|
||||
|
||||
Then reload the newly compiled `UserController.class` with the `redefine` command:
|
||||
|
||||
`redefine /tmp/com/example/demo/arthas/user/UserController.class`{{execute T2}}
|
||||
|
||||
```
|
||||
$ redefine /tmp/com/example/demo/arthas/user/UserController.class
|
||||
redefine success, size: 1
|
||||
```
|
||||
|
||||
### Check the results of the hotswap code
|
||||
|
||||
After the `redefine` command is executed successfully, visit https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/0 again.
|
||||
|
||||
The result is:
|
||||
|
||||
```
|
||||
{
|
||||
"id": 0,
|
||||
"name": "name0"
|
||||
}
|
||||
```
|
@ -0,0 +1,8 @@
|
||||
|
||||
The `mc-redefine Tutorial` demonstrates the usage of mc-redefine. If you have more tips or questions, please feel free to ask in Issue.
|
||||
|
||||
* Issues: https://github.com/alibaba/arthas/issues
|
||||
* Documentation: https://arthas.aliyun.com/doc/en
|
||||
|
||||
|
||||
If you are using Arthas, please let us know. Your use is very important to us: [View](https://github.com/alibaba/arthas/issues/111)
|
@ -0,0 +1,53 @@
|
||||
{
|
||||
"title": "Arthas mc-redefine Command",
|
||||
"description": "Arthas mc-redefine Command",
|
||||
"difficulty": "master",
|
||||
"time": "10-20 minutes",
|
||||
"details": {
|
||||
"steps": [
|
||||
{
|
||||
"title": "Start demo",
|
||||
"text": "start-demo.md"
|
||||
},
|
||||
{
|
||||
"title": "Start arthas-boot",
|
||||
"text": "arthas-boot.md"
|
||||
},
|
||||
{
|
||||
"title": "mc Command",
|
||||
"text": "mc.md"
|
||||
},
|
||||
{
|
||||
"title": "redefine Command",
|
||||
"text": "redefine.md"
|
||||
},
|
||||
{
|
||||
"title": "Hotswap Code",
|
||||
"text": "case-jad-mc-redefine.md"
|
||||
}
|
||||
],
|
||||
"intro": {
|
||||
"text": "intro.md"
|
||||
},
|
||||
"finish": {
|
||||
"text": "finish.md"
|
||||
},
|
||||
"assets": {
|
||||
"host01": []
|
||||
}
|
||||
},
|
||||
"environment": {
|
||||
"uilayout": "terminal",
|
||||
"showdashboard": true,
|
||||
"dashboards": [
|
||||
{
|
||||
"name": "Web Port 80",
|
||||
"port": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"backend": {
|
||||
"imageid": "java",
|
||||
"environmentsprotocol": "http"
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
|
||||
> Memory compiler, compiles `.java` files into `.class` files in memory.
|
||||
|
||||
The classloader can be specified with the `-c` option, the output directory can be specified with the `-d` option.
|
||||
|
||||
After compiling the `.class` file, you can use the [redefine](redefine.md) command to re-define the loaded classes in JVM.
|
@ -0,0 +1,32 @@
|
||||
|
||||
> Load the external `*.class` files to re-define the loaded classes in JVM.
|
||||
|
||||
Reference: [Instrumentation#redefineClasses](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#redefineClasses-java.lang.instrument.ClassDefinition...-)
|
||||
|
||||
### Frequently asked questions
|
||||
|
||||
* The class of `redefine` cannot modify, add or delete the field and method of the class, including method parameters, method names and return values.
|
||||
|
||||
* If `mc` fails, you can compile the class file in the local development environment, upload it to the target system, and use `redefine` to hot load the class.
|
||||
|
||||
* At present, `redefine` conflicts with `watch / trace / jad / tt` commands. Reimplementing `redefine` function in the future will solve this problem.
|
||||
|
||||
> Notes: Re-defined classes cannot be restored. There are chances that redefining may fail due to some reasons, for example: there's new field introduced in the new version of the class, pls. refer to JDK's documentation for the limitations.
|
||||
|
||||
> The `reset` command is not valid for classes that have been processed by `redefine`. If you want to reset, you need `redefine` the original bytecode.
|
||||
|
||||
|
||||
> The `redefine` command will conflict with the `jad`/`watch`/`trace`/`monitor`/`tt` commands. After executing `redefine`, if you execute the above mentioned command, the bytecode of the class will be reset.
|
||||
> The reason is that in the JDK `redefine` and `retransform` are different mechanisms. When two mechanisms are both used to update the bytecode, only the last modified will take effect.
|
||||
|
||||
### Options
|
||||
|
||||
|Name|Specification|
|
||||
|---:|:---|
|
||||
|`[c:]`|hashcode of the class loader|
|
||||
|`[p:]`|absolute path of the external `*.class`, multiple paths are separated with 'space'|
|
||||
|
||||
### Restrictions of the redefine command
|
||||
|
||||
* New field/method is not allowed
|
||||
* The function that is running, no exit can not take effect.
|
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
|
||||
|
||||
Download `demo-arthas-spring-boot.jar`, and start with `java -jar` command:
|
||||
|
||||
`wget https://github.com/hengyunabc/spring-boot-inside/raw/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar
|
||||
java -jar demo-arthas-spring-boot.jar`{{execute T1}}
|
||||
|
||||
`demo-arthas-spring-boot` is a simple Spring Boot demo, the source code here: [View](https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-arthas-spring-boot)
|
||||
|
||||
After booting, access port 80: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com
|
||||
|
||||
![Demo Web](/arthas/scenarios/common-resources/assets/demo-web.png)
|
Loading…
Reference in New Issue