形式存储,可以保证唯一。基础实现是通过Node{key,value, next},next指的是一个Entry存储对应的下一个Entry。链表结构形式。
hashMap存储数据结构
JDK1.8之前是数组+链表
JDK1.8开始是数组+l链表+红黑树
当数组中某个链表长度大于8之后是转换成红黑树存储,小于8用的是链表存储。
key是如何保证唯一性的
对key使用hash函数,确保唯一性。hash(key) % 16能够判断落到哪个数组中。hashmap实现用的是 15 & hash(key)位运算,和取模是一个效果,但是计算效率更高。
put操作
通过key来确定是否重复,key重复则直接替换原有的value,不重复则追加到当前链表的末端。
get操作
对key进行hash获取hash值,定位的数组中,在对链表或红黑树查询取值。
底层实现是Hashmap。
线程池的submit和execute方法区别
Callable接口和Runnable接口相似,区别就是Callable需要实现call方法,而Runnable需要实现run方法;并且,call方法还可以返回任何对象,无论是什么对象。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
JDK动态代理和CGLIB字节码生成的区别
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
以下是Spring 框架的基本模块:
这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。
Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从正真的应用代码中分离。
最常用的BeanFactory 实现是XmlBeanFactory 类。
通过listener入口,核心是在AbstractApplicationContext的refresh方法,在此处进行装载bean工厂,bean,创建bean实例,拦截器,后置处理器等。
SpringMVC核心处理流程:
1、DispatcherServlet前端控制器接收发过来的请求,交给HandlerMapping处理器映射器
2、HandlerMapping处理器映射器,根据请求路径找到相应的HandlerAdapter处理器适配器(处理器适配器就是那些拦截器或Controller)
3、HandlerAdapter处理器适配器,处理一些功能请求,返回一个ModelAndView对象(包括模型数据、逻辑视图名)
4、ViewResolver视图解析器,先根据ModelAndView中设置的View解析具体视图
5、然后再将Model模型中的数据渲染到View上
这些过程都是以DispatcherServlet为中轴线进行的。
七种传播属性。
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/
编程时和申明式
spring的声明式事务配置:
1.
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>/WEB-INF/classes/hibernate.cfg.xmlvalue>
property>
bean>
2. 配置事务管理器
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
property>
bean>
3. 配置事务特性
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
tx:attributes>
tx:advice>
4. 配置哪些类的哪些方法配置事务
<aop:config>
<aop:pointcut id="allManagerMethod" ession="execution(* com.yyaccp.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="allManagerMethod">
aop:config>
https://www.cnblogs.com/dobestself-994395/p/4272429.html
单例注入是通过单例beanFactory进行创建,生命周期是在创建的时候通过接口实现开启,循环注入是通过后置处理器,aop其实就是通过反射进行动态代理,pointcut,advice等。
Aop相关:https://blog.csdn.net/weixin_38399962/article/details/79882226
BeanFactory 可以理解为含有bean集合的工厂类。BeanFactory 包含了种bean的定义,以便在接收到客户端请求时将对应的bean实例化。
BeanFactory还能在实例化对象的时生成协作类之间的关系。此举将bean自身与bean客户端的配置中解放出来。BeanFactory还包含了bean生命周期的控制,调用客户端的初始化方法(initialization methods)和销毁方法(destruction methods)。
从表面上看,application context如同bean factory一样具有bean定义、bean关联关系的设置,根据请求分发bean的功能。但application context在此基础上还提供了其他的功能。
提供了支持国际化的文本消息
统一的资源文件读取方式
已在监听器中注册的bean的事件
核心组件:bean,context,core
我们知道 Bean 包装的是 Object,而 Object 必然有数据,如何给这些数据提供生存环境就是 Context 要解决的问题,对 Context 来说他就是要发现每个 Bean 之间的关系,为它们建立这种关系并且要维护好这种关系。所以 Context 就是一个 Bean 关系的集合,这个关系集合又叫 Ioc 容器,一旦建立起这个 Ioc 容器后 Spring 就可以为你工作了。那 Core 组件又有什么用武之地呢?其实 Core 就是发现、建立和维护每个 Bean 之间的关系所需要的一些列的工具,从这个角度看来,Core 这个组件叫 Util 更能让你理解。
Spring容器初始化-> bean构造器-> bean字段注入-> 【init-method】调用的init-method属性指定的初始化方法->容器初始化成功 ->单例bean的使用->关闭容器->【destroy-method】调用的destroy-method属性指定的初始化方法->bean死亡。
https://www.cnblogs.com/zrtqsk/p/3735273.html
一般@Aspect 注解的切面, 通常可以用切面表达式, 和注解切面来完成我们的切面编程.
https://blog.csdn.net/weixin_38399962/article/details/83894823
• 独立运行
• 简化配置
• 自动配置
• 无代码生成和 XML 配置
• 应用监控
• 上手容易
详情移步: 为什么说JAVA程序员必须掌握SpringBoot?
Starters 可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成 Spring 及其他技术,而不需要到处找示例代码和依赖包。如你想使用 Spring JPA 访问数据库,只要加入 spring-boot-starter-data-jpa 启动器依赖就能使用了。
Starters 包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。
控制反转是应用于软件工程领域中的,在运行时被装配器对象来绑定耦合对象的一种编程技巧,对象之间耦合关系在编译时通常是未知的。在传统的编程方式中,业务逻辑的流程是由应用程序中的早已被设定好关联关系的对象来决定的。在使用控制反转的情况下,业务逻辑的流程是由对象关系图来决定的,该对象关系图由装配器负责实例化,这种实现方式还可以将对象之间的关联关系的定义抽象化。而绑定的过程是通过“依赖注入”实现的。
控制反转是一种以给予应用程序中目标组件更多控制为目的设计范式,并在我们的实际工作中起到了有效的作用。
依赖注入是在编译阶段尚未知所需的功能是来自哪个的类的情况下,将其他对象所依赖的功能对象实例化的模式。这就需要一种机制用来激活相应的组件以提供特定的功能,所以依赖注入是控制反转的基础。否则如果在组件不受框架控制的情况下,框架又怎么知道要创建哪个组件?
在Java中依然注入有以下三种实现方式:
设置过期时间,过期后的删除机制。
CAP定理
一致性(Consistency):对某个指定的客户端来说,读操作能返回最新的写操作。
可用性(Availability) :非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。可用性的两个关键一个是合理的时间,一个是合理的响应。
分区容错性(Partition tolerance): 当出现网络分区后,系统能够继续工作。打个比方,这里集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。
分布式事务的具体方案
两阶段提交方案应用非常广泛,几乎所有商业OLTP数据库都支持XA协议。但是两阶段提交方案锁定资源时间长,对性能影响很大,基本不适合解决微服务事务问题。
TCC方案
TCC方案在电商、金融领域落地较多。TCC方案其实是两阶段提交的一种改进。其将整个业务逻辑的每个分支显式的分成了Try、Confirm、Cancel三个操作。Try部分完成业务的准备工作,confirm部分完成业务的提交,cancel部分完成事务的回滚。
事务开始时,业务应用会向事务协调器注册启动事务。之后业务应用会调用所有服务的try接口,完成一阶段准备。之后事务协调器会根据try接口返回情况,决定调用confirm接口或者cancel接口。如果接口调用失败,会进行重试。
TCC方案让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。 当然TCC方案也有不足之处,集中表现在以下两个方面:
上述原因导致TCC方案大多被研发实力较强、有迫切需求的大公司所采用。微服务倡导服务的轻量化、易部署,而TCC方案中很多事务的处理逻辑需要应用自己编码实现,复杂且开发量大。
基于消息的最终一致性方案
消息一致性方案是通过消息中间件保证上、下游应用数据操作的一致性。基本思路是将本地操作和发送消息放在一个事务中,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。
消息方案从本质上讲是将分布式事务转换为两个本地事务,然后依靠下游业务的重试机制达到最终一致性。基于消息的最终一致性方案对应用侵入性也很高,应用需要进行大量业务改造,成本较高。
GTS–分布式事务解决方案
GTS是一款分布式事务中间件,由阿里巴巴中间件部门研发,可以为微服务架构中的分布式事务提供一站式解决方案。
参考:https://www.cnblogs.com/jiangyu666/p/8522547.html
Server- –多个service
Container级别的:–>engine–》host–>context
Listenter
Connector
Logging、Naming、Session、JMX等等
硬件上选择,操作系统选择,版本选择,jdk选择,配置jvm参数,配置connector的线程数量,开启gzip压缩,trimSpaces,集群。
http://blog.csdn.net/lifetragedy/article/details/7708724
SOA是面向服务的架构,他是一种设计方法,其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能。一个服务 通常以独立的形式存在与操作系统进程中。各个服务之间 通过网络调用。
微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。
SOA和微服务理念都是服务化架构。但是侧重点不同:
区别:
功能 | SOA | 微服务 |
---|---|---|
组件大小 | 大块业务逻辑 | 单独任务或小块业务逻辑 |
耦合 | 通常松耦合 | 总是松耦合 |
公司架构 | 任何类型 | 小型、专注于功能交叉团队 |
管理 | 着重中央管理 | 着重分散管理 |
目标 | 确保应用能够交互操作 | 执行新功能、快速拓展开发团队 |
类型 | Topic | Queue |
---|---|---|
概要 | Publish Subscribe messaging 发布订阅消息 | Point-to-Point 点对点 |
有无状态 | topic数据默认不落地,是无状态的。 | Queue数据默认会在mq服务器上以文件形式保存,比如Active MQ一般保存在$AMQ_HOME\data\kr-store\data下面。也可以配置成DB存储。 |
完整性保障 | 并不保证publisher发布的每条数据,Subscriber都能接受到。 | Queue保证每条数据都能被receiver接收。 |
消息是否会丢失 | 一般来说publisher发布消息到某一个topic时,只有正在监听该topic地址的sub能够接收到消息;如果没有sub在监听,该topic就丢失了。 | Sender发送消息到目标Queue,receiver可以异步接收这个Queue上的消息。Queue上的消息如果暂时没有receiver来取,也不会丢失。 |
消息发布接收策略 | 一对多的消息发布接收策略,监听同一个topic地址的多个sub都能收到publisher发送的消息。Sub接收完通知mq服务器 | 一对一的消息发布接收策略,一个sender发送的消息,只能有一个receiver接收。receiver接收完后,通知mq服务器已接收,mq服务器对queue里的消息采取删除或其他操作。 |
Lock底层实现是利用AQS来实现的,它的基本思想就是一个同步器,支持获取锁和释放锁两个操作。
synchronized底层用的是monitor监视器来实现,monitorenter,monitorexit获取锁和释放锁。
区别
优点:实现简单,语义清晰,便于JVM堆栈跟踪,加锁解锁过程由JVM自动控制,提供了多种优化方案,使用更广泛
缺点:悲观的排他锁,不能进行高级功能
优点:可定时的、可轮询的与可中断的锁获取操作,提供了读写锁、公平锁和非公平锁
缺点:需手动释放锁unlock,不适合JVM进行堆栈跟踪
相同点
都是可重入锁
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点(Node)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
AQS类底层的数据结构是使用双向链表,是队列的一种实现。包括一个head节点和一个tail节点,分别表示头结点和尾节点,其中头结点不存储Thread,仅保存next结点的引用。
当一个线程成功地获取了同步状态(或者锁),其他线程将无法获取到同步状态,转而被构造成为节点并加入到同步队列中,而这个加入队列的过程必须要保证线程安全,因此同步器提供了一个基于CAS的设置尾节点的方法:compareAndSetTail(Node expect,Nodeupdate),它需要传递当前线程“认为”的尾节点和当前节点,只有设置成功后,当前节点才正式与之前的尾节点建立关联。
同步队列遵循FIFO,首节点是获取同步状态成功的节点,首节点的线程在释放同步状态时,将会唤醒后继节点,而后继节点将会在获取同步状态成功时将自己设置为首节点。
设置首节点是通过获取同步状态成功的线程来完成的,由于只有一个线程能够成功获取到同步状态,因此设置头节点的方法并不需要使用CAS来保证,它只需要将首节点设置成为原首节点的后继节点并断开原首节点的next引用即可。
思路:利用队列来实现
利用Nginx自带的ip_hash,根据用户ip自动分配到对应的服务器,保证会话不丢失。使用upstream配置
优点:最简单,不用任何变动,只要改变下nginx.conf 的配置即可实现。
缺点:在重启服务后,等于将后台变成单例,那部分用户的会话还是会丢失,不能实现高可用;
如果用户ip比较集中,访问也会集中到一台服务器,那么负载均衡就失去意义。
利用Redis、memcached等缓存数据库实现会话的缓存。
优点:
性能高,可以实现高并发,大数据的系统支持;
水平扩展较容易,性能也不会降低。
缺点:引入第三方模块,增加架构复杂性,甚至需要修改源代码。
利用Tomcat集群自带的session共享模块。
优点:配置简单,不需要引入第三方模块,不用调整现有架构和代码。
缺点:不适合大规模集群架构,水平扩展存在性能瓶颈。
断路器的实现采用状态机模式 。
断路器概念很简单。它用跟踪故障的监视器包装了一个函数。断路器有3种不同的状态:关闭,打开和半打开:
关闭 - 当一切正常时,断路器保持闭合状态,所有调用都能访问到服务。当故障数超过预定阈值时,断路器跳闸,并进入打开状态。
打开 - 断路器在不执行该服务的情况下为调用返回错误。
半开 - 超时后,断路器切换到半开状态,以测试问题是否仍然存在。如果在这种半开状态下单个调用失败,则断路器再次打开。如果成功,则断路器重置回正常关闭状态。