JAVA基础知识集合

java基础

1. JAVA中的几种基本类型,各占用多少字节?

在这里插入图片描述
2.String, Stringbuffer, StringBuilder 的区别
String:
成员变量可以知道String类的值是final类型的,不能被改变的,所以只要一个值改变就会生成一个新的String类型对象;
创建String有两种方式:
1)直接赋值方式创建对象是在方法区的常量池
String str=“hello”;//直接赋值的方式
2) 通过构造方法创建字符串对象
  通过构造方法创建字符串对象是在堆内存
String str=new String(“hello”);//实例化的方式
两种创建方式比较:
在这里插入图片描述
3)字符串常量池:
  在字符串中,如果采用直接赋值的方式(String str=“xinxin”)进行对象的实例化,则会将匿名对象“xinxin”放入对象池,每当下一次对不同的对象进行直接赋值的时候会直接利用池中原有的匿名对象,这样,所有直接赋值的String对象,如果利用相同的“xinxin”,则String对象==返回true;
  在开发的过程中,equals()通过接受参数,可以避免空指向。
 举例:

String str = null;  
if(str.equals(“hello”)){//此时会出现空指向异常   …  
}
if(“hello”.equals(str)){//此时equals会处理null值,可以避免空指向异常     …  }

5)String 常用方法
在这里插入图片描述
//判断功能:

boolean equals(Object obj):比较字符串的内容是否相同
boolean equalsIgnoreCase(String str): 比较字符串的内容是否相同,忽略大小写
boolean startsWith(String str): 判断字符串对象是否以指定的str开头
boolean endsWith(String str): 判断字符串对象是否以指定的str结尾

//获取功能:

int length():获取字符串的长度,其实也就是字符个数
char charAt(int index):获取指定索引处的字符 int
indexOf(String str):获取str在字符串对象中第一次出现的索引
String substring(int start):从start开始截取字符串
String substring(int start,int end):从start开始,到end结束截取字符串。包括start,不包括end

//转化功能:

char[] toCharArray():把字符串转换为字符数组
String toLowerCase():把字符串转换为小写字符串
String toUpperCase():把字符串转换为大写字符串

//其它方法:

String trim():去除字符串两端空格
String[] split(String str):按照指定符号分割字符串

6)String不可变的好处:
可以实现多个变量引用堆内存中的同一个字符串实例,避免创建的开销。我们的程序中大量使用了String字符串,有可能是出于安全性考虑。大家都知道HashMap中key为String类型,如果可变将变的多么可怕。当我们在传参的时候,使用不可变类不需要去考虑谁可能会修改其内部的值,如果使用可变类的话,可能需要每次记得重新拷贝出里面的值,性能会有一定的损失。

2.2>StringBuffer和Stirngbuild区别:
StringBuffer:线程安全,
StringBuilder:线程不安全。
因为 StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有 StringBuilder 修饰,它们两个都是属于可变。

区别:缓冲区

StringBuffer 代码片段: private transient char[] toStringCache; @Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true); }

StringBuilder 代码片段:

@Override public String toString() {
// Create a copy, don’t share the array
return new String(value, 0, count); }

可以看出,StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。
而 StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。
所以,缓存冲这也是对 StringBuffer 的一个优化吧,不过 StringBuffer 的这个toString 方法仍然是同步的。
区别3:性能
既然 StringBuffer 是线程安全的,它的所有公开方法都是同步的,StringBuilder 是没有对方法加锁同步的,所以毫无疑问,StringBuilder 的性能要远大于 StringBuffer。

String,Stringbuffer,Stringbuilder关系:
在这里插入图片描述
3.类的实例化顺序
父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当 new 的时候, 他们的执行顺序。
(加载–>连接->初始化)。
父类静态代变量、
父类静态代码块、
子类静态变量、
子类静态代码块、
父类非静态变量(父类实例成员变量)、
父类构造函数、
子类非静态变量(子类实例成员变量)、
子类构造函数。
二、结论:

1、执行顺序:父类的静态代码块—>子类的静态代码块—>父类的构造代码块—>父类的构造方法—>子类的构造代码块—>子类的构造方法。

2、静态代码块随类的加载而执行,只执行一次,优先于main方法,用于初始化整个类。

3、构造代码块是给一个类的所有的对象进行初始化,可执行多次。只要对象一建立,就会调用构造代码块。构造代码块可以重复,可以有多份。

4、构造方法是给与之对应的对象进行初始化,有针对性。构造方法要么带参数,要么不带参数。当类中没有显式的构造方法时,jvm会默认执行一个不带参数的构造方法。同一个类中不能出现两个或两个以上相同的构造方法(方法名和参数列表都相同)。

5、在子类的所有构造方法中如果没有显式的super语句,则默认第一条语句为隐式的super语句:super();会访问父类的不带参数的构造方法。当父类中只有带参数的构造方法时,子类必须用显式的带参数的super语句访问父类的构造方法。若显示的super语句不带参数,则编译失败。当父类中只有显式的不带参数的构造方法时,子类必须用显示的不带参数的super语句访问父类的构造方法。否则,编译失败。

6、在创建子类的实例对象时未传入参数,若子类只有带参数的构造方法则编译失败。若子类有不带参数的构造方法,则执行子类的不带参数的构造方法。若子类没有构造方法,则执行隐式的不带参数的构造方法。

7、在创建子类的实例对象时传入参数:若子类有带参数的构造方法,则执行子类的带参数的构造方法;若子类没有带参数的构造方法或者没有构造方法,则编译失败。
4.抽象类和接口的区别,类的继承,接口的实现
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。
6、抽象方法只能申明,不能实现。abstract void abc();不能写成abstract void abc(){}。
7、抽象类里可以没有抽象方法
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
5.继承和聚合
继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识,在设计时一般没有争议性;
聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享;比如计算机与CPU、公司与员工的关系等;表现在代码层面,和关联关系是一致的,只能从语义级别来区分;
参考链接(https://www.cnblogs.com/jiqing9006/p/5915023.html)
6.hashcode、equels、==区别与联系
都是出自object类,进行重写的话都是指向对象的引用地址
**
**

分页

if (StringUtils.isNotEmpty(params.get("currPage"))) {
		currentPage = Integer.parseInt((params.get("currPage")));
	}
	if (StringUtils.isNotEmpty(params.get("pageSize"))) {
		pageSize = Integer.parseInt(params.get("pageSize"));
	}
	int totalcount = microStoreCfgBaseEntitys.size();
	int m = totalcount % pageSize;
	if (m > 0) {
		pagecount = totalcount / pageSize + 1;
	} else {
		pagecount = totalcount / pageSize;
	}
	if (m == 0) {
		returnList = microStoreCfgBaseEntitys.subList((currentPage - 1) * pageSize, pageSize * (currentPage));
	} else {
		if (currentPage == pagecount) {
			returnList = microStoreCfgBaseEntitys.subList((currentPage - 1) * pageSize, totalcount);
		} else {
			returnList = microStoreCfgBaseEntitys.subList((currentPage - 1) * pageSize, pageSize * (currentPage));
		}
	}

**

反射机制:

**
添加链接描述
**

nosql

菜鸟教程

**

中间件

nginx

1、nginx简介:
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,Nginx是一款轻量级的Web 服务器/反向代理服 务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强。
2、正向代理与反向代理的区别**
正向代理需要你主动设置代理服务器ip或者域名进行访问,由设置的服务器ip或者域名去获取访问内容并返回;而反向代理不需要你做任何设置,直接访问服务器真实ip或者域名,但是服务器内部会自动根据访问内容进行跳转及内容返回,你不知道它最终访问的是哪些机器。
  正向代理是代理客户端,为客户端收发请求,使真实客户端对服务器不可见;而反向代理是代理服务器端,为服务器收发请求,使真实服务器对客户端不可见。
  正向代理:比如我们要去访问谷歌网站,我们直接访问不通,那么我们就可以找一个代理服务器为我们服务,我们通过代理服务器请求到谷歌网站。对于谷歌而言他只知道有一个服务器访问了自己,并不知道这件事你是访问不了他,找了一个代理服务器访问自己。
  反向代理:比如 我们访问百度网站,百度的代理服务器对外的域名为 https://www.baidu.com 。具体内部的服务器节点我们不知道。现实中我们通过访问百度的代理服务器后,代理服务器给我们转发请求到他们N多的服务器节点中的一个给我们进行搜索后将结果返回。
.fastcgi 与 cgi 的区别?
1)cgi
web 服务器会根据请求的内容,然后会 fork 一个新进程来运行外部 c 程序(或 perl 脚本…), 这个进程会把处理完的数据返回给 web 服务器,最后 web 服务器把内容发送给用户,刚才 fork 的进程也随之退出。
如果下次用户还请求改动态脚本,那么 web 服务器又再次 fork 一个新进程,周而复始的进行。

2)fastcgi
web 服务器收到一个请求时,他不会重新 fork 一个进程(因为这个进程在 web 服务器启动时就开启了,而且不会退出),web 服务器直接把内容传递给这个进程(进程间通信,但 fastcgi 使用了别的方式,tcp 方式通信),这个进程收到请求后进行处理,把结果返回给 web 服务器,最后自己接着等待下一个请求的到来,而不是退出。
3.Nginx 常用命令?
启动 nginx 。
停止 nginx -s stop 或 nginx -s quit 。
重载配置 ./sbin/nginx -s reload(平滑重启) 或 service nginx reload 。
重载指定配置文件 .nginx -c /usr/local/nginx/conf/nginx.conf 。
查看 nginx 版本 nginx -v 。
检查配置文件是否正确 nginx -t 。
显示帮助信息 nginx -h 。
Nginx 有哪些负载均衡策略?
负载均衡,即是代理服务器将接收的请求均衡的分发到各服务器中。
Nginx 默认提供了 3 种负载均衡策略:
1、轮询(默认)round_robin
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器 down 掉,能自动剔除。
2、IP 哈希 ip_hash
每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 session 共享的问题。
当然,实际场景下,一般不考虑使用 ip_hash 解决 session 共享。
3、最少连接 least_conn

下一个请求将被分派到活动连接数量最少的服务器
通过 Nginx 插件,我们还可以引入 fair、url_hash 等负载均衡策略。
另外,我们还可以配置每一个后端节点在负载均衡时的其它配置:
weight=1; # (weight 默认为1.weight越大,负载的权重就越大)
down; # (down 表示单前的server暂时不参与负载)
backup; # (其它所有的非backup机器down或者忙的时候,请求backup机器)
max_fails=1; # 允许请求失败的次数默认为 1 。当超过最大次数时,返回 proxy_next_upstream 模块定义的错误
fail_timeout=30; # max_fails 次失败后,暂停的时间

Nginx 如何实现后端服务的健康检查?
方式一,利用 nginx 自带模块 ngx_http_proxy_module 和 ngx_http_upstream_module 对后端节点做健康检查。

方式二,利用 nginx_upstream_check_module 模块对后端节点做健康检查。
Nginx 是如何实现高并发的?
Nginx 不这样,每进来一个 request ,会有一个 worker 进程去处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后端)服务器转发 request ,并等待请求返回。那么,这个处理的 worker 不会这么傻等着,他会在发送完请求后,注册一个事件:“如果 upstream 返回了,告诉我一声,我再接着干”。于是他就休息去了。此时,如果再有 request 进来,他就可以很快再按这种方式处理。而一旦上游服务器返回了,就会触发这个事件,worker 才会来接手,这个 request 才会接着往下走。
为什么 Nginx 不使用多线程?
Apache: 创建多个进程或线程,而每个进程或线程都会为其分配 cpu 和内存(线程要比进程小的多,所以 worker 支持比 perfork 高的并发),并发过大会榨干服务器资源。

Nginx: 采用单线程来异步非阻塞处理请求(管理员可以配置 Nginx 主进程的工作进程的数量)(epoll),不会为每个请求分配 cpu 和内存资源,节省了大量资源,同时也减少了大量的 CPU 的上下文切换。所以才使得 Nginx 支持更高的并发。

zookeeper

1、zk简介:
Zookeeper是一个典型的分布式数据一致性解决方案,分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅,负载均衡,命名服务,分布式协调/通知,集群管理,Master选举,分布式锁和分布式队列等功能。满足cap理论中的ca理论。

分布式系统cap:
(1)consistency 数据一致性 即数据存在多副本的情况下由于网络原因可能部分副本数据写入成功,部分写入失败,造成副本间的数据有冲突,满足一致性则是更新操作成功之后,多副本之间保持一致。
(2)availability 在任何时候客户端对集群进行读写操作时,请求能够正常响应,可用性
(3)partition tolerace 分区容忍性,即发生故障,整个集群被分割为多个无法相互通信的分区,集群仍然可用。

Zookeeper一个最常用的使用场景就是用于担任生产者和服务消费者的注册中心,提供发布/订阅服务。服务生产者将自己提供的服务注册到Zookeeper中心,服务消费者在进行服务调用的时候先到Zookeeper中查找服务,获取到服务生产者的详细信息之后,再去调用服务生产者的内容与数据,如在Dubbo中,Zookeeper就担任注册中心的角色。
2、相关知识
(1)Zookeeper本身就是一个分布式程序,主要半数以上节点存活,Zookeeper就能正常服务。
(2)为了保证高可用,最好以集群方式不是Zookeeper,这样只要集群中大部分机器是可用的,Zookeeper本身仍然可用。
(3)Zookeeper将数据保存在内存中,保证了高吞吐和低延迟,但是内存限制了能够存储的容量不太大,限制了Znode中存储的数据量较小的进一步原因。
(4)Zookeeper是高性能的,在读多于写的应用程序中尤其的高性能,因为写会导致所有服务器同步状态。
(5)Zookeeper有临时节点的概念,当创建临时节点的客户端会话一直保持活动,瞬时节点就一直存在,而当会话终结时,瞬时节点被删除。持久节点是指一旦这个ZNode被创建,除非主动进行ZNode的移除操作,否则这个ZNode将一直保存在Zookeeper上。
3、session会话
Session是指Zookeeper服务器与客户端会话。在Zookeeper中,一个客户端连接是指客户端和服务器之间的一个TCP长连接。客户端启动的时候,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期就开始了。通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够像Zookeeper服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的Watch事件通知。

Session的sessionTimeout可以设置客户端会话的超时事件。由于服务压力大,网络故障或者客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。

在为客户端创建会话之前,服务端首先会为每个客户端都分配一个sissionID,由于sessionID是Zookeeper会话的一个重要标识,许多与会话相关的运行机制都是基于sessionID的,因此,无论是哪台服务器为客户端分配的sessionID,都需要保证全局唯一。
总结:
(1)客户端和server间采用长连接;
(2)连接建立后,server产生session ID(64位)返还 给客户端;
(3)客户端定期发送ping包来检查和保持和server的 连接;
(4)一旦session结束或超时,所有ephemeral节点会 被删除;
(5)客户端可根据情况设置合适的session超时时间;
(6)客户端能够异步接收来自服务端的Watcher事件通知;

ZNode
在Zookeeper中,节点分为两类,第一类是指构成集群的机器,称为机器节点,第二类是指数据模型中的数据单元,称为数据节点-ZNode。

Zookeeper将所有数据存储在内存中,数据模型是一棵树,由/进行分割的路径,就是一个(Namespace命名空间)Znode,每个节点上都会保存自己的数据内容,以及继承自该节点的孩子列表(临时节点下面不能创建子节点),zk旨在存储协调数据:状态信息,配置,位置信息等同时还会保存一系列属性信息。
一个znode维护了一个属性结构,该结构包括:版本号、ACL变更、时间戳。每次znode数据发生变化,版本号都会递增,这样客户端的读请求可以基于版本号来检索状态相关数据。每个znode都有一个ACL,用来限制是否可以访问该znode。
在一个命名空间中,对znode上存储的数据执行读和写请求操作都是原子的。

Zookeeper中node 分为两类,分为持久节点和临时节点。
临时节点(EPHEMERAL):临时创建的,会话结束节点自动被删除,也可以手动删除,临时节点不能拥有子节点.
持久节点(PERSISTENT):创建后永久存在,除非主动删除。
以上两种节点为Non-sequence节点,只有一个可创建成功,其它匀失败。并且创建出的节点名称与创建时指定的节点名完全一样.

临时顺序节点(EPHEMERAL_SEQUENTIAL):具有临时节点特征,但是它会有序列号。
持久顺序节点(PERSISTENT_SEQUENTIAL):具有持久节点特征,但是它会有序列号。
以上两种节点为sequence节点,创建出的节点名在指定的名称之后带有10位10进制数的序号。多个客户端创建同一名称的节点时,都能创建成功,只是序号不同。

节点信息:
每一个Znode都有对应的stat结构,和文件系统类似。stat状态主要包含下面的信息:
cZxid. 节点被创建时候的事务ID
mZxid 节点最后一次被修改时候的事务ID
pZxid 该节点的子节点最后一次被修改时的事务ID。子节点删除或添加才会影响pZxid
ctime 节点被创建的时间
mtime 节点被修改的世界
dataVersion 这个节点数据改变的次数
cversion 子节点被改变的次数
aclVersion 节点的ACL(访问控制列表被改变的次数)
ephemeralOwner 创建该临时节点的 session ID。如果是持久节点,设置为0
dataLength 数据内容长度
numChildren 当前节点子节点的个数

节点的访问控制(ACL):
zk提供了ACL来控制znode节点的访问,只有符合了ACL控制,才可以操作该节点,否则将无法操作。
Zookeeper支持可配置的认证机制。它利用一个三元组来定义客户端的访问权限:
(scheme:expression, perms) 。其中:
Schema 代表权限控制模式,分别为:
● World 任何人
● Auth 不需要ID
● Digest 用户名和密码方式的认证
● IP Address IP地址方式的认证
perms(权限),ZooKeeper支持如下权限
● CREATE: 创建子节点
● READ: 获取子节点与自身节点的数据信息
● WRITE:在Znode节点上写数据
● DELETE:删除子节点
● ADMIN:设置ACL权限
注意:

Znode的Acl只是针对某个节点,不会作用到它的子节点上
任何连接到ZooKeeper的客户端都可以使用exist操作,exist是不需要权限的

zk集群角色:

  1. Zookeeper 下 Server 工作状态服务器具有四种状态,分别是 LOOKING、FOLLOWING、LEADING、OBSERVING。

Leader
①Leader服务器是zk集群工作机制的核心.
②事务请求的唯一调度者和处理者,保证集群事务请求处理的顺序性.
Follower
①Follower服务器是zk集群状态的跟随者.
②处理非事务请求,转发事务请求给Leader服务器
③参与事务请求的proposal投票
④参与Leader选举投票
Observer
Observer是一种新型的zk节点,Observer服务器只提供非事务服务.通常用于不影响集群事务处理能力的前提下提升集群的非事务的处理能力,Observer有另外一个优势,因为它不参与投票,所以他们不属于zk集群的关键部位,即使他们Failed,或者从集群中断开,也不会影响集群的可用性。

leader、follower、observer选举过程
ZXID(zookeeper transaction id):每个改变Zookeeper状态的操作都会形成一个对应的zxid,并记录到transaction log中。 这个值越大,表示更新越新
myid:服务器SID,一个数字,通过配置文件配置,唯一
SID:服务器的唯一标识
成为Leader的必要条件: Leader要具有最高的zxid;当集群的规模是n时,集群中大多数的机器(至少n/2+1)得到响应并follow选出的Leader。
心跳机制:Leader与Follower利用PING来感知对方的是否存活,当Leader无法相应PING时,将重新发起Leader选举。
选举有两种情况,一是服务器启动的投票,二是运行期间的投票。

具体流程:

服务器启动时期的Leader选举
1.每个服务器发送一个投票(SID,ZXID)
其中sid是自己的myid,初始阶段都将自己投为Leader。

2.接收来自其他服务器的投票。
集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器。

3.处理投票
针对每个投票都按以下规则与自己的投票PK,PK后依据情况是否更新投票,再发送给其他机器。

a.优先检查ZXID,ZXID较大者优先为Leader
b.如果ZXID相同,检查SID,SID较大者优先为Leader
5.统计投票
每次投票后,服务器统计所有投票,判断是否有过半的机器收到相同的投票,如果某个投票达到一半的要求,则认为该投票提出者可以成为Leader。

6.改变服务器状态
一旦确定了Leader,每个服务器都更新自己的状态,Leader变更为Leading,Follower变更为Following
正常情况下一旦选出一个Leader则一直会保持,除非Leader服务器宕掉,则再进行重新选举。

服务器运行时期的Leader选举

1.变更状态
当Leader宕机后,余下的所非Observer的服务器都会将自己的状态变更为Looking,然后开启新的Leader选举流程。

2.每个服务器发出一个投票。
生成(SID,ZXID)信息,注意运行期间的ZXID可能是不同的,但是在投票时都会将自己投为Leader,然后发送给其他的服务器。

3.接收来自各个服务器的投票
与启动时过程相同

4.处理投票
与启动时过程相同

5.统计投票
与启动时过程相同

6.改变服务器状态
与启动时过程相同

分布式集群中为什么会有 Master主节点:
在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,于是就需要进行 leader 选举。

zookeeper特性:
顺序一致性
客户端的更新顺序与它们被发送的顺序一致。

原子性
更新操作要么成功,要么失败,没有第三种结果。

单系统映像
无论客户端连接到哪一个服务器,他都将看到相同的zk视图。

可靠性
一旦一个更新操作被应用,那么在客户端再次更新之前,其值不会再改变。

实时性
zk保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。 但由于网络延时等原因,zk不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。

ZooKeeper &ZAB 协议&Paxos算法
1、 ZAB 协议&Paxos算法
Paxos 算法应该可以说是 ZooKeeper 的灵魂了。但是,ZooKeeper 并没有完全采用 Paxos算法 ,而是使用 ZAB 协议作为其保证数据一致性的核心算法。另外,在ZooKeeper的官方文档中也指出,ZAB协议并不像 Paxos 算法那样,是一种通用的分布式一致性算法,它是一种特别为Zookeeper设计的崩溃可恢复的原子消息广播算法。

2、ZAB 协议介绍
ZAB(ZooKeeper Atomic Broadcast 原子广播) 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。 在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。

Zab协议的作用
使用一个单独的进程,保持leader和Learner之间的socket通信,任何写请求都将由Leader在集群中进行原子广播事务
保证了全部的变更序列在全局被顺序引用,写操作中都需要先check然后才能写,比如我们向create /a/b 它在创建b时,会先检查a存在否? 而且,事务性的request存在于队列中,先进先出,保证了他们之间的顺序
Zab协议原理
选举: 在Follower中选举中一个Leader
发现: Leader中会维护一个Follower的列表并与之通信
同步: Leader会把自己的数据同步给Follower,
广播: Leader接受Follower的事务Proposal,然后将这个事务性的proposal广播给其他learner
Zab协议内容
当整个集群启动过程中,或者当 Leader 服务器出现网络中弄断、崩溃退出或重启等异常时,Zab协议就会 进入崩溃恢复模式,选举产生新的Leader。

当选举产生了新的 Leader,同时集群中有过半的机器与该 Leader 服务器完成了状态同步(即数据同步)之后,Zab协议就会退出崩溃恢复模式,进入消息广播模式。

当Leader出现崩溃退出或者机器重启,亦或是集群中不存在超过半数的服务器与Leader保存正常通信,Zab就会再一次进入崩溃恢复,发起新一轮Leader选举并实现数据同步。同步完成后又会进入消息广播模式,接收事务请求

Zookeeper 保证主从节点的状态同步
Zookeeper 的核心是原子广播机制,这个机制保证了各个 server 之间的同步。实现这个机制的协议叫做 Zab 协议。Zab 协议有两种模式,它们分别是恢复模式和广播模式。

恢复模式
当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数 server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 server 具有相同的系统状态。

广播模式
一旦 leader 已经和多数的 follower 进行了状态同步后,它就可以开始广播消息了,即进入广播状态。这时候当一个 server 加入 ZooKeeper 服务中,它会在恢复模式下启动,发现 leader,并和 leader 进行状态同步。待到同步结束,它也参与消息广播。ZooKeeper 服务一直维持在 Broadcast 状态,直到 leader 崩溃了或者 leader 失去了大部分的 followers 支持。

Zookeeper数据同步
整个集群完成 Leader 选举之后,Learner(Follower 和 Observer 的统称)回向Leader 服务器进行注册。当 Learner 服务器想 Leader 服务器完成注册后,进入数据同步环节。

数据同步流程:(均以消息传递的方式进行)
Learner 向 Learder 注册
数据同步
同步确认
Zookeeper 的数据同步通常分为四类:
(1)直接差异化同步(DIFF 同步)
(2)先回滚再差异化同步(TRUNC+DIFF 同步)
(3)仅回滚同步(TRUNC 同步)
(4)全量同步(SNAP 同步)

选完 leader 以后,zk 就进入状态同步过程。
1、leader 等待 server 连接;
2、follower 连接 leader,将最大的 zxid 发送给 leader;
3、leader 根据 follower 的 zxid 确定同步点;
4、完成同步后通知 follower 已经成为 uptodate 状态;
5、follower 收到 uptodate 消息后,又可以重新接受 client 的请求进行服务了。

详细流程讲解
1.follower连接leader并发送自己最大的zixd
2.leader进行对比,将自己最大的zxid和follower发送过来的zxid进行对比,如果leader的zxid大于follower的,则通知follower进行数据同步
3.follower发送数据同步请求
4.leader确定当前的follower的数据同步点(从follower最大的zxid到leader最大的zxid之间数据需要同步)
5.follower开始同步数据,这个过程不对外提供读写服务。
6.follower同步完成,发送消息给leader
7.leader就会修改当前的follower的状态为update,这个时候follower就可以接受客户端的读写请求,但是只能读,如果是写入请求,需要转发给leader

zookeeper 保证事务的顺序一致性
zookeeper 采用了全局递增的事务 Id 来标识,所有的 proposal(提议)都在被提出的时候加上了 zxid,zxid 实际上是一个 64 位的数字,高 32 位是 epoch( 时期; 纪元; 世; 新时代)用来标识 leader 周期,如果有新的 leader 产生出来,epoch会自增,低 32 位用来递增计数。当新产生 proposal 的时候,会依据数据库的两阶段过程,首先会向其他的 server 发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行。

zk的通知机制:

集群支持动态添加机器?
其实就是水平扩容了,Zookeeper 在这方面不太好。两种方式:

全部重启:关闭所有 Zookeeper 服务,修改配置之后启动。不影响之前客户端的会话。

逐个重启:在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供服务。这是比较常用的方式。
zk与eureka的区别:
每一个微服务中都有eureka client,用于服务的注册于发现
(服务的注册:把自己注册到eureka server)
(服务的发现:从eureka server获取自己需要的服务列表)

每一个微服务启动的时候,都需要去eureka server注册

当A服务需要调用B服务时,需要从eureka服务端获取B服务的服务列表,然后把列表缓存到本地,然后根据ribbon的客户端负载均衡规则,从服务列表中取到一个B服务,然后去调用此B服务
当A服务下次再此调用B服务时,如果发现本地已经存储了B的服务列表,就不需要再从eureka服务端获取B服务列表,直接根据ribbon的客户端负载均衡规则,从服务列表中取到一个B服务,然后去调用B服务

微服务,默认每30秒,就会从eureka服务端获取一次最新的服务列表

如果某台微服务down机,或者添加了几台机器,
此时eureka server会通知订阅他的客户端,并让客户端更新服务列表,
而且还会通知其他eureka server更新此信息

心跳检测,微服务每30秒向eureka server发送心跳,
eureka server若90s之内都没有收到某个客户端的心跳,则认为此服务出了问题,
会从注册的服务列表中将其删除,并通知订阅它的客户端更新服务列表,
而且还会通知其他eureka server更新此信息

eureka server保护机制,通过打卡开关,可以让eureka server处于保护状态,主要是用于某eureka server由于网络或其他原因,导致接收不到其他微服务的心跳,此时不能盲目的将其他微服务从服务列表中删除。
具体规则:如果一段时间内,85%的服务都没有发送心跳,则此server进入保护状态,此状态下,可以正常接受注册,可以正常提供查询服务,但是不与其他server同步信息,也不会通知订阅它的客户端,这样就不会误杀其他微服务

zookeeper也可以作为注册中心,用于服务治理(zookeeper还有其他用途,例如:分布式事务锁等)
每启动一个微服务,就会去zk中注册一个临时子节点,
例如:5台订单服务,4台商品服务
(5台订单服务在zk中的订单目录下创建的5个临时节点)
(4台商品服务在zk中的商品目录下创建的4个临时接点)

每当有一个服务down机,由于是临时接点,此节点会立即被删除,并通知订阅该服务的微服务更新服务列表
(zk上有watch,每当有节点更新,都会通知订阅该服务的微服务更新服务列表)

每当有一个新的微服务注册进来,就会在对应的目录下创建临时子节点,并通知订阅该服务的微服务更新服务列表
(zk上有watch,每当有节点更新,都会通知订阅该服务的微服务更新服务列表)

每个微服务30s向zk获取新的服务列表
eureka基于AP
zookeeper基于CP

1、zk与nginx的区别:

dubbo

dubbo简介:
Dubbo 是一款高性能 Java RPC 架构。它实现了面向接口代理的 RPC 调用,服务注册和发现,负载均衡,容错,扩展性等等功能。
dubbo分为三层:
业务层
RPC 层
Remoting 层
在这里插入图片描述
在这里插入图片描述
上面介绍了 Dubbo 的框架分层,下图的工作流就展示了他们是如何工作的。
dubbo原理
dubbo九种协议:
一、9种协议
1、dubbo 协议 (默认)
2、rmi 协议
3、hessian 协议
4、http 协议
5、webservice 协议
6、thrift 协议
7、memcached 协议
8、redis 协议
9、rest ( 就是 RestFull)
长连接与短连接区别:
短连接
连接->传输数据->关闭连接
比如HTTP是无状态的的短链接,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
因为连接后接收了数据就断开了,所以每次数据接受处理不会有联系。 这也是HTTP协议无状态的原因之一。
长连接
连接->传输数据->保持连接 -> 传输数据-> …->直到一方关闭连接,多是客户端关闭连接。
长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。

redis

Redis 持久化机制
Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。
实现:单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时文件中,持久化的过程结束了,再用这个临时文件替换上次的快照文件,然后子进程退出,内存释放。

RDB是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。)
AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。
缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题
缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间
(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
解决办法:
大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开。

二、缓存穿透
缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
解决办法;
最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。
5TB的硬盘上放满了数据,请写一个算法将这些数据进行排重。如果这些数据是一些32bit大小的数据该如何解决?如果是64bit的呢?

对于空间的利用到达了一种极致,那就是Bitmap和布隆过滤器(Bloom Filter)。
Bitmap: 典型的就是哈希表
缺点是,Bitmap对于每个元素只能记录1bit信息,如果还想完成额外的功能,恐怕只能靠牺牲更多的空间、时间来完成了。

布隆过滤器(推荐)
就是引入了k(k>1)k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。
它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。
Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。
Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。
受提醒补充:缓存穿透与缓存击穿的区别
缓存击穿:指一个key非常热点,大并发集中对这个key进行访问,当这个key在失效的瞬间,仍然持续的大并发访问就穿破缓存,转而直接请求数据库。
解决方案;在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。

三、缓存预热
缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决思路:
1、直接写个缓存刷新页面,上线时手工操作下;
2、数据量不大,可以在项目启动的时候自动进行加载;
3、定时刷新缓存;

四、缓存更新
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
(1)定时去清理过期的缓存;
(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。
五、缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
以参考日志级别设置预案:
(1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
(2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
(3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
(4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

热点数据和冷数据是什么
热点数据,缓存才有价值
对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存
对于上面两个例子,寿星列表、导航信息都存在一个特点,就是信息修改频率不高,读取通常非常高的场景。
对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。
**数据更新前至少读取两次,**缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。
那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?有!比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力。
redis缓存问题及解决方案

Memcache与Redis的区别都有哪些?
1)、存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,redis可以持久化其数据
2)、数据支持类型 memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 ,提供list,set,zset,hash等数据结构的存储
3)、使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。 Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4). value 值大小不同:Redis 最大可以达到 512M;memcache 只有 1mb。
5)redis的速度比memcached快很多
6)Redis支持数据的备份,即master-slave模式的数据备份。

单线程的redis为什么这么快
(一)纯内存操作
(二)单线程操作,避免了频繁的上下文切换
(三)采用了非阻塞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 内部结构
dict 本质上是为了解决算法中的查找问题(Searching)是一个用于维护key和value映射关系的数据结构,与很多语言中的Map或dictionary类似。 本质上是为了解决算法中的查找问题(Searching)
sds sds就等同于char * 它可以存储任意二进制数据,不能像C语言字符串那样以字符’\0’来标识字符串的结 束,因此它必然有个长度字段。
skiplist (跳跃表) 跳表是一种实现起来很简单,单层多指针的链表,它查找效率很高,堪比优化过的二叉平衡树,且比平衡树的实现,
quicklist
ziplist 压缩表 ziplist是一个编码后的列表,是由一系列特殊编码的连续内存块组成的顺序型数据结构,
redis的过期策略以及内存淘汰机制
redis采用的是定期删除+惰性删除策略。
为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.
定期删除+惰性删除是如何工作的呢?
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。
在redis.conf中有一行配置

maxmemory-policy volatile-lru
1
该配置就是配内存淘汰策略的(什么,你没配过?好好反省一下自己)
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据,新写入操作会报错
ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

Redis 为什么是单线程的
官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!)Redis利用队列技术将并发访问变为串行访问
1)绝大部分请求是纯粹的内存操作(非常快速)2)采用单线程,避免了不必要的上下文切换和竞争条件
3)非阻塞IO优点:
1.速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
2. 支持丰富数据类型,支持string,list,set,sorted set,hash
3.支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
4. 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除如何解决redis的并发竞争key问题

同时有多个子系统去set一个key。这个时候要注意什么呢? 不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。
(1)如果对这个key操作,不要求顺序: 准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可
(2)如果对这个key操作,要求顺序: 分布式锁+时间戳。 假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。
(3) 利用队列,将set方法变成串行访问也可以redis遇到高并发,如果保证读写key的一致性
对redis的操作都是具有原子性的,是线程安全的操作,你不用考虑并发问题,redis内部已经帮你处理好并发的问题了。

Redis 集群方案应该怎么做?都有哪些方案?
1.twemproxy,大概概念是,它类似于一个代理方式, 使用时在本需要连接 redis 的地方改为连接 twemproxy, 它会以一个代理的身份接收请求并使用一致性 hash 算法,将请求转接到具体 redis,将结果再返回 twemproxy。
缺点: twemproxy 自身单端口实例的压力,使用一致性 hash 后,对 redis 节点数量改变时候的计算值的改变,数据无法自动移动到新的节点。

2.codis,目前用的最多的集群方案,基本和 twemproxy 一致的效果,但它支持在 节点数量改变情况下,旧节点数据可恢复到新 hash 节点

3.redis cluster3.0 自带的集群,特点在于他的分布式算法不是一致性 hash,而是 hash 槽的概念,以及自身支持节点设置从节点。具体看官方文档介绍。

有没有尝试进行多机redis 的部署?如何保证数据一致的?
主从复制,读写分离
一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。

对于大量的请求怎么样处理
redis是一个单线程程序,也就说同一时刻它只能处理一个客户端请求;
redis是通过IO多路复用(select,epoll, kqueue,依据不同的平台,采取不同的实现)来处理多个客户端请求的

Redis 常见性能问题和解决方案?
(1) Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件
(2) 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即: Master <- Slave1 <- Slave2 <-
Slave3…

讲解下Redis线程模型
文件事件处理器包括分别是套接字、 I/O 多路复用程序、 文件事件分派器(dispatcher)、 以及事件处理器。使用 I/O 多路复用程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
I/O 多路复用程序负责监听多个套接字, 并向文件事件分派器传送那些产生了事件的套接字。
工作原理:
1)I/O 多路复用程序负责监听多个套接字, 并向文件事件分派器传送那些产生了事件的套接字。
尽管多个文件事件可能会并发地出现, 但 I/O 多路复用程序总是会将所有产生事件的套接字都入队到一个队列里面, 然后通过这个队列, 以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字: 当上一个套接字产生的事件被处理完毕之后(该套接字为事件所关联的事件处理器执行完毕), I/O 多路复用程序才会继续向文件事件分派器传送下一个套接字。如果一个套接字又可读又可写的话, 那么服务器将先读套接字, 后写套接字.

为什么Redis的操作是原子性的,怎么保证原子性的?
对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。
Redis的操作之所以是原子性的,是因为Redis是单线程的。
Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗?
不一定, 将get和set改成单命令操作,incr 。使用Redis的事务,或者使用Redis+Lua==的方式实现.

Redis事务
Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的
Redis会将一个事务中的所有命令序列化,然后按顺序执行。
1.redis 不支持回滚“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。
2.如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
3.如果在一个事务中出现运行错误,那么正确的命令会被执行。
注:redis的discard只是结束本次事务,正确命令造成的影响仍然存在.

1)MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
2)EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。
3)通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。
4)WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。

Redis实现分布式锁
Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系Redis中可以使用SETNX命令实现分布式锁。
将 key 的值设为 value ,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作

解锁:使用 del key 命令就能释放锁
解决死锁:
1)通过Redis中expire()给锁设定最大持有时间,如果超过,则Redis来帮我们释放锁。
2) 使用 setnx key “当前系统时间+锁持有的时间”和getset key “当前系统时间+锁持有的时间”组合的命令就可以实现。

redis客户端常用操作命令

jvm

1)如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
在这里插入图片描述
2)JVM 的内存结构,Eden 和 Survivor 比例。
在这里插入图片描述
eden 和 survior 是按8比1分配的
参考链接

3)jvm 中一次完整的 GC 流程是怎样的,对象如何晋升到老年代,说说你知道的几种主要的jvm 参数。
对象诞生即新生代->eden,在进行minor gc过程中,如果依旧存活,移动到from,变成Survivor,进行标记代数,如此检查一定次数后,晋升为老年代,
参考链接
参考链接
参考链接
4)你知道哪几种垃圾收集器,各自的优缺点,重点讲下 cms,包括原理,流程,优缺点
Serial、parNew、ParallelScavenge、SerialOld、ParallelOld、CMS、G1
*
参考链接

Spring boot

Spring Boot简介
Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。
Spring Boot 优点:
容易上手,提升开发效率,为 Spring 开发提供一个更快、更广泛的入门体验。
开箱即用,远离繁琐的配置。提供了一系列大型项目通用的非业务性功能,例如:内嵌服务器、安全管理、运行数据监控、运行状况检查和外部化配置等。
没有代码生成,也不需要XML配置。避免大量的 Maven 导入和各种版本冲突。
Spring Boot 的核心注解
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描。
参考链接
JavaConfig
Spring JavaConfig 是 Spring 社区的产品,它提供了配置 Spring IoC 容器的纯Java 方法。因此它有助于避免使用 XML 配置。使用 JavaConfig 的优点在于:
(1)面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean 方法等。
(2)减少或消除 XML 配置。JavaConfig 为开发人员提供了一种纯 Java 方法来配置与 XML 配置概念相似的 Spring 容器。从技术角度来讲,只使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。
(3)类型安全和重构友好。JavaConfig 提供了一种类型安全的方法来配置 Spring容器。由于 Java 5.0 对泛型的支持,现在可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。
在 Spring Boot 里面,加载配置几种方式。
1)properties文件;
2)YAML文件;
3)系统环境变量;
4)命令行参数;
YAML简介与优势
YAML 是一种人类可读的数据序列化语言。它通常用于配置文件。与属性文件相比,如果我们想要在配置文件中添加复杂的属性,YAML 文件就更加结构化,而且更少混淆。可以看出 YAML 具有分层配置数据。
YAML 现在可以算是非常流行的一种配置文件格式了,无论是前端还是后端,都可以见到 YAML 配置。
YAML 配置和传统的 properties 配置相比到底有哪些优势
配置有序,在一些特殊的场景下,配置有序很关键
支持数组,数组中的元素可以是基本数据类型也可以是对象
简洁
相比 properties 配置文件,YAML 还有一个缺点,就是不支持 @PropertySource 注解导入自定义的 YAML 配置。
Spring Boot 是否可以使用 XML 配置
Spring Boot 推荐使用 Java 配置而非 XML 配置,但是 Spring Boot 中也可以使用 XML 配置,通过 @ImportResource 注解可以引入一个 XML 配置。
spring boot 核心配置文件是?bootstrap.properties 和 application.properties 有何区别
单纯做 Spring Boot 开发,可能不太容易遇到 bootstrap.properties 配置文件,但是在结合 Spring Cloud 时,这个配置就会经常遇到了,特别是在需要加载一些远程配置文件的时侯。
spring boot 核心的两个配置文件:
bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud Config 或者 Nacos 中会用到它。且 boostrap 里面的属性不能被覆盖;
application (. yml 或者 . properties): 由ApplicatonContext 加载,用于 spring boot 项目的自动化配置。
参考链接
Spring Profiles
Spring Profiles 允许用户根据在不同的环境加载不同的配置项,例如数据库、其它的配置项
在相应的dev、qa、prod环境进行相应的配置,在web.xml进行激活
参考链接
Spring Boot 应用程序的安全性及与Shiro的优缺点
为了实现 Spring Boot 的安全性,我们使用 spring-boot-starter-security 依赖项,并且必须在相应的配置文件中添加安全配置。配置类将必须扩展WebSecurityConfigurerAdapter 并覆盖其方法。
比较一下 Spring Security 和 Shiro 各自的优缺点 ?
Spring Security 是一个重量级的安全管理框架;Shiro 则是一个轻量级的安全管理框架
Spring Security 概念复杂,配置繁琐;Shiro 概念简单、配置简单
Spring Security 功能强大;Shiro 功能简单
参考链接
Spring Boot 跨域
跨域:
跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。
跨域参考链接
跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,无法发送其他类型的请求,在 RESTful 风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource sharing) 来解决跨域问题。这种解决方案并非 Spring Boot 特有的,在传统的 SSM 框架中,就可以通过 CORS 来解决跨域问题,只不过之前我们是在 XML 文件中配置 CORS ,现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。
@Configuration
public class CorsConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
            .allowedOrigins("*")
            .allowCredentials(true)
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .maxAge(3600);
}

}
项目中前后端分离部署,所以需要解决跨域的问题。
我们使用cookie存放用户登录的信息,在spring拦截器进行权限控制,当权限不符合时,直接返回给用户固定的json结果。
当用户登录以后,正常使用;当用户退出登录状态时或者token过期时,由于拦截器和跨域的顺序有问题,出现了跨域的现象。
我们知道一个http请求,先走filter,到达servlet后才进行拦截器的处理,如果我们把cors放在filter里,就可以优先于权限拦截器执行。

@Configuration
public class CorsConfig {

@Bean
public CorsFilter corsFilter() {
    CorsConfiguration corsConfiguration = new CorsConfiguration();
    corsConfiguration.addAllowedOrigin("*");
    corsConfiguration.addAllowedHeader("*");
    corsConfiguration.addAllowedMethod("*");
    corsConfiguration.setAllowCredentials(true);
    UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
    urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
    return new CorsFilter(urlBasedCorsConfigurationSource);
}

spring boot

CSRF与XSS攻击
参考CSRF原理简介

参考java面试参考链接
参考web全栈知识
参考中高级java链接
参考中高算法链接
参考springboot链接
参考最全的springboot
参考数据库链接
————————————————

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值