JDK动态代理和CGlib动态代理
JDK动态代理JDK动态代理是Spring动态代理的默认实现方法。如果我们的类实现了一个接口,那么Spring就会使用这种方法,因为使用JDK动态代理的一个缺陷就是代理的类必须实现接口。
JDK实现动态代理需要两个组件,首先第一个就是InvocationHandler接口。我们在使用JDK的动态代理时,需要编写一个类,去实现这个接口,然后重写invoke方法,这个方法其实就是我们提供的代理方法。
1234567891011121314151617181920212223242526/** * 这里是一个动态的代理类,只需要传入对应的类,就会执行对应类的对应方法 * 也就是说我们只用实现这一个代理类就行了,我们所有相同的代理实现都可以用该类 */public class MyInvocationHandler implements InvocationHandler { // 目标对象 private Object target; public MyInvocationHandler(Object target) { this.targ ...
ThreadLocal源码
以下源码是基于JDK11。
Get以及Set一开始,一直在疑惑这个ThreadLocal到底是用来干嘛的,其实他可以用来存储一些变量,这些变量普通存储会由于被多线程访问或导致一些并发问题,用ThreadLocal存储,就会为每一个线程存储它自己的副本。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354public T get() { // 这一步,它首先获取到了当前的线程,也就是说哪一个线程去调用ThreadLocal,就获取到哪一个线程。 Thread t = Thread.currentThread(); // 这一步就是获取到线程对应的ThreadLocalMap,后文会有具体的结构 ThreadLocal.ThreadLocalMap map = this.getMap(t); if (map != null) { // 这里就是从ThreadLocalMap中获取到 ...
如何使用3m内存来找到1G文件中词频前100的单词
题目原场景与标题相似,只不过文件的每一行是一个单词,然后在内存只有3m空间,需要获取到1G文件中所有单词频率前100的单词。
想到了用bitmap。
bitmapbitmap往往是做一个映射,来减小数原数据占用空间的大小。在Java中,一个int类型的大小是32位,也就是32bit,它占4字节,也就是4byte,现考虑如下场景:
现在要存储三个数字,比如说5,16,20,如果用一个int数组存储,那么就需要3 * 4 = 12字节,但是我们可以换一个思路,比如说使用byte数组,1byte是8位,也就是说可以通过这8位的值位0或1,来代表是否存在数字0-7。那么原来的4字节,就可以代表数字0-31是否存在于集合当中。具体如下表所示
数组值
1
1
0
0
0
0
1
1
下标
0
1
2
3
4
5
6
7
说明
值为1,代表存在数字0
值为1,代表存在数字1
值为0,代表不存在数字2
值为0,代表不存在数字3
值为0,代表不存在数字4
值为0,代表不存在数字5
值为1,代表存在数字6
值为1,代表存在数字7
通过这种转换,我们就使用了1字节存储原来需要1 ...
Spring的事务传播
事务传播
写在前面
Spring的事务是通过AOP这种代理的方式实现的。
事务传播就是多个事务方法相互调用时,事务如何在这些方法间传播。比如事务方法A调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务执行造成影响,而方法A的事务对方法B的事务执行也有影响,这种影响就由两个方法定义的事务传播类型所决定。
spring中的事务传播定义了七种类型:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。这七种类型以枚举的形式存储在org.springframework.transaction.annotation的Propagation。
下面根据集体的例子来解释每一个情况。
假设现在有两个方法A和B,A会在ATable中插入一条数据,B会在BTable中插入一条数据。伪代码如下:
12345678// 将传入参数a存入ATablepubilc void A(a){ insertIntoATable(a); }// 将传入参数b存入BTablepubli ...
HashMap源码
HashMap源码get操作12345678910111213141516171819202122232425262728293031323334353637public V get(Object key) { HashMap.Node e; return (e = this.getNode(hash(key), key)) == null ? null : e.value;}final HashMap.Node<K, V> getNode(int hash, Object key) { HashMap.Node[] tab; HashMap.Node first; int n; // 这里,先把tab指向了整个类的table,也就是先获取目前存储的所有元素 // 同时判断是否为空,长度是否大于0,并初始化first的值,n - 1 & hash是为了保证hash值有效 if ((tab = this.table) != null && (n = tab.length) & ...
单点登录
单点登录一个真正的单点登录流程如下:该流程属于不同域下的单点登录。
用户进入 A 系统,没有登录凭证(ticket),A 系统给他跳到 SSO。
SSO 没登录过,也就没有 sso 系统下没有凭证(怎么感知到的)(注意这个和前面 A ticket 是两回事),输入账号密码登录。
SSO 账号密码验证成功,通过接口返回做两件事:一是种下 sso 系统下凭证(记录用户在 SSO 登录状态);二是下发一个 ticket。
客户端拿到 ticket,保存起来,带着请求系统 A 接口。
系统 A 校验 ticket,成功后正常处理业务请求。
此时用户第一次进入系统 B,没有登录凭证(ticket),B 系统给他跳到 SSO。
SSO 登录过,系统下有凭证,不用再次登录,只需要下发 ticket。
客户端拿到 ticket,保存起来,并携带ticket请求系统B的接口。
至此,还存在问题,SSO 域下返回的数据要怎么存,才能在访问 A 的时候带上?因为浏览器对跨域有严格限制,cookie、localStorage 等方式都是有域限制的。
下面这张图是一个更细致的流程:
在 SSO 域下, ...
MySQL limit 1和limit 10000,1性能一样吗
考虑两条MySQL语句的执行,其中key1是二级索引,id是主键。
1select * from my_table order by key1 limit 1;
该条语句,key1是二级索引,本身就是有序的,那么当查询一条语句之后,它就会直接回表查询具体的数据然后返回。
但是如果SQL变为下面的写法:
1select * from my_table order by key1 limit 10000, 1;
执行这条语句就会发现它走了全表的扫描 + filesort,它并没有像想象中的那样,扫描到10001的索引,然后回表返回结果。
limit的执行MySQL分为Server层和具体的存储引擎层,Server层是统一的,而存储引擎我们这里默认使用InnoDB。
当具体执行一个带有limit操作的SQL时,它并不会在存储引擎层进行过滤,存储引擎查询到所需要的所有数据,然后返回给Server层,Server层依据具体的查询需求来进行过滤,也就是说需要先查询10001条数据返回给Server,然后进行过滤。
优化器选择优化器在执行这个查询时会有两种选择
全表扫描,然后返回,过滤掉不需 ...
远程调用
远程调用本地调用通常指的是,进程内函数之间的相互调用;而远程调用,是进程间函数的相互调用,是进程间通信 IPC(Inter-Process Communication)的一种方式。
根据进程是否部署在一台机器上,远程调用可以分为以下两类:
本地过程调用(Local Procedure Call,LPC),是指运行在同一台机器上的进程之间 的互相通信,即在多进程操作系统中,运行的不同进程之间可以通过 LPC 进行函数调 用。
远程过程调用(Remote Procedure Call,RPC),是指不同机器中运行的进程之间的 相互通信,某一机器上运行的进程在不知道底层通信细节的情况下,就像访问本地服务 一样,去调用远程机器上的服务。
远程调用的原理在B/S ( Browser/Server,浏览器 / 服务器) 架构中,被调用方(服务器)会有一个开放的接口,然后调用方(用户)会通过Browser使用这个接口,来间接的调用相应的服务。
但是,B/S 架构是基于 HTTP 协议实现的,每次调用接口时,都需要先进行 HTTP 请求,比较的耗时,不适合分 ...
服务治理
服务治理手段服务的调用涉及到注册中心,服务调用的发起方和提供方三者,这三者中任意两个的通信都是通过网络,那么不论是网络出现问题,还是说这三者中任意一个出现问题,此次调用都会失败。所以就需要服务治理。
节点管理该部分可以分为两点。
注册中心主动摘除机制。这种机制要求服务提供者定时向注册中心汇报心跳,当两次汇报中间的时间超过规定时间后,就认为服务提供者出现问题,进行摘除,然后把最近的可用服务列表推送给服务消费者。
服务消费者摘除机制。上述机制可能因为网络问题导致一个正常的服务被摘除,最坏情况所有服务都正常,但是全被摘除。一个更合理的摘除办法就是让服务的调用者去执行。即服务调用者调用失败时,就从本地缓存的可用服务列表中去移除该服务,而不进行实际的摘除。
负载均衡这里边有几个算法:
随机算法
轮询算法
最少活跃调用算法
一致性Hash算法
服务路由对于服务消费者而言,在内存中的可用服务节点列表中选择哪个节点不仅由负载均衡算法决定,还由路由规则确定。而指定路由规则的主要原因有以下两点:
服务功能做了变更,需要灰度发布,根据这部分人的使用反馈来决定是否全量发布时,就可以做一些路由规则的限 ...
多主复制
多主复制在主从复制的场景中,只有一个主节点,所有的写入操作都要先经过主节点,主节点压力大的问题还是没能解决。而且单主节点的容灾效果也不是很好。
为了达到好的容灾效果,各个机房的距离应该足够远,尽可能的分布在不同的地区,那么这种场景下,用户直接读写自己最近的数据中心,网络延迟最小,效果最好。因此就出现了多住复制。
如何实现它是指在一个数据系统中,存在多个主从 复制单元,每一个主从复制单元都可以处理读写请求,一个主从复制单元的主副本处理了写请 求后,需要复制到其他的主从复制单元的主副本,具体的流程见下图:
有几点需要注意:
首先,每一个主从复制单元内部是一个常规的主 从复制模式,这里的主副本、从副本之间的复制可以是同步的,也可以是异步的。
其次,多个主从复制单元之间,每一个主副本都会将自己的修改复制到其他的主副本,主副本 之间的复制可以是同步的,也可以是异步的。
如果主副本之间的复制是同步的,那么一个主副本的写入,需要等待复制到其他的主副本成功 后,才能返回给用户,但是,这样却失去了多主复制最重要的一个优点,即多个主副本都可以独立处理写入,这就导致整个模式 退化为主从复制的形式。所以一 ...