LOADING

加载过慢请开启缓存 浏览器默认开启

苏小妍面试题集

SpringCloud核心组件

  1. Eureka注册服务中心:用于存储服务提供者地址消息,服务发布相关的属性消息,消费这通过主动查询和被动通知的方式获取服务提供者的地址信息,从而实现服务的调用。而不再需要通过硬编码方式得到服务提供者的地址信息。

  2. Ribbon负载均衡:用于实现客户端的负载均衡,将客户端的请求分摊到多个服务上,从而实现系统的高可用性和性能的提升。Ribbon给restTemplate添加了⼀个拦截器interceptor方法,通过拦截器进行请求拦截,然后通过负载策略实现请求分发。服务端的负载均衡用Nginx实现。负载均衡的策略:

    1. 轮询
    2. 随机
    3. 重试
    4. 响应时间加权
    5. 区域权衡
    6. 最小并发
  3. Hystrix熔断器:用于实现服务的容错保护功能,当某个服务发生故障时,通过断路器的故障监控,向调用方返回一个错误响应,而不是长时间的等待,从而避免了故障在分布式系统中的蔓延,从而提高了系统的整体可用性和容错性。处理方法分为:降级,熔断,限流

  4. Feign服务调用:用于实现服务之间的调用,Feign是一个轻量级的服务调用组件,它基于Ribbon实现了客户端的负载均衡,基于Hystrix实现了容错保护功能,基于Eureka实现了服务的发现功能。

  5. GateWay网关组件:为微服务架构提供了一种简单有效的统一的API理由管理方式,不仅可以提供统一的路由方式,还可以做过滤,鉴权,流量控制,熔断,路径重写,日志监控等。

  6. Config配置中心:用于实现配置的集中管理,将配置信息存储在远程仓库中,然后通过配置中心将配置信息分发到各个微服务应用中,从而实现配置的集中管理和动态更新。

    1. Config server:创建Git仓库,将配置信息存储在Git仓库中,然后通过Config server将配置信息分发到各个微服务应用中,从而实现配置的集中管理和动态更新。环境配置文件的命名规则:{application}-{profile}.yml,其中application是应用名称,profile是环境名称,比如dev,test,prod等。
    2. Config Client:加入bootstrap支持,pom,创建Bootstrap.properties,拆分application.yml,本地application.yml中两个部分,一部分是公共部分,一部分是私有部分,公共部分放在Git仓库中,私有部分放在本地,这样就可以实现配置的动态更新。

Mysql中having和where的区别

  1. where对查询数据进程过滤,having对已分组的数据进行过滤,必须配合group by使用。
  2. 被执行的数据来源不同,where是数据从磁盘读入内存的时候进行判断,having是数据在内存中进行判断。
  3. 执行顺序不同,先where后having。
  4. where不可以使用字段的别名,但是having可以使用字段的别名。
  5. having能够使用聚合函数当做条件,但是where不能使用,只能使用存在的列当做条件。

MySQL分页

使用limit子句实现分页。limit m,n表示从第m+1条记录开始,取n条记录,m从0开始,n从1开始。limit 0,10表示取第1-10条记录,limit 10,10表示取第11-20条记录。

tcp和udp的区别

  1. tcp是面向连接的,但是udp是面向无连接的。
  2. 消息传输的可靠性:udp是不可靠的,tcp是可靠的。
  3. 是否有状态:TCP传输是有状态的,也就是说TCP会去记录自己发动的消息的状态:是否发送了,是否被接收了等,而UDP则不会管发送之后的事情了。
  4. 传输方式:TCP是面向字节流的,UDP是面向报文的。
  5. 传输效率:由于TCP传输的时候有连接,确认,重传等机制,所以传输效率比较低,而UDP则没有这些机制,所以传输效率比较高。
  6. TCP只支持点对点的通信,UDP支持一对一,一对多,多对一,多对多的通信。
  7. 首部开销:TCP的首部开销为20-60字节,UDP的首部开销为8字节。

Spring注解

  1. @Component:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
  2. @Controller:标注控制层组件,用于标注控制层组件,Spring会自动将该类注册为Spring容器中的Bean。
  3. @Service:标注服务层组件,用于标注服务层组件,Spring会自动将该类注册为Spring容器中的Bean。
  4. @Repository:标注数据访问层组件,用于标注数据访问层组件,Spring会自动将该类注册为Spring容器中的Bean。
  5. @Autowired:自动装配,Spring会自动将标注的属性或者方法参数进行装配,如果找不到匹配的Bean,会抛出异常。

git提交代码的流程

  1. git add . 将修改的文件添加到暂存区
  2. git commit -m “提交信息” 将暂存区的文件提交到本地仓库
  3. git push origin master 将本地仓库的文件推送到远程仓库

模糊匹配会用到索引吗

如果仅仅是尾部模糊匹配,会用索引,如果是头部模糊匹配,不会用索引,如果是中间模糊匹配,也不会用索引。

TCP如何保证可靠传输

  1. 校验和:由发送方计算校验和,然后将校验和添加到数据包中,接收方在接收到数据包后,会重新计算校验和,然后将计算的校验和和数据包中的校验和进行比较,如果两个校验和相同,则说明数据包没有损坏,如果两个校验和不相同,则说明数据包损坏了,接收方会丢弃该数据包,然后发送方会重新发送该数据包。计算方式:进位取反。
  2. 序列号与确认应答:发送方发送数据包时,会给数据包添加一个序列号,接收方接收到数据包后,会给发送方发送一个确认应答,确认应答中包含了接收到的数据包的序列号,发送方接收到确认应答后,会将确认应答中的序列号与发送的数据包的序列号进行比较,如果两个序列号相同,则说明数据包发送成功,如果两个序列号不相同,则说明数据包发送失败,发送方会重新发送该数据包。
  3. 超时重传:发送方发送数据包后,会启动一个定时器,如果在规定的时间内没有收到接收方的确认应答,则会重新发送该数据包。
  4. 流量控制:接收方会给发送方发送一个窗口大小,发送方会根据窗口大小来发送数据包,如果窗口大小为0,则发送方不会发送数据包,如果窗口大小不为0,则发送方会根据窗口大小来发送数据包。
  5. 拥塞控制:慢开始+拥塞避免+快重传+快恢复

MySQL存储引擎了解几个

InnoDB,MyISAM,Memory,Archive
InnoDB:默认数据库引擎,底层存储结构为B+树,B树的每个节点对应InnoDB的一个page,page大小固定
MyISAM:不提供对数据库事务的支持,也不支持行级锁和外键,使用在改,插入,删除频繁的场景要锁定整个表,效率低,但是读取速度快,占用空间小。
Memory:使用内存作为存储介质,数据存储在内存中,速度快,但是数据不稳定,重启会丢失数据。默认使用Hash索引
Archive:归档存储引擎,只支持insert和select操作,不支持update和delete操作,数据压缩存储,占用空间小,速度快。采用高度压缩的存储格式。只适用于需要偶尔查询的大型历史数据表。

消息队列

  1. 消息队列的作用:解耦,异步,削峰
  2. 消息队列的使用场景:异步处理,应用解耦,流量削峰,消息通讯,日志处理,消息通知
  3. 消息不丢失的原因:持久化,ack机制,消息重试,消息补偿(看使用的消息队列的类型,kafka,rabbitmq,rocketmq每个使用的方式都不太一样)

输入一个网址到页面展示的过程

  1. DNS解析:将域名解析成IP地址(本地缓存,本地域名服务器,根域名服务器,顶级域名服务器,主域名服务器)
  2. 建立TCP连接:三次握手
  3. 发送HTTP请求,请求中包含了请求方法,请求的资源路径,HTTP版本,请求头,请求体
  4. 服务器处理请求:服务器接收到请求后,会根据请求的资源路径,查找对应的资源,然后将资源返回给客户端,返回的内容包括:响应状态码,响应头,响应体
  5. 服务器响应:处理完成请求后生成一个HTTP响应,响应中包含了响应状态码,响应头,响应体
  6. 浏览器接受响应:浏览器接收到响应后,会根据响应头中的Content-Type来判断响应体的类型,如果是text/html,则浏览器会将响应体交给html解析器进行解析,如果是image/png,则浏览器会将响应体交给图片解析器进行解析,然后将解析后的内容展示在页面上。
  7. 渲染页面:根据HTML解析器解析后的内容,生成DOM树,根据CSS解析器解析后的内容,生成CSSOM树,然后将DOM树和CSSOM树结合生成渲染树,然后根据渲染树来渲染页面,最后将渲染后的页面展示在浏览器上。
  8. 断开连接:四次挥手

SQL优化

  1. 大表数据的查询:缓存,索引,分库分表,分区
  2. 查询过程中的数据访问:索引,覆盖索引,联合索引,最左前缀原则,索引失效,索引优化器,修改表结构,缓存数据

如在遇到问题时如何诊断问题出在何处,诊断的方法

  1. 问题的现象:是什么问题,什么时候出现的,出现的频率,出现的概率,出现的时间,出现的地点,出现的范围,出现的影响,出现的原因,出现的解决方案。
    例如:OOM问题:

    首先定位问题的现象:OOM是内存溢出,内存溢出是指程序在申请内存时,没有足够的内存空间供其使用,出现OOM后,程序会抛出OOM异常,导致程序崩溃。有两种情况:内存泄漏,内存溢出。
    通过内存监控软件来判断是否是出现了内存泄漏
    通过jmap命令来dump出内存快照,然后通过jhat命令来分析内存快照,找出内存泄漏的地方。
    使用jstat查看监控JVM的内存和GC情况,查看GC的次数和时间,看是否频繁GC,如果频繁GC,说明内存不够用,需要调整JVM的内存大小。

类加载器

通过一个类的全限定类名获取该类的二进制字节流叫做类加载器。类加载器分为四种:

  1. 启动类加载器:用来加载java核心类库,无法被java出程序直接引用
  2. 扩展类加载器:加载java的扩展库,java虚拟机会提供一个扩展库目录,该类加载器在扩展库目录里面查找并加载java类。
  3. 系统类加载器:根据java的类路径来加载类
  4. 自定义加载器:

类加载的过程:加载,验证,准备,解析,初始化,使用,卸载七部分。

垃圾回收

  1. 标记-清除算法:标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。标记清除算法会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。
  2. 复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当一块内存用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半,空间浪费多了一倍。
  3. 标记-整理算法:标记-整理算法分为标记和整理两个阶段,首先标记出所有需要回收的对象,然后将所有存活的对象都向一端移动,然后清除端边界以外的内存。标记-整理算法不会产生内存碎片,但是需要移动对象,效率较低。
  4. 分代收集算法:分代收集算法根据对象的存活周期将内存分为新生代和老年代,新生代中的对象存活周期较短,老年代中的对象存活周期较长。新生代中的对象使用复制算法,老年代中的对象使用标记-整理算法。

消息队列中消息失效的原因

消息超时:当消息在队列中等待过久,超过了设定的超时时间,就会被认为是失效的。

消息被消费者拒绝:当消费者无法处理某个消息时,可以选择将其拒绝,此时消息会被标记为失效。

消息队列被清空:当消息队列被清空时,所有未被消费的消息都会被标记为失效。

消息队列被删除:当消息队列被删除时,所有未被消费的消息都会被标记为失效。

消息队列出现故障:当消息队列出现故障时,可能会导致消息丢失或失效。

redis持久化

RDB和AOF两种,其中RDB是以快照数据方式保存到硬盘中,而AOF是用文本文件追加记录操作日志的方式来保存数据的。RDB的优点是文件小,恢复速度快,缺点是可能会丢失最后一次快照后的所有数据,AOF的优点是数据不丢失,缺点是文件大,恢复速度慢。

多线程的创建

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口
  4. 继承TimerTask类
  5. 通过线程池启动多线程

线程池的参数

  1. 核心线程数:线程池中的常驻核心线程数也就是最少的线程数
  2. 最大线程数:线程池中允许的最大线程数
  3. 空闲线程存活时间:空闲线程多久会被销毁
  4. 空闲线程存活时间单位:空闲线程存活时间的单位
  5. 工作队列:用于存储等待执行的任务的阻塞队列,有四种类型:直接提交队列,有界队列,无界队列,优先级队列
  6. 线程工厂:创建新线程使用的工厂
  7. 拒绝策略:当线程池中的线程数达到最大线程数时,如果还有新的任务提交,就会使用拒绝策略来处理新的任务,有四种拒绝策略:AbortPolicy:直接抛出异常,不处理新的任务;
    CallerRunsPolicy:使用调用者所在的线程来执行任务
    DiscardOldestPolicy:丢弃队列中最老的任务
    DiscardPolicy:直接丢弃新的任务

索引

一种用于加快查询的数据结构

观察者模式

定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。如 Spring 中 Listener 的实现 ApplicationListener。

设计模式

  1. 单例模型:保证一个类只有一个实例,并提供一个全局访问点。
  2. 工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实例化延迟到其子类。
  3. 抽象工厂模式:定义了一个接口用于创建相关或者依赖对象的家族,而不需要明确指定具体类。
  4. 生成器模式:分装一个复杂对象的构建过程,并可以按步骤构造。
  5. 原型模式:通过复制现有的实例来创建新的实例。无需知道相应类的信息。

结构模式

  1. 适配器模式:将某个类的接口转换成客户端期望的另一个接口的表示,目的是消除由于接口不匹配所造成的类的兼容性问题。
  2. 装饰者模式:动态的给对象添加一些额外的职责,就增加功能来说,装饰者模式比生成子类更加灵活。
  3. 代理模式:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。也就是所谓的中介
  4. 外观模式:隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。也就是所谓的门面模式。
  5. 桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立的变化。
  6. 组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
  7. 享元模式:运用共享技术有效的支持大量细粒度的对象。

关系模式

  1. 模板方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
  2. 策略模式:定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换。
  3. 观察者模式:定义了对象之间的一对多依赖,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
  4. 迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
  5. 责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
  6. 命令模式:将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。
  7. 状态模式:创建表示各种状态的对象和一个行为随着状态对象改变而改变的context对象。
  8. 备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态。
  9. 访问者模式:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离。
  10. 中介者模式:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变他们之间的交互。
// 工厂模式
// 定义一个工厂类,用于创建对象
public class Factory {
    public static Product createProduct() {
        return new Product();
    }
}
// 定义一个产品类
public class Product {
    public void show() {
        System.out.println("产品");
    }
}
// 定义一个测试类
public class Test {
    public static void main(String[] args) {
        Product product = Factory.createProduct();
        product.show();
    }
}
// 适配器模式
// 定义一个适配器类,用于将一个类的接口转换成客户端期望的另一个接口的表示
public class Adapter {
    public void request() {
        System.out.println("适配器");
    }
}
// 定义一个目标类
public class Target {
    public void request() {
        System.out.println("目标");
    }
}

// 定义一个测试类
public class Test {
    public static void main(String[] args) {
        Target target = new Adapter();
        target.request();
    }
}
// 装饰者模式
// 定义一个装饰者类,用于动态的给对象添加一些额外的职责
public class Decorator {
    private Component component;
    public Decorator(Component component) {
        this.component = component;
    }
    public void operation() {
        component.operation();
    }
}
// 定义一个抽象组件类
public abstract class Component {
    public abstract void operation();
}
// 定义一个具体组件类
public class ConcreteComponent extends Component {
    public void operation() {
        System.out.println("具体组件");
    }
}
// 定义一个测试类
public class Test {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Decorator decorator = new Decorator(component);
        decorator.operation();
    }
}

线程状态

  1. 新建状态
  2. 就绪状态:调用start方法后,线程进入就绪状态,等待CPU的调度
  3. 运行状态:线程获取到CPU的使用权后,进入运行状态,开始执行run方法中的代码
  4. 阻塞状态:放弃CPU使用权,进入阻塞状态,等待某个条件的触发,比如sleep,wait,join等
  5. 死亡状态:线程执行完run方法中的代码后,线程就会进入死亡状态

线程的start和run的区别

本文作者:GWB
当前时间:2023-11-09 11:11:09
版权声明:本文由gwb原创,本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 国际许可协议。
转载请注明出处!