MavenTip
项目开发中遇到的一些问题
1. DependencyManagement
DependencyManagement 用于在父项目中定义依赖版本,子项目在引入依赖时无需指定版本
DependencyManagement
中定义的只是依赖的声明,并不实现引入,因此子项目需要显式的声明需要用到依赖
举例
1 |
|
2. 中的 的作用
- import 在
中起作用
1
2
3
- 使用
<scope>import</scope>
解决Maven项目单继承问题
- 这个标签值只能在dependencyManagement标签下使用!
并且仅用于type为”pom”的dependency,其意义为引入该dependency的pom中定义的所有dependency定义。
test在本地会引入,不过打包的时候不会被引入,用于maven test结构中代码的正常执行,但不打入包中,以减小线上部署包的尺寸。
compile为正常引入模式,表示编译会用它,那么大概率情况,执行也会用它,package会打包进去,至少会把依赖的class打包进去。
runtime的作用,是为了本地编译不出错,但package时不打包进去,但按照字面理解,这个玩意儿是要运行时用的啊,那怎么办呢?
那就在运行环境中,必须有这个包。 比如tomcat中,会有servlet/jsp api,这种api是在运行时使用的,tomcat中自带; 但你项目中编译也会用到,为了本地编译不报错,就要引入,但为了不和线上tomcat发生版本冲突,就要标记为runtime,这样本地编译可用,但打包不引入
Linux查看线程数
- 查看所有线程,top默认显示所有进程第一行
top -H
- 查看所有存在的线程
ps xH
- 通过进程id查看线程数量
ps -mp
- 查看某个进程线程数
ps -T -p pid
- 查看特定某个进程线程使用内存情况
top -H -p pid
- 查看进程相关的线程信息
top -Hp pid
JavaAgent
JavaAgent 又称 Java 探针,是在 JDK5 引进的一种动态修改字节码的技术。它在 JVM 在加载字节码之前,获取字节码信息 通过字节码转换器修改字节码来实现一些额外的功能。JavaAgent本质可以理解成一个Jar包插件,使用 JVMTI(JVM Tool Interface)完成加载,利用 JVM 提供的 Instrumentatiion
接口来实现字节码加载前、加载后的修改。-javaagent 参数来指定一个 agent jar包,实现启动jvm时加载 agent。利用这一特性可以实现虚拟机级别的 AOP ,实时监控、分析虚拟机,干预虚拟机的执行。
Instrumentation(核心)JDK 官方提供
提供了注入字节码转换器的入口
ClassFileTransformer
字节码转换器接口,想要对字节码的转换需要实现该接口
Java 能做什么?
在 JVM 加载字节码之前对字节码修改
在 JVM 加载字节码之后对字节码修改
获取当前 JVM 加载的所有类信息
获取 JVM 初始化的类信息
修改 native 方法
获取某个对象的大小
将某个jar加入到bootstrapclasspath里作为高优先级被bootstrapClassloader加载
将某个jar加入到classpath里供AppClassloard去加载
实现方式
启动 JVM 时加载 Agent(premain)
定义 Agent
1 |
|
定义 MANIFEST.MF
,放在 resource/ META-INF/
1 |
|
修改 POM 文件
1 |
|
打包 mvn assembly:assembly 进行打包,会生成2个jar包,my-agent-1.0-SNAPSHOT.jar中不带这个项目所依赖的jar包,my-agent-1.0-SNAPSHOT-jar-with-dependencies.jar中包含这个项目所依赖的jar包,后面需要使用my-agent-1.0-SNAPSHOT-jar-with-dependencies.jar这个jar
项目工程引入自定义 agent ,在项目启动时 添加 vm 参数,-javaagent={agentpath}/myagent.jar
JVM 运行时加载 agent(attachemain)
- 该方法就不是使用 -javaagent 参数引入,需要使用 virtualMachine 下的 attche 方法引入,该类不是 jdk 官方提供,又sun公司提供,需要引入依赖
1 |
|
打包 POM
1 |
|
利用 jps 查看进程号,选择要加载agent的进程号,启动 agent 传入 进程号,完成 agent 的载入
Tomcat 线程模型
NioEndpoint
设计模式
- 模板设计模式 ( NioEndpoint extends AbstractEndpoint)
线程模型
1 |
|
Tomcat 线程池
重写了 LinkedBlockingQueue ,TaskQueue
1 |
|
重写 offer 方法,没有达到线程池的最大线程数,则返回false ,继续创建线程,如果大于则加入阻塞队列,队列满了 则抛出 拒绝策略,如果 已提交的任务数小于当前线程池中的线程数 则加入队列(不创建新的线程)
Tomcat 线程池扩展了原生的 ThreadPoolExecutor,通过重写 execute 方法实现了自己的任务处理逻辑:
● 前 corePoolSize 个任务时,来一个任务就创建一个新线程。
● 还有任务提交,直接放到队列,队列满了,但是没有达到最大线程池数则创建临时线程救火。
● 线程总线数达到 maximumPoolSize ,继续尝试把任务放到队列中。如果队列也满了,插入任务失败,才执行拒绝策略。
最大的差别在于 Tomcat 在线程总数达到最大数时,不是立即执行拒绝策略,而是再尝试向任务队列添加任务,添加失败后再执行拒绝策略。
代码质量
Spring MVC
Servlet容器的扩展机制
1 |
|
1 |
|
创建 ServletWebServerApplicationContext
AbstractApplicationContext.refresh() -> onRefresh() -> createWebServer() -> selfInitialize() 这里会读取 beanFactory 自动配置配类
DispatcherServletAutoConfiguration-> DispatcherServletRegistrationBean (底层实现接口
ServletContextInitializer)-> 执行 onStartup() -> 导入DispacherServlet
在第一次访问到 Servlet 时,调用 init() -> 加载HandlerMapping、HandlerAdpter
1 |
|
tip
Spring MVC 的配置(默认值):
spring: mvc: servlet: load-on-startup: 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
*
* servlet init 方法的调用时机,依赖 load-on-startup的配置信息
* init 方法是随 Servlet 实例化而被调用的,因为 load-on-startup 就是用来设置 Servlet 实例化时间的。
* 因此,init 方法执行的时刻有两种:
* (1) load-on-startup 的值大于等于0,则伴随 Servlet 实例化后执行。
* (2) load-on-startup 的值小于0 或者 不配置, 则在第一次 Servlet 请求的时候执行。
### tips
###### WebMvcAutoConfiguration
* 静态内部类 `EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration` 用来 注册 DispatcherServlet 各个handler (RequestMappingHandlerMapping)
```java
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
// Must be @Primary for MvcUriComponentsBuilder to work
return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
resourceUrlProvider);
}Controller 在注入 Bean 以后,什么时候放入 RequestMappingHandlerMapping.mappingRegistry
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
RequestMappingHandlerMapping extends AbstractHanlerMapping
AbstractHandlerMethodMapping implements InitializingBean
在 Bean 生命周期 afterPropertiesSet() 方法 注入 Controller 中的 Method
重写 RequestMappingHandlerMapping 中的 isHandler 方法即可注入其他注解或者类型的Bean
**/
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}创建 DispatcherServlet (
DispatcherServletConfiguration
)
1 |
|
- 通过 DispatcherServletRegistrationBean 注入
DispatcherServlet
,原理: RegistrationBean (注册 servlet 3.0+) 实现了ServletContextInitializer
接口 servletContext 初始化生命周期方法
何谓 ServiceMesh
Service Mesh
在 Service Mesh 架构出来之前,传统的微服务架构存在底层服务交互与业务逻辑耦合在一起,依赖冲突,考虑:透明升级,多语言服务通信等问题。
Service Mesh 通过部署独立的 Sidecar组件来拦截所有的出口与入口流量,集成丰富的负载均衡策略,开发本身只关注业务逻辑,开发更纯粹。
Service Mesh 解决了什么问题?
透明升级
多语言
依赖冲突
流量治理
经典 Mesh 在实施层面也面临成本过高的问题
需要运维控制面(Control Panel)
需要运维 Sidecar
需要考虑如何从原有 SDK 迁移到 Sidecar
需要考虑引入 Sidecar 后整个链路的性能损耗
Proxyless Mesh
- 为了解决 Service Mesh 的问题引入全新的 Proxyless Mesh 架构,Proxyless Mesh 指的是没有 Sidecar 部署,由 Dubbo SDK 直接与控制面交互。
ImportBeanDefinitionRegistrar
SpringBoot扩展点 ImportBeanDefinitionRegistrar
主要用于框架与Spring 对接时注入核心类
一般配合 @Import 使用,用于在 Spring 启动时创建 BeanDefinition 并导入 BeanDefinition
案例:
- Mybatis @MapperScan 导入 MapperScannerConfigurer 用于 (ClassPathMapperScanner) 扫描 @Mapper 利用 (BeanDefinitionRegistryPostProcesso) 生成 Mapper接口代理类 (MapperFactoryBean )]
1
2
3
4
5
6
7 有两个渠道会注入 MapperScannerConfigurer
1. @MapperScan 注解中的 @Import
2. mybatis-spring-boot-autoconfigure 自动配置
@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
MapperScannerRegistrarNotFoundConfiguration
注入条件是当前不存在 MapperScannerConfigurer,意思说 @MapperScan 配置优先级最高。也符合自动配置中约定大于配置的思想。
- TkMybatis
- Nacos
- Apollo @EnableApolloConfig -> @Import(ApolloConfigRegistrar.class) -> ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar -> 注入核心控制类
- OpenFegin