[片段] 方法参数收集
以前的代码,用于收集当前方法的所有参数,放在map中方便调取
1 | import com.google.common.collect.ImmutableMap; |
附送一段代码,用于将方法中收集的参数转换成Bean
1 | import org.springframework.beans.BeanUtils; |
使用实例:
1 |
|
以前的代码,用于收集当前方法的所有参数,放在map中方便调取
1 | import com.google.common.collect.ImmutableMap; |
附送一段代码,用于将方法中收集的参数转换成Bean
1 | import org.springframework.beans.BeanUtils; |
使用实例:
1 |
|
用来批量加密用@Decrypted注解的String字段,可能还有一些坑。
1 |
|
最简单的解决方法,实现简单。但是在微服务中调用接口次数太多,性能很差。
实现较复杂,但是性能好很多,下面主要介绍这种方法的思路
以分页读取数据为例:
为什么需要所属权限这个字段呢? 因为决定能否看到这行是有你所拥有的所有权限决定的,而决定能否看到哪个列是由这行所拥有的权限决定的。
如何获取该行所拥有的权限呢,我的做法是分不同的权限查询结果通过union 组合起来
记录原始顺序是因为后面分组后会打乱, 为什么要分组?分组后同样的查询才能聚合在一起,可以简化代码
这里我使用的graphql来选择需要查询的字段
可以使用guava Ordering工具类方便生成Compartor
[]: https://blog.yamato.moe/2018/11/06/2018-11-06-biz/ “根据权限查询时避免角色切换的一种思路”
[]: https://blog.yamato.moe/2019/04/04/Mybatis%20ResultSetHandler_2019-04-04%20%E7%BB%AD/ “【片段】 Mybatis ResultSetHandler 实践-续”
[]: https://blog.yamato.moe/2019/01/09/Mybatis%20ResultSetHandler_2019-01-09/ “【片段】 Mybatis ResultSetHandler 实践”
Java IO模型和操作系统IO模型息息相关,之前阻塞/非阻塞,同步/非同步之间的关系一直分不清,所以很有必要了解下操作系统(linux)提供了哪些接口来进行IO。目前我们只需要了解即可,使用相关可以直接查看java io教程。
以使用IO读取数据为例,一般操作系统分为两个独立的阶段进行操作:
这个是最常用的模型,望文生义就是阻塞IO,进行IO的两个阶段会都阻塞程序,直到读取到数据或者返回错误才会返回。
具体来说,通过调用系统recvfrom函数,而recvfrom函数会等到出错或者把数据拷贝到进程完成时才会返回。之后我们程序只需要处理错误或者处理数据就可以了。
阻塞模型对应java中绝大部分IO操作,比如网络请求api,io stream api,该模型优点在于简单直观,缺点在长时间阻塞很难支持大量并发IO请求。
该模型在java中没有对应,所以这里只做简单介绍。
使用轮询方式调用系统recvfrom函数,recvfrom函数在第一阶段完成前一直返回错误,直到第一阶段完成后,阻塞至第二阶段完成。
这个模型稍显鸡肋,特点是在第一阶段是非阻塞的(进程不会被切换),代码相比阻塞模型来说也更复杂。
非常著名的IO模型,可以支持大量并发IO。通过调用select
或者pull
并阻塞,而不是在实际调用系统IO时阻塞。使用select阻塞在第一阶段和Blocking I/O的阻塞不太一样,Blocking I/O阻塞在当前IO操作第一阶段,而I/O复用则可以注册多个I/O在select函数,当有一个I/O就绪时select函数就会返回,如果所有I/O处于第一阶段阻塞状态则select函数阻塞。
相比较Blocking I/O Model和Nonblocking I/O Model,I/O Multiplexing Model明显能在短时间内处理更多的I/O。如果使用多线程+Blocking I/O Model也能达到类似的效果,但是有可能消耗过多线程资源。
I/O Multiplexing Model对应java NIO的Selector等api
该模型在java中没有对应,所以这里只做简单介绍。
该模型特点是第一阶段调用sigaction函数非阻塞返回,在第一阶段完成后发送信号SIGIO
至进程,之后在signal handler
中进行第二阶段处理。相当于对Nonblocking I/O Model的一种改进。
Asynchronous I/O Model相比较Signal-Driven I/O Model的区别在于通知的时机不同:Asynchronous I/O Model在第一和第二阶段都完成时通过信号通知进程操作完成。
Asynchronous I/O Model对应java中AsynchronousSocketChannel,AsynchronousServerSocketChannel 和 AsynchronousFileChannel等api。
使用多个角色查询列表时,会遇到两个维度的不同点:
举一个例子:
假设存在一个登录员工拥有两个角色:
那么,在列表中他能看见:
基本信息 | 薪酬信息 | 长期激励信息 | |
---|---|---|---|
低职级/无长期激励 | √ | √ | x |
低职级/长期激励 | √ | √ | √ |
高职级/无长期激励 | x | x | x |
高职级/长期激励 | √ | x | √ |
基本思路已经在前期概要里介绍,本人已经实践了一段时间,挖了两个深坑正在解决中。
最开始的实现中数据是一条一条读取的,同时薪酬字段属于加密信息,使用了第三方微服务提供解密,读取字段多+解密字段多 导致了在百条分页的情况下接口在超时的边缘不断试探。。。
解决方案:
因为之前为了方便我们解密使用了mybatis的TypeHandler做到字段隐式加解密,目前我们的做法是对于单条数据的加解密,还是保持原来的typeHandler做法,而对批量数据处理,重新写一套数据实体,同时使用mybatis的拦截器对查询的批量数据做批量解密的处理。具体做法可以参见我的另一片文章:【片段】 Mybatis ResultSetHandler 实践-续
批量查询返回的列表中列字段都是一致的,而我们的需求是不同的行能看见不同的列字段,把批量查询出来的列表直接返回是有问题的,这个问题因为疏忽导致了线上的一次故障。
所以目前的思路是先做一次数据批量预取,之后在对列字段做处理,隐藏掉不能看见的字段。
没有想到当时想解决权限查询时避免角色切换这个问题时会遇到这么多困难,想法是正确的,在实际执行时还是困难重重。值得欣慰的在最开始的时候思路和方向都是正确的,同时也把其中遇到的各种问题和心得记录了下来,经过层层积累,才到达现在的高度。
[]: https://blog.yamato.moe/2018/11/06/2018-11-06-biz/ “根据权限查询时避免角色切换的一种思路”
[]: https://blog.yamato.moe/2019/04/04/Mybatis%20ResultSetHandler_2019-04-04%20%E7%BB%AD/ “【片段】 Mybatis ResultSetHandler 实践-续”
[]: https://blog.yamato.moe/2019/01/09/Mybatis%20ResultSetHandler_2019-01-09/ “【片段】 Mybatis ResultSetHandler 实践”
resilience4j 源码还是比较清晰简单的,比较适合阅读。
放一张主要类的结构图:
Retry接口是提供重试功能的入口,主要提供了方法模版,具体校验结构,失败后处理由Context子类实现。
1 | /** |
这里摘抄了一段核心代码,作用是循环直到context.onResult(result)返回true为止,需要留意context.onResult/onRuntimeError/onError可能执行多次, onSuccess只会执行一次,这里每次进入重试都是一个新的context对象。
1 | public boolean onResult(T result) { |
先关注onResult,它负责判断是否需要继续重试,如果通过校验或者重试超过此数,会停止重试。
onRuntimeError/onError, 负责把catch的异常存储在lastRuntimeException中。
1 | public void onSuccess() { |
onSuccess负责统计和发送事件。
总体来说retry比较简单,需要注意的点有一个如果设置了结果校验,如果一直校验不通过,将返回未通过的结果,而不是返回失败。
这次拦截的方法是handleResultSets(Statement stmt),用来批量解密用@Encrypted注解的String字段。
上次的局限是只能批量解密一个对象的所有加密字段,对批量数据来说稍显不足,这个主要改进了这一点。
1 |
|
1 | package app.pooi.common.encrypt; |
纯记录,供自己参考🤣。
1 | private final MybatisProperties properties; |
支持and查询、多选、多字段排序分页,缺少的功能:or 条件
核心类,有一些测试代码,将就一下。另外需要spring-data-redis 2.0版本以上
1 | package app.pooi.redissearch.search; |
辅助类
1 | import lombok.Data; |
做一个轻量级的搜索还是可以的。
一般来说可以使用getGenericSuperclass 获取子类范型信息,但是泛型有嵌套的话想获取完整信息还是有点复杂的。例如:Message<List
guava中有强大的TypeToken帮助你保存复杂泛型信息,可以参考:
1 | ParameterizedTypeReference<Message<T>> responseTypeRef = |
如果需要在spring框架中使用,需要一个适配器:
1 | public class ParameterizedTypeReferenceBuilder { |
关于java的泛型我就不多做吐槽了。
拦截器实现:
1 | package app.pooi.common.entity; |
配置:
1 |
|
目前是使用了spring aop 来拦截方法调用,把方法参数包装成Map形式
1 |
|
1 | public class BinderUtil { |
这次拦截的方法是handleResultSets(Statement stmt),用来批量解密用@Encrypted注解的String字段,可能还有一些坑。
1 |
|
1 | package app.pooi.common.encrypt; |
1 | /** |
稍微注意下在线程争用锁是才会初始化链表
1 | /** |
内部维护state和CLH队列,负责在资源争用时线程入队,资源释放时唤醒队列中线程。
而实现类只需要实现 什么条件获取资源成功 和 什么条件释放资源 成功就可以了
所以,最简单的CountDownLatch使用AbstractQueuedSynchronizer实现非常简单:
申明AbstractQueuedSynchronizer的state数量(比如十个)
await方法尝试获取资源,如果state>0表示获取失败( **什么条件获取资源成功** ,CountDownLatch实现),获取失败线程休眠(AbstractQueuedSynchronizer负责)
countDown方法state-1,如果state==0表示资源释放成功( **什么条件释放资源成功** ,CountDownLatch实现),唤醒队列中所有线程(AbstractQueuedSynchronizer负责)
顺着ReentrantLock lock、unlock看一遍我们就大致总结出AbstractQueuedSynchronizer工作原理了
先简单介绍下ReentrantLock特性:可重入,中断,有超时机制。
黄色表示ReentrantLock实现,绿色表示AbstractQueuedSynchronizer内部实现
1 | **public** final void acquire ( int arg) { |
获取的锁的逻辑:直接获取成功则返回,如果没有获取成功入队休眠(对就是这么简单)
下面我们仔细一个一个方法看
我这里贴的时非公平的所获取,公平和不公平的区别在于公平锁老老实实的会进入队列排队,非公平锁会先检查资源是否可用,如果可用不管队列中的情况直接尝试获取锁。
1 | final boolean nonfairTryAcquire ( int acquires) { |
ReentrantLock.tryAcquire读取到state==0时尝试占用锁,并保证同一线程可以重复占用。其他情况下获取资源失败。如果获取成功就没啥事了,不过关键不就是锁争用的时候是如何处理的吗?
1 | private Node addWaiter ( Node mode) { |
一旦锁争用,一定会初始化队列(因为排队的线程需要前驱节点唤醒,所以要初始化一个前驱节点),之后自旋成为队列尾节点。
简单来说就是获取不到锁就放进队列里维护起来,等锁释放的时候再用。
这里还有一个 很具有参考性的小细节 :先设置新节点的前驱结点,自旋成为尾节点后设置前驱的后驱
1 | final boolean acquireQueued ( final Node node, int arg) { |
前面只是维护下链表数据结构,这里负责找到合适的唤醒前驱,然后让线程休眠。
这里主要是一个循环过程:
1 | public final boolean release ( int arg) { |
unlock的代码特别简单:
Update your browser to view this website correctly.&npsb;Update my browser now