哈哈
线程池
线程池的产生
- 创建、销毁线程伴随着系统开销、线程是可以重复适用的
- 线程并发数量过多,抢占系统资源从而导致阻塞
- 对线程要进行管理
Executor
具体实现类是ThreadPoolExecutor类
核心参数
corePoolSize
线程池的基本大小,即 在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超过这个数量的线程。在刚刚创建threadpoolexecutor的时候,线程不会立即启动,而是在有任务提交的时候才会启动,除非调用了prtestartCoreThread/prestartAllcoreThreads事先启动核心线程,没有任务执行的时候,线程池的大小不一定是corepoolSize
maximumPoolSize
线程池允许的最大线程数,线程池中的当前线程数目不会超过这个值,如果队列中任务已满,并且当前线程个数小于maximumpoolsize,那么会创建新的线程来执行任务。
keepAliveTime
如果一个线程处于空闲状态的时间超过该属性,就会因为超时而退出,如果这个线程是核心线程,线程退出取决于allowCoreThreadTimeOut
queueCapacity
阻塞队列 任务队列容量
blockingQueue
1、ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
2、LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列,使用此阻塞队列时maximumPoolSizes就相当于无效
3、SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。可以避免在处理可能具有内部依赖性的请求集时出现锁。
4、PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
rejectedExecutionHandler
当线程池发生两种情况,此时拒绝增加新的任务,将会采用线程池的饱和策略
- 两种情况
- 当线程已经达到maxpoolsize时,且队列已满,会拒绝新任务
- 当线程池被调用shutdown时,会等待线程池里的任务执行完毕再shutdown
- 饱和策略的类型
- AbortPolicy 丢弃任务,抛运行时异常RejectedExecutionException 默认策略
- CallerRunsPolicy 由调用者线程执行任务,如果调用者已关闭,则丢弃
- DiscardPolicy 忽略
- DiscardOldestPolicy 从队列中踢出最先进入队列的惹怒五
- 实现RejectedExecutionHandler接口,可自定义处理器
执行顺序
1 | 1. 当线程数小于核心线程数时,创建线程。 |
线程池的关闭
shutdown 与shutdownnow
常见的几种线程池
Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池
线程池监控
actuator 组件来做线程池的监控。
ThreadPool api
参数设置的方法
任务的类型
- cpu密集型,
- 较小的线程池,cpu核心数+1,cpu密集型任务会使cpu使用率很高,若开过多的线程数,只会增加上下文切换的次数,带来额外的开销
- io密集型
- cpu使用率不高,可以让cpu在等待io的时候去处理别的任务,充分利用cpu的时间
- 混合型
- 分别用不同的线程池去处理,两个任务的结束时间取决与后执行完的任务,适用于两个任务的执行时间相差不大
2N+1
N为cpu核数
根据几个值设置
1 | * 需要根据几个值来决定 |
源码分析
线程池的创建
添加任务
任务的获取
任务的执行
shiro工作流程
基本描述以及shiro是如何执行的,包含认证和授权流程
基本描述
- Apache Shiro是Java的一个安全(权限)框架
- 可以在JavaSE,javaEE环境
- 功能点:认证,授权,加密、会话管理、web集成、缓存等

应用程序角度

Shiro内部架构

例子
1 | public class UserRealm extends AuthorizingRealm { |
工作流程
入口:DelegatingFilterProxy
web.xml中的shiro入口
1 | <filter> |
DelegatingFilterProxy 是Filter的代理,代理的是spring容器中的filter-name一样的bean,
1 | <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> |
初始化:shiroFilter

ShiroFilterFactoryBean 实现了spring FactoryBean ,getObject获取实例
1 | public Object getObject() throws Exception { |
1 | protected AbstractShiroFilter createInstance() throws Exception { |
property filterChainDefinitions 属性,会调用setFilterChainDefinitions
1 | public void setFilterChainDefinitions(String definitions) { |
SecurityManager
FilterChainManager
1 | protected FilterChainManager createFilterChainManager() { |
DefaultFilterChainManager
1 | public DefaultFilterChainManager() { |
1 | protected void addDefaultFilters(boolean init) { |
DefaultFilter 过滤器
11个
1 | anon(AnonymousFilter.class), |
filterChains过滤器链
是一个linkedhashmap,key为配置的url,value为对应的过滤器
PathMatchingFilterChainResolver
创建了新的filterchainfilter,然后又被前面创建的覆盖了,有问题!!!
基于ant路径匹配方法匹配配置的url,
pathMatchingFilterChainResolver设置创建的FilterChainManager对象,所以URL匹配上后可以获取该URL需要应用的FilterChain了。
filter执行—匹配filter
(错误)通过请求的url,获取要走的过滤链,或者说,所有请求都走一样的过滤链,过滤器中由判断自己是否支持该请求
(正解),所有的请求走一个过滤器org.springframework.web.filter.DelegatingFilterProxy#doFilter
1 | org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter==执行被代理类doFilter |
1 | protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain){ |
1 | eg:FormAuthenticationFilter#doFilter===父类OncePerRequestFilter的doFilter |
1 | AdviceFilter#doFilterInternal |
filter 执行顺序
https://www.cnblogs.com/ljdblog/p/6237683.html
https://www.cnblogs.com/q95265/p/6928081.html
filter执行—doFilter
1 | public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain){ |
shiro通过一系列url匹配符配置URL应用上的Filter,然后在Filter中完成相应的任务。核心逻辑-doFilterInternal
OncePerRequestFilter:保证每个filter都执行一次
–AbstractShiroFilter
–AdviceFilter
AbstractShiroFilter
springfactorybean返回的bean核心就是这个类,封装为shirofilter
构造方法
1 | public void init() throws Exception { |
1 | protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain) { |
createSubject
每次调用都会创建subject
executeChain
1 | protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain) { |
1 | protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) { |
AdviceFilter
这个类很多方法实现了spring中的aop特点,prehandle前置通知,posthandle后置通知,异常不执行,afterCompletion (最终通知,一定会执行),可以根据需求覆写这几个方法
1 | public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain){ |
eg:
- preHandle
- 根据配置,访问URL:”/authenticated.jsp”时,会匹配上authc(FormAuthenticationFilter)
- FormAuthenticationFilter继承自PathMatchingFilter,所以返回true ,而logoutfilter会返回false,啥也没有返回给页面,页面显示空白
1 | protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { |
1 | private boolean isFilterChainContinued(ServletRequest request, ServletResponse response, |
org.apache.shiro.web.filter.AccessControlFilter#onPreHandle
1 | public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { |
isAccessAllowed:表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false;
onAccessDenied:表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理;如果返回false表示该拦截器实例已经处理了,将直接返回即可
org.apache.shiro.web.filter.authc.AuthenticatingFilter#isAccessAllowed
1 | protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { |
org.apache.shiro.web.filter.authc.AuthenticationFilter#isAccessAllowed
1 | protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { |
1 | protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { |
login.jsp也会被拦截,执行到这里然后访问login.jsp,
再然后post请求这个url,就会执行executeLogin
org.apache.shiro.web.filter.authc.AuthenticatingFilter#executeLogin
1 | protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { |
认证流程
1 | public void login(AuthenticationToken token) throws AuthenticationException { |
1 | public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException { |
org.apache.shiro.mgt.AuthenticatingSecurityManager#authenticate
1 | public AuthenticationInfo authenticate(AuthenticationToken token) { |
org.apache.shiro.authc.AbstractAuthenticator#authenticate
org.apache.shiro.authc.pam.ModularRealmAuthenticator#authenticate
模板方法模式
1 | protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { |
1 | protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) { |
1 | public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { |
eg:自定义realmA实现AuthenticatingRealm
1 | public class ShiroRealmA extends AuthenticatingRealm { |
授权流程
perms(PermissionsAuthorizationFilter.class) :url是否有权限
roles(RolesAuthorizationFilter.class), :url是否有该角色
1 | public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { |
ModularRealmAuthorizer.isPermitted
1 | public boolean isPermitted(PrincipalCollection principals, String permission) { |
1 | public boolean isPermitted(String permission) { |
1 | public boolean isPermitted(PrincipalCollection principals, String permissionString) { |
1 | public boolean isPermitted(PrincipalCollection principals, String permission) { |
1 | protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) { |
ModularRealmAuthorizer.isPermittedAll
AuthorizationFilter#onAccessDenied
1 | protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException { |
FormAuthenticationFilter
Onceperrequestfilter#doFilter
adviceFilter#doFilterInternal
AdviceFilter#preHandle true
AuthenticatingFilter#cleanup
FormAuthenticationFilter#onAccessDenied
1 | protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { |
org.apache.shiro.web.filter.authc.AuthenticatingFilter#executeLogin
1 | protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { |
go to 认证流程
PermissionsAuthorizationFilter
Onceperrequestfilter#doFilter
adviceFilter#doFilterInternal
PathMatchingFilter#preHandle
AccessControlFilter#onPreHandle
go to 授权流程
PermissionsAuthorizationFilter#isAccessAllowed
AuthorizationFilter#onAccessDenied
RolesAuthorizationFilter
与PermissionsAuthorizationFilter类似
UserFilter
是登录页面或者必须登录后获得principalCollection,才能通过
1 | protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { |
1 | protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { |
参考文献
servlet-filter-listener-interceptor
概念
servlet
- 是一种运行再服务端的java应用程序,具有独立与平台和协议的特性,并且可以动态的生成web页面,它工作在客户端请求和服务端响应的中间层
- 创建并返回给客户端完整的html页面
filter
- 过滤器,是一个可以复用的代码片段、可以用来转换http请求、响应和头信息。不像servlet,它不能产生一个请求或响应、只能修改某一资源的请求、或者修改某一资源的响应
- 能够在一个请求到达servlet之前预处理用户请求,也可以在离开servlet时处理http响应
- url传来后,检查之后,可保持原来的流程继续向下执行,被下一个filter处理,servlet接收后,不会向下传递。
listenter
- 监听器、通过listener可以监听web服务器中的某一个执行动作、并根据其要求做出相应的响应。再application\session\request三个对象创建消亡或者添加修改属性时自动执行代码的功能组件
- servlet、filter时针对url之类的,而listener是针对对象的操作,在这些对象变化的时候做一些操作
interceptor
- 面向切面编程中,就是再你的servcie或者一个方法前后,比如动态代理就是拦截器的简单实现,只能对controller请求进行拦截
- 类似与框架带的filter
- 不在web.xml中配置,框架配置文件中
- 由action自己指定用哪个interceptor来接受处理,并且访问action上下文,过滤器可以对几乎所有的请求起作用
- 拦截器是基于java 反射的,过滤器是基于函数回调的
- 拦截器不依赖与serlvet容器,过滤器依赖于servlet容器
生命周期
servlet
它被装入web服务器的内存,并在web服务器终止或者重新装入servlet时结束。servlet一但装入web服务器,一般就不会在web服务器内存中删除、直到web服务器关闭或重新结束。
- 装入:启动服务器时加载servlet的实例
- 初始化:web服务器启动时或者接受到请求时,初始化工作init()方法负责执行完成
- 调用:从第一次到以后的多次访问,都只调用doget()或dopost方法
- 销毁:停止服务器时调用destory()方法,销毁实例
filter
Filter接口方法有init().doFilter(),dodestory()
- 启动服务器时加载过滤器实例,并调用init()方法来初始化实例
- 每一次请求时都只调用方法doFilter()进行处理,调用下一过滤器chain.doFilter
- 停止服务器时调用destory()方法,销毁实例
listener
interceptor
- struts 的拦截器在加载xml后,初始化相应拦截器,当action请求来时调用intercept方法,服务器停止销毁interceptor
- prehandle(action执行之前),posthandle(方法执行之后,不一定会执行),aftercompletion(最终执行)
顺序
web.xml的加载顺序context-param->listener->filter->servlet
执行顺序:
filter1————————–web.xml中的配置顺序
->interceptor1(prehandle)–框架中的拦截器
->servlet1
->interceptor1(posthandle,aftercompletion)
->filter1


应用举例
filter
在过滤器中修改字符编码,在过滤器中修改request的参数(链路跟踪)
interceptor
日志,在方法调用前后打印出字符串
参考文献
https://blog.csdn.net/sundenskyqq/article/details/8549932
https://blog.csdn.net/tanga842428/article/details/52175683
https://blog.csdn.net/zxd1435513775/article/details/80556034
性能监控
为什么要性能监控
了解系统的运行情况,有计划的改善项目的运行,提供系统的性能,保证系统稳定性等
监控哪些
系统层面
last min avg max
cpu
- cpu jumps
- context switchs per second:线程的切换,切换过多会导致cpu忙于切换,影响吞吐率
- interrupts per second
- cpu load cpu使用队列的长度的统计信息
- processor load (1 min average per core)
- processor load (5 min average per core)
- processor load (15 min average per core)
- cpu utilization 利用率
- cpu idle time
- cpu user time
- cpu system time
- cpu iowait time
- cpu nice time
- cpu interrupt time
- cpu steal time
memory内存
- memory usage
- avaulable memory
磁盘
- disk space usage
- total disk space in
- free disk space on
- disk space /opt
network网络
- network traffic on eth0
- incoming network traffic on eth0
- outgoing network traffic on eth0
软件层面
数据库
tomcat
jvm
应用方面
应用类型
- IO相关:Io相关的应用通常用来处理大量数据,需要大量内存和存储,频繁IO操作读写操作,而对cpu的要求比较少,大部分时候cpu再等待磁盘eg:数据库服务器、文件服务器
- CPU相关:CPU相关大的应用需要大量cpu资源,比如高并发的web/mail服务器、图像、视频处理、科学计算等
方法论
压测
jmeter
线程就是并发数、吞吐量就是QPS
监控工具
| 工具 | 简单介绍 |
|---|---|
| top | 查看进程活动状态以及一些系统状况 |
| vmstat | 查看系统状态、硬件和系统信息等 |
| iostat | 查看cpu负载、硬盘状态 |
| sar | 综合工具、查看系统状态 |
| mpstat | 查看多处理器状况 |
| netstat | 查看网络状况 |
| iptraf | 实时网络状况检测 |
| tcpdump | 抓取网络数据包、详细分析 |
| mpstat | 查看多处理器状况 |
| tcptrace | 数据包分析工具 |
| netperf | 网络带宽工具 |
| dstat | 综合工具、综合了vmstat\iostat\ifstat\netstat等 |
参考文献
https://blog.csdn.net/tianlesoftware/article/details/6198780
缓存初始
什么是缓存
临时存放数据(读多写少)的地方,介于外部请求和真实数据之间;
什么情况下要缓存
数据的类型,业务的特点:读多写少,在正常工作的前提下响应时间尽可能短。
缓存的类型
根据缓存位置的不同的不同分为硬件缓存,客户端缓存,服务端缓存
- 硬件缓存:硬盘缓存和cpu缓存,提高硬盘和cpu之间的暂存器
- 客户端缓存:会把用户之前浏览的东西存在本地,在下次访问是,如果本地的缓存有请求的内容,就直接取,不再向服务器请求
- 服务端缓存:如果每次客户端请求都要链接一次数据库,当用户请求多的时候,负载过大,这时把一些经常请求的数据存放在内存中,当有请求时直接返回,不经过数据库,这样就可以减轻数据库负担
进程缓存or分布式缓存or多级缓存
失效策略(缓存算法)
如果缓存满了,而当前请求又没有命中缓存,那么会按照一种策略,把缓存中的某一种旧资源剔除,而把新的资源加入缓存,这些决定应该剔除哪个旧资源的策略就是失效策略
- 成本:如果缓存对象有不同的存储成本,应该把那些难以获得的对象保存下来
- 容量:如果缓存对象有不同的大小,应该把那些大的缓存清除,让更多的小缓存对象进来
- 时间:一些缓存设定有过期时间,应该在到时间之后将他们失效
常见失效策略
simple time-based
通过绝对的时间周期删除缓存对象,对于新增的对象,保存设定的时间后,将其失效,原理简单,效果不好
extended time-based expiration
通过相对时间去失效缓存对象,对于新增的缓存对象,会保存特定的时间,比如每5分钟,每天12点
sliding time-based expiration
计算最后访问这个缓存的时间算起,最后一次访问对象1小时后
FIFO:First in First Out
判断被存储的时间,最早缓存数据优先被剔除
LRU:Least Recently Used
最近最少,使用一个链表来保存。新数据插入链表头部,命中后,将数据移到链表头部,满了后,尾部数据丢弃,有个热点数据有段时间没访问,会导致这个热点数据淘汰
LFU:Least Frequently Used
最不经常使用原则,在一段时间内,数据命中 次数最少,优先被剔除,利用额外的空间记录每个数据的使用频率,选出频率最低的进行淘汰
对比与分析
失效策略出发点有两个,基于时间(FIFO)、基于命中次数(LRU,MRU,2Q),最常使用的时FIFO,LRU,LFU
不同的访问模型导致命中率变化较大,实际应用中需要根据业务的需求和对数据的访问请求进行选择,并不是命中率越高越好,考虑点包括:命中率,复杂度?,代价(时间复杂度、空间复杂度)等,如何衡量。。。
更新缓存的方法
定时失效
mq通知
缓存问题
缓存穿透
查询一个不存在的数据,首先,缓存命不中,数据库查不到,这样的请求每次都会请求数据库,流量大的时候,可能数据库就挂了。
解决办法:
- 过滤器:布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截
- 设定指定值:如果一个查询返回的数据为空,依然要把空结果进行缓存,缓存的值设定位一个指定值,同时设置它的过期时间很短,最长不超过5分钟
缓存雪崩
在某一时刻,大量缓存同时失效,请求全部到数据库,导致数据压力过大
解决办法:
- 加锁或者队列的方式保证缓存的单线程来写,把失效时间分开
- 将数据失效时间均匀分布在时间轴上,在原有的失效时间基础上增加一个随机值
缓存并发
如果一个缓存如果失效,可能出现多个进程同时查询DB,
解决办法:
对缓存查询加锁,如果key不存在,就加锁,然后查DB写入缓存,然后解锁
大厂经验
爱奇艺
- 数据同步加Redis:依赖于Redis,一旦redis挂了,整个缓存系统不可用
- javamap加redis:进程内缓存作为一级缓存,redis作为二级缓存,进程内缓存无法做到实时更新,当不需要淘汰机制,可以使用map,问题:
- 锁竞争严重
- 不支持过期时间
- 不支持自动刷新
- guava cache加redis:设置写后刷新时间,进行刷新,解决了一直不更新的问题,但是依然没有解决实时刷新
- 外部缓存异步刷新:利用redis作为消息队列通知机制,通知其他应用进行刷新