一、实习
1、健康险核心 batch 自动查询和一键重启
2、后端如何实现免密登录
-
Spring Boot与Spring Security:
- 如果你使用的是Spring框架,Spring Security可以为你提供大量的安全功能。
- 创建一个基于Spring Boot的新项目,并添加Spring Security依赖。
-
使用JWT:
- 使用JSON Web Tokens (JWT)为用户生成令牌。这些令牌在第一次验证后,可以为用户提供有限的访问时间,而不需要再次输入密码。
- 为Spring Security配置一个JWT过滤器,用于检查和验证请求头中的令牌。
-
存储和管理令牌:
- 当用户首次登录时,生成一个JWT并将其发送给客户端。
- 客户端应将此令牌保存在cookie或localStorage中,并在随后的每次请求中附带它。
- 服务器端应验证这个令牌,以确定用户是否已经验证,并且令牌是否仍然有效。
-
安全性:
- 使用HTTPS来加密客户端和服务器之间的所有通信。
- 定期旋转你的JWT签名密钥以增加安全性。
- 将令牌的有效期设置为相对较短,例如15分钟或1小时,但提供刷新机制。
-
日志和监控:
- 使用Java的日志库(如SLF4J或Logback)记录所有的登录尝试、令牌生成和验证失败。
- 这将帮助你在发生安全事件时进行调查和审计。
3、JWT登录流程?包含哪些结构?摘要算法用的是什么?
-
JWT登录流程:
- 用户登录:用户使用他们的凭据(如用户名和密码)来请求访问。
- 验证凭据:服务器验证用户提交的凭据。如果凭据是有效的,服务器将生成一个JWT。
- 生成JWT:一旦验证成功,服务器将使用密钥(通常只有服务器知道)来生成JWT,并将其发送回客户端。
- 客户端存储JWT:客户端接收JWT并存储在本地,常见的存储位置是Cookie或LocalStorage。
- 发送请求携带JWT:之后,每当客户端向服务器发送请求(尤其是对受保护资源的请求),它都会在其请求头中附带JWT。
- 服务器验证JWT:服务器接收到请求后,会验证请求头中的JWT。如果JWT有效,服务器会处理该请求;如果无效,服务器可能会返回一个错误。
- 到期/注销:JWT有一个到期时间,过了这个时间JWT就会失效。此外,如果用户注销,客户端通常会丢弃保存的JWT。
-
JWT结构包括三部分:
-
头(Header):它通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法,如HMAC SHA256或RSA。
-
有效载荷(Payload):包含声明,这是关于实体(通常是用户)以及其他一些元数据的语句。这些声明被称为“claims”。
-
签名(Signature):为了获得签名部分,您必须获取编码的header,编码的payload,一个秘密,然后使用header中指定的算法进行签名。
-
摘要算法:JWT支持多种摘要算法,但最常见的算法是:
- HS256 (HMAC with SHA-256)
- RS256 (RSA signature with SHA-256)
其中,HS256使用共享密钥(客户端和服务器都知道的),而RS256使用私钥/公钥对,只有服务器知道私钥。这使得RS256更适合于公开的、可扩展的环境,因为只有生成JWT的服务器才能验证和接受令牌。
4、个人保险凭证打印,利用 Java 的 PDF 生成库 iText 为用户提供凭证的 PDF 生成功能
-
添加iText库依赖:
- 如果你使用的是Maven,可以在
pom.xml
中添加iText的依赖。不同版本的iText可能有所不同,所以要确保选择一个适合的版本。
- 如果你使用的是Maven,可以在
-
创建PDF文档:
- 使用
Document
类来创建一个新的PDF文档。 - 使用
PdfWriter.getInstance()
方法将这个Document
对象与一个文件输出流关联起来。
- 使用
-
开始编写内容:
- 打开文档:
document.open()
- 使用
document.add()
方法添加内容。iText提供了多种元素,如Paragraph
,Chapter
,Section
,List
,PdfPTable
等,你可以根据需要加入。 - 在为保险凭证填写具体信息时(如保单号、购买日期、姓名等),可以用这些基础元素组装。
- 打开文档:
-
格式化内容:
- 使用iText的字体和样式类来调整文本的外观。
- 使用
BaseFont
和Font
类来创建和应用不同的字体和样式。
-
创建表格:
- 如果需要整齐地显示某些信息,例如保险明细,可以使用
PdfPTable
类来创建表格。 - 表格可以有标题、多个行和列,并可以定义边框、背景等样式。
- 如果需要整齐地显示某些信息,例如保险明细,可以使用
-
添加图片或公司标志:
- 使用
Image
类来添加图片到文档。 - 图片可以是外部文件,也可以是内部资源。
- 使用
-
页眉和页脚:
- 使用
HeaderFooter
类或事件处理来添加页眉和页脚。这样可以为每一页自动添加页码、日期、公司标志等信息。
- 使用
-
完成文档:
- 一旦你添加了所有需要的内容,使用
document.close()
来关闭并保存文档。
- 一旦你添加了所有需要的内容,使用
-
提供给用户:
- 根据你的应用环境,你可以将生成的PDF文件保存到服务器、数据库,或直接作为HTTP响应的一部分发送给用户以供下载或在线预览。
二、项目(RPC)
1、RPC的原理图和调用过程
图中服务端启动时将自己的服务节点信息注册到注册中心,客户端调用远程方法时会订阅注册中心中的可用服务节点信息,拿到可用服务节点之后远程调用方法,当注册中心中的可用服务节点发生变化时会通知客户端,避免客户端继续调用已经失效的节点。那客户端是如何调用远程方法的呢,来看一下远程调用示意图:
- 客户端模块代理所有远程方法的调用
- 将目标服务、目标方法、调用目标方法的参数等必要信息序列化
- 序列化之后的数据包进一步压缩,压缩后的数据包通过网络通信传输到目标服务节点
- 服务节点将接受到的数据包进行解压
- 解压后的数据包反序列化成目标服务、目标方法、目标方法的调用参数
- 通过服务端代理调用目标方法获取结果,结果同样需要序列化、压缩然后回传给客户端
2、RPC的重要组成有哪些?
-
客户端(Client):发起RPC请求的部分。客户端包含代表远程过程的存根(stub),它提供与本地过程相同的接口。
-
服务器(Server):接受RPC请求并执行服务的部分。服务器同样包含一个存根,负责接受请求、解码参数、执行请求并返回结果。
-
传输层:RPC需要一种通信方式来在客户端和服务器之间传输数据。这通常通过网络完成,例如使用TCP/IP或UDP。
-
消息格式/序列化:由于网络传输层通常只能传输字节流,RPC需要将数据(如过程参数和返回值)转换为这种格式。这个转换过程叫做序列化(将数据转换为字节流)和反序列化(将字节流转回原始数据)。
-
请求与响应:客户端发起的是请求,服务器返回的是响应。每个请求都与一个响应匹配。
-
服务注册与发现:在某些RPC系统中(如gRPC、Apache Thrift等),服务器可以注册其提供的服务,并且客户端可以发现这些服务。这可以使得客户端和服务器的连接更加动态和灵活。
-
错误处理:如果远程调用中发生错误(如网络问题、服务不可用等),RPC框架应该能够捕获并处理这些错误。
-
身份验证和授权:为了确保只有合法的客户端可以访问服务,RPC系统可能会包含身份验证和授权机制。
-
负载均衡:在多个服务器实例中,RPC系统可能会提供负载均衡功能,使得客户端的请求可以均匀地分配到不同的服务器。
3、注册中心怎么选?CP更重要还是AP?ZooKeeper是CP还是AP?
(1)选择CP还是AP取决于你的系统需求:
- 如果系统需要确保数据的一致性,并且可以承受某些请求失败或延迟,那么CP可能更合适。
- 如果系统需要确保高可用性,即使这意味着在某些情况下返回的数据可能是过时的,那么AP可能更合适。
(2)ZooKeeper 是一个CP系统。当网络分区发生时,为了维护一致性,ZooKeeper可能会牺牲可用性。这意味着在某些情况下,ZooKeeper可能不会响应客户端的请求,以确保数据的一致性。
4、序列化的作用是什么?Serializable的原理?
(1)在RPC(远程过程调用)中,序列化的主要作用是将数据或对象转化为可传输的格式,使其能够在网络上进行传输,从而实现不同节点或服务之间的通信。
具体作用如下:
-
数据交换:通过序列化,客户端可以将请求参数转化为字节流,在网络上发送到服务器;服务器接收到字节流后,再通过反序列化恢复为原始的请求参数。
-
保证数据完整性:序列化过程中可以将数据结构完整地转化为字节流,确保数据在传输过程中不丢失任何信息。
-
兼容性:有些序列化协议(如Protocol Buffers, Avro等)提供了版本控制和兼容性管理,使得数据格式可以随着时间演进而不影响已有的客户端和服务器之间的通信。
(2)Serializable的原理
Serializable
是Java中的一个标记性接口,用于指示一个类的对象可以被序列化。当一个类实现了Serializable
接口时,Java的对象序列化机制可以将其转换为字节流,反之也可以从字节流中重构对象。
5、服务启动的时候服务基本信息被注册到注册中心,如果服务提供者挂了,注册中心如何知道服务不可用了呢?
服务掉线分为主动下线和心跳检测。
比如服务由于发版时,在重启之前先主动通知注册中心:我要重启了,有流量进来先不要分给我,让别的机器服务,等我重启成功后在放流量进来,或者是在管理后台手动直接摘掉机器,这个是主动下线。
增加 Netty 心跳机制 : 保证客户端和服务端的连接不被断掉,避免重连。
心跳检测是处理服务非正常下线(如断电断网)的情况,这个时候如果注册中心不知道该服务已经掉线,一旦被其调用就会带来问题。为了避免出现这样的情况,注册中心增加一个心跳检测功能,它会对服务提供者(Provider)进行心跳检测,比如每隔 30s 发送一个心跳,如果三次心跳结果都没有返回值,就认为该服务已下线,赶紧更新 Consumer 的服务列表,告诉 Consumer 调用别的机器
6、如果注册中心挂了,比如你用的是 Zookeeper,如果 Zookeeper 挂了,那服务之间还能相互调用吗?
首先注册中心挂掉也要分两种情况,如果数据库挂了,ZK 还是能用的,因为 ZK 会缓存注册机列表在缓存里。其次 ZK 本身就是一个集群的,一台机器挂了,ZK 会选举出集群中的其他机器作为 Master 继续提供服务,如果整个集群都挂了也没问题,因为调用者本地会缓存注册中心获取的服务列表。省略和注册中心的交互,Consumer 和 Provider 采用直连方式,这些策略都是可配置的。
7、在RPC框架中Netty 如何实现 Client 端与 Server 端的异步通信
在 Netty 中,RPC 框架的实现是基于 Netty 的异步通信机制的。RPC 框架中,客户端与服务端的异步通信是通过 Channel 和 EventLoop 实现的。Channel 是一个连接到网络套接字的组件,而 EventLoop 是处理 Channel 事件的线程。在 Netty 中,每个 Channel 都有一个与之相关联的 EventLoop,它会处理所有的 I/O 事件和请求。
具体步骤如下:
- 客户端通过 Channel 向服务端发送请求。
- 服务端通过 Channel 接收请求。
- 服务端将请求交给 EventLoop 处理。
- EventLoop 处理请求并返回结果。
- 服务端通过 Channel 将结果返回给客户端。
- 客户端通过 Channel 接收结果。
8、为什么用序列化机制 Protostuff 替代 JDK 自带的序列化机制
- 使用简单。Protobuf 每次要编写接口定义文件,然后还要编译,操作太繁琐。
- 高性能。相对 JSON 等文本序列化库,protostuff 是二进制的,因此性能比 JSON 等方式高。
- 序列化后的数据更小,性能更高。protostuff 占用内存最少,protobuf 其次,XML 最后。
- 序列化速度快。protostuff 比 protobuf 快 3 倍左右,protobuf 比 XML 快 4-5 倍
9.ZooKeeper有什么特点,选举机制说一下,什么时候会出现选举问题,他是AP的还是CP的,为什么
(1)ZooKeeper的特点有以下几点
- 高可用性:ZooKeeper集群中只要有一台机器存活,就能对外提供服务。
- 严格顺序访问:所有的更新请求都会按照其发送顺序被逐个执行。
- 数据一致性:ZooKeeper保证数据的最终一致性,即在一定时间内,所有客户端能够读取到同样的数据。
- 可靠性:ZooKeeper保证在分布式环境下数据的可靠性。
(2)ZooKeeper的选举机制是基于Paxos算法。当集群中的Leader节点挂掉时,ZooKeeper会自动进行Leader选举。选举过程分为两个阶段:选举和投票。选举阶段是为了选出一个唯一的Leader,投票阶段是为了让其他节点知道谁是Leader。在选举过程中,每个节点都可以成为候选人,然后通过投票来决定哪个候选人成为Leader。
当出现网络分区或者节点故障时,就会出现选举问题。如果出现网络分区,那么可能会出现多个Leader,这时需要手动干预解决。如果出现节点故障,那么ZooKeeper会自动进行Leader选举。
ZooKeeper是CP系统。它保证了数据的一致性和分区容错性,但不保证可用性。因此,在网络分区或者节点故障时,可能会导致部分客户端无法访问。
10.ZooKeeper做分布式锁说一下
ZooKeeper分布式锁的实现方式是:首先需要创建一个父节点,尽量是持久节点(PERSISTENT类型),然后每个要获得锁的线程,都在这个节点下创建个临时顺序节点。当一个线程需要获得锁时,它会在父节点下创建一个临时顺序节点,然后获取父节点下所有子节点的列表,判断自己创建的节点是否是最小的那个。如果是,则表示该线程获得了锁;否则,该线程就需要监听比自己小的那个节点的删除事件,当该节点被删除时,该线程再次判断自己创建的节点是否是最小的那个。如果是,则表示该线程获得了锁。
11、Duboo的四种负载均衡策略
客户端调用远程服务的时候进行负载均衡 :调用服务的时候,从很多服务地址中根据相应的负载均衡算法选取一个服务地址。
(1)RandomLoadBalance:根据权重随机选择(对加权随机算法的实现)。这是Dubbo默认采用的一种负载均衡策略。
(2)LeastActiveLoadBalance:最小活跃数负载均衡。
Dubbo 就认为谁的活跃数越少,谁的处理速度就越快,性能也越好,这样的话,我就优先把请求给活跃数少的服务提供者处理。
(3)ConsistentHashLoadBalance:一致性Hash负载均衡策略。
ConsistentHashLoadBalance
中没有权重的概念,具体是哪个服务提供者处理请求是由你的请求的参数决定的,也就是说相同参数的请求总是发到同一个服务提供者。另外,Dubbo 为了避免数据倾斜问题(节点不够分散,大量请求落到同一节点),还引入了虚拟节点的概念。通过虚拟节点可以让节点更加分散,有效均衡各个节点的请求量。
(4)RoundRobinLoadBalance:加权轮询负载均衡。
轮询就是把请求依次分配给每个服务提供者。加权轮询就是在轮询的基础上,让更多的请求落到权重更大的服务提供者上。
12.为什么学习RPC
RPC(远程过程调用,Remote Procedure Call)是一种计算机通信协议,它允许程序在一个地址空间中请求服务,而不需要明确提供该服务的详细知识。在分布式系统和微服务架构中,RPC经常被用作通信机制。学习RPC的原因有很多:
-
分布式系统设计:随着业务规模的增长,很多企业都会从单体应用转向分布式系统。在分布式系统中,不同的服务或组件可能部署在不同的机器或数据中心上。RPC为这些服务或组件之间提供了一种快速、高效的通信方式。
-
微服务架构:微服务是近年来非常热门的软件架构模式,每个服务通常负责执行单一的、小的功能。这些服务之间需要通过某种方式进行通信,而RPC是其中之一。
-
性能和优化:与其他通信机制相比,如HTTP RESTful API,RPC通常能提供更好的性能和更少的开销。学习如何优化RPC可以帮助你构建更高效的系统。
-
多语言支持:很多RPC框架,如gRPC,支持多种编程语言,这意味着你可以在不同的语言中编写服务,然后使用RPC进行交互。
-
跨平台通信:RPC允许不同的系统、应用或设备之间进行通信,这为构建跨平台应用提供了可能性。
-
抽象和封装:RPC隐藏了网络通信的复杂性,开发者只需像调用本地函数一样调用远程函数,而不需要关心底层的网络细节。
-
拓展知识和技能:作为软件工程师,了解不同的技术和方法可以帮助你在面对各种问题时更具备选择权和判断力。
三、Java八股
一面问的多态特性,运行时多态怎么实现的
四、Spring八股
1.Spring中的IOC和AOP介绍一下
Sring拥有两大特性:IoC和AOP。IoC,英文全称Inversion of Control,意为控制反转(或者叫依赖注入)。AOP,英文全称Aspect-Oriented Programming,意为面向切面编程。
Spring核心容器的主要组件是Bean工厂(BeanFactory),Bean工厂使用控制反转(IoC)模式来降低程序代码之间的耦合度,并提供了面向切面编程(AOP)的实现。
2.IOC要解决什么问题,AOP要解决什么问题
- IoC解决对象之间的耦合问题,例如当service层调用dao层时,传统方式下我们需要在service中new出dao层的具体实现类,这时当我们实现类需要改变时,service层也需要做相应的改变,这就造成了service层和dao层的强耦合。而使用IOC实例化对像时,我们只需要关注调用的dao层的接口,在service中声明接口属性,具体的实现类在IOC容器中进行切换,因此也不会产生对象中强耦合的情况。
- AOP是OOP的延续,opp思想是一种垂直纵向的继承体系,解决了代码开发中的大多数代码重复问题。AOP提出了横向抽取机制,将横切逻辑代码和业务逻辑代码分离,在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。
3.AOP动态代理实现的方式
Spring AOP中动态代理的两种实现方式分别是JDK动态代理和CGLIB动态代理。 JDK动态代理是通过反射机制来实现的, CGLIB动态代理则是通过继承目标类来实现的,它不要求目标类实现接口,代理类继承了目标类并在代理类中重写了目标类的方法。
4.Bean的作用域
-
singleton(单例):这是默认的作用域。在Spring IoC容器的上下文中,每个Bean定义对应的实例只有一个。无论多少次请求该Bean,都会返回该容器中的同一个Bean实例。它确保Bean在Spring上下文中是一个单例,但如果有多个Spring上下文,则每个上下文都会有一个Bean的实例。
-
prototype(原型):每次请求都将创建一个新的Bean实例。当你获取Bean时,Spring IoC容器都会返回一个新的实例,这意味着prototype作用域的Bean不会被重用。
-
request:这是一个Web-specific作用域,用于Web应用。每次HTTP请求都会产生一个新的Bean,该Bean仅在当前HTTP请求内有效。
-
session:这也是一个Web-specific作用域。在Web应用中,为每一个HTTP Session创建一个Bean实例。这意味着该Bean的状态会保持在整个用户会话中。
-
application:这是另一个Web-specific作用域。为每一个ServletContext创建一个Bean实例(通常是Web应用的全局作用域)。这个Bean对所有的HTTP Session是可见的。
-
websocket:在WebSocket生命周期内,为每个WebSocket创建一个Bean实例。
当你定义一个Bean时,你可以指定其作用域。如果没有明确指定,那么Bean的默认作用域是singleton。
五、数据库八股
六、网络八股
1.URL输入到网页展示
-
地址解析:
- 浏览器检查URL是否在本地缓存中有对应的IP地址。
- 如果没有,浏览器会向系统的默认DNS服务器请求解析该URL对应的IP地址。
- DNS服务器响应请求,返回对应的IP地址。
-
建立TCP连接:
- 浏览器与远程服务器通过三次握手建立TCP连接。
-
发送HTTP请求:
- 浏览器发送HTTP请求到服务器。
-
服务器处理请求并返回HTTP响应:
- 服务器处理接收到的请求。
- 服务器返回一个HTTP响应给浏览器。
-
浏览器解析并渲染页面:
- 浏览器首先解析HTML来构建DOM树。
- 浏览器解析CSS样式信息,与DOM树结合,形成渲染树。
- 浏览器布局渲染树(进行布局计算)。
- 浏览器绘制渲染树,展示页面内容。
-
加载嵌套的资源:
- HTML页面中可能包含嵌套的资源,如图片、CSS、JavaScript等。浏览器会对这些资源进行上述相同的过程:解析地址、建立连接、请求资源、获取响应、处理和渲染。
-
执行JavaScript:
- 浏览器解析和执行JavaScript代码。这可能会修改页面内容。
-
关闭连接:
- 如果HTTP/1.1的“Keep-Alive”参数没有被使用,浏览器会关闭TCP连接。若使用了HTTP/2,则连接在多个请求中可能被重用。
2.TCP三次握手和四次挥手
(1)三次握手的步骤如下:
- 客户端向服务器发送一个SYN包,表示请求建立连接。
- 服务器接收到客户端发来的SYN包后,对该包进行确认后结束LISTEN阶段,并返回一段TCP报文,表示确认客户端的报文序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接。
- 客户端接收到服务器发来的TCP报文后,再向服务器发送一段确认报文,表示客户端已经准备好发送数据。
(2)四次挥手的步骤如下:
- 客户端向服务器发送一个FIN包,表示请求断开连接。
- 服务器接收到客户端发来的FIN包后,对该包进行确认后进入CLOSE_WAIT状态。
- 服务器向客户端发送一个ACK包,表示已经准备好断开连接。
- 客户端接收到服务器发来的ACK包后,进入TIME_WAIT状态,并向服务器发送一个FIN包,表示已经准备好断开连接。
(3) 三次握手原因:
-
防止已失效的连接请求报文段突然传到了服务端:考虑一个场景,客户端发送了第一个连接请求,但是由于网络原因这个请求被延迟了,于是TCP又发送了一个连接请求。当网络好转时,两个连接请求几乎同时到达服务端,如果此时是两次握手,服务端就会建立两个连接,但客户端只建立了一个连接,这就造成了服务端资源的浪费。
-
更为可靠地确认双方的接收与发送能力:三次握手可以确保双方都有接收和发送消息的能力。两次握手无法保证这一点。
-
设定序列号:三次握手还可以使得双方都能为TCP连接初始的序列号达成一致
3、TCP 与 UDP 的区别
4、为什么第四次挥手客户端需要等待 2*MSL(报文段最长寿命)时间后才进入 CLOSED 状态?