java基础

  • jdk jre jvm三者之间的关系?

图片来源于网络

  • 数据类型?

    在这里插入图片描述

  • equals和==区别?

A: ==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同
B: ==是指对内存地址进行比较 , equals()是对字符串的内容进行比较
C: ==指引用是否相同, equals()指的是值是否相同

引用面试题:
两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
答案:不对
原因:hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠
equal()相等的两个对象他们的hashCode()肯定相等。

  • String 属于基础的数据类型吗?

答案:不是,String是final修饰的java类

  • String str="i"与 String str=new String(“i”)一样吗?

答案:不一样,内存地址不同。

  • 请你描述一下什么是多态?

多态是继封装、继承之后,面向对象的第三大特性。
多态现实意义理解:
现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。
Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
多态体现为父类引用变量可以指向子类对象。

  • 多态的转型?

向上转型:多态本身就是向上转型过的过程
使用格式:父类类型 变量名=new 子类类型();
适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作。

向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型
使用格式:子类类型 变量名=(子类类型)父类类型的变量;
适用场景:当要使用子类特有功能时。

  • String、StringBuffer和StringBuilder区别

三者共同之处:都是final类,不允许被继承,主要是从性能和安全性上考虑的,因为这几个类都是经常被使用着,且考虑到防止其中的参数被参数修改影响到其他的应用。
StringBuffer是线程安全,可以不需要额外的同步用于多线程中;
StringBuilder是非同步,运行于多线程中就需要使用着单独同步处理,但是速度就比StringBuffer快多了;
StringBuffer与StringBuilder两者共同之处:可以通过append、indert进行字符串的操作。

面向对象

  • 面向过程与面向对象的区别?

面向过程:将问题分解成步骤,然后按照步骤实现函数,执行时依次调用函数。数据和对数据的操作是分离的。
面向对象:将问题分解成对象,描述事物在解决问题的步骤中的行为。对象与属性和行为是关联的。

  • 面向过程与面向对象的优缺点?

面向过程的优点是性能比面向对象高,不需要面向对象的实例化;缺点是不容易维护、复用和扩展。
面向对象的优点是具有封装、继承、多态的特性,因而容易维护、复用和扩展,可以设计出低耦合的系统;缺点是由于需要实例化对象,因此性能比面向过程低。

  • 对象创建的过程?

对象创建之前,首先类被加载(先加载父类再加载子类),执行静态代码块,执行构造器((先构造父类再构造子类)),创建对象实例静态方法和类有关,直接使用类名.方法来进行调用

  • 抽象类和接口有什么区别?

抽象类当中可以有普通方法,必须有抽象方法,可以有成员变量,不可以被实例化
接口当中只能有抽象方法,不能有普通方法,可以有静态常量,不可以被实例化,接口可以看做是一种特殊的抽象类

  • java创建对象的几种方式

new
反射
clone
序列化机制
前两者都需要显式调用构造方法,造成耦合性最高的恰好是第一种,因此你发现无论什么框架,只要涉及解耦必先减少new的使用。

  • switch参数

byte/short/char/int/String 以及对应的封装类和enum类型

  • 你对String对象的inertn()熟悉么?

intern()方法会首先从常量池中查找是否存在该常量值,如果常量池中不存在则现在常量池
中创建,如果已经存在则直接返回。
比如
Strings1=“aa”;
String s2=s1.intern();
System.out.print(s1==s2);//返回true

  • Object有哪些公共的方法?

equals()
clone()
getClass()
notify() notifyAll() wait()
tostring

  • 深拷贝和浅拷贝的区别是什么?

浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用
仍然指向原来的对象。换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
深拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而那些引用其他对象的变量
将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深拷贝把要复制的 对象所引用的对象都复制了一遍。

  • String, StringBuffer 和StringBuilder区别

String是字符串常量,final 修饰: StringBuffer 字符串变量(线程安全);
StringBuilder字符串变量(线程不安全)。

垃圾回收

  • 你知道哪些垃圾回收算法?

垃圾回收从理论.上非常容易理解,具体的方法有以下几种:
1.标记-清除
2.标记复制
3.标记-整理
4.分代回收

  • 如何判断一一个对象是否应该被回收

这就是所谓的对象存活性判断,常用的方法有两种:
1.引用计数法;
2.对 象可达性分析。
由 于引用计数法存在互相引用导致无法进行GC的问题,所以目前JVM虚拟机多使用对象可达 性分析算法。

  • 简单的解释一下垃圾回收

Java垃圾回收机制最基本的做法是分代回收。内存中的区域被划分成不同的世代,对象根据其存活的时间被保存在对应世代的区域中。一般的实现是划分成3个世代:年轻、年老和永久。内存的分配是发生在年轻世代中的。当一个对象存活时间足够长的时候,它就会被复制到年老世代中。对于不同的世代可以使用不同的垃圾回收算法。进行世代划分的出发点是 对应用中对象存活时间进行研究之后得出的统计规律。一般来说,一个应用中的大部分对象的存活时间都很短。比如局部变量的存活时间就只在方法的执行过程中。基于这一点,对于年轻世代的垃圾回收算法就可以很有针对性。

  • 调用System. gc()会发生什么?

通知GC开始工作,但是GC真正开始的时间不确定。

  • 常用的数据结构 数组和链表

数组会在内存当中开辟一块连续的存储空间,需要指定长度
数组当中的每一个元素都有索引,查询的话通过索引可以直接定位到
某一个元素,效率很高,但是进行删除的话,数组会进行移动,
所以效率很低.
链表不需要连续的存储单元,链表中的上一个元素通过指针指向
下一个元素,所以链表结构进行查询的话,头部尾部比较快,
中间元素查找速度比较慢,但是删除的话,只需要删除前后指针,重新
建立指针就可以了,所以删除的性能很高.

  • java当中的集合类或集合框架

collection接口是集合类的顶层接口,collections是工具类
collection有两个子接口,一个list接口,一个是set接口
list接口有序可重复,set接口无序不可重复
list接口常用的实现类有
arrayList 基于数组实现的
linkedList 基于链表实现的 区别参考数组和链表区别
vector 基于数组实现的,但是是线程安全的(synchronized)
copyOnwriteArrayList 复制数组副本来实现的
set接口常用实现类
HashSet 基于hashMap来实现的,实现不可重复是通过 hashCode方法和equals方法进行两次比较,先比较hashCode,再通过equals
TreeSet 基于二叉树来实现的,可以对元素进行排序(排序规则默认是自然顺序,可以自定义比较器,实现自定义排序,自定义实现类Java.lang.Comparable接口,并重写compareTo(Object obj)方法)
Map接口(key value结构的)
map接口的实现类
HashMap key不可重复,无序,可以为null
实现原理 基于数组和链表来实现的
当存入一组键值对的时候,先对key进行hash,然后映射到一个初始化长度为16的数组上,当不同的key产生hash碰撞的时候,value会通过链表结构来进行存储,jdk1.8之后对hashMap进行了改进,当链表长度达到临界值8,会通过红黑树来存储value,hashMap有两个参数,一个是初始化数组长度16,负载因子0.75,当满足扩容阈值的时候(当数组的12个元素被存满,并且有hash碰撞了),动态扩容,以2倍的增长方式进行扩容.
负载因子是0.75的时候,空间利用率比较高,而且避免了相当多的Hash冲突,使得底层的链表或者是红黑树的高度比较低,提升了空间效率
容量是2的n次幂,可以使得添加的元素均匀分布在HashMap中的数组上,减少hash碰撞,避免形成链表的结构,使得查询效率降低!
HashTable
HashTable是线程安全的hashMap(synchronized机制)key不准许为null
TreeMap基于二叉树来实现的,可对key进行自然排序(自定义比较器,写比较规则)
ConcurrentHashMap是线程安全的,对整个hash桶采用分段策略,拆分成若干个段segment,对每一个段上锁(synchronized),极大的提高了并发修改的效率.默认分为16个段

异常处理

jvm

反射

多线程与线程安全

  • 说说进程,线程,协程之间的区别

简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有-一个进程,-一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。线程是进程的一一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。同一进程中的多个线程之间可以并发执行。

  • 你了解守护线程吗?它和非守护线程有什么区别

程序运行完毕,jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程。守护线程 最典型的例子就是GC线程。

  • 什么是多线程上下文切换.

多线程的.上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。

  • 创建两种线程的方式?他们有什么区别?

通过实现java.lang.Runnable或者通过扩展java.lang.Thread类。相比扩展Thread,实现Runnable接口可能更优.原因有二:
Java不支持多继承。因此扩展Thread类就代表这个子类不能扩展其他类。而实现Runnable接口的类还可能扩展另一个类。
类可能只要求可执行即可,因此继承整个Thread类的开销过大。

  • Thread类中的start()和run()方法有什么区别?

start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

  • 线程的生命周期

新建状态(new一个线程的时候)
调用线程.start方法的时候,等待cpu分配时间片 可运行状态
当被cpu分配到了时间片之后,线程开始运行,运行run方法当中的内容 运行状态
当线程遇到sleep、wait等方法的时候,线程会进入阻塞状态 阻塞状态
当休眠时间到期,或者被notify了,线程又回到了可运行状态 死亡状态
线程运行结束之后,就会销毁,被jvm的gc回收

  • 多线程并发或线程安全问题如何解决?

1:通过volatile 关键字修饰变量,可以实现线程之间的可见性,
避免变量脏读的出现,底层是通过限制jvm指令的重排序来实现的
适用于一个线程修改,多个线程读的场景
2:通过synchronized锁(任意对象)来实现线程同步,自动锁的思想,
底层实现原理:当有线程进入同步代码块之后,利用jvm的程序计数器将
锁的标记置为1,当别的线程再想进入的时候,发现锁的标记为1,
该线程就去锁池等待,当第一个线程出来之后,锁的标记会置为0,
之后cpu会随机分配一个线程再次进入同步代码块.
3:通过lock锁的机制,进行手动lock,和unlock,但是这种很容易出现死锁。
注意加锁以及解锁的顺序,就可以避免死锁
4:通过线程安全的集合类,可以解决并发问题
ConcurrentHashMap
CopyonWriteArrayList
5:使用并发包下面的原子类,底层使用的是cas机制(乐观锁),可以解决并发问题 atomicInteger 线程安全的原子整型类
6:使用线程池来创建和管理线程,也可以一定程度上解决并发问题
7:使用ThreadLocal来修饰变量,可以解决并发问题
ThreadLocal底层是怎么实现的?
多个线程会复制一份ThreadLocal变量的副本进行操作,互不影响,来保证线程安全的

自旋锁:自旋锁在JDK1.6之后就默认开启了。基于之前的观察,共享数据的锁定状态只会持续很短的时间,为了这一小段时间而去挂起和恢复线程有点浪费,所以这里就做了一个处理,让后面请求锁的那个线程在稍等一会,
但是不放弃处理器的执行时间,看看持有锁的线程能否快速释放。为了让线程等待,所以需要让线程执行一个忙循环也就是自旋操作。在jdk6之后,引入了自适应的自旋锁,也就是等待的时间不再固定了,而是由上一次在同一个锁上的自旋时间及锁的拥有者状态来决定。

偏向锁:在JDK1.之后引入的-项锁优化,目的是消除数据在无竞争情况下的同步原语。进一步提升程序的运行性能。偏向锁就是偏心的偏,意思是这个锁会偏向第一个获得他的线程,如果接下来的执行过程中,改锁没有被其他线程获取,则持有偏向锁的线程将永远不需要再进行同步。偏向锁可以提高带有同步但无竞争的程序性能,也就是说他并不一-定总是对程序运行有利,如果程序中大多数的锁都是被多个不同的线程访问,那偏向模式就是多余的,在具体问题具体分析的前提下,可以考虑是否使用偏向锁。

轻量级锁:为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁"和“轻量级锁”,所以在JavaSE1.6里锁-
.共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。

悲观锁的思想对线程并发持悲观态度,使用互斥锁的机制来解决并发问题

乐观锁的思想是使用cas(compareAndSwapper)机制,不上锁,但也能解决并发问题
int count=100;
最后一次修改的时间戳或者版本号;
1:先查询一下变量的值以及它的最后一次修改的时间戳;
2:在本地修改变量的值
3:对变量进行修改的时候,先比对一下最后一次修改的时间戳是否发生变化了,如果没发生变化,修改变量的值, 如果发生变化了,重试以上流程

  • 线程的使用场景:

项目当中使用线程池(注册完毕发邮件和短信)
tomcat本身就是通过线程的机制来处理用户的请求

容器技术

  • docker

技术选型

数据库

设计数据库首先要遵循三大范式要求:原子性、依赖性、关联性
1.原子性是指数据库表的每一列都是不可分割的原子数据项
2.依赖性是指实体的属性完全依赖于主关键字。
3.关联性是指任何非主属性不依赖于其它非主属性 对于数据库设计来说,不仅仅要考虑范式要求,为了节省查询效率,允许适当的有一些冗余字段。

  • sql怎么调优,索引命中怎么知晓?

使用explain关键字查看sql的执行计划,可以看到该sql的
索引命中情况、索引命中类型、命中率等相关的信息,通过这种方式
可以对sql语句进行调优,对关键查询的条件创建索引以及注意一下sql语句的
用法
尽量不使用like,尽量避免在某一列上进行运算,
不使用 in not in 使用exist not exist等等代替
如果已知道查询的结果最大条数可以加limit限制查询条数,防止全盘扫描

  • 索引的常用数据结构?

hash索引 hash 均匀分布 B树索引 B树 分布有序
比如我对A列分别建立Hash索引和B树索引,当我频繁对A列的数据进行修改的时候,
性能高,但是如果按照范围进行查找的话,B树索引的性能高(where A>100 and A<1000)

  • redis持久化

Redis的高性能是由于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。
Redis支持两种方式的持久化,一种是RDB方式,一种是AOF方式。可以单独使用其中一种或将二者结合使用。

  • RDB持久化

RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。
RDB是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置: save 900 1 save
开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件),每个条件之间是“或”的关系

  • AOF持久化

默认情况下Redis没有开启AOF(append only
file)方式的持久化,可以通过appendonly参数开启:appendonly yes
开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬
盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof,
可以通过appendfilename参数修改:appendfilename appendonly.aof.

缓存

缓存的穿透和雪崩问题,你们是如何解决的? 穿透 顾名思义,就是大量的请求穿过缓存层,达到数据库,造成数据库压力过大
举例:你登录淘宝之后,订单搜索,你输入一个无效的订单,通过压测工具恶意攻击
解决方案:1.每次请求时,可以在redis层,做一个bitMap,先去bitmap中查询一下是否存在该条件,进行过滤
2.每次请求,查询数据库就算不存在,我也将查询条件和null在redi中进行缓存

雪崩是指在某一个时间点,大量的缓存同时失效,请求达到数据库,造成数据库压力过大
解决方案:对大量数据设置过期时间时,哪怕业务需要同时失效,我也不设置同一个时间,分别对每一组key value 设置失效时间,让每一组key value的失效时间间隔个几毫秒

  • 什么是redis?

redis是一个高性能的key-value数据库,它是完全开源免费的,而且redis是一个NOSQL类型数据库,是为了解决高并发、高扩展,大数据存储等一系列的问题而产生的数据库解决方案,是一个非关系型的数据库

  • Reids的特点

Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过
10万次读写操作,是已知性能最快的Key-Value DB。

Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像
memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能,比方说用他的List来做FIFO双向链表,实现一个轻量级的高性
能消息队列服务,用他的Set可以做高性能的tag系统等等。另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一
个功能加强版的memcached来用。

Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

  • 使用redis有哪些好处?

速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)

支持丰富数据类型,支持string,list,set,sorted set,hash

String 常用命令 :set/get/decr/incr/mget等;

应用场景 :String是最常用的一种数据类型,普通的key/value存储都可以归为此类;

实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr、decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。

Hash 常用命令 :hget/hset/hgetall等

应用场景 :我们要存储一个用户信息对象数据,其中包括用户ID、用户姓名、年龄和生日,通过用户ID我们希望获取该用户的姓名或者年龄或者生日;

实现方式:Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。如图所示,Key是用户ID,
value是一个Map。这个Map的key是成员的属性名,value是属性值。这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field),也就是通过
key(用户ID) + field(属性标签)
就可以操作对应属性数据。当前HashMap的实现有两种方式:当HashMap的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,这时对应的value的redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时redisObject的encoding字段为int。

List 常用命令 :lpush/rpush/lpop/rpop/lrange等;

应用场景 :Redis list的应用场景
非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现;

实现方式:Redis
list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。

Set 常用命令 :sadd/spop/smembers/sunion等;

应用场景 :Redis
set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的;

实现方式:set 的内部实现是一个
value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

Sorted Set 常用命令 :zadd/zrange/zrem/zcard等;

应用场景 :Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted
set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted
set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。

实现方式:Redis sorted
set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

  • redis相比memcached有哪些优势?

memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型4.2 redis的速度比memcached快很多
(3) redis可以持久化其数据

  • Memcache与Redis的区别都有哪些?

存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。Redis有部份存在硬盘上,这样能保证数据的持久性。

数据支持类型 Memcache对数据类型支持相对简单。Redis有复杂的数据类型。

使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

  • redis适用于的场景?

Redis最适合所有数据in-momory的场景,如:

会话缓存(Session Cache)

最常用的一种使用Redis的情景是会话缓存(session
cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。

全页缓存(FPC)

除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。

队列

Reids在内存存储引擎领域的一大优点是提供 list 和 set
操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list
的 push/pop 操作。

如果你快速的在Google中搜索“Redis
queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。

排行榜/计数器

Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted
Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:

当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:

ZRANGE user_scores 0 10 WITHSCORES

Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。

发布/订阅

最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。

  • redis的缓存失效策略和主键失效机制

作为缓存系统都要定期清理无效数据,就需要一个主键失效和淘汰策略.

在Redis当中,有生存期的key被称为volatile。在创建缓存时,要为给定的key设置生存期,当key过期的时候(生存期为0),它可能会被删除。

1、影响生存时间的一些操作

生存时间可以通过使用 DEL 命令来删除整个 key 来移除,或者被 SET 和 GETSET
命令覆盖原来的数据,也就是说,修改key对应的value和使用另外相同的key和value来覆盖以后,当前数据的生存时间不同。

比如说,对一个 key 执行INCR命令,对一个列表进行LPUSH命令,或者对一个哈希表执行HSET命令,这类操作都不会修改 key
本身的生存时间。另一方面,如果使用RENAME对一个 key 进行改名,那么改名后的 key的生存时间和改名前一样。

RENAME命令的另一种可能是,尝试将一个带生存时间的 key 改名成另一个带生存时间的 another_key ,这时旧的
another_key (以及它的生存时间)会被删除,然后旧的 key 会改名为 another_key ,因此,新的
another_key 的生存时间也和原本的 key 一样。使用PERSIST命令可以在不删除 key 的情况下,移除 key
的生存时间,让 key 重新成为一个persistent key 。

2、如何更新生存时间

可以对一个已经带有生存时间的 key
执行EXPIRE命令,新指定的生存时间会取代旧的生存时间。过期时间的精度已经被控制在1ms之内,主键失效的时间复杂度是O(1),

EXPIRE和TTL命令搭配使用,TTL可以查看key的当前生存时间。设置成功返回 1;当 key 不存在或者不能为 key
设置生存时间时,返回 0 。

最大缓存配置 在 redis 中,允许用户设置最大使用内存大小 server.maxmemory
默认为0,没有指定最大缓存,如果有新的数据添加,超过最大内存,则会使redis崩溃,所以一定要设置。redis
内存数据集大小上升到一定大小的时候,就会实行数据淘汰策略。redis 提供 6种数据淘汰策略:

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(驱逐): 禁止驱逐数据

注意这里的6种机制,volatile和allkeys规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的lru、ttl以及random是三种不同的淘汰策略,再加上一种no-enviction永不回收的策略。

使用策略规则:

1、 如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru2、
如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random

三种数据淘汰策略:

ttl和random比较容易理解,实现也会比较简单。主要是Lru最近最少使用淘汰策略,设计上会对key
按失效时间排序,然后取最先失效的key进行淘汰

  • 为什么redis需要把所有数据放到内存中?

Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。在内存越来越便宜的今天,redis将会越来越受欢迎。

如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。

  • Redis是单进程单线程的

redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销

  • redis的并发竞争问题如何解决?

Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是

由于客户端连接混乱造成。对此有2种解决方法:

客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。

服务器角度,利用setnx实现锁。注:对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用synchronized也可以使用lock;第二种需要用到Redis的setnx命令,但是需要注意一些问题。

  • redis常见性能问题和解决方案

Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。

Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久

化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。

Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。

Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。

  • redis事物的了解CAS(check-and-set 操作实现乐观锁 )?

和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制。在Redis中,MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石。相信对有关系型数据库开发经验的开发者而言这一概念并不陌生,即便如此,我们还是会简要的列出

Redis中

  • 事务的实现特征:

12.1 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。

12.2 和关系型数据库中的事务相比,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。

12.3 我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其理解为"BEGIN TRANSACTION"语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作。这两个Redis命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。

12.4 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事务中的所有命令都会被服务器执行。

12.5 当使用Append-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。Redis服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。此时,我们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动Redis服务器了。

  • WATCH命令和基于CAS的乐观锁?

在Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Null
multi-bulk应答以通知调用者事务

执行失败。例如,我们再次假设Redis中并未提供incr命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如下:

val = GET mykey val = val + 1 SET mykey $val

以上代码只有在单连接的情况下才可以保证执行结果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会出现多线程程序中经常出现的一种错误场景–竞态争用(race
condition)。比如,客户端A和B都在同一时刻读取了mykey的原有值,假设该值为10,此后两个客户端又均将该值加一后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。为了解决类似的问题,我们需要借助WATCH命令的帮助,见如下代码:

WATCH mykey val = GET mykey val = val + 1 MULTI SET mykey $val EXEC

和此前代码不同的是,新代码在获取mykey的值之前先通过WATCH命令监控了该键,此后又将set命令包围在事务中,这样就可以有效的保证每个连接在执行EXEC之前,如果当前连接获取的mykey的值被其它连接的客户端修改,那么当前连接的EXEC命令将执行失败。这样调用者在判断返回值后就可以获悉val是否被重新设置成功。

  • 使用过Redis分布式锁么,它是什么回事?

先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。

这时候对方会告诉你说你回答得不错,然后接着问如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?

这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。

  • 假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用keys指令可以扫出指定模式的key列表。

对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

  • 使用过Redis做异步队列么,你是怎么用的?

一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。

如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。

如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。

如果对方追问pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。

如果对方追问redis如何实现延时队列?我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。

  • 如果有大量的key需要设置同一时间过期,一般需要注意什么?

如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。

  • Redis如何做持久化的?

bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。在redis实例重启时,会使用bgsave持久化文件重新构建内存,再使用aof重放近期的操作指令来实现完整恢复重启之前的状态。

对方追问那如果突然机器掉电会怎样?取决于aof日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。

对方追问bgsave的原理是什么?你给出两个词汇就可以了,fork和cow。fork是指redis通过创建子进程来进行bgsave操作,cow指的是copy
on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。

  • Pipeline有什么好处,为什么要用pipeline?

可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。

  • Redis的同步机制了解么?

Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

  • 是否使用过Redis集群,集群的原理是什么?

Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。

Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

设计模式

  • mybatis使用了哪些设计模式?你了解过哪些设计模式?

mybatis当中 sqlSessionFactoryBuilder 建造者模式
sqlSessionFactory 单例 singleTon
sqlSession 工厂模式 beanFactory
mapper 动态代理 springaop
springmvc handlerAdapter 适配器模式
BufferedReader 装饰者设计模式
思考题:装饰者模式和动态代理有什么区别?
装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问

网络

  • ISO7层模型

物理层
数据链路层
网络层
传输层
会话层
表示层
应用层
tcp/udp属于传输层的协议

  • tcp和udp有什么区别?

tcp是面向连接的,建立连接前需要进行三次握手,断开连接前需要进行 四次挥手,可靠的不会造成数据丢失的,可以简单理解成打电话,效率低
udp协议是面向无连接,不可靠有可能造成数据丢失,可以理解成发电报, 效率高

TCP握手:
客户端发送syn包
服务端接收到syn包之后,回复syn+ack
客户端接收到服务端的syn+ack之后,会回复一个ack

分布式

框架

  • spring的特性?谈谈你对spring的理解?spring用来干嘛?

spring有两大核心,IOC和AOP
IOC即控制反转,通常我们在项目当中需要手动new去创建对象,这种方式不利于
对对象的管理,现在我们将对象的创建权利反转给spring容器,这就是控制反转。
spring容器在实例化对象的时候,会根据对象之间的依赖关系,自动完成属性
注入工作,这就是依赖注入
AOP即面向切面编程,底层是通过动态代理的机制来实现的,支持jdk和cglib两种。
默认通过jdk动态代理。通常我们在项目当中,一些公共功能的实现可以通过aop来
进行解耦和统一实现,比如事务管理、日志、权限等等。
我们在项目当中的事务管理是这样配置的
1:声明spring的事务管理器 transactionManager
2:配置一下事务增强的传播特性 tx:advice tx:method
对于增删改开头的方法,使用事务进行管理
对于查询开头的方法,只读模式
3:配置切面,通常我们切面设置在service实现类这一层 pointcut

  • jdk的动态代理和cglib动态代理有什么区别?

jdk的动态代理要求业务类必须得实现业务接口,底层是通过生成业务接口的动态代理实现类来完成功能增强
cglib不需要业务类实现接口,底层是通过衍生出当前业务类的子类对象来完成功能增强 CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类

  • SpringBoot快速开发框架

springboot是spring4.0之后提供的一个自动化启动框架,采用习惯优于配置
的理念,可以进行自动化配置(EnableAutoConfiguration),框架采用
注解+properties(或yaml)代替传统的xml配置,极大的提高了开发效率.
springboot通过main方法启动,而且内置web容器(tomcat),打包方式为jar springboot的常用注解有 @springbootapplication 标识启动类
@configuration 标识配置类 @EnableAutoConfiguration 开启自动配置 @ComponentScan 注解扫包器 @MapperScan 扫描mybatis的mapper接口
@RestController rest风格返回json

  • springboot自动配置的原理

spring-boot-autoconfigure jar包下meta-inf文件夹下,
spring.factories配置文件提供了各种技术框架的默认配置
我看过很多自动配置类的源码,比如springmvc,自动配置里
指定dispatcherServlet请求路径为/,指定了处理器映射器
requestMappingHandlerMapping等组件的配置

  • SpringBoot装配Bean的原理

通过@EnableAutoConfiguration自动获取配置类信息,使用反射实例化为spring类,然后加载到spring容器

  • SpringBoot执行流程

使用SpringApplication.run()启动,在该方法所在类添加@SpringBootApplication注解,该注解由@EnableAutoConfiguration和@ComponentScan等注解组成,@EnableAutoConfiguration自动加载SpringBoot配置和依赖包,默认使用@ComponentScan扫描当前包及子包中的所有类,将有spring注解的类交给spring容器管理

  • spring当中bean的作用域有哪些?

singleton 单实例
protoTyle 多实例
request 请求结束就销毁
session 会话结束就销毁
globalSession 全局

  • spring当中bean的生命周期

bean的创建 依赖注入 功能增强 根据作用域进行销毁

微服务框架SpringCloud

Eureka 注册中心
Ribbon 本地负载均衡
Fegin 服务调用
Hystrix 熔断降级
Zuul 网关
链路追踪
配置中心

  • 什么是SpringCloud?

SpringCloud是微服务的一种解决方案,依赖SpringBoot实现。包含注册中心(eureka)、客户端负载均衡(Ribbon)、网关(zull)、分布式锁、分布式会话等。

  • SpringCloud服务注册发现原理

每个SpringCloud服务器启动后向注册中心注册本服务器信息,如服务别名、服务器IP、端口号等,其他服务进行请求时先根据服务别名从注册中心获取到目标服务器IP和端口号,并将获取到的信息缓存到本地,然后通过本地使用HttpClient等技术进行远程调用。

  • Ribbon底层实现原理

Ribbon使用discoveryClient从注册中心读取目标服务信息,对同一接口请求进行计数,使用%取余算法获取目标服务集群索引,返回获取到的目标服务信息。

底层源码

  • 你看过spring的源码么?说说aop的源码?

spring的ioc有两大核心
接口beanFactory和applicationContext,beanFactory是通过工厂模式来生产
bean,默认生产的是单例bean。
applicationContext是beanFactory的子接口,增强了BeanFactory的功能。通常我们
都是使用applicationContext,其中ClassPathXmlApplicationContext 来初始化IOC容器
Aop的源码
aop通过设置切面,当切面设置的目标类的方法被调用时,aop框架会拦截
此次调用,我看过pointCut类的源码,里面有两个核心属性,ClassFilter 类过滤器
MethodMatcher 方法匹配器,所以aop来可以进行拦截.拦截之后,aop机制会通过
jdk或cglib帮咱们生成动态代理对象,调用增强类的增强方法,进行功能织入。

  • aop的实现原理

首先通过aop配置切面以及切面的增强逻辑,当spring的容器中目标类的对象的方法被调用时,aop框架就会拦截此次调用,aop框架会帮你生成一个动态代理对象,先运行增强类中的前置增强方法,再运行原业务类的业务方法,最后再运行增强类的后置增强方法
注意:aop默认通过jdk提供的proxy来生成动态代理对象,但是也可以手动指定使用cglib来生成动态代理对象

  • spring bean作用域有以下5个:

singleton:单例模式,当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;

prototype:原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;

(下面是在web项目下才用到的)

request:搞web的大家都应该明白request的域了吧,就是每次请求都新产生一个实例,和prototype不同就是创建后,接下来的管理,spring依然在监听;

session:每次会话,同上;

global session:全局的web域,类似于servlet中的application。

  • spring当中bean的生命周期

bean的创建 依赖注入 功能增强 根据作用域进行销毁

  • spring当中的单例,是如何保障它线程安全的?

spring是通过ThreadLocal来保障单例线程安全的

  • springmvc的原理?springmvc的工作流程?springmvc的源码你看过吗?

首先请求到达核心控制器dispatcherServlet,dispatcherServlet会判断请求
后缀是否满足格式要求,如果满足格式要求,会查询handlerMapping,查找
对用的handler,调用handlerAdapter进行参数绑定等适配工作,之后调用具体的
handler进行业务逻辑处理,返回一个modelAndView对象给dispatcherServlet。
dispatcherServlet调用viewResolver进行视图解析渲染,然后返回给客户端

  • mybatis的mapper代理底层怎么实现?

mybatis是先通过xml解析,反射,最后通过动态代理机制来生成mapper接口的实现类对象namespace的值等于mapper接口的类路径、sql标签的id值等于mapper接口的方法名

调优

  • jvm的调优机制

通常我们在项目当中,需要对jvm进行调优,以防止内存溢出的现象。主要通过调整jvm的启动参数,包括最小堆内存xms、最大堆内存xmx 栈内存xss,永久代permSize。由于jdk1.8永久代被弃用了,使用操作系统本地内存,所以不需要调节永久代大小了.
还包括使用并行的垃圾收集器,比如商用的cms、G1垃圾收集器
通常情况下,我们将最小堆内存和最大堆内存设置的一样,防止内存回落造成GC在生产环境,我们通过使用Jconsole JvisualVm 对jvm可以进行监控

  • 数据库调优

1:数据库结构优化
设计数据库的时候,要预估单表的峰值,因为以Mysql为例,当单表的
数据超过千万条,查询速度就特别缓慢了,这里就需要对数据库进行
分库分表(数据分片),为了提高查询和写入性能,mysql可以采取主从结构
主数据库采用innodb引擎,从数据库采用myIsam引擎,主从之间通过
同步binlog日志实现数据一致
2:SQL优化,本质是通过创建索引,使用explain关键字,查询索引命中情况,
对sql进行调优
3:使用cache层,预先对经常常用的数据库数据进行缓存,以减少数据库的压力
提高数据库性能
4: 可以使用mycat中间件来搭建主从集群,数据库有两个线程,一个是IO线程,一个是sql线程,从数据库通过IO线程监听主数据库的binlog日志然后写入mycat中,sql线程读取mycat文件并对自身执行sql命令从而保证数据的一致性

  • tomcat调优

场景

谈谈服务雪崩效应

雪崩效应是在大型互联网项目中,当某个服务发生宕机时,调用这个服务的其他服务也会发生宕机,大型项目的微服务之间的调用是互通的,这样就会将服务的不可用逐步扩大到各个其他服务中,从而使整个项目的服务宕机崩溃.发生雪崩效应的原因有以下几点
1.单个服务的代码存在bug.
2请求访问量激增导致服务发生崩溃(如大型商城的枪红包,秒杀功能).
3.服务器的硬件故障也会导致部分服务不可用.

在微服务中,如何保护服务?

一般使用使用Hystrix框架,实现服务隔离来避免出现服务的雪崩效应,从而达到保护服务的效果。当微服务中,高并发的数据库访问量导致服务线程阻塞,使单个服务宕机,服务的不可用会蔓延到其他服务,引起整体服务灾难性后果,使用服务降级能有效为不同的服务分配资源,一旦服务不可用则返回友好提示,不占用其他服务资源,从而避免单个服务崩溃引发整体服务的不可用.

服务雪崩效应产生的原因

因为Tomcat默认情况下只有一个线程池来维护客户端发送的所有的请求,这时候某一接口在某一时刻被大量访问就会占据tomcat线程池中的所有线程,其他请求处于等待状态,无法连接到服务接口。

谈谈Hystrix服务保护的原理

谈谈服务降级、熔断、服务隔离

服务降级:当客户端请求服务器端的时候,防止客户端一直等待,不会处理业务逻辑代码,直接返回一个友好的提示给客户端。
服务熔断是在服务降级的基础上更直接的一种保护方式,当在一个统计时间范围内的请求失败数量达到设定值(requestVolumeThreshold)或当前的请求错误率达到设定的错误率阈值(errorThresholdPercentage)时开启断路,之后的请求直接走fallback方法,在设定时间(sleepWindowInMilliseconds)后尝试恢复。
服务隔离就是Hystrix为隔离的服务开启一个独立的线程池,这样在高并发的情况下不会影响其他服务。服务隔离有线程池和信号量两种实现方式,一般使用线程池方式。

  • 服务降级底层是如何实现的?

Hystrix实现服务降级的功能是通过重写HystrixCommand中的getFallback()方法,当Hystrix的run方法或construct执行发生错误时转而执行getFallback()方法。

谈谈网站跨域解决方案

a、使用jsonp 缺点只能发送get请求
b、使用httpclient进行转发,效率低
c、设置响应头允许跨域
d、使用Nginx搭建api网关
e、使用Zuul微服务搭建api接口网关

分布式Session一致性问题

a、使用Nginx反向代理,即IP绑定,同一个ip只能在同一个机器上访问
b、使用数据库,但性能不高
c、tomcat内置了对session同步的支持,但可能会产生延迟
d、使用Spring-Session框架,相当于把session放到redis中
e、使用token令牌代替session

分布式事务解决方案

1.两阶段提交(2PC)

2.补偿事务(TCC)

3.本地消息表(异步确保)

4.MQ 事务消息

5.Sagas 事务模型

分布式任务调度平台解决方案

分布式日志收集解决方案

分布式全局id生成方案

分布式服务追踪与调用链系统解

高并发优化方案

前端优化 解决高并发的入口是客户端与服务器端传输宽带速度, 如果宽带速度不足的情况,可能会导致客户端延迟等待。 一个网站核心
分为静态资源(css、img、js)和动态资源(jsp、ftl)组合,绝大数的情况下静态资源占了整个网站宽带传输,
这时候应该采用网站动静分离架构,将动态资源与静态资源分开服务器存放。动静分离可以使用
nginx,或者是使用第三方静态资源服务器比如七牛云、阿里云等。还要对静态资源实现压缩、减少宽带的传输,使用 CDN
实现内容分发,从最近服务器访问。静态资源压缩解决方案,使用 nginx 开启 gzip、maven 打包压缩 min 格式、或者使用 cdn
自带压缩

后端优化 如果客户端发送请求已经达到服务器端的话,服务端处理响应产生延迟,那么开始采用后端优化方案。
可以对整个服务进行拆分、对服务实现集群、负载均衡、采用MQ异步传输来提高服务器的吞吐量,使用redis做缓存层,减轻数据库访问压力,代码优化,数据库采用主从结构、读写分离、分库分表、数据分片,程序采用多线程,jvm参数调优、数据库调优、tomcat调优,服务实现保护机制(服务降级、服务隔离、服务熔断、服务限流)等。

服务器集群后,会产生了那些问题

分布式锁
分布式全局ID
分布式Session一致性问题
分布式事务
分布式任务调度
分布式日志收集
分布式配置中心

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐