面试总结 :
Dubbo并发通信原理解析
从Dubbo开源文档中看到:Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的
情况。
Dubbo通信默认采用的是Netty框架。Netty实质就是通过Socket进行通信,Socket(TCP)通信是全双工的方式。
因为采用单一长连接,所以如果消费者多线程请求,服务端处理完消息后返回,就会造成消息错乱的问题。解决这个问题的思路跟解决socket中的粘包问题类似。
socket粘包问题解决方法:用的最多的其实是定义一个定长的数据包头,其中包含了完整数据包的长度,以此来完成服务器端拆包工作。
类似的,那么dubbo解决上述问题的方法:就是给包头中添加一个全局唯一标识id,服务器端响应请求时也要携带这个id,供客户端多线程领取对应的响应数据提供
线索。
分布式服务框架:
–高性能和透明化的RPC远程服务调用方案
–SOA服务治理方案
-Apache MINA 框架基于Reactor模型通信框架,基于tcp长连接
Dubbo缺省协议采用单一长连接和NIO异步通讯,
适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况
分析源代码,基本原理如下:
client一个线程调用远程接口,生成一个唯一的ID(比如一段随机字符串,UUID等),Dubbo是使用AtomicLong从0开始累计数字的
将打包的方法调用信息(如调用的接口名称,方法名称,参数值列表等),和处理结果的回调对象callback,全部封装在一起,组成一个对象object
向专门存放调用信息的全局ConcurrentHashMap里面put(ID, object)
将ID和打包的方法调用信息封装成一对象connRequest,使用IoSession.write(connRequest)异步发送出去
当前线程再使用callback的get()方法试图获取远程返回的结果,在get()内部,则使用synchronized获取回调对象callback的锁, 再先检测是否已经获取到结果,
如果没有,然后调用callback的wait()方法,释放callback上的锁,让当前线程处于等待状态。
服务端接收到请求并处理后,将结果(此结果中包含了前面的ID,即回传)发送给客户端,客户端socket连接上专门监听消息的线程收到消息,分析结果,取到ID,
再从前面的ConcurrentHashMap里面get(ID),从而找到callback,将方法调用结果设置到callback对象里。
监听线程接着使用synchronized获取回调对象callback的锁(因为前面调用过wait(),那个线程已释放callback的锁了),再notifyAll(),唤醒前面处于等待状态
的线程继续执行(callback的get()方法继续执行就能拿到调用结果了),至此,整个过程结束。
当前线程怎么让它“暂停”,等结果回来后,再向后执行?
答:先生成一个对象obj,在一个全局map里put(ID,obj)存放起来,再用synchronized获取obj锁,再调用obj.wait()让当前线程处于等待状态,然后另一消息
监听线程等到服 务端结果来了后,再map.get(ID)找到obj,再用synchronized获取obj锁,再调用obj.notifyAll()唤醒前面处于等待状态的线程。
正如前面所说,Socket通信是一个全双工的方式,如果有多个线程同时进行远程方法调用,这时建立在client server之间的socket连接上会有很多双方发送的消息
传递,前后顺序也可能是乱七八糟的,server处理完结果后,将结果消息发送给client,client收到很多消息,怎么知道哪个消息结果是原先哪个线程调用的?
答:使用一个ID,让其唯一,然后传递给服务端,再服务端又回传回来,这样就知道结果是原先哪个线程的了。
Redis常用命令
keys键操作
exists-----测试key是否存在
del-----删除key
type-----返回key的类型
keys-----匹配满足的key
rename------改key名
dbsize-----当前数据库key的数量
expire-----设置key过期时间
ttl-----key剩余过期时间
move-----将key移动到指定数据库
flushdb-----删除当前数据库的所有key
flushall-----删除所有数据库的key
String键操作
set-----设置单个key
mset-----批量添加key
mget-----批量获取
incr-----key值+1
decr-----key值-1
incrby-----对key加指定值
decrby-----对key减定值
appeand-----在key值后追加
substr-----截取字符串(包前包后)
List类型操作
lpush-----在list头部添加
rpop-----批量添加
llen-----(存在对应key返回长度,反之-1,类型不对应会报错)
lrange-----在指定区间查找元素
rpush-----删除尾部元素
lpop-----删除头部元素
ltrim-----截取list(保留)
Set类型操作
sadd-----添加set
srem-----删除set中的指定元素
smove set1 set2-----将元素从set1转移到set2
scard-----返回set中元素的个数
sismember-----判断元素是否在set中
sinter set1 set2 set3...-----给所定set的交集
sunion
set1 set2 set3...-----给所定set的并集
sdiff set1 set2 set3...-----给所定set的差集
smembers-----返回set所对应的元素
Scort set
zadd-----添加
zrem--删除
zincrby (键 加值 元素) -----对set指定元素加值
zrank-----返回元素下标(按‘权’从小到大排列)
zrevrank-----返回元素下标(按‘权’从大到小排列)
zrange-----返回集合,(按‘权’从小到大排列)
zrevrange-----返回集合(按‘权’从大到小排列)
zcard-----返回集合元素个数zscorde-----返回给定元素的‘权’
zremrangebyrank-----删除集合指定区间的元素
使用命令fdisk -l查看当前磁盘的分区状态
df -lh(df -k或者df -m) 是来自于coreutils 软件包,系统安装时,就自带的;我们通过这个命令可以查看磁盘的使用情况以及文件系统被挂载的位置;
查询项目日志 : tail -f catalina.out
SpringMVC与Struts2区别与比较总结
一、框架机制
1、Struts2采用Filter(StrutsPrepareAndExecuteFilter)实现,SpringMVC(DispatcherServlet)则采用Servlet实现。
2、Filter在容器启动之后即初始化;服务停止以后坠毁,晚于Servlet。Servlet在是在调用时初始化,先于Filter调用,服务停止后销毁。
二、拦截机制
1、Struts2
a、Struts2框架是类级别的拦截,每次请求就会创建一个Action,和Spring整合时Struts2的ActionBean注入作用域是原型模式prototype(否则会出现线程并发问题),然后通过setter,getter吧request数据注入到属性。
b、Struts2中,一个Action对应一个request,response上下文,在接收参数时,可以通过属性接收,这说明属性参数是让多个方法共享的。
c、Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了
2、SpringMVC
a、SpringMVC是方法级别的拦截,一个方法对应一个Request上下文,所以方法直接基本上是独立的,独享request,response数据。而每个方法同时又何一个url对应,参数的传递是直接注入到方法中的,是方法所独有的。处理结果通过ModeMap返回给框架。
b、在Spring整合时,SpringMVC的Controller Bean默认单例模式Singleton,所以默认对所有的请求,只会创建一个Controller,有应为没有共享的属性,所以是线程安全的,如果要改变默认的作用域,需要添加@Scope注解修改。
三、性能方面
SpringMVC实现了零配置,由于SpringMVC基于方法的拦截,有加载一次单例模式bean注入。而Struts2是类级别的拦截,每次请求对应实例一个新的Action,需要加载所有的属性值注入,所以,SpringMVC开发效率和性能高于Struts2。
四、拦截机制
Struts2有自己的拦截Interceptor机制,SpringMVC这是用的是独立的Aop方式,这样导致Struts2的配置文件量还是比SpringMVC大。
五、配置方面
spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高(当然Struts2也可以通过不同的目录结构和相关配置做到SpringMVC一样的效果,但是需要xml配置的地方不少)。
SpringMVC可以认为已经100%零配置。
六、设计思想
Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在servlet上扩展。
七、集成方面
SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。
git常用命令 :
git init 创建仓库
git add XX 将代码添加到stage暂存区, 或手动解决代码冲突后标记解决
git commit -m '跟新说明' 提交 ,
git commit -a -m '跟新说明' 把所有已跟踪但未暂存的文件提交
git branch 查看分支
git branch test 创建test分支, git checkout test 切换test分支, git checkout -b test 前两句命令简写创建并且切换到test分支
git branch -d test 删除分支
git merge test (合并分支主要步骤是1:先切换回主线分支2:然后再执行这条命令,将分支代码合并到主线分支上)
git status -s 文件详情 git diff 未暂存作了哪些修改 git diff --stage 暂存区作了哪些修改
虚拟机中类的加载 ;类的数据从class文件加载到内存,并对数据进行效验,类型的加载,连接和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是
虚拟机的类加载机制。
类的加载时间 : 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括 : 加载(Loading),严重,准备,解析,初始化,使用和卸载
七个阶段。其中验证,准备,解析三个部分统称为连接。
加载 : 在加载阶段,虚拟机需要完成三件事情 :
1.通过一个类的全限定名来获取定义类的二进制字节流。
2.将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构。
3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
MyBatis的可以返回key为任何数据的值 :
spring boot与spring mvc的区别是什么?
Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring 的 ioc和 aop ioc 提供了依赖注入的容器 aop ,解决了面向横切面的编程,然后在此两者的基础上实现了其他延伸产品的高级功能。Spring MVC是基于 Servlet 的一个 MVC 框架 主要解决 WEB 开发的问题,因为 Spring 的配置非常复杂,各种XML、 JavaConfig、hin处理起来比较繁琐。于是为了简化开发者的使用,从而创造性地推出了Spring boot,约定优于配置,简化了spring的配置流程。
说得更简便一些:Spring 最初利用“工厂模式”(DI)和“代理模式”(AOP)解耦应用组件。大家觉得挺好用,于是按照这种模式搞了一个 MVC框架(一些用Spring 解耦的组件),用开发 web 应用( SpringMVC )。然后有发现每次开发都写很多样板代码,为了简化工作流程,于是开发出了一些“懒人整合包”(starter),这套就是 Spring Boot。
Spring MVC的功能
Spring MVC提供了一种轻度耦合的方式来开发web应用。
Spring MVC是Spring的一个模块,式一个web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。
Spring Boot的功能
Spring Boot实现了自动配置,降低了项目搭建的复杂度。
众所周知Spring框架需要进行大量的配置,Spring Boot引入自动配置的概念,让项目设置变得很容易。Spring Boot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。
Spring Boot只是承载者,辅助你简化项目搭建过程的。如果承载的是WEB项目,使用Spring MVC作为MVC框架,那么工作流程和你上面描述的是完全一样的,因为这部分工作是Spring MVC做的而不是Spring Boot。
对使用者来说,换用Spring Boot以后,项目初始化方法变了,配置文件变了,另外就是不需要单独安装Tomcat这类容器服务器了,maven打出jar包直接跑起来就是个网站,但你最核心的业务逻辑实现与业务流程实现没有任何变化。
所以,用最简练的语言概括就是:
Spring 是一个“引擎”;
Spring MVC 是基于Spring的一个 MVC 框架 ;
Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。
为什么会产生内存泄漏?
当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。
内存泄漏对程序的影响?
内存泄漏是造成应用程序OOM的主要原因之一。我们知道Android系统为每个应用程序分配的内存是有限的,而当一个应用中产生的内存泄漏比较多时,这就难免会导致应用所需要的内存超过系统分配的内存限额,这就造成了内存溢出从而导致应用Crash。
如何检查和分析内存泄漏?
因为内存泄漏是在堆内存中,所以对我们来说并不是可见的。通常我们可以借助MAT、LeakCanary等工具来检测应用程序是否存在内存泄漏。
1、MAT是一款强大的内存分析工具,功能繁多而复杂。
2、LeakCanary则是由Square开源的一款轻量级的第三方内存泄漏检测工具,当检测到程序中产生内存泄漏时,它将以最直观的方式告诉我们哪里产生了内存泄漏和导致谁泄漏了而不能被回收。
一、Duboo基本概念解释
Dubbo是一种分布式服务框架。 Webservice也是一种服务框架,但是webservice并不是分布式的服务框架,他需要结合F5实现负载均衡。因此,dubbo除了可以提供服务之外,还可以实现软负载均衡。它还提供了两个功能Monitor 监控中心和调用中心。这两个是可选的,需要单独配置。
我们解释以下这个架构图:
Consumer服务消费者,Provider服务提供者。Container服务容器。消费当然是invoke提供者了,invoke这条实线按照图上的说明当然同步的意思了,多说一句,在实际调用过程中,Provider的位置对于Consumer来说是透明的,上一次调用服务的位置(IP地址)和下一次调用服务的位置,是不确定的。这个地方就是实现了软负载。
服务提供者先启动start,然后注册register服务。
消费订阅subscribe服务,如果没有订阅到自己想获得的服务,它会不断的尝试订阅。新的服务注册到注册中心以后,注册中心会将这些服务通过notify到消费者。
Monitor这是一个监控,图中虚线表明Consumer 和Provider通过异步的方式发送消息至Monitor,Consumer和Provider会将信息存放在本地磁盘,平均1min会发送一次信息。Monitor在整个架构中是可选的(图中的虚线并不是可选的意思),Monitor功能需要单独配置,不配置或者配置以后,Monitor挂掉并不会影响服务的调用。
二、dubbo原理
本篇博客的内容总体上比较抽象,如果一个想马上使用dubbo的同学来说,读这篇博客效果不太好,本篇博客没有写怎么使用、配置dubbo,接下来,我再令写一篇dubbo入门包含demo的博客。
分布式服务框架:
–高性能和透明化的RPC远程服务调用方案
–SOA服务治理方案
-Apache MINA 框架基于Reactor模型通信框架,基于TCP长连接
Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。
分析源代码,基本原理如下:
client一个线程调用远程接口,生成一个唯一的ID(比如一段随机字符串,UUID等),Dubbo是使用AtomicLong从0开始累计数字的
将打包的方法调用信息(如调用的接口名称,方法名称,参数值列表等),和处理结果的回调对象callback,全部封装在一起,组成一个对象object
向专门存放调用信息的全局ConcurrentHashMap里面put(ID, object)
将ID和打包的方法调用信息封装成一对象connRequest,使用IoSession.write(connRequest)异步发送出去
当前线程再使用callback的get()方法试图获取远程返回的结果,在get()内部,则使用synchronized获取回调对象callback的锁, 再先检测是否已经获取到结果,如果没有,然后调用callback的wait()方法,释放callback上的锁,让当前线程处于等待状态。
服务端接收到请求并处理后,将结果(此结果中包含了前面的ID,即回传)发送给客户端,客户端socket连接上专门监听消息的线程收到消息,分析结果,取到ID,再从前面的ConcurrentHashMap里面get(ID),从而找到callback,将方法调用结果设置到callback对象里。
监听线程接着使用synchronized获取回调对象callback的锁(因为前面调用过wait(),那个线程已释放callback的锁了),再notifyAll(),唤醒前面处于等待状态的线程继续执行(callback的get()方法继续执行就能拿到调用结果了),至此,整个过程结束。
当前线程怎么让它“暂停”,等结果回来后,再向后执行?
答:先生成一个对象obj,在一个全局map里put(ID,obj)存放起来,再用synchronized获取obj锁,再调用obj.wait()让当前线程处于等待状态,然后另一消息监听线程等到服 务端结果来了后,再map.get(ID)找到obj,再用synchronized获取obj锁,再调用obj.notifyAll()唤醒前面处于等待状态的线程。
正如前面所说,Socket通信是一个全双工的方式,如果有多个线程同时进行远程方法调用,这时建立在client server之间的socket连接上会有很多双方发送的消息传递,前后顺序也可能是乱七八糟的,server处理完结果后,将结果消息发送给client,client收到很多消息,怎么知道哪个消息结果是原先哪个线程调用的?
答:使用一个ID,让其唯一,然后传递给服务端,再服务端又回传回来,这样就知道结果是原先哪个线程的了。
I、初始化过程细节:
上图中的第一步start,就是将服务装载容器中,然后准备注册服务。和Spring中启动过程类似,spring启动时,将bean装载进容器中的时候,首先要解析bean。所以dubbo也是先读配置文件解析服务。
解析服务:
1)、基于dubbo.jar内的Meta-inf/spring.handlers配置,spring在遇到dubbo名称空间时,会回调DubboNamespaceHandler类。
2)、所有的dubbo标签,都统一用DubboBeanDefinitionParser进行解析,基于一对一属性映射,将XML标签解析为Bean对象。
源码截图:
在ServiceConfig.export 或者ReferenceConfig.get 初始化时,将Bean对象转会为url格式,将所以Bean属性转成url的参数。
然后将URL传给Protocol扩展点,基于扩展点的Adaptive机制,根据URL的协议头,进行不同协议的服务暴露和引用。
暴露服务:
a、 只暴露服务端口
在没有使用注册中心的情况,这种情况一般适用在开发环境下,服务的调用这和提供在同一个IP上,只需要打开服务的端口即可。
即,当配置 or
ServiceConfig解析出的URL的格式为:
Dubbo://service-host/com.xxx.TxxService?version=1.0.0
基于扩展点的Adaptiver机制,通过URL的“dubbo://”协议头识别,直接调用DubboProtocol的export()方法,打开服务端口。
b、向注册中心暴露服务:
和上一种的区别:需要将服务的IP和端口一同暴露给注册中心。
ServiceConfig解析出的url格式为:
registry://registry-host/com.alibaba.dubbo.registry.RegistryService?export=URL.encode(“dubbo://service-host/com.xxx.TxxService?version=1.0.0”)
基于扩展点的Adaptive机制,通过URL的“registry://”协议头识别,调用RegistryProtocol的export方法,将export参数中的提供者URL先注册到注册中心,再重新传给Protocol扩展点进行暴露:
Dubbo://service-host/com.xxx.TxxService?version=1.0.0
引用服务:
a、直接引用服务:
在没有注册中心的,直连提供者情况下,
ReferenceConfig解析出的URL格式为:
Dubbo://service-host/com.xxx.TxxService?version=1.0.0
基于扩展点的Adaptive机制,通过url的“dubbo://”协议头识别,直接调用DubboProtocol的refer方法,返回提供者引用。
b、从注册中心发现引用服务:
此时,ReferenceConfig解析出的URL的格式为:
registry://registry-host/com.alibaba.dubbo.registry.RegistryService?refer=URL.encode(“consumer://consumer-host/com.foo.FooService?version=1.0.0”)
基于扩展点的Apaptive机制,通过URL的“registry://”协议头识别,就会调用RegistryProtocol的refer方法,基于refer参数总的条件,查询提供者URL,如:
Dubbo://service-host/com.xxx.TxxService?version=1.0.0
基于扩展点的Adaptive机制,通过提供者URL的“dubbo://”协议头识别,就会调用DubboProtocol的refer()方法,得到提供者引用。
然后RegistryProtocol将多个提供者引用,通过Cluster扩展点,伪装成单个提供这引用返回。
ActiveMQ的功能解耦合和减轻写的压力。
ActivityMQ消息发送接收流程 :
1.消息发送者: 把消息发送mq消息服务器进行存储。
首先开辟存储消息空间,且给消息空间起一个名称,发送者把消息发送到这个空间。
2.消息接受者: 监听此消息空间(此消息空间必须和发送消息空间相同),一旦监听消息空间有消息,立马接收。
点对点消息模式特定 :
1.一个消息只能被一个人接受。
2.消息被接受以后,自动消失。
3.一条消息一直没有被接受,一直等待,直到被接受。
4.如果有多个服务同时监听消息,先到先得。
模式二 : 发布订阅模式
activeMQ消息发送、接受流程 :
1.消息发送者把消息发送到消息服务器。开辟一块存储消息空间,开辟空间同时,必须给这个空间起一个名称;
发送者可以把消息发送到这个空间。
2.消息接受者监听开启的这个空间,一旦发现此空间有消息,立马触发监听器,来接受消息。
发布订阅模式消息特点 :
1.一条可以被多个人接受。
2.发布订阅模式消息接受模式 : 只能是监听模式(异步模式)
3.消息接收者必须先监听,否则将会收不到消息。
JMS发布订阅模式流程 :
1.客户机发送消息到JMS消息中间件。
2.服务端负责监听JMS消息目的地。
3.发现JMS里面有消息产生,服务就可以订阅。
特点 :
1.消息可以被多个服务共享。
2.消息直到被所有消费后,消息小时。
3.用来在服务和服务之间进行异步通信(直接发送字符串)
4.流量削峰(MQ消息服务器可以提供缓冲区,让消息进行排队,等待处理。实现异步通信)
发布订阅 :
点对点模式 : 发布数据模型 : 对列queue
发布模式 : 发布数据模型 : Topic
发布订阅模式使用监听异步接受消息。
发布订阅模式的通信方式 : 默认情况下只通知一次,如果接收不到此消息就没有了。这种场景只适用于对消息送达率要求不高的情况。如果要求消息必须送到不可以
丢失的话,需要配置持久订阅。每个订阅端定义一个id,在订阅是向activityMq注册。发布消息和接收消息时需要配置发送模式为持久化。此时,如果客户的接收不到
消息,消息会持久化到服务端,直到客户端正常接收后为止。
ActiveMQ的作用及原理?
Activemq的作用是系统之间进行通信。当然可以使用其他方式进行系统间通信,如果使用Activemq的话可以对系统之间的调用进行解耦,实现系统间的异步通信。
原理就是生产者生产消息,把消息发送给activemq.Activemq接收到消息,然后查看有多少个消费者,然后把消息转发给消费者,此过程中生产者无需参与。消费者接受到
消息后做相应的处理和生产者没有任何关系。
ActiveMQ在项目中应用场景?
activeMQ在项目中主要是完成系统之间通信,并且将系统之间的调用进行解耦。例如在添加,修改商品信息后,需要将商品信息同步到索引库,同步缓存中的数据以及
生成静态页面一系列操作。在此场景下就可以使用activemq.一旦后台对商品信息进行修改后,就向activemq发送一条消息,然后通过activemq将消息发送到消息的消费端,
消费端接收到消息可以进行相应的业务处理。
项目优化 :
面向服务分布式架构SOA:
拆分架构 :
1.分担服务器压力。
2.提高项目并发能力。
拆分原则 :
1.根据业务和职能拆分。
为了减轻数据库压力,提高数据库效率?
解决方案 :
1.集群(主主,主备,读写分离)
2.分表,分库。
3.开启缓存。
4.开启索引.
5.sql语句优化。
6.数据库设计优化。
数据库本身优化,还可以加redis缓存?
1.减轻数据库压力(查询缓存,不再查询数据库)。
2.提高查询效率(redis是内存版nosql数据库)
3.提高并发能力。
dubbo : 服务治理中间件(分布式服务架构)
特点 :
1.rpc 远程通信
2.NIO new IO 异步通信。
第一级优化 :
1.集群(主主,主备,读写分离)
作用 :
高可用和高并发。
2.分表和分库(大数据查询效率低)
3.开启缓存
4.开启索引
5.优化sql查询
5.数据库设计
第二级优化 :redis缓存
作用 :
1.减轻数据库压力
2.提高项目查询效率
(redis是一个nosql版内存版数据库)
redis服务器本身优化:
1.内存淘汰策略。
2.线程安全问题。
3.redis3.0 : 自动高可用,自动容错。
第三极优化 : solr进行搜索
作用 :
1.减轻数据库压力。
2.提高检索效率(搜索索引)
案例 :
solr服务器本身优化:
1.集群(高可用,高容错)
第四级优化 :SOA面向服务分布式的架构
作用 :
1.分担服务器压力。
2.提高项目并发能力。 jvm优化。
优化 : soa使用dubbo+zookeeper tomcat服务器优化
dubbo优化 :
1.服务集群。
2.序列化优化 kryo
3.失败重试。
第五级优化 : fastDFS分布式文件系统
作用 : 存储图片
1.访问效率高。
2.自动容错。
3.线性扩容。
第六级优化 : 使用mq消息服务器,应用于服务与服务之间进行通信。
作用 : 1.异步通信。2.任务异步处理。优势:流量削峰。
优化mq : 集群。
第七级优化 : 页面静态化。
1.查询效率提高(访问静态数据)
2.并发能力提高。
技术 : freemarket实现静态化。
思考 : 优化。
缺点 :
html页面商品数据。
商品描述,规格,详情。
不能及时和数据库同步。
优化 :
mq进行同步静态页面。
同步流程:
商品添加,修改,删除。
发送消息
详情系统接收消息动态生成,删除html页面。
第8级优化 : nginx的使用。
1.http服务器。
2.负载均衡。
优化:
集群。
页面静态化 :
技术选型 ; freemarket
优势 : 简单,容易上手,语法简单,功能更强大。
可以生成HTML页面、生成XML文件、生成JAVA代码。
三要素 :
1.模板(ftl)
2.数据
3.API代码
1.B+/-Tree原理
B-Tree是一种多路搜索树(并不是二叉树):
B-树的特性 :
1.关键字集合分布在整棵树中;
2.任何一个关键字出现且只出现在一个结点中;
3.搜索很可能在非叶子结点结束;
4.其搜索性能等价于关键字全集内做一次二分查找;
5.自动层次控制;
B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的
儿子指针为空,或已经是叶子结点;
B+Tree介绍
B+树是B-树的变体,也是一种多路搜索树;
1.其定义基本与B-树相同;
2.非叶子结点的子树指针与关键字个数相同;
3.非叶子结点的子树指针P[i],指向关键字值属于(K[i],K[i+1])的子树(B-树是开区间);
4.为所有叶子结点增加一个链指针;
5.所有关键字都在叶子结点出现;
B+树的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;
B+特性 :
1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
2.不可能在非叶子结点命中;
3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
4.更适合文件索引系统;
mysql中的索引 :
mysql中普遍使用B+Tree做索引,但在实现上又根据聚簇索引和非聚簇索引而不同。
聚簇索引 :
所谓聚簇索引,就是指主索引文件和数据文件为同一份文件,聚簇索引主要用在Innodb存储引擎中。在该索引实现方式中B+Tree的叶子节点上的data就是数据本身,
key为主键,如果是一般索引的话,data便会指向对应的主索引,如下图所以:
在B+Tree的每个叶子结点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree.做这个优化的目的是为了提高区间访问的性能,例如图4中
如果要查询key为18到49的所有数据记录,当找到18后,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提高了区间查询效率。
非聚餐索引 :
非聚餐索引就是指B+Tree的叶子节点上的data,并不是数据本身,而是数据存放的地址。主索引和辅助索引没啥区别,只是主索引中的key一定的是唯一的。主要
用在MyISAM存储引擎中,如下图 :
非聚簇索引比聚簇索引多了一次读取数据的IO操作,所以查找性能上会差。
MyISAM索引与InnoDB索引相比较 :
MyISAM支持全文索引(FULLTEXT)、压缩索引,InnoDB不支持;
InnoDB支持事务,MyISAM不支持;
MyISAM顺序存储数据,索引叶子节点保存对应数据行地址,辅助索引和主键索引相差无几;InnoDB主键节点同时保存数据行,其他辅助索引保存的是主键索引的值;
MyISAM键值分离,索引载入内存(key_buffer_size),数据缓存依赖操作系统;InnoDB键值一起保存,索引与数据一起载入InnoDB缓存池;MyISAM主键(唯一)索引
按升序来存储,InnoDB则不一定;
MyIASM索引的基数值(Cardinality,show index 命令可以看见)是精确的,InnoDB则是估计值。这里涉及到信息统计的知识,MyISAM统计信息是保存磁盘中,在alter
表或Analyze table操作更新此信息,而InnoDB则是在表第一次打开的时候估计值保存在缓存区内;
MyISAM处理字符串索引时用增量保存的方式,如第一个索引是‘preform’,第二个是‘preformence’,则第二个保存时'7,ance',这个明显的好处是缩短索引,但是缺陷就是不支持
倒序提取索引,必须顺序遍历获取索引。
为什么选用B+/-Tree
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,
相对于内存存取,I/O存储的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。
换句话说,索引的结构组织要尽力减少查找过程中磁盘I/O的存取次数。
B-Tree : 如果一次检索需要访问4个节点,数据库系统设计者利用磁盘预读原理,把节点的大小设计为一个页,那读取一个节点只需要一次I/O操作,完成这次
检索操作,最多需要3次I/O(根节点常驻内存)。数据记录越小,每个节点存放的数据就越多,树的高度也就越小,I/O操作就少了,检索效率也就上去了。
B+Tree : 非叶子节点只存Key,大大的减少了非叶子节点的大小,那么每个节点就可以存放更多的记录,树更矮了,I/O操作更少了。所以B+Tree拥有更好的性能。
幂等性 内存划分,threadLocal,多线程和线程池,local,syncal,map,map里面的排序,hashmap,hashtable,分布式和分布式锁的
解决分布式锁的问题可以使用redis,zookeeper,tair。
1.zookeeper分布式锁,基于自增节点
2.redis分布式锁,基于setnx命令
使用Zookeeper实现分布式锁的优点
有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题。实现起来较为简单。
使用Zookeeper实现分布式锁的缺点
性能上不如使用缓存实现分布式锁。 需要对ZK的原理有所了解。
使用缓存实现分布式锁的优点
性能好,实现起来较为方便。
使用缓存实现分布式锁的缺点
通过超时时间来控制锁的失效时间并不是十分的靠谱。
但是推荐Cerberus分布式锁:
有以下几个特点 :
1.一套接口多种引擎。
Cerberus分布式锁使用了多种引擎实现方式(Tair,ZK,未来支持Redis),支持使用方自主选择所需的一种或多种引擎。
Cerberus分布式锁将不同引擎的接口抽象为一套,屏蔽了不同引擎的实现细节。使得使用方可以专注于业务逻辑,也可以任意选择并切换引擎而不必更改任何的业务代码。
2.使用灵活,学习成本低。
3.支持一键降级。(Cerberus提供了实现切换引擎的接口)
GTIS就是解决分布式系统中幂等性问题。它是一个轻量的重复操作关卡系统,它能够确保分布式环境中操作的唯一性。我们可以用它来间接保证每个操作的幂等性。
高效 : 低延时,单个方法平均响应时间在2ms内,几乎不会对业务造成影响;
可靠 : 提供降级策略,以应对外部存储引擎故障所造成的影响;提供应用鉴权,提供集群配置自定义,降低不同业务之间的干扰;
简单 : 接入简捷方便,学习成本低。只需简单的配置,在代码中进行两个方法的调用即可完成所有的接入工作;
灵活 : 提供多种接口参数,使用策略,以满足不同的业务需求。
基本原理 :
GTIS的实现思路是将每一个不同的业务操作赋予其唯一性。这个唯一性是通过对不同操作所对应的唯一的内容特性生成一个唯一的全局ID来实现的。
基本原则为 : 相同的操作生成相同的全局ID;不同的操作生成不同的全局ID。
生成的全局ID需要存储在外部存储引擎中,数据库,redis亦或是Tair等均可实现。考虑到Tair天生分布式和持久化的优势,目前的GTIS存储在Tair中,其相应的key
和value如下 :
key : 将对不同的业务,采用APP_KEY+业务操作内容特性生成一个唯一标识trans_coontents.然后对唯一标识进行加密生成全局ID作为key。
value : current_timestamp + trans_contents,current_timestamp用于标识当前的操作线程。
判断是否重复,主要利用Tair的SETNX方法,如果原来没有值则set且返回成功,如果已经有值则返回失败。
GTIS的内部实现流程为 :
1.业务方在业务操作之前,生成一个能够唯一标识该操作的transContents,传入GTIS;
2.GTIS根据传入的transContents,用MD5生成全局ID;
3.GTIS将全局ID作为key,current_timestamp+transContents作为value放入tair进行setNx,将结果返回给业务方;
4.业务方根据返回结果确定能否开始进行业务操作;
5.若能,开始进行操作,若不能,则结束当前操作;
6.业务方将操作和请求结果传入GTIS,系统进行一次请求结果的检验;
7.若该次操作成功,GTIS根据key取出value值,根传入的返回结果进行比对,如果两者相等,则将该全局ID的过期时间改为较长时间;
8.GTIS返回最终结果。
既然全局ID会失效并且可能会被删除,那就需要保证删除的不是另一个相同操作的全局ID。这就需要将特殊的标识记录下来,并由此来判断。这里所用的标识为当前时间戳。
程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。