基础篇
----------------------------------------------------------------------------------------------------
JVM类加载:
加载:class文件通过类加载器加载到内存
链接:验证class字节码是否满足jvm约束条件;给静态字段分配内存;将符号引用解析成实际引用
初始化:給静态字段赋值,给常量字段赋值
双亲委派:一个类需要加载时,类加载器会先让父类加载器加载,当父类加载器无法加载时,子类加载器才会加载。
类加载器:
启动类加载器:加载jre的lib中的类。c++实现的,没有java对应的对象。
扩展类加载器:与上面父子关系,加载jre/lib/ext下的类。java.lang.ClassLoader的子类。
应用类加载器:加载应用程序下的类。java.lang.ClassLoader的子类。
----------------------------------------------------------------------------------------------------
Spring Bean的生命周期:实例化;属性注入;初始化;销毁四步
初始化过程:
BeanPostProcessor的postProcessorBeforeInitialization(初始化执行前)
InitializingBean的afterPropertiesSet(属性设置后执行)
Bean中定义的initMethod
BeanPostProcessor的postProcssorAfterInitialization(初始化执行后)
销毁过程(容器关闭):
DisposableBean的destroy
Bean中定义的destroy方法
----------------------------------------------------------------------------------------------------
创建bean的方式有几种:实现beanFactory 或者 加bean注解
----------------------------------------------------------------------------------------------------
SpringBoot自定义starter过程:
定义一个属性类提供get、set方法,使用@ConfigurationProperties(prefix="redis")接收配置文件中的属性
定义一个自动配置属性类,使用@EnableConfigurationPreperties(上面类.class),使用@bean创建bean
定义一个注解,EnableRedis.java,导入上面的自动配置类,@Import(上面类.class)
如何使用这个starter:
在别的项目pom文件中引入这个gav坐标
在@SpringBootApplication所在的启动类上,添加@EnableRedis
在application.properties中配置属性,如ip、端口等
程序中直接获取bean,使用即可
----------------------------------------------------------------------------------------------------
接口的幂等性:
用户对于同一操作发起的一次或多次请求的结果是一致的,不会因为多次点击产生副作用。
比如,用户第一次操作执行成功,但是网络断了,没有返回结果,用户又操作了一次,提示操作成功,但实际操作了两次,可能产生副作用。
增删查改中,尤其注意增加或修改。查询是天然的幂等操作。
删除操作,删除一次或多次都会删除掉,可能提示语不同,删除也是幂等的。
修改操作,id为1的A字段设置为1,是幂等的;id为1的A字段增加1,不是幂等的
新增操作,多次点击新增多次,不是幂等的。输入了唯一id主键时是幂等的
怎样保证幂等:
1)通过代码逻辑判断实现
2)使用token机制实现:
页面跳转前生成唯一token,放到页面pageScope和redis(或jvm内存)中,提交成功后删除redis中token,每次提交前,先判断redis中是否有token,如果没有则说明 是重复提交。
或者执行业务前先获取token,并把token保存到redis中。调接口时,携带token放在请求头部。服务器判断token是否存在redis中,如果存在,说明是第一次请求,删除token,继续执行业务方法。如果token不存在,表示重复操作。
先删除token还是后删除token?先删除。
如果先删除了token,业务执行失败了,再请求时发现redis中没有token,误认为重复操作,实质没有操作业务,影响不大。
如果业务执行成功了,删除token失败了,再请求发现redis中有token,不认为是重复操作,影响数据,没有保证幂等性。
----------------------------------------------------------------------------------------------------
浏览器输入回车,发生什么:
DNS域名解析。浏览器向DNS服务器查找URL对应的IP地址(浏览器先从缓存找,否则从本地host文件中获取域名对应的ip,否则通过dns)
DNS服务器返回网站的IP地址
浏览器根据IP地址,与web服务器在80端口上建立tcp连接
浏览器获取请求页面的html代码
浏览器渲染html
窗口关闭时,浏览器终止与服务器的连接
----------------------------------------------------------------------------------------------------
SpringBoot的特有注解:
SpringBootApplication
SpringBootConfiguration
ConfigurationProperties
EnableConfigurationProperties
EnableAutoConfiguration
ConditionalOnBean
ConditionalOnMissingBean
ConditionalOnClass
ConditionalOnMissingClass
ConditonalOnProperty
----------------------------------------------------------------------------------------------------
工作流,Activity:
RepositoryService,管理流程定义,增删查改
RuntimeService,处理运行状态的流程实例、任务。启动、推进、删除流程实例。
TaskService,任务管理,通过、驳回、指派、查询任务。
HistoryService,历史管理,查询所有历史数据,流程、任务、变量等
IdentityService,组织机构管理,管理查询用户、组
FormService,表单管理,解析表单配置,定义、解析用户节点流转
ManagerService,引擎管理service,查询引擎配置、数据库、作业等
----------------------------------------------------------------------------------------------------
性能测试:LoadRunner11、Jmeter 创建虚拟用户,并发测试。NMON监控操作系统资源使用情况
----------------------------------------------------------------------------------------------------
select、poll、epoll:
都是IO多路复用的机制。都是同步IO,自己负责读写(异步IO无需自己负责读写),读写过程阻塞
select,时间复杂度O(n),知道有IO事件发生了,不知道哪个流,轮询所有流找到能读写数据的流,流越多越耗时。
poll,时间复杂度O(n),本质与select一样,区别是基于链表存储,没有最大连接数限制。
epoll(Linux特有,Linux2.6以后有的),时间复杂度O(1),不轮询,事件驱动,epoll把哪个流发生了怎样的IO事件通知我们
--------------------------------------------------------
IO:面向流(Stream),阻塞式IO
面向流:一次性从流中读取一批数据,这些数据不会缓存在任何地方,不能移动
阻塞式:线程调用read/write方法时会阻塞,直到完成读写,线程什么都不做
NIO:面向缓冲区(Buffer),非阻塞式IO,引入Selector概念,可以实现多路复用
面向缓冲:数据先读取到缓冲区,可多次读取数据,数据在缓冲区中可以前后移动
非阻塞:线程从channel读写数据不会阻塞了一次性干完,期间可以干别的,等读写完成,线程在继续处理这部分事,单个线程管理多个channel的输入输出。
Selector:多个channel注册到一个Selector中,单个线程监控多个channel,选出哪个channel准备好数据,或准备好被写入。
优缺点及场景:
IO中readLine阻塞,直到读完一行,读后处理简单;NIO不确定,可能读半行,需要不断检查buffer中数据是否可处理,读数据复杂。
NIO适合单个或少量线程管理多个channel(网络连接或文件),如管理千计连接,每个连接发少量数据,如聊天服务器。但是读数据复杂。
IO适合少量连接,每个连接占用大量带宽,短时间发大量数据
--------------------------------------------------------
NIO:New IO缩写,是JDK1.4以后提供的基于事件驱动的同步非阻塞的一套API,实现多路复用,解决BIO的大并发问题。
标准IO使用字节流、字符流;NIO使用通道(Channel)和缓冲区(Buffer),数据从channel读到buffer或从buffer写到channel;
buffer理解为内存块,可以从里面读、或者往里写。NIO主要包括Buffer、Channel、Selector。
channel理解为一个流,可以读数据到buffer或者写数据到channel
一般buffer、channel配对使用,从channel读到buffer中,从buffer写到channel中
selector通过少量线程监控大量连接,实现多路复用。往selector中注册一些channel,指定监听事件类型,监控这些channel,获取就绪事件执行下一步操作。
--------------------------------------------------------
内核空间:内核代码运行的空间,进程运行在内核空间是内核态
用户空间:应用程序运行的空间,进程运行在用户空间是用户态
--------------------------------------------------------
文件描述符:理解为标识一个文件的整数,当程序请求内核打开/新建文件时,由内核返回给程序,对应这个文件
--------------------------------------------------------
Linux缓存IO:操作系统将IO的数据缓存到文件系统的页缓存中。数据先被拷贝到操作系统内核缓冲区,然后从内核缓冲区拷贝到应用程序的地址空间中。数据传输过程中在应用程序地址空间和内核缓冲区多次数据拷贝,cpu和内存开销大,是缓存IO缺点。
--------------------------------------------------------
一次IO访问,比如read,数据先复制到内核缓冲区,再从内核缓冲区复制到应用程序地址,经历两个阶段:
数据准备阶段;将数据从内核复制到进程中,由这两个阶段,Linux产生了5种IO模型:
阻塞IO:两个阶段都阻塞用户进程
非阻塞IO:用户进程不断轮询内核数据准备好没,这个过程不阻塞,将数据从内核复制到用户内存阻塞
多路复用IO:依赖seelct poll epoll这些function轮询socket,当socket有数据到达就通知用户进程。建立在非阻塞IO基础上,单个线程监视多个IO连接,谁准备就绪了就处理谁。处理过程与非阻塞IO一样
信号驱动IO:
异步IO:两个阶段都不阻塞用户进程
--------------------------------------------------------
阻塞:进程或线程等待,什么都不干,数据准备阶段进程或线程阻塞的IO就是阻塞式IO,反之是非阻塞式IO,第二阶段都是阻塞的。
同步:第二阶段,数据从内核复制到进程,用户线程或进程阻塞,阻塞式IO、非阻塞式IO、多路复用都是同步
异步:第二阶段,用户线程或进程不阻塞。异步IO属于异步
--------------------------------------------------------
BIO(Blocking IO)模型:阻塞IO网络模型:
服务器启动后阻塞,等客户端连接,客户端连接上后,服务器为每一个客户端起一个线程,accept、read、write都是阻塞的。
为啥每一个客户端启动一个线程?假如只有一个线程,客户端传一个大文件,服务器一直阻塞,其他client无法连接。
NIO(Non-Blocking IO)模型
NIO-单线程模型:selector轮询查看client端有没有要处理的消息。一个线程处理client端的连接和通道内client端的读写请求。
NIO-reactor(响应式编程模型):selector轮询查看client端有没有要处理的消息。将server端的单线程换成了线程池。即多个线程处 理client端的连接和通道内client端的读写请求。
AIO(Asynchronous IO)模型:
不轮询,当client端发出连接服务器的请求时,由操作系统通知selector处理连接请求。
Netty:实现了对NIO的封装,代码通俗易懂。
BIO与NIO单线程模型的区别:
BIO为阻塞模型,通道为Socket是单向的,没有selector,每个客户端启动一个线程
NIO为非阻塞模型,通道为ServerSocketChannel是双向的,有selector,为每个client注册一个key,表示唯一一个通道,轮询处理client连接或读写请求
--------------------------------------------------------
再理解一下同步、异步、阻塞、非阻塞
同步异步关注消息通信机制,同一个线程处理同一件事是同步(可能期间处理了别的事儿,中间打断了)
阻塞非阻塞关注等待消息时状态,线程等着这个事儿,不干别的,是阻塞
同步阻塞:一个线程死等一件事儿
同步非阻塞:一个线程干甲这件事儿,没干完又干乙这件事,最终还是干完甲这件事儿
异步阻塞:A线程发起做某事儿,一直等着B线程完成这件事儿,事儿是B完成的
异步非阻塞:A线程发起做某事儿,然后该干啥干啥,不等,B线程完成了这件事儿
--------------------------------------------------------