个人面试总结(一)

目录

1、Cookie和Session的区别?

2、常用的集合有哪些,有什么应用场景?

3、浏览器地址栏输入请求到页面展示,描述一下具体流程。

4、现有一个student表sql 查出1班学生男女生人数

5、MySQL数据是如何存储的?什么是B+树?

6、Java导致内存泄漏的原因。

7、nginx的作用是什么?是如何配置的?

1、正/反向代理

2、负载均衡

3、动静分离

4、web服务器

8、常用的linux命令有哪些?

9、SpringBoot执行流程是什么?

10、socket编程三次握手和四次挥手

握手异常

挥手异常

11、SpringCloud五大基本组件?

12、RabbitMQ解决消息丢失和重复消费?

MQ消息丢失

1、生产者未能成功将消息发给MQ

13、RabbitMQ宕机是如何处理的?

14、Redis有哪些基本类型?都有什么常用命令?

15、Redis是如何实现持久化的?

1、RDB

2、AOF

16、Java如何实现多个线程按规定顺序执行?

17、Redis 雪崩、缓存穿透、缓存击穿是什么?如何解决?

18、URL和URI区别?

19、有哪些常见的设计模式?

20、Spring中用到了哪些设计模式?

21、索引失效


1、Cookie和Session的区别?

Session是基于Cookie实现的。

1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
 
2、cookie不是很安全,别人可以从本地的cookie获取你的信息,
考虑到安全性应当使用session。
 
3、session会在一定时间内保存在服务器上。当访问增多,
会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
 
4、个站点单个cookie保存的数据不能超过4K,
很多浏览器都限制一最多保存20个cookie。
 
5、可以考虑将登陆信息等重要信息存放为session,
其他信息如果需要保留,可以放在cookie中。
  

6、存取方式的不同

Cookie中只能保管ASCII字符串,假如需求存取Unicode字符或者二进制数据,需求先进行编码。Cookie中也不能直接存取Java对象。若要存储略微复杂的信息,运用Cookie是比拟艰难的。

而Session中能够存取任何类型的数据,包括而不限于String、Integer、List、Map等。Session中也能够直接保管Java Bean乃至任何Java类,对象等,运用起来十分便当。能够把Session看做是一个Java容器类。

7、安全性(隐私策略)的不同

Cookie存储在浏览器中,对客户端是可见的,客户端的一些程序可能会窥探、复制以至修正Cookie中的内容。而Session存储在服务器上,对客户端是透明的,不存在敏感信息泄露的风险。 假如选用Cookie,比较好的方法是,敏感的信息如账号密码等尽量不要写到Cookie中。最好是像Google、Baidu那样将Cookie信息加密,提交到服务器后再进行解密,保证Cookie中的信息只要本人能读得懂。而假如选择Session就省事多了,反正是放在服务器上,Session里任何隐私都能够有效的保护。

8、有效期上的不同

只需要设置Cookie的过期时间属性为一个很大很大的数字,Cookie就可以在浏览器保存很长时间。 由于Session依赖于名为JSESSIONID的Cookie,而Cookie JSESSIONID的过期时间默许为–1,只需关闭了浏览器(一次会话结束),该Session就会失效。

9、对服务器造成的压力不同

Session是保管在服务器端的,每个用户都会产生一个Session。假如并发访问的用户十分多,会产生十分多的Session,耗费大量的内存。而Cookie保管在客户端,不占用服务器资源。假如并发阅读的用户十分多,Cookie是很好的选择。

10、 跨域支持上的不同

Cookie支持跨域名访问,例如将domain属性设置为“.baidu.com”,则以“.baidu.com”为后缀的一切域名均能够访问该Cookie。跨域名Cookie如今被普遍用在网络中。而Session则不会支持跨域名访问。Session仅在他所在的域名内有效。

11、Session作用范围
本次会话的所有页面都关闭后再重新访问某个Jsp或者Servlet将会创建新的会话。

注意事项:注意原有会话还存在,只是这个旧的SessionID任然存在服务端,只不过再也没有客户端会携带它然后交予服务端校验。
销毁:
Session的销毁只有三种方式:
1.调用了session.invalidate()方法
2.session过期(超时)
3.服务器重新启动

2、常用的集合有哪些,有什么应用场景?

集合是用来存储对象的容器 

先说一说集合和数组的区别:
1.数组的长度不可变,而集合的长度是可变的
2.数组可以存基本数据类型和引用数据类型,而集合只能存储引用数据类型

List集合:ArrayList和LinkedList,他们的区别是
ArrayList是基于动态数组,查询快,增删改的速度较慢
LinkedList是以链表的形式存储它的数据,查询慢增删快,
LinkedList比ArrayList更占用内存因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。

List 有序,可重复,可存在多个空值


Set集合:HashSet、TreeSet、LinkedHashSet
一般来说,如果我们需要保证集合元素是唯一的,就用set集合
Set 有且只能有一个空元素,无序,不可重复

TreeSet 相对于Set多了一个有序。


Map集合:HashMap、LinkedHashMap、TreeMap、HashTable


HashMap是最常用的Map,他根据键的HashCode值存储数据,根据键可以直接获取他的值,具有很快的访问速度。

重写equals时必须要重写hashcode以保证相同对象有相同的hashcode。

HashMap存储过程:

1.根据key调用hashcode计算出hash值。
2.在put的时候判断严肃,如果不存在调用resize方法创建默认容量为16的数组。
3.确定node在数组中的位置,根据hash值和数组的最大索引值进行与运算得到索引的位置。

(将hashCode高16位和低16位异或(^)操作,然后与当前数组长度-1结果进行与(&)操作,最终结果就是数组的下标值。)
4.获取该位置是否有元素,如果没有元素,直接创建一个node放在该位置。
5.如果有元素,判断key是否完全相等,如果相同,把原来的node赋值给一个变量。
6.再去判断此位置是红黑树还是链表。
7.如果是红黑树,则以红黑树的方式将node放在红黑树上。
8.如果是链表,就遍历链表,把node放在最后一位,放完之后需要去判断链表的长度是否超过8,如果超过就需要判断是否将链表转换为红黑树,当数组容量小于64的时候,只会对数组进行扩容,如果数组容量大于64,才会进行链表转红黑树。
9.返回被覆盖的值。
10.判断整个数组是否需要扩容。


遍历时,取到的数据顺序是随机的
HashMap最多只允许一条记录的键为Null,而可以允许多个记录的值为null
HashMap不支持线程同步,是非线程安全的
HashTable与HashMap类似,他不允许记录的键或者值为空,支持线程同步,所以在写入时较慢。
如果考虑线程安全的问题时用ConcurrentHashMap,我们很多时候把ConcurrentHashMap用于本地缓存。

3、浏览器地址栏输入请求到页面展示,描述一下具体流程。

1) SpringMVC 前端控制器 DispatcherServlet捕获浏览器请求。
2) DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
a) 不存在
i. 再判断是否配置了mvc:default-servlet-handler
ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误
iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404
错误

b) 存在则执行下面的流程

3) 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及
Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。

4) DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。

5) 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】

6) 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。
在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定
的响应信息
b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

7) Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。

8) 此时将开始执行拦截器的postHandle(…)方法【逆向】。

9) 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行
HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model
和View,来渲染视图。

10) 渲染视图完毕执行拦截器的afterCompletion(…)方法。

11) 将渲染结果返回给客户端
————————————————
版权声明:本文为CSDN博主「Njupt_dc」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_43467956/article/details/124381984

4、现有一个student表sql 查出1班学生男女生人数

namesexclass
a1
b女        1
...        ......

select count(name) from stu where class=1 group by sex ;
select count(name) from stu where class=1 and sex='男' union select  count(name) from stu where class=1 and sex='女';

5、MySQL数据是如何存储的?什么是B+树?

在这里插入图片描述

 在这里插入图片描述

MySQL Server架构自顶向下大致可以分网络连接层、服务层、存储引擎层和系统文件层。

一、网络连接层

客户端连接器(Client Connectors):提供与MySQL服务器建立的支持。目前几乎支持所有主流的服务端编程技术,例如常见的 Java、C、Python、.NET等,它们通过各自API技术与MySQL建立连接。
二、服务层(MySQL Server)

  服务层是MySQL Server的核心,主要包含系统管理和控制工具、连接池、SQL接口、解析器、查询优化器和缓存六个部分。

连接池(Connection Pool):负责存储和管理客户端与数据库的连接,一个线程负责管理一个连接。
系统管理和控制工具(Management Services & Utilities):例如备份恢复、安全管理、集群管理等
SQL接口(SQL Interface):用于接受客户端发送的各种SQL命令,并且返回用户需要查询的结果。比如DML(增删改)、DDL(数据定义语言DDL用来创建数据库中的各种对象-----表、视图、索引、同义词、聚簇等如:CREATE TABLE/VIEW/INDEX/SYN/CLUSTER)、存储过程、视图、触发器等。
解析器(Parser):负责将请求的SQL解析生成一个"解析树"。然后根据一些MySQL规则进一步检查解析树是否合法。
查询优化器(Optimizer):当“解析树”通过解析器语法检查后,将交由优化器将其转化成执行计划,然后与存储引擎交互。

缓存(Cache&Buffer): 缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,权限缓存,引擎缓存等。如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。
三、存储引擎层(Pluggable Storage Engines)

  存储引擎负责MySQL中数据的存储与提取,与底层系统文件进行交互。MySQL存储引擎是插件式的,服务器中的查询执行引擎通过接口与存储引擎进行通信,接口屏蔽了不同存储引擎之间的差异 。现在有很多种存储引擎,各有各的特点,最常见的是MyISAM和InnoDB。

四、系统文件层(File System)

  该层负责将数据库的数据和日志存储在文件系统之上,并完成与存储引擎的交互,是文件的物理存储层。主要包含日志文件,数据文件,配置文件,pid 文件,socket 文件等。

日志文件
错误日志(Error log)
默认开启,show variables like ‘%log_error%’
通用查询日志(General query log)
记录一般查询语句,show variables like ‘%general%’;
二进制日志(binary log)
记录了对MySQL数据库执行的更改操作,并且记录了语句的发生时间、执行时长;但是它不记录select、show等不修改数据库的SQL。主要用于数据库恢复和主从复制。
show variables like ‘%log_bin%’; //是否开启
show variables like ‘%binlog%’; //参数查看
show binary logs;//查看日志文件
慢查询日志(Slow query log)
记录所有执行时间超时的查询SQL,默认是10秒。
show variables like ‘%slow_query%’; //是否开启
show variables like ‘%long_query_time%’; //时长
配置文件
用于存放MySQL所有的配置信息文件,比如my.cnf、my.ini等。
数据文件
db.opt 文件:记录这个库的默认使用的字符集和校验规则。
frm 文件:存储与表相关的元数据(meta)信息,包括表结构的定义信息等,每一张表都会
有一个frm 文件。
MYD 文件:MyISAM 存储引擎专用,存放 MyISAM 表的数据(data),每一张表都会有一个
.MYD 文件。
MYI 文件:MyISAM 存储引擎专用,存放 MyISAM 表的索引相关信息,每一张 MyISAM 表对
应一个 .MYI 文件。
ibd文件和 IBDATA 文件:存放 InnoDB 的数据文件(包括索引)。InnoDB 存储引擎有两种
表空间方式:独享表空间和共享表空间。独享表空间使用 .ibd 文件来存放数据,且每一张
InnoDB 表对应一个 .ibd 文件。共享表空间使用 .ibdata 文件,所有表共同使用一个(或多
个,自行配置).ibdata 文件。
ibdata1 文件:系统表空间数据文件,存储表元数据、Undo日志等 。
ib_logfile0、ib_logfile1 文件:Redo log 日志文件。
pid 文件
pid 文件是 mysqld 应用程序在 Unix/Linux 环境下的一个进程文件,和许多其他 Unix/Linux 服务端程序一样,它存放着自己的进程 id。
socket 文件
socket 文件也是在 Unix/Linux 环境下才有的,用户在 Unix/Linux 环境下客户端连接可以不通过TCP/IP 网络而直接使用 Unix Socket 来连接 MySQL。
————————————————
原文链接:https://blog.csdn.net/cyd_0619/article/details/113627509

6、Java导致内存泄漏的原因。

1、单例造成的内存泄漏

2、非静态内部类创建静态实例造成的内存泄漏

3、Handler造成的内存泄漏(创建匿名内部类的静态对象)

4、线程造成的内存泄漏

5、资源未关闭造成的内存泄漏

6、集合容器中的内存泄露

7、nginx的作用是什么?是如何配置的?

1、正/反向代理

2、负载均衡

3、动静分离

为了加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来进行解析,加快解析速度,降低原来单个服务器的压力。

4、web服务器

########### 每个指令必须有分号结束。#################
#user administrator administrators;  #配置用户或者组,默认为nobody nobody。
#worker_processes 2;  #允许生成的进程数,默认为1
#pid /nginx/pid/nginx.pid;   #指定nginx进程运行文件存放地址
error_log log/error.log debug;  #制定日志路径,级别。这个设置可以放入全局块,http块,server块,级别以此为:debug|info|notice|warn|error|crit|alert|emerg
events {
    accept_mutex on;   #设置网路连接序列化,防止惊群现象发生,默认为on
    multi_accept on;  #设置一个进程是否同时接受多个网络连接,默认为off
    #use epoll;      #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
    worker_connections  1024;    #最大连接数,默认为512
}
http {
    include       mime.types;   #文件扩展名与文件类型映射表
    default_type  application/octet-stream; #默认文件类型,默认为text/plain
    #access_log off; #取消服务日志    
    log_format myFormat '$remote_addr–$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for'; #自定义格式
    access_log log/access.log myFormat;  #combined为日志格式的默认值
    sendfile on;   #允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。
    sendfile_max_chunk 100k;  #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。
    keepalive_timeout 65;  #连接超时时间,默认为75s,可以在http,server,location块。

    upstream mysvr {   
      server 127.0.0.1:7878;
      server 192.168.10.121:3333 backup;  #热备
    }
    error_page 404 https://www.baidu.com; #错误页
    server {
        keepalive_requests 120; #单连接请求上限次数。
        listen       4545;   #监听端口
        server_name  127.0.0.1;   #监听地址       
        location  ~*^.+$ {       #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。
           #root path;  #根目录
           #index vv.txt;  #设置默认页
           proxy_pass  http://mysvr;  #请求转向mysvr 定义的服务器列表
           deny 127.0.0.1;  #拒绝的ip
           allow 172.18.5.54; #允许的ip           
        } 
    }
}

注意事项:

1、几个常见配置项:

  • 1.$remote_addr 与 $http_x_forwarded_for 用以记录客户端的ip地址;
  • 2.$remote_user :用来记录客户端用户名称;
  • 3.$time_local : 用来记录访问时间与时区;
  • 4.$request : 用来记录请求的url与http协议;
  • 5.$status : 用来记录请求状态;成功是200;
  • 6.$body_bytes_s ent :记录发送给客户端文件主体内容大小;
  • 7.$http_referer :用来记录从那个页面链接访问过来的;
  • 8.$http_user_agent :记录客户端浏览器的相关信息;

2、惊群现象:一个网路连接到来,多个睡眠的进程被同时叫醒,但只有一个进程能获得链接,这样会影响系统性能。

3、每个指令必须有分号结束。

8、常用的linux命令有哪些?

shutdown -h now/min 或者 poweroff  //立即/定时关机

shutdown -r now/min 或者 reboot      //立即/定时重启

su 用户名                                           //用户切换

cd 目录                                               //切换目录

ls                                                        //查看当前目录下的所有目录和文件

pwd                                                    //显示当前位置路径

mkdir 文件夹名                                  //在当前位置新建文件夹

ps -ef                                                 //查看所有正在运行的进程

mkdir -p /a/b/文件夹名                       //在指定目录位置,创建文件夹,并创建父文件夹

mv 当前目录名 新目录名                   //修改目录名,同样适用与文件操作

cp /usr/tmp/tool /opt                           //将/usr/tmp目录下的tool目录复制到 /opt目录下面

find /bin -name 'a*'                             //查找/bin目录下的所有以a开头的文件或者目录

touch a.txt                                          //在当前目录下创建名为a的txt文件(文件不存在),如果文件存在,将文件时间属性修改为当前系统时间

rm 文件名                                          //删除当前目录下的文件

cat a.txt                                              //查看文件最后一屏内容
less a.txt                                            //PgUp向上翻页,PgDn向下翻页,"q"退出查看
more a.txt                                          //显示百分比,回车查看下一行,空格查看下一页,"q"退出查看

tail -100 a.txt                                     //查看文件的后100行,"Ctrl+C"退出查看

top                                                     //显示当前系统中占用资源最多的一些进程, shift+m 按照内存大小查看
yum install xxx                                  //使用yum安装xxxx

kill pid                                               //杀死该pid的进程

kill -9 pid                                           //强制杀死该进程 
 

ifconfig                              //查看网络

service iptables status      //查看iptables服务的状态
  service iptables start       //开启iptables服务
  service iptables stop        //停止iptables服务
  service iptables restart     //重启iptables服务
  chkconfig iptables off       //关闭iptables服务的开机自启动
  chkconfig iptables on        //开启iptables服务的开机自启动
  ##centos7 防火墙操作
  systemctl status firewalld.service     //查看防火墙状态
  systemctl stop firewalld.service       //关闭运行的防火墙
  systemctl disable firewalld.service    //永久禁止防火墙服务

  tar -zcvf 打包压缩后的文件名 要打包的文件
  参数说明:z:调用gzip压缩命令进行压缩; c:打包文件; v:显示运行过程; f:指定文件名;
  示例:
  tar -zcvf a.tar file1 file2,...      //多个文件压缩打包

 文件权限简介:'r' 代表可读(4),'w' 代表可写(2),'x' 代表执行权限(1),括号内代表"8421法"
  ##文件权限信息示例:-rwxrw-r--
  -第一位:'-'就代表是文件,'d'代表是文件夹
  -第一组三位:拥有者的权限
  -第二组三位:拥有者所在的组,组员的权限
  -第三组三位:代表的是其他用户的权限


 普通授权    chmod +x a.txt    
  8421法     chmod 777 a.txt     //1+2+4=7,"7"说明授予所有权限
 

  vi 文件名              //打开需要编辑的文件
  --进入后,操作界面有三种模式:命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode)
  命令模式
  -刚进入文件就是命令模式,通过方向键控制光标位置,
  -使用命令"dd"删除当前整行
  -使用命令"/字段"进行查找
  -按"i"在光标所在字符前开始插入
  -按"a"在光标所在字符后开始插入
  -按"o"在光标所在行的下面另起一新行插入
  -按":"进入底行模式
  公众号:网络技术联盟站
  插入模式
  -此时可以对文件内容进行编辑,左下角会显示 "-- 插入 --""
  -按"ESC"进入底行模式
  底行模式
  -退出编辑:      :q
  -强制退出:      :q!
  -保存并退出:    :wq
  ## 操作步骤示例 ##
  1.保存文件:按"ESC" -> 输入":" -> 输入"wq",回车     //保存并退出编辑
  2.取消操作:按"ESC" -> 输入":" -> 输入"q!",回车     //撤销本次修改并退出编辑
  ## 补充 ##
  vim +10 filename.txt                   //打开文件并跳到第10行
  vim -R /etc/passwd                     //以只读模式打开文件
 

9、SpringBoot执行流程是什么?

(1)创建 Spring Application 实例,调用 run 方法,同时将启动入口类作 为参数传递进去,由此开始了 Spring Boot 内部相关核心组件以及配置的 启动和加载;

(2)通过 Spring Factories Loader 加载 META-INF/spring.factories 文 件,获取并创建 SpringApplicationRunListener 对象;

(3)然后由 SpringApplicationRunListener 来发出 starting 消息;

(4)创建参数,并配置当前 SpringBoot 应用需要使用的 Environment 实 例;

(5)完成之后,依然由 SpringApplicationRunListener 来发出 environmentPrepared 消息;

(6)创建 Spring 的应用上下文实例:ApplicationContext,初始化该实例 并设置应用环境配置实例:Environment,同时加载相关的配置项;

(7)由 SpringApplicationRunListener 发出 contextPrepared 消息,告 知 SpringBoot 应用当前使用的 ApplicationContext 已准备完毕;

(8)将各种 Bean 组件装载入 Spring 的 IO 容器/应用上下文: ApplicationContext 中,继续由 SpringApplicationRunListener 来发出 contextLoaded 消息,告知 SpringBoot 应用当前使用的 ApplicationContext 已准备完毕;

(9)重新刷新 Refresh Spring 的应用上下文实例:ApplicationContext, 完成 IOC 容器可用的最后一步;

(10)由 SpringApplicationRunListener 发出 started 消息,完成最终的 程序的启动;

(11)由 SpringApplicationRunListener 发出 running 消息,告知程序已 成功运行起来了。

10、socket编程三次握手和四次挥手

三次握手:

第一次握手:客户端尝试连接服务器,向服务器发送syn包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入SYN_SEND状态等待服务器确认。
第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

握手异常

重传存在最大重传次数

第一次握手丢失:客户端会重传。

由于第一次握手后,客户端会进入SYN_SEND状态,客户端长时间未收到服务端SYN+ACK包,一定时间后,会触发客户端重传机制。

第二次握手丢失:客户端和服务端都会重传

第二次握手后,客户端在SYN_SEND状态中,一直接收不到服务端的SYN+ACK包,因此客户端会认为第一次握手失败,会触发重传机制,同时,服务端在SYN_RECV状态长时间未收到客户端的ACK包,因此服务端也会触发重传机制。

第三次握手丢失:服务端会重新进行第二次握手,即重传SYN+ACK包

因为,客户端发送ACK包不需要回应,因此客户端不回重传,服务端会重传SYN+ACK。

四次挥手:

 ● 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入 FIN_WAIT_1 状态。
● 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSED_WAIT 状态。
● 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。
● 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
● 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态
● 服务器收到了 ACK 应答报文后,就进入了 CLOSED 状态,至此服务端已经完成连接的关闭。
● 客户端在经过 2MSL 一段时间后,自动进入 CLOSED 状态,至此客户端也完成连接的关闭。
● 这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。
 

挥手异常

第一次挥手:客户端触发重传

第一次挥手会进入FIN_WAIT_1状态,长时间会触发重传,重传次数达到tcp_orphan_retries 会直接进入close

第二次挥手:客户端会触发重传

因为第二次挥手是服务端发送ACK报文,而ACK报文不会重传,所以,客户端长时间未收到服务端ACK报文会触发重传机制。

第三次挥手:服务端触发重传

服务端进入LAST_ACK状态后,长时间未收到客户端的ACK报文,触发重传机制。

第四次挥手:服务端触发重传

服务端进入LAST_ACK状态后,长时间未收到客户端的ACK报文,触发重传机制。

11、SpringCloud五大基本组件?

1、Eureka实现服务治理;

2、Ribbon主要提供客户侧的软件负载均衡算法;

3、Hystrix断路器,保护系统,控制故障范围;

4、Zuul,api网关,路由,负载均衡等多种作用;

5、Config配置管理。

12、RabbitMQ解决消息丢失和重复消费?

MQ消息丢失

1、生产者未能成功将消息发给MQ

原因:因为网络传输的不稳定性,当生产者在向MQ发送消息的过程中,MQ没有成功接收到消息,但是生产者却以为MQ成功接收到了消息,不会再次重复发送该消息,从而导致消息的丢失。

解决:事务机制和confirm机制

2、MQ收到消息后丢失消息

原因:RabbitMQ接收到生产者发送过来的消息,是存在内存中的,如果没有被消费完,此时RabbitMQ宕机了,那么再次启动的时候,原来内存中的那些消息都丢失了。

解决:

消息持久化:1、队列持久化     2、消息持久化

注意:

1、持久化要起作用必须同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。

2、如果开启了消息的持久化,只有当消息成功持久化磁盘之后,才会回调生产者的接口返回ack消息,否则都算失败。

3、消费者弄丢了消息

原因:MQ确认消息发送成功后,消费者宕机丢失消息。

解决:关闭 RabbitMQ 的自动 ack,采用手动ack (消息应答机制:分为自动应答和手动应答)。消息在接收到消息并成功消费后,消费者进行应答。

MQ消息重复消费

原因:网络故障等问题导致,消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。

解决:保证消息的唯一性;保证消息的幂等性;(幂等性:用户对于同一操作发起的一次请求或者多次请求的结果是一致的)

  • 在消息生产时,MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重和幂等的依据(消息投递失败并重传),避免重复的消息进入队列;
  • 在消息消费时,要求消息体中必须要有一个Id(对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重和幂等的依据,避免同一条消息被重复消费。

举例:

  • 数据库insert相同id,会产生冲突;
  • redis的set操作,具有幂等性;
  • 通过第三方来进行消费记录,例如redis记录消费,消费前进行查询;

MQ应答机制        

/**
         * 消费者消费消息
         * 1.消费哪个队列
         * 2.消费成功之后是否要自动应答。 true代表自动应答 ,false手动应答
         * 3.消费者,成功消费的回调
         * 4.消费者 取消消费的回调
         */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
 

MQ事务

// 开启事务
channel.txSelect
 
try {
// 这里发送消息
 
} catch (Exception e) {
channel.txRollback
 
// 这里再次重发这条消息
}

// 提交事务
channel.txCommit
 

MQ Confirm

RabbitMQ可以开启 confirm 模式,在生产者那里设置开启 confirm 模式之后,生产者每次写的消息都会分配一个唯一的 id,如果消息成功写入 RabbitMQ 中,RabbitMQ 会给生产者回传一个 ack 消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,生产者可以重新发送。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么可以重发。

三种确认方式:

同步-单独:

一个消息之后只有它被确认发布,后续的消息才能继续发布。

同步-批量发布:

先发布一批消息然后一起确认可以极大地提高吞吐量。

缺点就是:当发生故障导致发布出现问题时,不知道是哪个消息出现问题了,我们必须将整个批处理保存在内存中,以记录重要的信息而后重新发布这些消息。

异步处理:

  • 第一步:发消息时记录下所有发送的消息
  • 第二步:在消息确认成功的回调函数中删除已经确认的消息,剩下的就是未确认的消息
  • 第三步:在消息确认失败的回调函数中对剩余的消息做处理。

三种确认速度对比:

  • 同步-单独发布消息:同步等待确认,简单,但吞吐量非常有限。
  • 同步-批量发布消息:批量同步等待确认,简单,合理的吞吐量,一旦出现问题但很难推断出是那条消息出现了问题。
  • 异步处理:最佳性能和资源使用,在出现错误的情况下可以很好地控制,但是实现起来稍微难些。

13、RabbitMQ宕机是如何处理的?

消息的持久化,将消息存储到数据库里,宕机后重新读取数据消息。

14、Redis有哪些基本类型?都有什么常用命令?

基本类型:String,Set,Zset,Hash,List

服务命令:

  • redis-cli   :连接本地的 redis 服务
  • redis-cli -h host -p port -a password   :连接远程redis服务
  • ping : 检测连接是否存活
  • echo: 在命令行打印一些内容
  • quit、exit: 退出客户端
  • shutdown: 退出服务器端
  • info: 返回redis相关信息
  • config get dir/* 实时传递接收的请求
  • showlog: 显示慢查询
  • select n: 切换到数据库n,redis默认有16个数据库(DB 0~DB 15),默认使用的第0个
  • dbsize: 查看当前数据库大小
  • move key n: 不同数据库之间数据是不能互通的,move移动键到指定数据库
  • flushdb: 清空当前数据库中的键值对。
  • flushall: 清空所有数据库的键值对。

key相关命令:

  • keys * :查看当前数据库中所有的key
  • dbsize: 键总数
  • exists key: 检查键是否存在
  • del key [key …]: 删除键
  • expire key seconds: 键过期
  • ttl key: 获取键的有效时长
  • persist key: 移除键的过期时间
  • type key: 键的数据结构类型
  • randomkey: 随机返回数据库中一个键
  • rename key1 key2 : 重命名
  • renamex key1 key2 : 当key2不存在时,key1重命名

————————————————
原文链接:https://blog.csdn.net/Lzy410992/article/details/116094703

15、Redis是如何实现持久化的?

1、RDB

把当前进程数据生成快照保存到磁盘上

  • 触发方式
    • 手动:
      •  save命令:阻塞当前Redis服务器,直到RDB过程完成为止 
      • bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子 进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。
    • 自动:
      • redis.conf中配置save m n,即在m秒内有n次修改时,自动触发bgsave生成rdb文件;

      • 主从复制时,从节点要从主节点进行全量复制时也会触发bgsave操作,生成当时的快照发送到从节点;

      • 执行debug reload命令重新加载redis时也会触发bgsave操作;

      • 默认情况下执行shutdown命令时,如果没有开启aof持久化,那么也会触发bgsave操作;

  • 优点

    • RDB文件是某个时间节点的快照,默认使用LZF算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景;
    • Redis加载RDB文件恢复数据要远远快于AOF方式;
  • 缺点

    • RDB方式实时性不够,无法做到秒级的持久化;
    • 每次调用bgsave都需要fork子进程,fork子进程属于重量级操作,频繁执行成本较高;
    • RDB文件是二进制的,没有可读性,AOF文件在了解其结构的情况下可以手动修改或者补全;
    • 版本兼容RDB文件问题

2、AOF

Redis先执行命令,把数据写入内存,然后将命令记录日志。

  • 实现:
  • 命令追加:

         当AOF持久化功能打开了,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。 

  • 文件写入和同步

         关于何时将 aof_buf 缓冲区的内容写入AOF文件中,Redis提供了三种写回策略:

  • Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
  • Everysec,每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
  • No,操作系统控制的写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。

注:Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。

16、Java如何实现多个线程按规定顺序执行?

1、子线程join()

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("打开冰箱!");
            }
        });
  
        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("拿出一瓶牛奶!");
            }
        });
  
        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("关上冰箱!");
            }
        });
  
        //下面三行代码顺序可随意调整,程序运行结果不受影响。
        thread3.start();
        thread2.start();
        thread1.start();

2、主线程join()

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("打开冰箱!");
            }
        });
 
        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("拿出一瓶牛奶!");
            }
        });
 
        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
               
                System.out.println("关上冰箱!");
            }
        });

        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();
        thread3.start();

 

3、 通过倒数计时器CountDownLatch实现

CountDownLatch通过计数器提供了更灵活的控制,只要检测到计数器为0当前线程就可以往下执行而不用管相应的thread是否执行完毕。

public class ThreadCountDownLatchDemo {
 
    private static CountDownLatch countDownLatch1 = new CountDownLatch(1);
 
    private static CountDownLatch countDownLatch2 = new CountDownLatch(1);
 
    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("打开冰箱!");
                countDownLatch1.countDown();
            }
        });
 
        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    countDownLatch1.await();
                    System.out.println("拿出一瓶牛奶!");
                    countDownLatch2.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
 
        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    countDownLatch2.await();
                    System.out.println("关上冰箱!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
 
        //下面三行代码顺序可随意调整,程序运行结果不受影响
        thread3.start();
        thread1.start();
        thread2.start();
    }
}

4、通过创建单一化线程池newSingleThreadExecutor()实现 

public class ThreadPoolDemo {
 
   static ExecutorService executorService = Executors.newSingleThreadExecutor();
 
    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("打开冰箱!");
            }
        });
 
        final Thread thread2 =new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("拿出一瓶牛奶!");
            }
        });
 
        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("关上冰箱!");
            }
        });
        executorService.submit(thread1);
        executorService.submit(thread2);
        executorService.submit(thread3);
        executorService.shutdown();        //使用完毕记得关闭线程池
    }
 
}

参考:https://www.jb51.net/article/246666.htm 

17、Redis 雪崩、缓存穿透、缓存击穿是什么?如何解决?

缓存穿透:访问缓存和数据库都没有的数据,导致数据库压力大。

解决:

  • 将空key添加到缓存中。

    从缓存取不到的数据,在数据库中也没有取到,新增key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。

  • 使用布隆过滤器过滤空key。

    bloomfilter类似于一个hash set,用于快速判某个元素是否存在于集合中,布隆过滤器的关键就在于hash算法和容器大小。

  • 一般对于这种访问可能由于遭到攻击引起,可以对请求进行身份鉴权、数据合法行校验等。

缓存击穿:缓存中没有但数据库中有的数据

解决:

  • 设置热点数据永远不过期。定时更新缓存,但如果更新出问题会导致缓存中的数据一直为旧数据。
  • 接口限流与熔断,降级。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。
  • 加互斥锁或者分布锁。

雪崩:缓存服务宕机或大量的key值同时过期,导致所有请求都直接访问数据库导致数据库压力增大。

解决:避免同时过期

  • 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

  • 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。

  • 设置热点数据永远不过期。

缓存污染:不用的旧数据依然留存在缓存中,消耗缓存空间。

解决:根据淘汰策略去淘汰数据。

八种淘汰策略:

  • 不淘汰
    • noeviction (v4.0后默认的)
  • 对设置了过期时间的数据中进行淘汰
    • 随机:volatile-random
    • ttl:volatile-ttl
    • lru:volatile-lru
    • lfu:volatile-lfu
  • 全部数据进行淘汰
    • 随机:allkeys-random
    • lru:allkeys-lru
    • lfu:allkeys-lfu

18、URL和URI区别?

URL(统一资源定位器)用于标识资源;

URI(统一资源标识符)提供了更简单和可扩展的标识资源的方法。URL是URI的子集,下面我们就来看看一下URL和URI的主要区别是什么。

URN(统一资源名称)

1、作用的区别

URL(统一资源定位符)主要用于链接网页,网页组件或网页上的程序,借助访问方法(http,ftp,mailto等协议)来检索位置资源。

URI(统一资源标识符)用于定义项目的标识,此处单词标识符表示无论使用的方法是什么(URL或URN),都要将一个资源与其他资源区分开来。

2、可以说URL是URI(URL是URI的子集),但URI永远不能是URL。

3、协议区别

URL指定要使用的协议类型,而URI不涉及协议规范。

19、有哪些常见的设计模式?

创建型模式

  • 单例模式:某个类只能有一个实例,提供一个全局的访问点。
  • 工厂模式:一个工厂类根据传入的参数决定创建出那一种产品类的实例。
  • 抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。
  • 建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。

结构型模式

  • 适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。
  • 组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。
  • 装饰模式:动态的给对象添加新的功能。
  • 代理模式:为其他对象提供一个代理以便控制这个对象的访问。
  • 亨元模式:通过共享技术来有效的支持大量细粒度的对象。
  • 外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
  • 桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。

行为型模式

  • 模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
  • 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
  • 策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
  • 状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
  • 观察者模式:对象间的一对多的依赖关系。
  • 备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
  • 中介者模式:用一个中介对象来封装一系列的对象交互。
  • 命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
  • 访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
  • 责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
  • 迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

————————————————
参考链接:https://blog.csdn.net/weixin_57002282/article/details/124263340

20、Spring中用到了哪些设计模式?

工厂模式、单例模式、代理模式、模板模式、观察者模式、适配器模式。

21、索引失效

1、like 以%开头,索引无效;当like前缀没有%,后缀有%时,索引有效。

2、or语句前后没有同时使用索引。

3、组合索引,不是使用第一列索引,索引失效。

4、如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引。

例如 select * from student where name=1;name字段为varchar字段,传入参数1为数值

5、在索引列上使用 IS NULL 或 IS NOT NULL操作。(不一定)

6、在索引字段上使用not,<>,!=。

7、对索引字段进行计算操作、字段上使用函数

例如 select * from student  where concat('张',name)='张三';

8、当全表扫描速度比索引速度快时,mysql会使用全表扫描,此时索引失效。

9、违背最左匹配原则且不满足索引覆盖

(索引覆盖:使用索引时,索引树查询到的叶子节点上的数据可以覆盖到你查询的所有字段)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值