1.JWT使用

JWT token的格式:header.payload.signature

  • header中用于存放签名的生成算法

    {"alg": "HS512"}
  • payload中用于存放用户名信息以及token的生成时间和过期时间

    {"sub":"admin","created":1489079981393,"exp":1489684781}
  • signature为以header和payload生成的签名,一旦header和payload被篡改,验证将失败

    //secret为加密算法的密钥
    String signature = HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
      //签名:payload字符串+盐值 
  • 用户调用登录接口,登录成功后获取到JWT的token;
  • 之后用户每次调用接口的时候,都在http的请求头中添加一个Authorization的头,值为jwt的token
  • 后台程序通过对Authorization头中信息解码及数据签名进行验证,从而获取到用户信息,实现认证授权。

优点:1.无需存放在服务器端,减轻了服务器压力 缺点:一旦生成就无法修改,并且无法销毁。

2.Oauth2.0协议

Oauth2.0是一种协议,用于对外(商户)开放api接口或者应用app授权认证用的。

1.用户通过携带appId去认证授权平台获取授权码,进行授权

2.通过授权码,再携带上appId以及appKey就可以换取到accessToken

3.用户通过accessToken就可以去资源平台请求资源,底层还会再次去认证授权平台去验证accesToken是否有效。

微信、QQ同理,第二步 既可以获取到accessToken,也可以获取到openId,通过openId就可以获取到用户信息。

3.HashMap集合

3.1 HashMap和HashTable的区别

hashMap线程不安全,方法非同步;hashtable线程安全,方法同步。

继承不同,HashTable继承Dictionary,HashMap继承AbstractMap。

hashMap中键可以为null,有且只有一个(下标0的位置),值为null的可以为多个;HashTable键值都不可以为空。

哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。

3.2 时间复杂度

时间复杂度为O(1) :只查询一次,例如数组通过下标获取值

时间复杂度为O(n) :从头查到尾,效率非常低

时间复杂度为O(logn) 平方查询 比如红黑树

3.3 为什么重写equals还要重写hashcode方法

hashcode值是对象内存地址通过哈希算法计算得到的。根据hashcode的规定,两个对象相等,hashcode值也一定相等。

防止hash冲突,虽然两个对象的hashcode的值相等,但是equals方法不一定相等,反之,equals相等,hashcode值一定相等。

3.4 HashMap如何避免内存泄漏问题

自定义对象作为key的时候,一定要重写equals和hashcode方法,保证key不重复创建。

3.5 HashMap底层实现方式

1.基于ArrayList集合的方式实现:不考虑hash冲突问题

2.基于数组+链表(jdk1.7)

通过hashcode方法计算键存放的下标位置,当存放的下标位置有值的情况下,就在当前数组的下标下以链表的方式存放;反之则直接在当前数组下标下存放。链表存放的是哈希值相同,内容值不同的数据。

3.基于数组+链表+红黑树(jdk1.8)

数组容量大于64并且链表长度大于8,转换成红黑树

HashMap使用Entry对象存放键值对

3.6 HashMap底层是有序存放的吗?

无序 散列 不保证连续

3.7 LinkedHashMap的底层原理

有序的集合,双向链表存放;将每个index中链表实现关联,效率比HashMap低。O(n)

当前链表尾节点与下一个链表头节点相关联

3.8 为什么HashMap链表长度超过8会转成红黑树

jdk8中HashMap引入了红黑树,若链表中的元素个数大于等于8的时候,就会将链表转化成红黑树;若红黑树元素个数小于等于6时,就会将红黑树转换成链表。7是为了防止链表与红黑树频繁转换,影响效率。

因为链表的时间复杂度为O(n),红黑树的时间复杂度为Olog(n)。

当链表长度为8时,转换成红黑树存放,红黑树的平均查找长度为8的平方根约等于3,链表的平均查找长度为8/2=4,红黑树相对于链表快的多。当链表长度为6时,转换成链表,链表的平均查找长度为6/2=3,相对于红黑树是慢,但是转换成树结构和生成树的时间并不会太短。

3.9 LinkedHashMap 和 TreeMap底层如何实现有序的

LinkedHashMap底层是通过哈希表+链表实现的,链表记录了添加数据的顺序。

TreeMap底层是通过二叉树实现的,二叉树保证了数据的有序性。

3.10 HashMap如何实现数组扩容问题

HashMap默认的容量大小为16,扩容因子为0.75,扩容的倍数为2。

当HashMap中元素的个数等于16*0.75=12的时候,就会进行扩容,将数组的大小扩大一倍,然后重新计算每个元素在数组中的位置。

3.11 为什么把0.75作为负载因子呢?

这是通过大量的实验统计出来的,如果扩容因子小于0.75的时候进行扩容,资源的浪费的概率就很高;大于0.75时候扩容,会造成get、put操作的碰撞机率增加。

3.12 HashMap8中hash函数是怎么实现的?

(n-1)&(h ^ (h >>> 16))

得到的hashcode转换为32位二进制,前16位和后16位做异或,与上数组的长度-1。

3.13 Arraylist、LinkeList、HashMap的时间复杂度

ArrayList:1.下标查询O1 2.内容查询On

LinkeList:不管是小标还是内容都是O1

HashMap:以1.8为例 数组+链表+红黑树 hash不冲突O1 存放在链表上On 存放在红黑树上Ologn

3.14 二叉查找树优缺点

优点:搜索效率高方便,但是删除或者插入某个元素比较麻烦

缺点:顺序存储可能会造成空间的浪费,但是读取某个节点的时候效率很高。

3.15 HashMap 8与7实现有那些区别

1.HashMap7采用头插法,8采用尾插法。因为HashMap7是采用单链表进行纵向延伸,当采用头插法很容易出现逆序和链表死循环问题;HashMap8中加入红黑树使用尾插法,能够避免出现逆序和链表死循环性问题。

2.扩容后数据存储位置的计算方式也不一样

3.存储方式不同 7->数组+链表 8->数组+链表+红黑树

3.16 HashMap为什么没有缩容?

因为HashMap是散列存放的,若进行缩容,需要重新计算index值,影响效率。

3.17 HashSet底层实现原理

HashSet底层是基于HashMap实现的,key存放的是HashSet的元素,value为空的Object对象,所以是无序的。

底层数据结构:数组+链表+红黑树

4.ArrayList、LinkedList、Vector集合

4.1 ArrayList集合原理分析

ArrayList底层是通过数组来存放的,初始化容量为10,扩容倍数为1.5倍。

add方法:主要进行数组容量初始化操作,以及根据当前存放的下标判断是否需要扩容,扩容倍数为1.5倍。

remove方法:第一种是根据value删除,时间复杂度为On,首先遍历数组获取value对应的下标值,然后根据下标进行删除。第二种是根据下标直接删除,通过System.arraycopy方法将当前数组下标之后的元素前移,并且将最后一个下标位置的元素置空。

get方法:直接通过数据下标获取元素。

4.2 Arraylist底层是如何实现扩容

在add方法中,首次添加元素的时候,会对数组进行容量初始化。扩容是通过判断当前存放的数组下标是否超出数组的容量,若超出就进行1.5倍扩容。

4.3 ArrayList线程是否安全

ArrayList线程不安全。

在多线程情况下,多个线程对数组进行add可能会导致数组越界或发生一个线程覆盖另一个线程添加的值。

4.4 Arraylist集合中ModCount++作用

为了防止在多线程情况下,当一个线程在进行遍历时,另一个线程在不能对数据进行删除、添加操作,否则modCount值发生改变,遍历就会抛出异常。

4.5 ArrayList和Vector集合区别

相同点:ArrayList和Vector的默认初始化容量为10

不同点:1、ArrayList线程不安全,Vector线程安全 2.ArrayList扩容倍数为1.5,Vector扩容倍数为2 3.Vector可以设置容量增长的参数,ArrayList不能。

4.3 LinkedList集合原理分析

1.LinkedList采用双向链表存放,在底层使用静态内部类Node节点存放节点元素,节点元素包含三个信息:关联的上一个节点信息、当前节点值、关联的下一个节点信息

2.add原理:一直通过last在链表结尾添加元素

3.get原理:查询效率低,采用折半的方法查找节点元素

4.remove原理:就是改变节点信息中的头尾信息,然后将删除当前元素值回收

4.4 Arraylist fail-fast机制实现原理

fail-fast是集合框架中的一种错误重试机制。当多个线程对同一个集合进行操作的时候,就有可能产生fail-fast事件。

意思就是:在多线程情况下,一个线程正在进行集合的遍历操作,其他线程对该集合中的进行了删除和添加操作,导致记录集合修改次数的modcount值增加,遍历的线程就会抛出异常,从而产生fail-fast事件。在底层fail-fast机制,就是通过在迭代器中,通过验证modcount的值是否改变,从而实现fail-fast机制。

4.5 CopyOnWriteArrayList 底层实现原理

add方法:

1.CopyOnWriteArrayList集合在每次直接添加元素的时候,都会给add方法进行上锁,从而保证线程安全性。再而将原数组copy到新的数组,并且长度加1,然后将值赋给最后一个下标位置的元素,最后再释放锁。线程安全,效率必低。

2.通过下标位置添加元素的时候,首先也是给方法进行上锁,然后验证是否越界,接着判断添加的下标位置是否为最后一个下标,如果是就直接将数组copy给新的数组并且将长度加一;反之不是就新建一个数组,将旧的数组该下标位置之前的元素copy给这个新的数组,再将当前下标以及当前下标之后的元素copy给新的数组,最后再给新数组下标位置赋值,最后再释放线程。

remove方法

1.通过下标位置删除:和添加元素类似,如果是最后一个元素,就直接将旧的数组copy给新的数组长度减1;如果不是也是创建一个新的数组长度减1,再将当前下标位置的之前的元素copy给新的数组,之后的也copy给新的数组,最后释放锁。

2.通过元素删除:加锁,获取元素位置下标,然后再根据下标删除,最后释放锁。

get方法:

不加锁,直接从从数组中获取。

4.5 CopyOnWriteArrayList和ArrayList区别

1.继承方式不同。和ArrayList继承于AbstractList不同,CopyOnWriteArrayList没有继承AbstractList,只是实现了List接口。

2.ArrayList的iterator迭代器方法是AbstractList中实现的,CopyOnWriteArrayList是自己实现的。

3.CopyOnWriteArrayList线程安全,ArrayList线程不安全。ArrayList中通过modcount值,控制集合在多线程情况下遍历的时候,其他线程进行添加删除,就会抛出异常,产生fail-fast机制。CopyOnWriteArrayList通过锁的机制,保证数据的安全性,没有ArrayList所谓的modcount值,以及fail-fast机制。

5.设计模式

5.1 说说你使用过的设计模式

设计模式有23种,在项目中,我所使用过的有单例模式、工厂模式、代理模式、策略模式、责任链模式、装饰器模式。

5.2 策略模式有了解过吗?有那些应用场景

了解过,策略模式主要解决多重if判断的问题,存在抽象共同行为,将不同的策略,交给不同的子类实现。

应用场景:联合登陆、聚合支付、调用第三方短信接口(多个平台)

5.3 责任链模式有了解过吗?有那些应用场景

了解过,责任链模式就是客户端发送一个请求,处理这一个请求的多个对象形成了一个链式结构,这些对象都有机会处理这一请求,而客户端不需要知道谁是具体的处理对象。

应用场景:过滤器、工作流、erp审批

5.4 模版方法有了解过吗?有那些应用场景

模板方法一般情况下是和策略模式一起使用的。

模板方法的设计思想在于 提供共同的骨架,相同的代码放入父类中,不同的代码通过子类去实现。

5.5 装饰模式有了解过吗?有那些应用场景

装饰模式主要的作用就是不影响我们当前的代码情况下,新增附加功能 。

应用场景:多级缓存的设计(mybatis的一级二级就是采用装饰模式实现的)

与代理模式区别:装饰模式是在不影响我们当前的代码添加新的功能,而代理模式是在我们的目标方法之前和之后进行添加。

5.6 为什么需要使用代理模式?有那些应用场景

代理模式:是在我们目标方法之前和之后实现处理。

应用场景:Aop、mybatis的mapper对象、事务、日志、权限控制

实现方式:动态代理和静态代理

动态代理和静态代理区别:静态代理需要自己编写代理类,动态代理使用反射或者字节码技术CGLIB生成代理类。

5.7 动态代理的实现方式有哪些

jdk动态代理、CGLIB动态代理

5.8 JDK动态代理与CGLIB动态代理区别

jdk动态代理采用反射机制回调,必须依赖接口。

CGLIB采用字节码技术调用,采用重写的形式。

相对比与来说jdk动态代理比CGLIB广泛,但是Cglib效率比较Jdk动态代理高。

为什么Cglib效率比Jdk动态代理效率高?还用Jdk动态代理,首先Jdk动态代理是Java原生就支持,其次我们项目中大多数都是采用面向接口编程,所以Jdk动态代理比CGlib高。

5.9 Spring中如何采用jdk和cglib代理

需要被代理的类是否有实现过接口,如果实现了接口就采用Jdk动态代理,没有实现的情况下就采用cglib代理。

5.10 什么是观察者模式? 观察者应用场景?应用场景

观察者模式作用:对我们代码实现监听 (生产者与消费者模式),使用MQ的时候就可以采用观察者模式实现监听,消费监听MQ的消息。

应用场景: MQ、Zookeeper、Redis、分布式配中心等。

5.11 什么是门面模式?门面模式的应用场景?

门面模式实际上就是包装模式,对我们代码实现包装,将复杂的流程简单化。

5.12 什么是适配器模式?适配器模式原理

适配器模式:将不能被调用的接口,采用适配器转换可以调用。

应用场景:多环境不同版本的兼容、Mybatis日志接口

5.13 什么是单例模式、应用场景

在同一个jvm中只能存在一个实例

应用场景:配置文件、SpringBean等、Servlet也是单例的。

5.14 单例模式有那些创建方式

饿汉式、懒汉式、枚举、静态内部类、双重检验锁。

5.15 饿汉式与懒汉式区别

懒汉式:先天性线程不安全,当真正需要该实例的时候才去加载,需要我们自己人为上锁控制线程安全的问题。

饿汉式:先天性线程安全,当我们项目在启动的时候创建该实例,会导致项目启动比较慢。

5.16 静态内部类方式与双重检验锁的区别

静态内部类使用静态关键字和保证我们实例是单例,

而为我们的双重检验锁采用lock锁保证安全的

6.核心框架源码(需复习)

7.高并发和高可用

7.1 nginx了解吗?有哪些作用

nginx可以实现反向代理、负载均衡、故障转移、黑白名单、解决跨域问题、静态资源的缓存、服务器限流(nginx+lua+openresty)、ddos攻击

7.2 服务器集群存在哪些问题

1.分布式session一致性问题:通过spring-session框架解决,是通过将session存到redis中,所以可以使用token代替session

2.分布式任务job幂等性问题:xxl-job

3.分布式日志采集问题:elk+kafka

4.分布式全局id的生成:雪花算法

7.3 软负载和硬负载

软负载是基于软件的形式进行负载,比如nginx和ribbon

硬负载是通过硬件进行负载,比如F5

7.4 网站跨域问题产生和解决方案

产生:由于浏览器的限制,域名、端口、协议有一种不一致时,就会出现跨域。

解决方案:

1.在请求头中设置允许跨域,适合小公司

2.使用httpcliect转发请求,效率低

3.使用jsonp处理,缺陷在于只支持get请求,不支持post请求

4.通过nginx配置允许跨域 “Access-Control-Allow-Origin”, “*”

5.网关中配置与nginx类似的允许跨域的代码 “Access-Control-Allow-Origin”, “*”

6.如果是springboot项目中的话,在controller层可以加上@CrossOrigin注解

7.5 lvs基本作用

lvs是Liunx的虚拟vip技术,使用lvs可以管理我的nginx集群,注意的是云服务器默认不支持虚拟vip技术。

7.6 keepAlived基本的作用

keepAlived是一个心跳脚本的检测软件。比如可以间隔的发送心跳到tomcat检查其是否宕机,如果宕机,keepAlived就会帮我们执行重启脚本。如果多次重启还是失败的话,就会发送邮件告诉运维人员。

7.7 项目正在发布时,如果解决用户无法访问的问题

可以采用集群的方式部署,然后采用故障转移到下一台服务器访问或者通过灰度发布解决。

7.8 如果nginx宕机的话,如何保证服务可用

nginx是我们tomcat服务器请求的入口,如果单台nginx服务器宕机的话,可能会导致整个tomcat不可用。所以nginx最好采用集群的方式部署,比如一主一备、多主多备,然后再通过lvs管理我们的nginx集群,实现故障转移,最后还可以使用keepalived通过发送心跳去检测nginx是否宕机,宕机的话,去执行重启脚本重启nginx服务器。即nginx+lvs+keepAlived

7.9 nginx+lvs+keepAlived分别作用

nginx:反向代理,服务请求的入口

lvs:通过虚拟vip的技术来管理nginx服务

keepAlived:通过发送心跳去检测nginx服务器是否宕机,然后执行重启脚本

7.10 tomcat服务器宕机了,如何解决?

我们可以采用tomcat集群实现故障转移,还可以通过keepAlived去发送心跳给tomcat服务器检测服务器是否宕机,宕机的话,keepAlived就会执行重启脚本,如果重启多次还是失败的话,就会发送邮件给运维人员手动重启。

7.11 什么是网站动静分离的架构模式,有哪些实现方案

动静分离的架构模式就是将动态资源和静态资源分服务器部署。

我们可以基于nginx实现动静分离,也可以使用第三方的静态资源服务器,比如oss对象云存储、七牛云。

7.12 CDN内容分发基本的概念

将我们的静态资源缓存到全国各个节点,再遵循就近原则访问,从而可以减少我们服务器的带宽距离的传输。

7.13 高并发解决方案的思路有哪些

在web项目中,因为网页中大多数都是静态资源占用了我们服务器的带宽,接口响应一般占不了多大的带宽,所以我们可以采用动静分离的架构模式。

前端优化:

1.采用动静分离的架构模式,将静态资源部署到单独的服务器上或者第三方静态资源服务器,比如oss对象存储、七牛云(自带cdn加速)

2.对我们的静态资源进行压缩。

3.可以采用nginx+lua+openresty对我们静态页面实现布局缓存

后端优化:

1.使用微服务网关或者nginx服务器对我们的接口进行api限流、服务保护、黑白名单

2.使用redis缓存减轻我们服务器的压力,redis采用集群的方式部署或者读写分离的形式,提高吞吐量。

3.使用多线程或者MQ的形式异步处理我们的代码,从而将请求结果快速响应给客户端。

4.jvm、tomcat实现参数调优提高吞吐量、以及代码优化。

5.mysql服务器实现性能优化、分表分库、采用读写分离模式提高吞吐量。

8.分布式协调工具zookeeper(需复习)

9.网路通讯协议(非必须)

10.分布式Redis缓存(需复习)

10.1 redis和Ehcache缓存的区别

redis的缓存属于分布式缓存技术,数据存放在服务器内存中,也会持久化到硬盘上,它是通过socket访问缓存服务,效率比Ehcache低,集群数据共享很快、很方便。

Ehcache直接jwm中进行缓存,可以支持集群数据共享,但是效率非常的低。

10.2 redis的应用场景

1.存放token令牌,实现登录认证

2.存放短信验证码

3.缓存热点数据

4.实现网页计数器

5.分布式锁

6.延迟操作

10.3 redis线程为什么效率高

1.数据存放在内存中,访问快

2.数据结构简单,对数据的操作也简单。

3.采用单线程,不用担心多线程所导致的cpu的消耗问题,也不用考虑锁的问题,不存在加锁释放锁,从而不会出现死锁而导致的性能消耗。

4.使用多路IO复用模型,非阻塞IO。使用一个线程维护多个不同redis客户端请求的操作

10.4 redis线程安全为什么效率还非常的高

redis采用的NIO的多路IO复用机制,能够非常好的支持并发,从而保证线程安全问题。

NIO在不同系统上的实现方式不同,在windows上通过使用select实现轮训,时间复杂度为On,效率非常的低,默认对轮训的数据有所限制,很难支持上万级的tcp连接。

在Liunx上采用epoll实现事件驱动回调,不会存在空轮训的情况,只对活跃的socket连接实现主动回调从而提高了性能,所以时间复杂度为O1

10.5 redis的数据类型有哪些

string、list、hash、sorted set(有序)、Set(无序)

10.6 redis如何存放对象

1.基于json序列化存放,优点:阅读性强、可垮语言 缺点:明文不安全

2.基于String二进制直接存放我们的对象,阅读性差,相对安全。

10.7 mysql和redis一致性解决方案

产生背景:在分布式中同步数据,很难保证强一致性,都是保证最终一致性的思想。短暂的数据延迟是允许的,但是最终数据必须要保证一致性。

1.直接在修改的时候,删除redis缓存

2.通过mq的形式订阅mysql的binlog文件,异步的将数据同步到redis中

3.基于canal订阅binlog二进制文件,然后再通过mq异步同步到redis中

10.8 全量同步和增量同步实现的区别

全量同步:每天避开高峰期,将所有的数据实现备份同步,优点:数据不易丢失,效率高,但是会产生数据同步的延迟。

增量同步:对行为的操作实现数据同步,从而导致数据同步延迟的概率比较高、效率低。

10.9 redis内存满了,如何处理

1.redis内存满了,可以采用扩容服务器内存的方式,但是缺点:治标不治本,扩容硬件的成本较高,不推荐。

2.采用redis内存淘汰策略,redis如果内存满了,删除不经常被使用的key。

10.10 redis宕机了,数据会丢失吗

不会丢失,z因为redis有持久化机制,采用RDB和AOF进行持久化。

10.11 RDB和AOF同步存在哪些区别

RDB采用定时全量同步,将所有redis缓存写入到硬盘中。

AOF采用行为增量同步,以日志文件(redis的执行命令)的形式存放到硬盘中进行追加。

RDB的恢复速度比AOF高。

10.12 redis事务支持回滚吗

redis中存在事务,但是没有回滚只有取消事务。

10.13 为什么redis中没有回滚事务

在mysql中,如果mysql对某行数据开启了事务,采用了行锁的机制,其他线程客户端不能够对该行数据进行写操作。

在redis中,如果对某个key开启了事务,其他线程是可以对该key做写的操作。

10.14 redis实现分布式锁的思路

分布式锁产生背景:在集群模式下,多个进程同时修改同一个共享的数据时,这就可能会造成共享数据不一致,这样就需要通过分布式锁来解决。即在同一时刻只能有一个进程可以操作该共享数据。

实现思路:在分布式下,通过redis的setnx命令设置锁,setnx命令只有第一次执行才会成功并返回1,其他情况再setnx同一key值时则返回0。 redis实现分布式锁的思路,多个进程在同一时刻,通过执行setnx命令锁返回的值,判断锁是否释放,如果返回1就是获取到锁,然后执行业务代码,然后删除redis中的key值,释放锁;如果返回0,则该业务代码已上锁,进程执行结束或者进行重试。

10.15 Redis中SetnX与Set命令的区别

setnx命令:只有第一次执行才会成功并且返回1,其他情况下再次setnx该key值就会失败并返回0。

相对于set命令:setnx不允许修改已存在的key相对于的value值,而set命令是允许的,多次set同一key,value值会进行覆盖,也就是说setnx对已存在key,是不能再次做修改操作的。

10.16 在分布式锁中如果超时了,怎么办

1.进行续命,第一种可以在任务执行的时候,开启一个守护线程,在守护线程中每隔一段时间重新去设置过期时间。第二种通过redisson中的看门狗来实现。

2.如果多次续命还是超时的话,直接回滚数据库事务,同时释放锁。

10.17 redis主从复制原理

在单台redis的情况下,如果redis宕机了,可能会造成数据的缺失。然后我们就可以通过redis的主从复制来进行数据备份。

主从复制实现原理:多个redis节点为主从关系,主节点进行写操作,然后同步到每个从节点,而从节点负责读操作。

可以实现redis的备份,读写分离,简单的集群模式。

10.18 redis主从复制存在的问题

1.复制数据的延迟

2.异步复制导致数据的丢失:因为主节点到从节点的复制是异步的,如果在复制的过程中,从节点就宕机了,这时候就导致从节点数据的丢失。

3.规避全量复制

10.19 哨兵集群架构设计原理

哨兵模式实现的是监控我们的主节点,如果主节点宕机了,就会从从节点中选举出一个主节点,也就是一个自动选举的过程。

10.20 哨兵是如何确定主节点宕机,然后并重新选举的(复习)

在哨兵模式中,通过两种机制判断主节点是否宕机,主观下线和客观下线。

主观下线:就是哨兵sentinel节点向主节点发送一个ping命令,如果超出了有效回复时间,不好意思,我就认为你挂了。

客观下线:就是需要知道主观下线的哨兵节点的个数,比如:如果有5个哨兵节点,quorum=2,那么只要有2个哨兵节点主观下线,那么就可以判断主节点客观下线。

10.21 Redis缓存穿透概念与解决方案

概念:频繁查询不存在的key,从而直接查询数据库,对数据库造成压力。

解决方案:1.记录空查询key,设置过期时间 2.采用布隆过滤器 3.对我们的api接口进行限流、设置黑白名单。

10.22 redis缓存雪崩概念与解决方案

概念:在同一时间大量的key同时失效,导致同时查询我们的数据库,从而对数据库造成压力。

解决方案:1.对我们redis的key设置随机的过期时间 2.设置redis的key不过期

10.23 redis缓存击穿的解决方案

概念:在高并发情况下,多个请求同时访问同一个过期的key,多个请求同时查询数据库,对数据库造成的压力。

解决方案:1.基于分布式锁解决 2.设置软过期时间

10.24 redis的集群方式有哪些

1.主从复制

2.哨兵机制

3.redis的cluster集群模式:动态实现扩容和缩容 而且保证每个节点的数据不冗余存放

10.25 RedisCluster分片集群实现原理

原理:在我们的Redis集群模式中分为16384个卡槽,类似于数据库的中分表模式。当我们在写入一个key的时候,会对该key计算crc16算法得出一个数字16384=卡槽的位置。每个卡槽对应具体节点存放的位置,这样的话就可以将我们的数据可以均摊的存放各个节点。

10.26 RedisCluster如何快速扩容和缩容

重新分配每个卡槽对应的Redis的存放节点。

11.分布式消息中间件(需复习)

11.1 消息中间件可以帮我们解决哪些问题

1.解耦 2.流量削峰 3.日志收集(elk+kafka大量日志传输)4.分布式事务问题

11.2 为什么MQ可以解决我们的流量的削峰问题

因为在我们的MQ服务器中投递消息,可以设置队列最大的阈值极限,当投递的消息超出了队列所设置的阈值,生产者投递消息就会被拒绝。(拒绝了可以将消息存放在死信队列中)

11.3 rabbitmq的常用队列模型

1.点对点 2.工作队列模式 3.发布订阅 4.路由模式Routing 5.通配符模式Topics

11.4 mq宕机后,消息会丢失吗

不会,因为mq会将消息缓存到硬盘中。

11.5 MQ如何保证消息不缺失

1.消息确认机制,生产者必须确定我们的消息一定投递到mq服务器中了。

2.mq服务器会将消息持久化到我们的硬盘

3.消费者一定要确保我们的消息一定消费成功,采用手动ack模式,确定我们的消息消费成功的情况下,再通知我们的MQ服务器端将该消息从队列中移除。

11.6 MQ如何保证消息的幂等性问题

产生原因:1.消费者在规定的时间内没有将消费结果返回给服务器端或者没有抛出异常,导致MQ服务器端默认消息消费失败,然后自动触发重试机制。2.MQ在触发重试机制的时候,导致消息重复消费的问题

解决方案:1.生产者在消息头中设置全局业务id 2.消费者根据全局业务id判断是否已经处理过该业务,如果处理了,就直接告诉MQ已经消费成功,防止出发重试机制。

11.7 MQ服务器端如果消息堆积,该如何处理

产生原因:生产者的投递速率和消费者的消费熟虑不匹配导致

解决方案:1.提高消费者的消费速率 2.消费者应该批量的去消费消息

11.8 投递到MQ服务器端的消息已经满了,怎么办

1.如果满了将消息存放到死信队列中,后期消费。

2.记录生产者投递消息的日志,后期通过定时job来自动化补偿。

11.9 RabbitMQ如何实现消息确认的机制

rabbitmq通过开启生产者confirm消息确认机制,来确保消息投递成功的。生产者投递消息后,如果broker服务器端收到消息,则会给我们的生产者一个应答。生产者通过接收到应答,来确定消息是否投递成功。

12.10 RabbitMQ如何保证消息不丢失

1.消息持久化:消息默认存放在内存中,我们需要通过设置,将其持久化到硬盘中。要想消息做到持久化,必须满足三个条件:Exchange设置持久化、Queue设置持久化、Message持久化发送

2.ACK确定机制:多个消费者同时接收消息,比如消息接收到一半的时候,一个消费者挂掉了(超时、断网等等)。这时候我们就可以通过ACK消息确定机制来保证消息不丢失。ACK就是消费者端消费消息完成后需要通知服务器端,服务器端收到消费成功的消息之后,才能将消息从内存中删除,从而就解决了,即使一个消费者挂了,还有其他消费者可以去消息该消息。

3.消息补偿机制:为什么还要消息补偿机制?假如保存到硬盘的过程中,当前队列节点挂了,存储节点硬盘又坏了,消息丢了怎么办?这时候就需要消息补偿机制,记录消息写入DB日志,发送日志和接受日志,然后就可以根据DB日志记录判断消息发送消费是否成功,不成功,进行消息补偿措施,从小发送消息处理。

12.11 RabbitMQ如何实现公平队列***

公平队列的原理:队列服务器向消费者发送消息的时候,消费者采用手动应答模式,队列服务器必须要收到消费者发送ack结果通知,才会发送下一个消息。(快的处理的多,消费的多)。

12.12 RabbitMQ交换机的作用

rabbitmq中交换机,是用来接收生产者投递到的消息,并将其转发到对应的队列中,然后再被消费者消费。

rabbitmq中有地种不同的交换机类型:

Direct exchange:直连交换机,转发消息到routigKey指定的队列

Fanout exchange:扇形交换机,转发消息到所有绑定队列(速度最快)

Topic exchange:主题交换机,按规则转发消息(最灵活)

Headers exchange:首部交换机

12.13 死信队列的作用与应用场景

死信队列,又叫延迟队列,是rabbitmq队列中的一种,指的是进入该队列中的消息会被延迟消费,它和普通队列的最大差异就是进入普通队列会立即被消费处理,而延迟队列则会通过一定时间再进行消费。

作用:在一些重要的业务队列中,为了确保没有被正确消费的消息不被遗弃,一般发送消费异常主要原因有 由于消息本身存在错误导致处理异常、处理过程中参数校验异常、以及网络波动造成的异常等等,当发生异常的时,总不能每次通过日志的形式去获取原消息,再让运维重新投递消息吧。所以我们通过死信队列,将没有被正确消费的消息转发到死信队列中,待后续排查好原因,再编写相应的处理处理这些死信消息。

应用场景:

1.订单在十分钟之内未支付则自动取消。
2.用户注册成功后,如果三天内没有登陆则进行短信提醒。
3.用户发起退款,如果三天内没有得到处理则通知相关运营人员。
4.预定会议后,需要在预定的时间点前十分钟通知各个与会人员参加会议。

12.13 MQ如何保证消息顺序一致性问题

产生背景:MQ服务器集群或者mq采用分区模型存放消息,每个分区对应一个消费者消费消息。

比如mysql与redis的实现异步同步数据。

核心办法:消息一定要投递到同一个mq、同一个分区模型、最终被同一个消费者消息。投递消息的时候,设置相同的消息key,根据相同的消息key计算hash存放同一个分区中。

12.14 RabbitMQ如何解决分布式事务

产生背景:以现在流行的外卖为例,用户下单之后订单服务调用派单服务通知外卖人员送单,这时候订单服务和派单服务可以通过mq进行异步通讯,保证订单表和派单表必须一致。

1.确保生产者一定要将数据投递到MQ服务器中

生产者采用comfirm机制,确认应答机制。如果投递失败的话,进行重试。

2.MQ消费者能正常消息消息

采用手动ACK模式,使用补偿机制,注意幂等性问题。

3.采用补偿机制

再创建一个补单的消费者进行监听,如果订单创建后,又进行回滚了,此时需要将订单进行补偿。

交换机采用路由键模式,补单队列和派单队列都绑定同一个路由键。

订单30分钟超时有那些实现方案

MQ如何合理选择重试策略

MQ如果重试多次还是失败呢如何解决

kafka的设计原理?

kafka如何实现分区

kafka的Partition Broker、Offset作用有那些

kafka集群如何实现相互感知

单点登录

12.分布式数据存储

13.微服务与分布式解决方案

14.互联网安全架构设计

其他:Java8新特性

1.函数接口

只有一个抽象方法的接口,加上@FunctionalInterface注解,否则会编译报错

2.默认接口、静态默认方法

在接口中,支持使用default、static修饰符的方法,并且可以写方法体。

3.Stream流

比如集合过滤、集合拆解、求集合中对象某个属性的最大小值

4.方法引用

方法引用就是lambda表达式中直接引用的方法。必须遵循规范:引入的方法参数列表返回类型必须要和函数接口参数列表、返回类型保持一致。

4种:静态方法引用、对象方法引用、实例方法引用、构造方法引用

5.时间日期API

LocalDate、LocalDateTime

6.Base64库

encode编码、decode解码

7.Optional类

可以解决帮我们解决空指针问题


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

2021 迈步向前 下一篇