JAVA后端开发知识总结(持续更新…)
private final char value[];:final(只是引用地址不可变);private;
String是不可变的关键都在底层的实现,而不是一个final;
安全
final stringbuilder:char[] value;不能被继承
final stringbuffer:transient char[];,StringBuffer中大部分方法由synchronized关键字修饰,在必要时可对方法进行同步
JVM:加载、连接、初始化、使用、和卸载
v
(默认是double,类型最后输出是0.29999995)
- 精度丢失:浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals 来判断。
- 使用 BigDecimal 来定义浮点数的值,再进行浮点数的运算操作。
讲到在jvm的存储
- 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。
- 被static 声明的成员变量属于静态成员变量,静态变量存放在 Java 内存区域的方法区。
- static修饰类的话只能修饰内部类。
- 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。
- 静态内部类没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建;2. 它不能使用任何外围类的非static成员变量和方法。
开放定址法(线性探测/平方再散列);再哈希法、链地址法
类加载三步,运行时处理
new
反射:newInstance
String s = “a”
反序列化
clone
- 单例(饿汉式):Runtime
- 简单工厂:Calendar
- 装饰者模式:IO流
- 策略模式:Arrays
- JAVA的泛型化:直接让当前已有类型原地泛型化。
- 类型擦除:编译后的字节码文件中类型泛型化实例被替换为了裸类型(共同父类型);也就是在运行期间两个不同的泛型其实没区别。
- 裸类型的实现:直接就把ArrayList< Integer >还原为ArrayList,只在修改、元素访问时自动插入强制类型转换。
- 运行期无法取得泛型类型信息。
- 限定类型替换类型参数。
- 桥方法用来保持多态。
解决重载:通过对方法添加不同的返回值类型。- 擦除是擦除的属性表中的Code,元数据中依然保留有泛型信息,可通过反射获得。
- 将数组转换为集合后,底层其实还是数组。
- Arrays.asList()是泛型方法,传入的对象必须是对象数组,即Integer而非int。
- 使用集合的修改方法:add()、remove()、clear()会抛出异常,因为Arrays.asList() 是 java.util.Arrays 的一个内部类,这个内部类并没有实现集合的修改方法或者说并没有重写这些方法。
- 一般情况下,快速排序效率要高于堆排序,因为堆排序的常数项较大(不过也是1~2之间)。
- 快速排序的平均时间复杂度是O(1.39nlogn),一般来说,除非有需要绝对保证不能出现O(n^2)的要求,不使用堆排。
- 堆排序数据访问的方式没有快速排序友好,不是像快速排序那样局部顺序访问,所以对CPU缓存不友好。
- Runtime.exec()
- ProcessBuilder的start方法
Stream;parallelStream? 《这里》
- 十进制的小数在转化成二进制浮点数时会产生精度问题。
- 十进制整数在转化成二进制数时不会有精度问题,那么把十进制小数扩大N倍让它在整数的维度上进行计算,并保留相应的精度信息。
- 在jdk1.6及之前switch语句只能支持byte、char、short、int和枚举型,不支持String类型。
- 在jdk1.7及之后加入了对String类型的支持。
- 不支持long、float、double、boolean四种基本类型。
- NullPointerException
- ArrayIndexOutOfBoundsException
- NoSuchMethodError
- IndexOutOfBoundsException
- NumberFormatException
- OutOfMemoryException
- 不需要try-catch
- 不需要显式捕捉
- 非受检异常是通过继承 RuntimeException 来实现的
- 需要try-catch
- 需要显式捕捉
- IOException,SQLException,1/0 问题,FileNotFoundException、ClassNotFoundException
- 受检异常是通过继承 Exception 来实现的
- Java对象的序列化(Serialization)和反序列化详解
- Java的序列化可以帮助实现持久化。
- 对象序列化机制是java语言内建的一种对象持久化方式,对象的序列化可以很容易地在JVM中的活动对象和字节数组(流) 之间进行转换。
- 广泛应用到RMI(远程方法调用)及网络传输中。
- 序列化对象到文件中: ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“template”));
- 反序列化:ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
- 实现Serializable接口。
- 序列化保存的是对象的状态,静态变量属于类的状态,因此序列化并不保存静态变量。
- 如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存,这是能用序列化解决深拷贝的重要原因。
- 当序列化之后,不能修改对象的类中的成员,否则也出异常;解决的方案:保证对象类的序列号不改变即可。
- Integer
- 字节数组
- 例如借助Netty中的channelBuffer来实现
《彻底理解从二进制到序列化、跨平台》
- CPU处理器与操作系统的整体叫平台,编译器负责把Java程序转成class文件(字节码),方便JVM来读取它。
- JVM是java虚拟机,它是解释器,把class文件中的命令转成某种平台的命令,比如把java命令转成Windows下的命令,字节码文件提供了跨平台运行的特性,class字节码对于任何操作系统都是一样的。
- AppClassLoader
- ExtClassLoader
- null
public class stream1 {
public static void main(String[] args) {
stream1 user = new stream1();
Class<? extends stream1> userClass = user.getClass();
System.out.println(userClass.getClassLoader());
System.out.println(userClass.getClassLoader().getParent());
System.out.println(userClass.getClassLoader().getParent().getParent());
}
}
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@7ba4f24f
null
《java中创建子类对象时会不会创建父类对象》
《Java中子类继承了父类的私有属性及方法吗?》
- 创建子类对象时不会创建父类对象,只是创建了父类空间,并进行了初始化。
- 如果说创建子类的时候就会创建它的父类,那内存空间中就都是Object类的对象了。
- 主要涉及到java对象的内存布局,当new一个对象时,其实jvm已经把对象的整个空间已经分配好,并且整个对象的实例域布局已经确定下来了。
- 实例化方法< init > 就是将对象实例域的值设置到相应空间中。< init >方法以调用父类的< init >方法开始,以自身构造方法作为结束。
- 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象,而且对象的类型可以不一致。
- 集合中传入基本类型可以实现自动装箱、,并将具体类型传入集合。
- 不指定List类型,存入数据,再次取出时,默认是Object类型。
两个数值进行二元操作时,会有如下的转换操作:
如果两个操作数其中有一个是double类型,另一个操作就会转换为double类型。
否则,如果其中一个操作数是float类型,另一个将会转换为float类型。
否则,如果其中一个操作数是long类型,另一个会转换为long类型。
否则,两个操作数都转换为int类型。
类图最适合于描述系统的静态结构、类、对象及它们之间的关系。
状态、序列、协作和活动图则适合于描述系统的动态行为,即描述系统中的对象在执行期间不同的时间点是如何动态交互的。
《使用Policy文件来设置Java的安全策略》
《JAVA泛型-泛型类的继承》
《JAVA泛型及继承问题》
《Java泛型的协变和逆变》
《Class.forName()用法详解》
33.stream流
- 《stream流详解》
- 数据源可以是集合,数组或IO资源。
- 静态(简单)工厂模式、工厂方法模式、抽象工厂模式。
- 对象的创建和调用分离。
- 《这篇》
- 避免大量if…else,将复杂的逻辑判断分成一个个单独的类
- 凡是可以枚举的业务,往往都需要使用设计模式才能更好解决
v
v
普通索引:最基本的索引,没有任何约束限制。
唯一索引:和普通索引类似,但是具有唯一性约束,可以有 null。
主键索引:特殊的唯一索引,不允许有 null,一张表最多一个主键索引。
组合索引:多列值组成一个索引,用于组合搜索,效率大于索引合并。
全文索引:对文本的内容进行分词、搜索。
覆盖索引:查询列要被所建的索引覆盖,不必读取数据行。
v
v
v
频繁改动和删除的数据、数据量小、区分度小的字段
中到大数据量表适合使用索引
小数据量表,大部分情况全表扫描效率更高
特大数据量表,建立和使用索引的代价会随之增大,适合使用分区或分库
v
(next-key锁和MVCC)
v
v
v
Explain、慢日志查询
v
- 索引统计的更新机制:采样统计,优化器存在选错索引的可能性。
- 对于由于索引统计信息不准确导致的问题,可以用 analyze table 来解决。
- 而对于其它优化器误判的情况,可以在应用端用 force index 来强行指定索引,也可以通过修改语句来引导优化器,还可以通过增加或者删除索引来绕过这个问题。
v
这里
v
自增主键的优缺点
索引树分裂
- select *问题
- 对于排序语句,必须加过滤条件(LIMIT也行)才能生效
- WHERE条件索引失效同样会导致ORDER BY后面的索引失效
- MySQL 为了提高性能,会将表的索引装载到内存中;
- 当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降。
- 与实际记录的条数无关。
主键KEY或者具体数据
- 《参考文章》
- 数据库中设置SQL慢查询: slow_query_log=On long_query_time=2
- 利用explain关键字可以模拟优化器执行SQL查询语句,来分析sql慢查询语句。
- 索引没起作用的情况
- 优化数据库结构:将字段很多的表分解成多个表;增加中间表
- 分解关联(join)查询
- 优化 LIMIT + offset 分页:尽可能使用索引覆盖扫描,而不是查询所有的列;
select id,title from collect limit 90000,10;:该语句存在的最大问题在于limit M,N中偏移量M太大- 上述SQL的改进:先查询出主键id;建立复合索引
- select * from a where id in (select id from b );:mysql会把in子查询转换成exists相关子查询,所以它实际等同于这条sql语句:select * from a where exists(select * from b where b.id=a.id );
- exists相关子查询的执行原理是:循环取出a表的每一条记录与b表进行比较,比较的条件是a.id=b.id,看a表的每条记录的id是否在b表存在,如果存在就行返回a表的这条记录,顺序是固定的。
- 但是由于顺序固定,a表(外表)使用不了索引,必须全表扫描,建索引只能在b表的id字段建。
- 采用 inner join不让顺序固定,让数据库自己去处理,此时就可以A、B表都建索引。
- MySQL采用了基于开销的优化器,以确定处理查询的最优化解方式。也就是说执行查询之前,都会先选择一条自以为最优的方案,然后执行这个方案来获取结果。
- 一个主要的目标是尽量用索引,而且使用条件最严格的索引来尽可能多、尽可能快地排除那些不符合索引条件的数据行。
- 在同一个事务中先执行update 再执行select,select 的数据是update之后的结果,此时事务还没有提交;直接查询数据库,数据库的数据还没有发生变更。
- 在数据库事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行,这是一种机制,用以维护数据库的完整性。
- 执行计划就是sql的执行查询的顺序,以及如何使用索引查询,返回的结果集的行数。
- 即 Explain
《3种DataSource实现及主要的数据源》
《JDBC、驱动管理器与DataSource》
《对JDBC驱动注册–DriverManager.registerDriver和 Class.forName()的理解》
v
v
反射+CAS来实现
乐观锁,提到了ABA问题及解决办法
volatile关键字,可见性+有序性,这里主要是可见性
无锁-> 偏向锁-> 轻量级锁-> 重量级锁
CountDownLatch、CycleBarrier
v
shutdown…
子进程会获得父进程的几乎所有资源,包括文件描述符,所以是拥有的。
v
v
- 线程出现异常,如果当前线程有异常处理器(默认没有),则优先使用该线程的异常处理器。
- 否则,如果当前线程所属的线程组存在异常处理器,则使用线程组的异常处理器。
- 否则,使用全局默认的DefaultUncaughtExceptionHandler。
- 如果都没有的话,子线程就会退出。
- 为线程设置异常处理器,具体做法有以下两种:
(1)Thread.setUncaughtExceptionHandler:设置当前线程的异常处理器。
(2)Thread.setDefaultUncaughtExceptionHandler:为整个程序设置默认的异常处理器。- 使用线程池提交一个能获取到返回信息的方法,即Future,通过get()方法获取异常信息。
- ThreadPoolExecutor
- 自定义线程池需要配置最大线程数maximumPoolSize,为了高效的并发运行,需要看业务是IO密集型还是CPU密集型。
- CPU密集的意思是该任务需要大量的运算,而没有阻塞,CPU一直全速运行。CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),CPU密集型任务配置尽可能少的线程数量,以保证每个CPU高效的运行一个线程。CPU密集型:corePoolSize = CPU核数 + 1
- I0密集型,即该任务需要大量的IO,即大量的阻塞。在单线程上运行I0密集型的任务会导致浪费大量的CPU运算能力浪费在等待。I0密集型时,大部分线程都阻塞,故需要多配置线程数。IO密集型:corePoolSize = CPU核数 * 2
- execute 方法中的的核心方法为 addWorker(), Worker为 thread 运行的任务,如果添加 Worker 成功,就会调用 start 方法启动线程。
- 线程池的线程复用就是通过取 Worker 的 firstTask 或者通过 getTask 方法从 workQueue 中不停地取任务,并直接调用 Runnable 的 run 方法来执行任务。
- 这样就保证了每个线程都始终在一个循环中,反复获取任务,然后执行任务,从而实现了线程的复用。
threadlocalmap:Entry[] ??
- 一条指令的执行可以分为很多步骤,汇编指令不是一步就可以执行完毕的,在CPU实际工作时,它需要分为多个步骤依次执行。当然,每个步骤所涉及的硬件也可能不同。
- 因此,CPU和编译器为了提升程序执行的效率,采用了流水线技术来执行指令,但流水线总是害怕被中断的。流水线满载时性能确实不错,但一旦中断,所有的硬件设备都会进入一个停顿期。
- 指令重排就是为了尽量少地中断流水线,指令重排可以保证串行语义一致,但是没有义务保证多进程间的语义也一致。
- Happen-Before原则
《Java多线程——线程池异常捕获》:try/catch
…
事件
双写模式;失效模式
一开始是先删除再更新,这种情况会有读写并发脏数据的问题。吹了一波牛逼,说怎么排查到这种错误的,排查后使用先删除再更新再删除的策略。第一次的删除可以避免 Redis 宕机导致脏数据的问题,后面先更新再删除可以避免读写并发脏数据的问题。
Sentinel
RDB
(缓存和分布式锁(Redisson框架))
v
v
v
- 《白话解析:一致性哈希算法 consistent hashing》
- 分布式缓存中对服务器取模的问题:当服务器数量发生改变时,所有缓存在一定时间内是失效的。
- 一致性哈希:对2^32取模—》hash(服务器A的IP地址) % 2^32,算出的这个整数,代表服务器A;
- 同样,将需要缓存的对象映射到hash环上—》hash(图片名称) % 2^32,此时,沿顺时针方向遇到的第一个服务器就是将会被缓存到的服务器;
- 使用一致性哈希算法时,服务器的数量如果发生改变,并不是所有缓存都会失效,而是只有部分缓存会失效;
- 虚拟节点解决hash环偏斜问题。
O(logN)
- ? 避免内存碎片:连续内存块
- 节约内存
这里
- 网络分区情况,导致redis master节点跟redis slave节点和sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到master的存在,所以将slave节点提升为master节点。
- 如果客户端还在基于原来的master节点继续写入数据,那么新的master节点将无法同步这些数据,当网络问题解决之后,sentinel集群将原先的master节点降为slave节点,此时再从新的master中同步数据,将会造成大量的数据丢失。
- redis的配置文件中,存在两个参数:
min-slaves-to-write 3:第一个参数表示连接到master的最少slave数量
min-slaves-max-lag 10:第二个参数表示slave连接到master的最大延迟时间- 如果连接到master的slave数量小于第一个参数,且ping的延迟时间小于等于第二个参数,那么master就会拒绝写请求,配置了这两个参数之后,如果发生集群脑裂,原先的master节点接收到客户端的写入请求会拒绝,就可以减少数据同步之后的数据丢失。
- redis——为什么选择了跳表而不是红黑树?
- 范围查找时,平衡树比skiplist操作要复杂。在平衡树上,找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。而在skiplist上进行范围查找就非常简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。
- 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。
- 从内存占用上来说,skiplist比平衡树更灵活一些。
- 从算法实现难度上来比较,skiplist比平衡树要简单得多。
- Redis所有单个命令的执行都是原子性的,这与它的单线程机制有关。
- Redis命令的原子性使得不用考虑并发问题,可以方便地利用原子性自增操作。
- 服务即使是多机器多进程的,incr也能保证每次返回的结果不会出现相同的值。
- 计数器是 Redis 的原子性自增操作可实现的最直观的模式了,它的想法相当简单:每当某个操作发生时,向 Redis 发送一个 INCR 命令。
- 限流:和expire / setex配合使用。
- incr性能不行,大概500左右qps。
- IO多路复用
- 运行在内存中
Redis的瓶颈不是cpu的运行速度,而往往是网络带宽和机器的内存大小。
《Redis集群的节点通信原理》
- Redis集群采用P2P的Gossip(流言)协议,Gossip协议工作原理就是节点彼此不断通信交换信息,一段时间后所有的节点都会知道集群完整的信息,这种方式类似流言传播。
- 集群中的每个节点都会单独开辟一个TCP通道,用于节点之间彼此通信。
- MEET三次握手。
- Gossip协议的主要职责就是信息交换,信息交换的载体就是节点彼此发送的Gossip消息,常用的Gossip消息可分为:ping消息、pong消息、meet消息、fail消息。
- 《Ping命令原理》
- 《ping 的原理》
- Ping发送一个ICMP,即因特网际信报控制协议,回声请求消息给目的地并报告是否收到所希望的ICMP echo (ICMP回声应答)。
- 利用网络上机器IP地址的唯一性,给目标IP地址发送一个数据包,再要求对方返回一个同样大小的数据包来确定两台网络机器是否连接相通,时延是多少。
- 涉及Mac(ARP)、ICMP,分为同一网段和不同网段。
- MAC地址称为物理地址、硬件地址,用来定义网络设备的位置。在OSI模型中,第二层数据链路层负责 MAC地址。因此一个主机会有一个MAC地址,而每个网络位置会有一个专属于它的IP地址。
- IP地址和MAC地址是成对出现的,映射关系由ARP(地址解析协议)协议完成。
- MAC地址与网络无关。
- 《NAT(地址转换技术)详解》
- NAT不仅实现了网络地址转换,同时还起到防火墙的作用,隐藏内部网络的拓扑结构,保护内部主机。
- 减缓IPv4地址的消耗。
- 网络地址转换,就是替换IP报文头部的地址信息。
- NAT通常部署在一个组织的网络出口位置,通过将内部网络 IP地址替换为出口的IP地址提供公网可达性和上层协议的连接能力。
- 由组网方式决定的,如今比较流行的接入Internet的方式是把主机通过局域网组织在一起,然后再通过交换机和 Internet相连接。
- IP只是逻辑上标识,任何人都能随意修改,因此不能用来标识用户;而 MAC地址则不然,它是固化在网卡里面的。
- 局域网采用了MAC地址来标识具体用户的方法,在交换机内部通过“表”的方式把MAC地址和IP地址一一对应,也就是IP、MAC绑定。
- 只有IP而没有对应的MAC地址在这种局域网内是不能上网的,于是解决了IP盗用问题。
- 一般使用私网ip作为局域网内部的主机标识,使用公网ip作为互联网上通信的标识。双向流量必须都要经过NAT网关。
- 网络访问只能先由私网侧发起,公网无法主动访问私网主机。
- NAT网关的存在对通信双方是保持透明的。
- NAT网关为了实现双向翻译的功能,需要维护一张关联表,把会话的信息保存下来。
- NAT重载,是动态NAT,它利用源端口将多个私网ip地址映射到一个公网ip地址(多对一)。
- 即利用端口号实现公网和私网的转换。
v
v
v
v
v
v
v
v
v
v
v
v
(80 和 443)
v
v
v
先定大方向,可能是操作系统io方面的、CPU方面的、再是数据库,可以用logger打印一下。
先排查代码效率;再排查 SQL,看是否命中索引
ping对应的ip,多半是ip地址写错了
域名没被解析,肯定是对应的浏览器缓存映射(DNS)、或者本级host被修改,面试官说就是这个
- 《流量整形,延迟以及ACK丢失对TCP发送时序的影响》
- ACK是发送端的时钟, 如果连续的ACK丢失了,就会出现一个ACK确认了大块数据的场景。由于前面ACK连续丢失,发送端久久未收到时钟反馈导致数据不能发送。
- 快重传机制
- TCP重传计时器
TCP为每一个链接设计了一个持续计数器,当窗口大小为0时,就会启动这个计数器,当计数器到期后会发送一个零窗口探测报文段(一个字节),接收方通过确认这个探测报文时可以告知发送方最新的窗口大小(TCP规定,就算窗口为0,也要接收零窗口探测报文)。
v
v
v
线程
v
v
v
v
v
v
《什么是上下文切换》
《深入理解Linux内核进程上下文切换》
《线程上下文切换详解》
- CPU上下文:CPU 寄存器 和 程序计数器(PC)。
- 上下文切换的步骤:
1.将前一个 CPU 的上下文保存起来;
2.加载新任务的上下文到寄存器和程序计数器;
3.最后跳转到程序计数器所指的新位置,运行新任务。- 被保存起来的上下文会存储到系统内核中,等待任务重新调度执行时再次加载进来。
- CPU上下文切换分三种:进程上下文切换、线程上下文切换、中断上下文切换。
- 一次系统调用,发生两次 CPU 上下文切换,只涉及用户态和内核态。
- 系统调用过程中,不涉及虚拟内存等进程用户态的资源,也不会切换进程。
- 系统调用过程中一直是同一个进程在运行。
- 进程是由内核管理和调度的,进程的切换只能发生在内核态。
- 进程的上下文不但包括虚拟内存、栈、全局变量等用户空间资源,还包括内核堆栈、寄存器等内核空间状态。
- 保存上下文和恢复上下文需要内核在 CPU 上运行才能完成。
- 当一个任务的时间片用完,就会切换到另一个任务。在切换之前会保存上一个任务的状态,当下次再切换到该任务,就会加载恢复这个状态。
- 任务从保存到再加载的过程就是一次上下文切换。
- 线程上下文切换时,共享相同的虚拟内存和全局变量等资源不需要修改。
- 而线程自己的私有数据,如栈和寄存器等,上下文切换时需要保存。
- 计算机内存是计算机的重要部件之一,它是外存与CPU进行沟通的桥梁,内存性能的强弱影响计算机整体发挥的水平。
- 计算机中所有程序的运行都在内存中进行,是CPU直接与之沟通,并用其存储数据的部件。
- 内存只用于暂时存放程序和数据,一旦关闭电源或发生断电,其中的程序和数据就会丢失。
- 物理实质就是一组或多组具备数据输入输出和数据存储功能的集成电路。
- ROM:只读存储器,所存数据稳定 ,断电后所存数据也不会改变;
- RAM:随机存取存储器,在断电时将丢失其存储内容,故主要用于存储短时间使用的程序;
- Cache:做在CPU里面的。
v
v
是一个进程,因为进程之间的通信也有共享内存,而两个main方法之间如果是分开的(不在一个项目上),想要进行通信,无疑是只有两种办法,一是通过本地JVM中的对象通过反射机制获取,二通过网络通信,所以是一个进程。
v
v
v
v
v
find
v
v
v
v
v
v
v
v
v
v
v
v
v
v
- EmbeddedTomcatServer类作为程序的入口,代码逻辑其实就是通过tomcat提供的外部接口类在配置server.xml这个文件。
- 关键是在类ServletWebServerApplicationContext的onRefresh方法。
- Servlet3.0
- 《Spring管理事务默认回滚的异常是什么?》
- RuntimeException或者Error
- 由源码决定——RuleBasedTransactionAttribute
- rollBackFor:默认情况下是empty,默认情况下,如果是RuntimeException或Error的话,就返回True,表示要回滚,否则返回False,表示不回滚。
1.先看 nginx 和后端服务是否挂掉。
2.如果没有挂掉,看后端日志,根据日志异常排错。
3.如果根据日志没有找出问题,就本地复现。
v
v
v
不一定,如果经过逃逸分析后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配,这样就无需在堆上分配内存,也无须进行垃圾回收了。
- 把一个应用程序开发为了一套小服务,每个服务一个进程
- 可能分布在不同地域,容灾
- 模块解耦
- 服务降级/熔断的安全性
- 可针对性地对并发量大的模块纵向扩展
- 抽象了微服务中都需要的公共功能,帮助解决很多API管理难题。
- 客户端负载均衡、服务自动熔断、灰度发布、统一认证、限流流控、日志统计等丰富的功能。
- 请求浏览的入口:路由转发、权限校验、限流控制。
- API网关:所有的外部请求都会先经过 API 网关这一层,可以复用既有接口,保证了服务集群中REST API无状态的特点。
- Route
- Predicate断言
- Filter
- 《限流算法与设计思路》
- 《【算法设计】限流算法》
- Nginx
- 网关