1.Mybatis
一、概念
mybatis是一个优秀的持久层框架,他对jdbc操作数据库的过程进行了封装,使开发着只用关注sql本身,不用去关注例如注册驱动,加载链接,得到statement,处理结果集等复杂的过程。
mybatis通过xml或者注解的方式,将要执行的各种sql语句配置起来,并通过Java对象和statement中的sql语句映射生成最终的sql语句,最后由mybatis框架执行sql语句,并将结果映射成Java对象返回。
架构体系
1.接口应用层(对外提供服务)
2.数据处理层(处理数据访问问题)
3.基础服务层
二、工作过程
首先会加载核心配置文件里面会自动连接数据库,
并且自动的加载配置好的映射文件.
之后通过sqlSession调用方法执行sql时,
根据用户输入的namespace,确定是哪一个映射文件,在根据ID找到映射文件中的sql语句.
开始执行sql语句.拿到结果集之后,再次根据配置好的映射对象,自动实现对象的封装
三、编程步骤
Step01:创建maven 桌面项目(Java 项目)
Step02:添加mybatis依赖以及mysql驱动依赖
Step03:创建mybatis 配置文件,映射文件
Step04:配置数据访问(配置文件),SQL映射(映射文件)
Step05:创建MyBatis API(例如SqlSession)对象,执行SQL操作.
四、缓存机制
Mybatis中含有二级缓存
1 一级缓存,在sqlSession内部共享数据
Mybatis中默认开启一级缓存,在一级缓存内部共享数据
2 二级缓存,在sqlSessionFactory内共享数据
Mybatis中默认的二级缓存是关闭的,标签
六、MyBatis开发优势
1)封装了JDBC共性,简化了代码的编写,提高了代码的开发速度,以及可维护性。
2)合理的架构设计,提高了系统的稳定性,访问性能,可扩展性。
七、MyBatis开发劣势
1)SQL语句编写的工作量相对较大。(相对hibernate框架)
2)SQL语句依赖于数据库,移植性相对较差。(不是最大劣势)
八、Dao接口的工作原理
1.Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。
2.Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement,举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。
3.在Mybatis中,每一个、、、标签,都会被解析为一个MappedStatement对象。
4. Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
5. Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
九、Mybatis是如何进行分页的
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
十、Mybatis将sql执行结果封装形式
第一种是使用标签,逐一定义列名和对象属性名之间的映射关系。
第二种是使用sql列的别名功能,将列别名书写为对象属性名。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
十一、如何获取自动生成的(主)键值
insert 方法总是返回一个int值 - 这个值代表的是插入的行数。
而自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中
十二、在mapper中如何传递多个参数
1.对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可
2.使用 @param 注解
3.多个参数封装成map
十三、Mybatis动态sql
1.Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。
2.Mybatis提供了9种动态sql标签:trim|where|set|foreach|if|choose|when|otherwise|bind。
3.其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。
备注:OGNL它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能
十四、MyBatis实现一对一有几种方式?具体怎么操作的?
有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成; 嵌套查询是先查一个表,根据这个表里面 的结果的外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
十五、MyBatis实现一对多有几种方式,怎么操作的?
有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次,通过在resultMap里面配 置collection节点配置一对多的类就可以完成; 嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。
十六、简述Mybatis的插件运行原理
答:Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
编写插件:实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
十七、Mybatis是否支持延迟加载
答:Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
十八、什么是MyBatis的接口绑定,有什么好处?
接口映射就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定, 我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置.
接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;另外一种就是通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.
当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。
十九、使用MyBatis的mapper接口调用时有哪些要求?
① Mapper接口方法名和mapper.xml中定义的每个sql的id相同
② Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
③ Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
④ Mapper.xml文件中的namespace即是mapper接口的类路径。
a) 构建对象的过程变的复杂了.
b) 对象结构的可读性降低了.
四、编程步骤
1.创建maven 桌面项目
2.添加spring 依赖及配置文件(spring-configs.xml)
3.编写并配置java类(beans.HelloService)
4.初始化容器,获取java对象.
五、Bean对象构建
1.通过构造器实例化Bean对象
2.通过静态工厂方法实例化Bean对象
3.通过实例工厂方法实例化Bean对象。
六、Bean对象生命周期
1)singleton作用域的对象可以在容器关闭时销毁(会调用对象的销毁资源的方法)
2)prototype作用域的对象可以由容器创建对象,初始化对象,但不负责销毁.
七、Bean对象作用域
1.Singleton spring容器中此bean的实例只有一份
单利Bean的最大好处是线程无关性,不存在多线程并发访问的问题,也即是线程安全的。
2.Prototype spring 容器中每次获取bean都会创建新一个新的对象
4. Sping MVC
一、概念
Spring MVC是MVC架构模式的一种完美实现,它简化了Java WEB 中基于MVC架构的编程过程,是Spring中的WEB应用模块。
二、核心组件
1)DispatcherServlet (前端控制器, 处理请求的入口)
2)HandlerMapping (映射器对象, 用于管理url与对应controller的映射关系)
3)Interceptors(拦截器)
4)Controller (后端控制器, 负责处理请求的控制逻辑)
5)ViewResolver(视图解析器,解析对应的视图关系:前缀+view+后缀)
6)HandlerAdapter 处理器适配器 根据特定的规则执行用户的请求.
三、调用过程
第一步:用户发送请求到前端控制器(DispatcherServlet)
第二步:前端控制器请求HandlerMapping查找 能够处理该请求的后台处理器Controller 【可以根据xml配置、注解进行查找】
第三步:处理器映射器HandlerMapping向前端控制器返回该Controller路径
第四步:前端控制器请求处理器适配器去执行该Controller
第五步:处理器适配器去执行Controller
第六步:处理器适配器执行完成后,Controller返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView
ModelAndView是springmvc框架的一个底层对象,包括Model和view
第八步:前端控制器请求视图解析器去进行视图解析【根据逻辑视图名解析成真正的视图(jsp)】
第九步:视图解析器向前端控制器返回View
第十步:前端控制器进行视图渲染【视图渲染即:模型数据(在ModelAndView对象中)填充到request域】
第十一步:前端控制器向用户响应结果
四、编程步骤
Step01:创建maven web 项目并解决项目中的错误问题
Step02:添加项目依赖(spring-webmvc)及spring核心配置文件
Step03:配置前端控制器DispatcherServlet(web.xml)
Step04:创建后端控制器(Controller)及页面
Step05:spring配置文件中配置核心应用组件
Step06:部署及测试springmvc 应用。
Spring中声明式事务处理有两种方式:
一种是在配置文件(xml)中做相关的事务规则声明。
另一种是基于@Transactional 注解的方式。
四、事务属性
事务属性包含5个方面:
传播行为
隔离级别
是否只读
事务超时
回滚规则
通过Shiro框架进行权限管理时,要涉及到的一些核心对象,主要包括:
认证管理对象,授权管理对象,会话管理对象,缓存管理对象,加密管理对象
以及Realm管理对象(领域对象:负责处理认证和授权领域的数据访问题)
1)Subject:与软件交互的一个特定的实体(用户、第三方服务等)。
2)SecurityManager :Shiro 的核心,用来协调管理组件工作。
3)Authenticator:负责执行认证操作
4)Authorizer:负责授权检测
5)SessionManager:负责创建并管理用户 Session 生命周期,提供一个强有力的 Session 体验。
6)SessionDAO:代表 SessionManager 执行 Session 持久(CRUD)动作,它允许任何存储的数据挂接到 session 管理基础上。
7)CacheManager:提供创建缓存实例和管理缓存生命周期的功能
8)Cryptography:提供了加密方式的设计及管理。
9)Realms:是shiro和你的应用程序安全数据之间的桥梁。
三、认证流程
用户访问系统资源时假如这个资源不能匿名访问就需要此用户进行身份认证,在Shiro框中的认证流程如下:
具体流程分析如下:
1)系统调用subject的login方法将用户信息提交给SecurityManager
2)SecurityManager将认证操作委托给认证器对象Authenticator
3)Authenticator借助认证策略对象将身份信息传递给Realm。
4)Realm访问数据库获取用户信息然后对信息进行封装并返回。
5)Authenticator对象对realm返回的信息进行身份认证。
四、授权流程
用户访问系统资源时,首先要检测此资源是否允许匿名访问,假如不允许访问则需要先对用户信息进行认证,认证通过以后能否访问对应的资源还要看是否有访问权限,对用户权限进行检测并授予访问权限的过程称之为授权.Shiro框架中的授权流程如下:
1)系统调用subject相关方法将用户信息(例如isPermitted)递交给SecurityManager
2)SecurityManager将权限检测操作委托给Authorizer对象
3)Authorizer将用户对应权限信息的获取委托给realm对象
4)Realm访问数据库获取用户权限信息并封装返回。
5)Authorizer对用户授权信息进行判定并授权。
8. Ajax
一、概念
Ajax 即 Asynchronous Javascript And XML,是WEB应用程序开发的一种方法,它使用客户端脚本与web 服务器交互数据,并由最初的全局页面同步加载刷新升级为异步局部加载刷新方式动态刷新页面。
二、解决的问题
页面加载性能问题(刷新整个页面快还是刷新局部页面快?)
用户体验问题(页面加载速度快了)
三、应用模型
Ajax异步Web应用模型:(多线程并发,尽量减少客户端的阻塞)
四、Ajax编程步骤
获取Ajax 请求对象(由浏览器端的Ajax引擎提供)
发送Ajax异步请求(发送到服务端)
获取Ajax异步数据(状态监听:状态码4,200)
局部刷新页面 (通过服务端返回的数据)
五、JQuery中的Ajax应用
1.ajax 函数应用
$.ajax({ url: “address”,
type:”GET”,
data: { param1: “foo bar”, param2: "baz“ },
dataType: “json”,
success: successHandlerFunction,
error: errorHandlerFunction,
cache: false,
})
Ajax 函数应用案例
$(function() {
$("#data-button-2").click(processAjaxRequest);
});
function processAjaxRequest () {
var params ={ param1: $("#field3").val(),param2: $("#field4").val() };
$.ajax({ url: “doFindPageObjects.do”,
data: params,
success: function(result){
………
} );
}
2.ajax 函数应用
jquery 中的load函数一般用于在某个位置异步加载某个
url页面,
语法:load(url,[data],[callback])
其中:
1)url 表示远程一个地址
2)data 表示要传递数据(key/value)
3)callback 表示一个回调函数
例如: 案例1
$("#time-result-1").load(“listUI.do”);
例如: 案例2
$(“body”).load(“listUI.do”,{color:red},function(){
……
});
3.getJson函数应用
Jquery中的getJSON函数用于向服务端发起Ajax GET请求
获取json格式的数据
语法:
$.getJSON(“url”,[data], [callback])
例如:
$.getJSON(“doFindPageObjects.do”,{pageCurrent:1},function(result){
//处理返回结果
})
4.post 函数应用
POST方法为Jquery中的发送Ajax post请求的一种方法.
语法:post(url,[data],[callback])
例如:
$.post(“doFindPageObjects.do”,{pageCurrent:1},function(result){
……
})
9. Maven
一、概述
Maven可以一站式的构建项目和管理项目.并且提供了良好的jar包的支持和依赖.
二、Maven工作原理
1.当用户需要使用jar包时,首先检测本地仓库,如果本地仓库中没有则根据配置文件链接私服镜像进行下载.
2.如果私服接收到用户获取jar包的请求.则查找私服中的jar包如果私服中有则返回给用户.如果私服中没有则连接中央仓库下载.
3.如果用户成功获取jar包后,将jar包保存到本地仓库方便下次使用.
三、Maven jar包管理
1.Maven是如何实现jar包依赖?
因为maven在加载jar包时,会自动的扫描该jar包的pom.xml文件,会根据jar包的依赖自动加载.
2.Maven在数据传输时,如何保证jar包文件不被篡改?
{id:1,name:”tom”,age:18}
2.Array格式
例子:
[“value1”,”value2”,”asdfasdf”]
3.复杂格式
说明:将前两种格式进行无限层级的嵌套.
三、Jsonp
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
四、jQuery中JSONP使用
说明:使用jQuery中的JSONP实现跨域请求,效率更高.更简单
操作步骤:
1.定义url
2.定义请求方式
3.设定返回值类型 jsonp
4.获取数据,解析参数
$.ajax({
url:“http://manage.jt.com/web/testJSONP”,
type:“get”,
dataType:“jsonp”, //返回参数的类型要求 hello(json)
//jsonp: “callback”, //指定参数名称
//jsonpCallback: “hello”, //指定回调函数名称
success:function (data){
alert(data.id);
//alert(data.name);
alert(data.age);
//转化为字符串使用
//var obj = eval("("+data+")");
//alert(obj.name);
}
});
调用原理:
1.当用户通过网络地址访问资源时,如果访问的地址刚好是Nginx反向代理的地址.则该请求访问Nginx.
2.Nginx接收到用户请求后,通过内部的配置文件做路径的转换.将image.jt.com动态的转化为e:jt-upload路径.之后请求真实的磁盘资源.
3.当获取到真实的资源后,将数据再次返回给客户端.
4.最终用户得到真实的数据,用户数据回显.
四、Nginx实现负载均衡
1.轮询方式
2.权重策略
3.IP_HASH技术
说明:将用户的IP动态的绑定到特定的服务器中.之后用户的全部的请求都访问特定的服务器.
五、实现Nginx上下线处理
问题描述:
当后台的tomcat服务器,如果宕机,那么如果没有人修改Nginx的配置文件.则会导致Nginx的请求任然会发往宕机的机器.这时只能等待用户连接超时.
方案A:
如果发现宕机的机器,则人为的修改配置文件,将该服务器进行下线处理.
该方案的用途:一般上线部署时使用.
方案B:Nginx健康检测(心跳检测)
说明:Nginx内部有健康检测的机制.如果检测到服务器不能正常连接.那么会在指定的周期内,不会再将用户的请求发往该机器.
如果运维人员将服务器修复后,Nginx的健康检测,查询到该机器可以正常提供服务后,则在下一个周期内.使用该机器.
如果当用户访问恰好访问到故障机.那么在Nginx超时时间过后,访问下一台服务器.为用户提供服务.
Amoeba/Mycat
一、Amoeba介绍
Amoeba是一个以MySQL为底层数据存储,并对应用提供MySQL协议接口的proxy。它集中地响应应用的请求,依据用户事先设置的规则,将SQL请求发送到特定的数据库上执行。基于此可以实现负载均衡、读写分离、高可用性等需求。
二、Amoeba实现读写分离
说明:实现数据库的读写分离,提供数据库处理能力,减少宕机的风险.
当用户有更新操作时,访问主数据库.当用户有读操作时访问从数据库.并且主从实现数据同步.实现读和写分离.高升数据库性能.
三、数据库双机热备形式
根据数据库主从的配置.当主数据库做更新操作时,从库复制备份数据.当主库宕机时,通过某些技术手段可以实现数据库的高可用,实现故障迁移.当用户再有写入操作时,应该将数据写入从库中.如果主库修复完成重启数据库时,主库发现从库的数据较多.则数据库备份将失效.最终导致数据不一致的问题.
解决:B备份A的数据
A备份B的数据
四、Mycat概述
数据库分库分表中间件
国内最活跃的、性能最好的开源数据库中间件!
实现数据库高可用
五、Mycat关键特性
1.支持SQL92标准
2.支持MySQL、Oracle、DB2、SQL Server、PostgreSQL等DB的常见SQL语法
3.遵守Mysql原生协议,跨语言,跨平台,跨数据库的通用中间件代理。
4.基于心跳的自动故障切换,支持读写分离,支持MySQL主从,以及galera cluster集群。
5.基于Nio实现,有效管理线程,解决高并发问题。
6.支持数据的多片自动路由与聚合,支持sum,count,max等常用的聚合函数,支持跨库分页。
7.支持zookeeper协调主从切换、zk序列、配置zk化(1.6)
8.支持库内分表(1.6)
9. 集群基于ZooKeeper管理,在线升级,扩容,智能优化,大数据处理(2.0开发版)。
六、数据库优化策略
1.优化sql语句(多表操作) where 左连接 右连接 内连接
原则:尽可能根据主键查询,尽可能少用关联查询.
2.创建索引(对经常查询的数据创建索引)
3.定期进行数据转储(将一些查询较少的数据保存到历史表,让当前表维护可控的数据量)
4.添加缓存(Redis/MemCache)
5.分库分表(需要大量的数据库服务器)
七、如何优化大型网站的性能
业务数据库 -》 数据水平分割(分区分表分库)、读写分离
业务应用 -》 逻辑代码优化(算法优化)、公共数据分布式缓存
应用服务器 -》 反向静态代理、配置优化、负载均衡(nginx+tomcat集群)
系统环境 -》 JVM调优
页面优化 -》 减少页面连接数、页面尺寸瘦身
缓存:
前端:异步请求+资源静态化+cdn缓存
后端:请求队列+轮询分发+负载均衡+共享缓存
数据层:redis缓存+数据分表+写队列
八、分库分表
Sharding的基本思想就要把一个数据库切分成多个部分放到不同的数据库(server)上,从而缓解单一数据库的性能问题。
不太严格的讲,对于海量数据的数据库,如果是因为表多而数据多,这时候适合使用垂直切分,即把关系紧密(比如同一模块)的表切分出来放在一个server上。
如果表并不多,但每张表的数据非常多,这时候适合水平切分,即把表的数据按某种规则(比如按ID散列)切分到多个数据库(server)上。
当然,现实中更多是这两种情况混杂在一起,这时候需要根据实际情况做出选择,也可能会综合使用垂直与水平切分,从而将原有数据库切分成类似矩阵一样可以无限扩充的数据库(server)阵列。
说明:
1.根据IP+算法经过哈希值运算,最终确定内存的地址
2.当需要set数据时,将key经过hash值运算确定位置后,顺时针方向找到最近的node节点进行数据挂载.
1.均衡性
问题:由于hash算的特点,导致内存划分不均.导致有的节点负载过高,有的节点负载较低的现象.
均衡性:让内存中的数据尽肯能的均衡分配到不同的节点中.保证内存利用率
解决:
引入虚拟节点的概念.虚拟节点可能有多个.
2.单调性
说明:当新增节点时,数据会进行动态的迁移.同时保证已经挂载的数据尽可能的不变.减少服务的负载.
当启动虚拟节点时,数据会动态的挂载到新的节点中.实现数据的动态迁移.
3.分散性
特点:由于分布式的原因用户不能看到全部的内存空间.导致同一个key有多个位置
4.负载
特点:同一个位置存储了多个Key
总结:
好的哈希一致性算法要求,尽可能的降低负载和分散性.解决方式,要求用户使用redis时,应该能够使用全部节点数据
四、为什么使用redis
1.性能
在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应。
2.并发
在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库。
五、使用redis有什么缺点
(一)缓存和数据库双写一致性问题
(二)缓存雪崩问题
(三)缓存击穿问题
(四)缓存的并发竞争问题
六、单线程的redis为什么这么快
1.纯内存操作
2.单线程操作,避免了频繁的上下文切换
3.采用了非阻塞I/O多路复用机制
七、redis的数据类型,以及每种数据类型的使用场景
(一)String
这个其实没啥好说的,最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。
(二)hash
这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。
(三)list
使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。本人还用一个场景,很合适—取行情信息。就也是个生产者和消费者的场景。LIST可以很好的完成排队,先进先出的原则。
(四)set
因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重?因为我们的系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共服务,太麻烦了。
另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
(五)sorted set
sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作。
七、redis的过期策略以及内存淘汰机制
redis采用的是定期删除+惰性删除策略:
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
九、redis和数据库双写一致性问题
分析:一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。答这个问题,先明白一个前提。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。
首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。
十、如何应对缓存穿透和缓存雪崩问题
缓存穿透:即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。
解决方案:
(一)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
(二)采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
(三)提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。
缓存雪崩:即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。
解决方案:
(一)给缓存的失效时间,加上一个随机值,避免集体失效。
(二)使用互斥锁,但是该方案吞吐量明显下降了。
(三)双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。
十一、如何解决redis的并发竞争问题
(1)如果对这个key操作,不要求顺序
这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。
(2)如果对这个key操作,要求顺序
假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC.
期望按照key1的value值按照 valueA–>valueB–>valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。假设时间戳如下
系统A key 1 {valueA 3:00}
系统B key 1 {valueB 3:05}
系统C key 1 {valueC 3:10}
那么,假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。
其他方法,比如利用队列,将set方法变成串行访问也可以。总之,灵活变通。
我们项目在搭建的时候利用完全的松耦合的,分成功能不同的子项目,项目之间的通信我们是利用httpclient实现的,其中有get方法,也有post方法,比较页面上直接url调用,更加安全,不会暴漏,系统间通过http调用不依赖语言,并且实现简单;
二、HttpClient缺点
1.调用层级较多。
2.参数传递只能传输String类型。
3.程序调用复杂。类型转化繁琐。
三、JSONP和httpClient区别
1.发送请求位置不同
Jsonp:浏览器解析JS发起的请求
httpClient:在业务层中模拟http请求协议发起的请求.
2.调用层级不同
Jsonp调用层级只有后台的3级
httpClient调用层级有前天2级后台的3级共5级
3.返回值不同
Jsonp要求返回值必须进行特殊格式封装 callback(JSON)
httpClient可以直接返回json数据.
4.名称不同
跨域请求指jsonp,
httpClient进行业务层远程调用.
5.安全性问题
Jsonp全部执行过程都可以被浏览器监控.安全性弱.
httpClient相对安全.
17. 单点登录
一、概念
SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。
二、业务需求
因为jt-web采用集群的方式进行部署.如果将用户的数据保存到Session中.由于多个服务器之间Session不能共享.导致用户频繁的登陆.如何解决??
三、实现原理
1.当用户输入用户名和密码时,将数据发送给JT-SSO单点登录系统.
2.JT-SSO接收前台数据后,进行用户名和密码的校验.如果用户名和密码错误则告知用户用户名或密码错误;.
如果用户名和密码正确,则为用户生成唯一的一个秘钥TOKEN,将用户对象转化USERJSON数据.之后将token和userJSON保存到redis集群中.token充当key,userJSON充当value.
3.将token信息返回给JT-WEB.JT-WEB将token数据保存到客户端的Cookie中.
4.当用户访问需要权限的系统时,通过request对象获取Cookie信息.之后校验token数据是否正确.如果正确则放行.如果不正确则跳转到用户登陆页面重新登陆.
该业务的实现需要借助拦截器实现.
18. Dubbo
一、微服务思想-服务自动发现
工作原理:
1.当服务端程序启动时,将服务端信息 服务名称/IP:端口写入注册中心。
2.注册中心接口到数据后,自动的更新服务列表。
3.当客户端程序启动时,先链接注册中心,获取最新的服务列表保存到客户端本地。即使这时注册中心宕机,则在一段时间内不会影响服务。
4.当有新的服务器启动时,首先会将新的消息写入注册中。注册中心会维护自身列表。同时将服务列表发送给客户端。进行服务列表的更新。
5.最终实现了服务器自动发现。全自动化
二、SOA思想
面向服务的架构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。
三、RPC
RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
总结:RPC名为远程过程调用协议。底层通过二进制流形成通信。通信时RPC会自动的加密。可以实现远程对象传输。 要求:对象必须序列化
四、Dubbo框架介绍
Dubbo是 [1] 阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 [2] Spring框架无缝集成。
组件介绍:
消费者:获取调用接口的方法,实现其业务功能。
注册中心:负责服务协调调度。管理服务的。
提供者: 负责将接口最终实现。完成业务逻辑操作。
监控: 检测程序的调用过程。哪些是消费者、提供者、IP:端口
dubbo能做什么
远程通讯:基于长连接的NIO框架抽象封装
集群容错:提供多协议支持,支持软负载均衡、失败容错、地址路由、动态配置
自动发现:基于注册中心目录服务,使服务消费方能动态的查找服务提供方,支持平滑减少或增加服务器
五、Dubbo调用流程
调用原理:
1.dubbo调用原理实质将微服务的思想和SOA思想进行融合。
2.当提供者启动时,将自身的信息(服务名称/IP:端口(20880))写入到注册中心。
3.当消费者启动时,首先通过注册中获取全部的服务列表信息。之后保存到本地。
4.当消费者(客户端)需要完成业务时,调用第三方中立接口中的方法即可。如果有参数则直接传参。(RPC协议)
5.提供者接收到请求时,调用接口方法的最终实现类完成业务操作。之后将结果通过RPC返回给消费者即可。
6.当消费者接收到返回数据后。进行后续业务操作。
六、Zookeeper介绍
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
ZooKeeper包含一个简单的原语集, [1] 提供Java和C的接口。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在zookeeper-3.4.3\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。
总结:Zookeeper是一个分布式的调度服务器。
作用:缓解服务器压力.实现了用户请求和入库操作的异步.
特点:平峰削谷
别名:消息中间件
二、RabbitMQ介绍
MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。MQ和JMS类似,但不同的是JMS是SUN JAVA消息中间件服务的一个标准和API定义,而MQ则是遵循了AMQP协议的具体实现和产品。
生产者:将数据写入队列中
消费者:将消息队列中的数据获取后执行.
三、消息队列的工作模式
1.简单模式
工作原理:
1.提供者将消息写入队列中
2.消费者从队列中获取消息后执行.
2.工作模式
说明:由一个生产者负责将消息写入队列中.由多个消费者共同消费一个队列中的消息.使得消息处理更快.
3.发布订阅模式
说明:
X:表示交换机
当生产者生产消息后通过交换机,将消息发往连接交换机的队列中.
特点:
一个消息被执行多次.
应用场景:微信公众号推送,邮件发送
4.路由模式
说明:通过路由key的定义,将消息发往特定的队列中.
5.主题模式
说明:
如果需要给一类路由key发送消息,则使用主题模式.在原有路由模式的基础上添加通配符的概念.
通配符介绍:
*号:可以配个单个.
#号:可以匹配任意个.
20. 全文检索
一、倒排索引
倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。
二、Lucene
1.Lucene介绍
Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。人们经常提到信息检索程序库,虽然与搜索引擎有关,但不应该将信息检索程序库与搜索引擎相混淆
2.全文检索步骤
1.查询全部的数据库数据封装为document对象.
2.为全部的document创建索引文件.
3.当用户输入关键字检索时.根据倒排索引的策略.
根据关键字确定文章的位置.获取文章给用户展现即可.
3.Lucene中缺点
1.如何整表创建索引.效率太低了
2.如何为新增的数据创建索引.
3.如何定期的维护索引.
三、Solr(重点)
Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。
Solr是一个高性能,采用Java5开发,
基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。
总结:
Solr是企业级搜索引擎服务器,可以实现动态的整表的索引创建.同时可以实现索引的动态的更新.
容器:APP真实的运行环境 tomcat/nginx/Redis等服务
Image:镜像文件 内部编辑APP的应用.
远程仓库:保存APP镜像的地址
调用过程:
1.首选通过远程仓库下载镜像到本地.(不可修改)
2.之后通过镜像启动容器(开启服务)
3.用户通过IP:端口形式进行调用
22. SVN
一、概述
SVN是Subversion的简称,是一个开放源代码的版本控制系统,相较于RCS、CVS,它采用了分支管理系统,它的设计目标就是取代CVS。互联网上很多版本控制服务已从CVS迁移到Subversion。说得简单一点SVN就是用于多个人共同开发同一个项目,共用资源的目的。 [1]
二、运行方式
1.独立服务器
2.借助apache
三、数据存储
1.BDB(一种事务安全型表类型)
2.FSFS(一种不需要数据库的存储系统)
因为BDB方式在服务器中断时,有可能锁住数据,所以还是FSFS方式更安全一点。
四、工作流程
集中式管理的工作流程如下图:
集中式代码管理的核心是服务器,所有开发者在开始新一天的工作之前必须从服务器获取代码,然后开发,最后解决冲突,提交。所有的版本信息都放在服务器上。如果脱离了服务器,开发者基本上可以说是无法工作的。下面举例说明:
开始新一天的工作:
1、从服务器下载项目组最新代码。
2、进入自己的分支,进行工作,每隔一个小时向服务器自己的分支提交一次代码(很多人都有这个习惯。因为有时候自己对代码改来改去,最后又想还原到前一个小时的版本,或者看看前一个小时自己修改了哪些代码,就需要这样做了)。
3、下班时间快到了,把自己的分支合并到服务器主分支上,一天的工作完成,并反映给服务器。
五、优缺点
1.优点:
1、管理方便,逻辑明确,符合一般人思维习惯。
2、易于管理,集中式服务器更能保证安全性。
3、代码一致性非常高。
4、适合开发人数不多的项目开发。
2.缺点:
1、服务器压力太大,数据库容量暴增。
2、如果不能连接到服务器上,基本上不可以工作,看上面第二步,如果服务器不能连接上,就不能提交,还原,对比等等。
3、不适合开源开发(开发人数非常非常多,但是Google app engine就是用svn的)。但是一般集中式管理的有非常明确的权限管理机制(例如分支访问限制),可以实现分层管理,从而很好的解决开发人数众多的问题。
23. GIT
一、概述
Git(读音为/gɪt/。)是一个开源的分布式版本控制系统,可以有效、高速的处理从很小到非常大的项目版本管理。
二、功能特性
分布式相比于集中式的最大区别在于开发者可以提交到本地,每个开发者通过克隆(git clone),在本地机器上拷贝一个完整的Git仓库。
下图是经典的git开发过程。
Git的功能特性:
从一般开发者的角度来看,git有以下功能:
1、从服务器上克隆完整的Git仓库(包括代码和版本信息)到单机上。
2、在自己的机器上根据不同的开发目的,创建分支,修改代码。
3、在单机上自己创建的分支上提交代码。
4、在单机上合并分支。
5、把服务器上最新版的代码fetch下来,然后跟自己的主分支合并。
6、生成补丁(patch),把补丁发送给主开发者。
7、看主开发者的反馈,如果主开发者发现两个一般开发者之间有冲突(他们之间可以合作解决的冲突),就会要求他们先解决冲突,然后再由其中一个人提交。如果主开发者可以自己解决,或者没有冲突,就通过。
8、一般开发者之间解决冲突的方法,开发者之间可以使用pull 命令解决冲突,解决完冲突之后再向主开发者提交补丁。
三、优缺点
1.优点:
1.适合分布式开发,强调个体。
2.公共服务器压力和数据量都不会太大。
3.速度快、灵活。
4.任意两个开发者之间可以很容易的解决冲突。
5.离线工作。
2.缺点:
1.资料少(起码中文资料很少)。
2.学习周期相对而言比较长。
3.不符合常规思维。
4.代码保密性差,一旦开发者把整个库克隆下来就可以完全公开所有代码和版本信息。
24. 集合
一、ArrayList和LinkedList区别
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 (LinkedList是双向链表,有next也有previous)
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
可以这样说:
当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;
当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
二、Hashtable以及HashMap和Hashtable的区别
Hashtable HashMap HashSet
继承类 Dictionary AbstractMap AbstractSet
存储 键值对 键值对 对象
KV 不允许为null 允许为 null 允许为null
初始容量 11 16 16
添加元素 put() put() add()
方法 同步 不同步 不同步
哈希值 对key的hashCode 对key的hashcode进行了二次hash 使用成员对象来计算hashcode值
是否线程安全 线程安全 线程不安全
注意:
线程安全类:
vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
statck:堆栈类,先进后出
hashtable:就比hashmap多了个线程安全
enumeration:枚举,相当于迭代器
除了这些之外,其他的都是非线程安全的类和接口。
三、MAP /Set /List 区别
List:
1.可以允许重复的对象。
2.可以插入多个null元素。
3.是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
4.常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
Set:
1.不允许重复对象
2. 无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。
3. 只允许一个 null 元素
4.Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。
Map:
1.Map不是collection的子接口或者实现类。Map是一个接口。
2.Map 的 每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。
3. TreeMap 也通过 Comparator 或者 Comparable 维护了一个排序顺序。
4. Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
5.Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)
四、list,set,map使用场景
1、如果你经常会使用索引来对容器中的元素进行访问,那么 List 是你的正确的选择。如果你已经知道索引了的话,那么 List 的实现类比如 ArrayList 可以提供更快速的访问,如果经常添加删除元素的,那么肯定要选择LinkedList。
2、如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是 List,因为 List 是一个有序容器,它按照插入顺序进行存储。
3、如果你想保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个 Set 的实现类,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的实现类都遵循了统一约束比如唯一性,而且还提供了额外的特性比如 TreeSet 还是一个 SortedSet,所有存储于 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 进行排序。LinkedHashSet 也按照元素的插入顺序对它们进行存储。
4、如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。
五、HashMap工作原理
1、HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。
2、当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket(哈希桶)位置来储存Entry对象(键值对封装成Entry对象)。
3、如果bucket(哈希桶)位置为空,直接放入;
如果bucket(哈希桶)位置有数据,依次用equals()方法比较key是否相等,如果key相等,值覆盖;如果没有相等的,使用链表连在一起。
4.当容量达到0.75的负载率时,数组容量翻倍,数据重新哈希放入新数组。
5.当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。
25.面向对象
一、面向对象三大特征
1.封装
首先,属性能够描述事物的特征,方法能够描述事物的动作。封装就是把同一类事物的共性(包括属性和方法)归到同一类中,方便使用。
封装:封装也称信息隐藏,是指利用抽象数据类型把数据和基于数据的操作封装起来,使其成为一个不可分割的整体,数据隐藏在抽象数据内部,尽可能的隐藏数据细节,只保留一些接口使其与外界发生联系。也就是说用户无需知道内部的数据和方法的具体实现细节,只需根据留在外部的接口进行操作就行。
封装的好处:
1) 实现了专业的分工
2) 良好的封装能够减少耦合
3) 类内部的结构能够自有修改
4) 可以对成员进行更精确的控制
5) 隐藏信息,实现细节
2.继承
继承是面向对象的又一重要特征。继承可以使用不同的类的对象具有相同的行为;为了使用其他类的方法,我们没有必要重新编写这些旧方法,只要这个类(子类)继承包含的方法的类(父类)即可。
从下往上看,继承可以重用父类的功能;从上往下看,继承可以扩展父类的功能
3.多态
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
实现多态,有二种方式,覆盖,重载。
覆盖,是指子类重新定义父类的虚函数的做法。
重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)
26.线程
一、线程安全
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。(Vector,HashTab;le)
线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。(ArrayList,LinkedList,HashMap等)
解决线程安全问题的方法:
1.给共享的资源加把锁,保证每个资源变量每时每刻至多被一个线程占用。
使用锁机制 synchronize、lock方式
2.让线程也拥有资源,不用去共享进程中的资源。
多实例、或者是多副本(ThreadLocal):ThreadLocal可以为每个线程的维护一个私有的本地变量
线程安全工作原理: jvm中有一个main memory对象,每一个线程也有自己的working memory,一个线程对于一个变量variable进行操作的时候, 都需要在自己的working memory里创建一个copy,操作完之后再写入main memory。
当多个线程操作同一个变量variable,就可能出现不可预知的结果。
而用synchronized的关键是建立一个监控monitor,这个monitor可以是要修改的变量,也可以是其他自己认为合适的对象(方法),然后通过给这个monitor加锁来实现线程安全,每个线程在获得这个锁之后,要执行完加载load到working memory 到 use && 指派assign 到 存储store 再到 main memory的过程。才会释放它得到的锁。这样就实现了所谓的线程安全。
二、线程同步
线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作
实现线程同步的方法:
答:wait():使一个线程处于等待状态,并且释放所持有的对象的 lock;
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方
法要捕捉 InterruptedException 异常;
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,
并不能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且
不是按优先级;
notityAll():唤醒所有处入等待状态的线程, 注意并不是给所有唤醒线程一
个对象的锁,而是让它们竞争
Java中交互方式分为同步和异步两种
同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程;
异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。 区别:一个需要等待,一个不需要等待,在部分情况下,我们的项目开发中都会优先选择不需要等待的异步交互方式。
哪些情况建议使用同步交互呢?比如银行的转账系统,对数据库的保存操作等等,都会使用同步交互操作,其余情况都优先使用异步交互。
三、单例模式
1.恶汉模式
//饿汉模式,很饿很着急,所以类加载时即创建实例对象
public class Singleton1 {
private static Singleton1 singleton = new Singleton1();
private Singleton1(){ }
public static Singleton1 getInstance(){
return singleton;
}
}
2.饱汉模式:
//饱汉模式,很饱不着急,延迟加载,啥时候用啥时候创建实例,存在线程安全问题
public class Singleton2 {
private static Singleton2 singleton;
private Singleton2(){ }
public static synchronized Singleton2 getInstance(){
if(singleton == null)
singleton = new Singleton2();
return singleton;
}
}
3.双重锁模式
//饱汉模式的双重锁模式,提高效率
public class Singleton3 {
private static Singleton3 singleton;
private Singleton3(){ }
public static Singleton3 getInstance(){
if(singleton == null){
synchronized(Singleton3.class){
if(singleton == null){
singleton = new Singleton3();
}
}
}
return singleton;
}
}
几种模式的比较:
1、饿汉模式是线程安全的,因为实例对象在类加载过程中就会被创建,在getInstance()方法中只是直接返回对象引用。之所以被称为“饿汉”,是因为这种模式创建实例对象比较“急”,真的是饿坏了……
好处:简单明了,无需关注线程安全问题。
缺点:如果在一个大环境下使用了过多的饿汉单例,则会生产出过多的实例对象,无论你是否要使用他们。
2、饱汉模式不是线程安全的,因为是在需要的时候才会产生实例对象,生产之前会判断对象引用是否为空,这里,如果多个线程同时进入判断,就会生成多个实例对象,这是不符合单例的思想的。所以饱汉模式为了保证线程安全,就用synchronized关键字标识了方法。之所以被称为“饱汉”,因为它很饱,不急着生产实例,在需要的时候才会生产。
好处:延时加载,用的时候才会生产对象。
缺点:需要保证同步,付出效率的代价。
3、双重锁模式,是饱汉模式的优化,进行双重判断,当已经创建过实例对象后就无需加锁,提高效率。也是一种推荐使用的方式。
四、线程生命周期
(1)新建(new Thread)
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
例如:Thread? t1=new Thread();
(2)就绪(runnable)
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();
(3)运行(running)
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
(4)堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
正在等待:调用wait()方法。(调用motify()方法回到就绪状态)
被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
(5)死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
自然终止:正常运行run()方法后终止
异常终止:调用stop()方法让一个线程终止运行
27.HTTP
一、请求
(1)请求行:
规定了请求方式、请求的资源路径、所遵循协议的版本, 例如:
GET / HTTP/1.1
GET: 指定浏览器发送请求的方式, 常用的有GET和POST
/ : 指定浏览器所请求资源的路径, / 表示访问服务器的主页
HTTP/1.1: 指定请求所遵循的协议及版本
(2)若干请求头:
定义了浏览器发送请求时所指明的一些相关信息, 例如:
Accept: text/html 指明浏览器能够接收的数据类型
Accept-Language: zh-ch 指明浏览器能够接收的语言环境
Host: www.it211.com.cn 指明浏览器所请求的主机名
…
(此处有一个空行, 用于表明请求头结束了, 下面的内容为请求实体内容)
(3)请求实体内容
当请求方式为GET时, 请求实体内容中没有数据
只有当请求方式为POST时, 并且发送请求时, 请求中携带了参数信息, 此时请求实体内容中才会有数据.
二、响应
(1)状态行:
指明了服务器响应时所遵循的协议及版本、请求处理的结果、描述信息, 例如
HTTP/1.1 200 OK
HTTP/1.1: 指明服务器响应时所遵循的协议及版本
200: 状态码, 表示服务器处理请求的结果, 是一个三位的数字, 常用的状态码有:
200: 表示服务器成功的处理了请求
302: 表示请求重定向(后面会讲解)
404: 表示客户端请求的资源不存在
500: 表示服务端错误, 处理请求时抛出了异常
OK: 请求处理结果的描述信息(可以不用关注)
(2)若干响应头:
定义服务器在响应时所指明的一些相关信息, 例如:
Content-Type: 指明服务器响应数据的类型
Content-Type:text/html 响应的数据为 html格式的网页
Content-Type:image/jpeg 响应的数据为 jpg格式的图片
Content-Type:image/png响应的数据为 png格式的图片
Content-Type:image/gif响应的数据为 gif格式的图片
Content-Type:image/ico响应的数据为 ico格式的图片
Content-Length: 指定服务器响应的数据大小(单位是字节)
(3)响应实体内容:
浏览器需要解析及显示在窗口中的内容
28. SpringBoot
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
一.特点
二.核心注解
@SpringBootApplication注解,放在入口类,这是整个Spring Boot的核心注解,它的目的就是开启Spring Boot的自动配置,系统会去入口类的同级包以及下级包中去扫描实体类。
三、配置
1.添加父类配置:增加父pom比较简单,而且spring-boot-starter-parent包含了大量配置好的依赖管理
2.编码字符集与JDK版本:1.8及以上
3.添加spring-boot-starter-web依赖:表明是web工程
4.spring-loaded热部署 :spring-boot-devtools
29.SpringCloud
一、微服务概念
简而言之,微服务就是开发一组小型服务的方式来开发一个独立的应用系统,每个小型服务都运行在自己的进程中,并采用HTTP资源API轻量级的机制来互相通信。这些服务围绕业务功能进行构建,并能通过全自动的部署机制来进行独立部署。这些微服务可以使用不同的语言来编写,并且可以使用不同的数据库存储技术。
二、微服务的优点
1.易于开发和维护
业务分拆,一个微服务只关注一个特定的业务功能,所以它的业务清晰、代码量较少。开发和维护单个微服务相对简单。每个微服务业务复杂度低,方便理解、维护和调试。整个应用由若干个微服务构成。
2.单个微服务启动快
单个微服务代码量少,启动比庞大的项目要快。
3.故障隔离
某个服务宕机,其他服务照常使用。单体项目就可能发生雪崩,造成整个系统宕机。
4.局部修改容易部署
传统单体项目修改一个功能就需要重新部署整个应用,而微服务只需对需要修改的服务重新部署,其他服务无需停止,甚至不相关的业务仍然可以继续执行。
5.技术栈不受限
在微服务中,支持技术异构,可以根据软件团队擅长的技术去实现,如java、c#、c、php等,也支持异构数据库mysql、oracle、sqlServer等。
三、Springcloud
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
四、核心组件架构
注册中心Eureka eureka + provider-user + consumer-client
前端负载均衡Ribbon consumer-ribbon
RESTFul简易封装 consumer-ribbon-feign
断路器支持 consumer-ribbon-feign-hystrix
API网关 Zuul gateway-zuul
异构开发语言Sidecar sidecar + nodejs
配置中心config configserver+ consumer-ribbon-feign-hystrix
五、注册中心Eureka
注意它的特点,结构类似于MessageQueue消息队列,服务(提供者、消费者)先都注册到注册中心。它的特点在于,不会每次都去注册中心获取,而是有本地缓存,加快访问性能。内部含有心跳机制,当注册中心信息改变,自动快速获取新的信息到本地。心跳机制还保证分布式环境下,某个服务失败后,自动列表从注册中心移除。注册中心中保证所有可用的链接。
1.ZooKeeper和Eureka对比
Eureka本身是Netflix开源的一款提供服务注册和发现的产品,并且提供了相应的Java封装。在它的实现中,节点之间相互平等,部分注册中心的节点挂掉也不会对集群造成影响,即使集群只剩一个节点存活,也可以正常提供发现服务。哪怕是所有的服务注册节点都挂了,Eureka Clients(客户端)上也会缓存服务调用的信息。这就保证了我们微服务之间的互相调用足够健壮。
Zookeeper主要为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。曾经是Hadoop项目中的一个子项目,用来控制集群中的数据,目前已升级为独立的顶级项目。很多场景下也用它作为Service发现服务解决方案。
六、负载均衡Ribbon
和nginx不同,它是客户端侧负载均衡。
1.负载均衡策略
常见提供的负载均衡算法有三种:
第一种也是默认为轮询
第二种为random随机
第三种为WeightedResponseTimeRule,响应时间
七、Feign
Feigh是一个声明式web服务客户端。它能让开发web服务变得容易。使用Feign需要创建一个接口并注解它。它拥有包括Feign注解和JAX-RS注解的可插拔支持。它还支持可插拔的编码器和解码器。Spring Cloud拥有Spring MVC支持,并使用Spring Web中默认同样的HttpMessageConverters。在使用Feign时,Spring Cloud集成了Ribbon和Eureka来提供负载均衡的HTTP客户端。
总结:Feign简化HttpClient开发,封装了JAX-RS和springmvc的注解,学习成本很低。
1.调用过程
首先,提供者provider-user和消费者custorm-feign都注册到Eureka中。用户请求feign中的controller,feign中的controller调用feign定义的接口方法。接口的方法根据注解去找到eureka注册中心中的provider-user地址,然后请求远程provider-user所在服务器的地址,然后调用远程的provider-user提供者的具体服务。提供者响应返回json,json被feign封装传输给“接口”的返回值,“接口”在返回给feign的controller,最终响应给用户。
Feign是典型的基于接口,基于动态代理技术自动生成代理对象。
八、断路器Hystrix
特别像微服务这样基于多个服务,服务之间都是远程调用,如果一个服务长时间等待,用户体验会极差的,那怎么办呢?断路器模式应运而生。
1.断路器模式
断路器可以实现快速失败,如果它在一段时间内检测到许多失败,如超时,就会强迫其以后的多个调用快速失败,不再请求所依赖的服务,从而防止应用程序不断地尝试执行可能会失败的操作,这样应用程序可以继续执行而不用等待修正错误,或者浪费CPU时间去等待长时间的超时。断路器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。
断路器模式像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。
2.状态
断路器有三种状态:
1.关闭:当访问没有问题时,断路器处于关闭未使用。
2.打开:当访问开始出现异常,错误次数增多,达到阀值时就会打开断路器,这样服务直接访问断路器,进行快速失败返回。
3.半开:那服务一直走断路器,系统就没法用了,万一被调用的服务以及稳定了呢。断路器的优势就来了,过一定时间窗口后(若干秒)它就会自动分流一部分服务再去尝试访问之前失败的服务。如果继续失败,那就不再转发,如果成功了,成功率高了,那会关闭断路器。
3.结构图
当服务B不可用时,开发人员需要写一个Fallback快速失败响应。可以设置为一个固定的值或者一个空值。
Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码。而首次请求往往会比较慢(因为Spring的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了。
九、API网关Zuul
通常来说,使用 API 网关是更好的解决方式。API 网关是一个服务器,也可以说是进入系统的唯一节点。API 网关封装内部系统的架构,并且提供 API 给各个客户端。它还可能还具备授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等功能。下图展示了一个适应当前架构的 API 网关。
API 网关负责服务请求路由、组合及协议转换。客户端的所有请求都首先经过 API 网关,然后由它将请求路由到合适的微服务。API 网关经常会通过调用多个微服务并合并结果来处理一个请求。它可以在 web 协议(如 HTTP 与 WebSocket)与内部使用的非 web 友好协议之间转换。
API 网关还能为每个客户端提供一个定制的 API。通常,它会向移动客户端暴露一个粗粒度的 API。
1.API 网关的优点和缺点
如你所料,使用 API 网关有优点也有不足。使用 API 网关的最大优点是,它封装了应用程序的内部结构。客户端只需要同网关交互,而不必调用特定的服务。API 网关为每一类客户端提供了特定的 API,这减少了客户端与应用程序间的交互次数,还简化了客户端代码。
API 网关也有一些不足。它增加了一个我们必须开发、部署和维护的高可用组件。还有一个风险是,API 网关变成了开发瓶颈。为了暴露每个微服务的端点,开发人员必须更新 API 网关。API网关的更新过程要尽可能地简单,这很重要;否则,为了更新网关,开发人员将不得不排队等待。不过,虽然有这些不足,但对于大多数现实世界的应用程序而言,使用 API 网关是合理的。
2.Zuul
Zuul提供了一个框架,可以对过滤器进行动态的加载,编译,运行。
Zuul可以通过加载动态过滤机制,从而实现以下各项功能:
验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
动态路由: 以动态方式根据需要将请求路由至不同后端集群处。
压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。
十、配置中心
开发分布式系统如果还是各个服务配置文件单独配置肯定是不行的,springcloud使用的解决方案是搭建配置中心将并指定一个配置文件路径如git项目对配置文件进行统一管理。
在Spring Cloud中,提供了分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在实现cloud config中,主要有两个角色:作为配置中心连接配置路径的 config server,连接配置中心读取配置的config client。