关于next.js:NextjsReactNode系统实战搞定SSR服务器渲染内附文档源码

1次阅读

共计 5571 个字符,预计需要花费 14 分钟才能阅读完成。

Next.js+React+Node 零碎实战,搞定 SSR 服务器渲染内附文档源码

下载地址:百度网盘
Java 诊断工具 Arthas- 实操案例
实操案例
排查函数调用异样
通过 curl 请求接口只能看到返回异样,然而看不到具体的请求参数和堆栈信息。
shell@Alicloud:~$ curl
{“timestamp”:1655435063042,”status”:500,”error”:”Internal Server Error”,”exception”:”java.lang.IllegalArgumentException”,”message”:”id < 1″,”path”:”/user/0″}
复制代码
查看 UserController 的 参数 / 异样
在 Arthas 里执行:
watch com.example.demo.arthas.user.UserController * ‘{params, throwExp}’
复制代码

第一个参数是类名,反对通配
第二个参数是函数名,反对通配 拜访 watch 命令会打印调用的参数和异样
再次通过 curl 调用可能在 arthas 外面查看到具体的异样信息。
把获取到的后果开展,可能用 - x 参数:
watch com.example.demo.arthas.user.UserController * ‘{params, throwExp}’ -x 2
复制代码
返回值表达式
在下面的例子里,第三个参数是返回值表达式,它实际上是一个 ognl 表达式,它反对一些内置对象:

loader
clazz
method
target
params
returnObj
throwExp
isBefore
isThrow
isReturn

比如返回一个数组:
watch com.example.demo.arthas.user.UserController * ‘{params[0], target, returnObj}’
复制代码
条件表达式
watch 命令反对在第 4 个参数里写条件表达式,比如:
当拜访 user/1 时,watch 命令没有输入
当拜访 user/101 时,watch 会打印出后果。

当异样时捕捉
watch 命令反对 - e 选项,示意只捕捉抛出异样时的请求:
watch com.example.demo.arthas.user.UserController * “{params[0],throwExp}” -e
复制代码
按照耗时进行过滤
watch 命令反对按请求耗时进行过滤,比如:
watch com.example.demo.arthas.user.UserController * ‘{params, returnObj}’ ‘#cost>200’
复制代码
热更新代码
这个也是真的秀。

拜访 http://localhost:61000/user/0,会返回 500 异样:
shell@Alicloud:~$ curl http://localhost:61000/user/0
{“timestamp”:1655436218020,”status”:500,”error”:”Internal Server Error”,”exception”:”java.lang.IllegalArgumentException”,”message”:”id < 1″,”path”:”/user/0″}
复制代码
通过热更新代码,修改这个逻辑。
jad 反编译 UserController
jad –source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
复制代码
jad 反编译的后果保存在 /tmp/UserController.java 文件里了。
再打开一个 Terminal 窗口,而后用 vim 来编辑 /tmp/UserController.java:
vim /tmp/UserController.java
复制代码
比如当 user id 小于 1 时,也失常返回,不抛出异样:
@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);
}

复制代码
sc 查找加载 UserController 的 ClassLoader
[arthas@1266]$ sc -d *UserController | grep classLoaderHash
classLoaderHash 19469ea2
复制代码
classLoaderHash 是 19469ea2,前面需要使用它。
mc
保存好 /tmp/UserController.java 之后,使用 mc(Memory Compiler)命令来编译,并且通过 - c 或者 –classLoaderClass 参数指定 ClassLoader:

mc –classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp

[arthas@1266]$ mc –classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 2879 ms.
复制代码
也可能通过 mc -c /tmp/UserController.java -d /tmp,使用 - c 参数指定 ClassLoaderHash:
mc -c 19469ea2 /tmp/UserController.java -d /tmp
复制代码
redefine
再使用 redefine 命令从新加载新编译好的 UserController.class:
[arthas@1266]$ redefine /tmp/com/example/demo/arthas/user/UserController.class
redefine success, size: 1, classes:
com.example.demo.arthas.user.UserController
复制代码
热修改代码后果
redefine 胜利之后,再次拜访 user/0,后果失常
shell@Alicloud:~$ curl http://localhost:61000/user/0
{“id”:0,”name”:”name0″}
复制代码
动静更新利用 Logger Level
查找 UserController 的 ClassLoader
[arthas@1266]$ sc -d *UserController | grep classLoaderHash
classLoaderHash 19469ea2
复制代码
用 ognl 获取 logger

ognl –classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader ‘@com.example.demo.arthas.user.UserController@logger’

[arthas@1266]$ ognl –classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader ‘@com.example.demo.arthas.user.UserController@logger’
@Logger[

serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],

]
复制代码
可能知道 UserController@logger 实际使用的是 logback。可能看到 level=null,则说明实际最终的 level 是从 root logger 里来的。
独自设置 UserController 的 logger level
ognl –classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader ‘@com.example.demo.arthas.user.UserController@logger.setLevel(@ch.qos.logback.classic.Level@DEBUG)’
复制代码
再次获取 UserController@logger,可能发现已经是 DEBUG 了。
修改 logback 的全局 logger level
通过获取 root logger,可能修改全局的 logger level:
ognl –classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader ‘@org.slf4j.LoggerFactory@getLogger(“root”).setLevel(@ch.qos.logback.classic.Level@DEBUG)’
复制代码
获取 Spring Context, 在获取 bean, 再调用函数

使用 tt 命令获取到 spring context
tt 即 TimeTunnel,它可能记录下指定方法每次调用的入参和返回信息,并能对这些不同的工夫下调用进行观测

tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod
复制代码
拜访 user/1,
curl http://localhost:61000/user/1
复制代码
可能看到 tt 命令捕捉到了一个请求:

输出 q 或者 Ctrl + C 退出下面的 tt - t 命令。
使用 tt 命令从调用记录里获取到 spring context
tt -i 1000 -w ‘target.getApplicationContext()’
复制代码
获取 spring bean,并调用函数

tt -i 1000 -w ‘target.getApplicationContext().getBean(“helloWorldService”).getHelloMessage()’

后果如下:
[arthas@1266]$ tt -i 1000 -w ‘target.getApplicationContext().getBean(“helloWorldService”).getHelloMessage()’
@String[Hello World]
Affect(row-cnt:1) cost in 1 ms.
复制代码
排查 HTTP 请求返回 401
请求接口没有权限的时候一般就返回 401 Unauthorized。
401 通常是被权限治理的 Filter 拦挡了,那么到底是哪个 Filter 处理了这个请求,返回了 401?
跟踪所有的 Filter 函数
开始 trace:
trace javax.servlet.Filter *
复制代码
可能在调用树的最深层,找到 AdminFilterConfig$AdminFilter 返回了 401
+—[3.806273ms] javax.servlet.FilterChain:doFilter()
| `—[3.447472ms] com.example.demo.arthas.AdminFilterConfig$AdminFilter:doFilter()
| `—[0.17259ms] javax.servlet.http.HttpServletResponse:sendError()
复制代码
通过 stack 获取调用栈
下面是通过 trace 命令来获取信息,从后果里,咱们可能知道通过 stack 跟踪 HttpServletResponse:sendError(),同样可能知道是哪个 Filter 返回了 401
执行:
stack javax.servlet.http.HttpServletResponse sendError ‘params[0]==401’

查找 Top N 线程
查看所有线程信息
thread
复制代码
查看具体线程的栈
查看线程 ID 2 的栈:
thread 2
复制代码
查看 CPU 使用率 top n 线程的栈
thread -n 3
复制代码
查看 5 秒内的 CPU 使用率 top n 线程栈
thread -n 3 -i 5000
复制代码
查找线程是否有阻塞
thread -b

正文完
 0