站点图标 梦呓

抖机灵的面试题

以0为下标

问题:为什么Java,C等大多数编程语言下标都是以0开头的。

因为是基于计算机内存寻址特性来的,因为是a=b+(i*c),所以用零开头,可以少一次计算,如果从1开始就是a=b+(i-1)*c,会多一次计算。

网关

在微服务中,网关都有什么职责,鉴权的话,网关鉴过权了拿了token请求后面服务,那后面服务还需要去验证token有效性么,去网关验证么?能说出非对称加密就算过关。

遵循RESTful 规范,还是只用POST

问题:接口只用post还是post、get、update、delete都用,为什么,只用一种不行么。

只用 POST 可以吗?(RPC 风格)

可以。 这种所有接口都用 POST(或者 GET)的做法,本质上属于 RPC(远程过程调用)风格。 在这种风格下,URL 只代表一个“动作的执行入口”,比如:

优点:前端和后端都不需要思考该用什么动词,无脑 POST 传 JSON 即可;传参方式统一,不需要管是放在 URL 路径、Query 参数还是 Body 里。许多早期的系统、内部微服务调用,甚至像 GraphQL 这样的现代查询语言,底层都是全靠 POST 支撑的。


为什么要区分 GET、POST、PUT、DELETE?(RESTful 风格)

现代 Web 开发更推崇 RESTful 风格。它把网络上的所有事物都看作“资源”,而 HTTP 方法就是对这些资源操作的“动词”。区分使用它们有以下几个决定性的工程原因:

语义化与代码的自解释性(契约精神)

使用不同的 HTTP 方法,能让接口像自然语言一样清晰易懂。 在开发停车管理系统时,针对“停车记录(parking-records)”这个资源,标准的做法是:

前端只要看到 URL 和 HTTP 方法,不看长篇大论的文档就能猜到接口是干嘛的。而如果全用 POST,URL 就会千奇百怪,起名全靠开发者的英语词汇量。

幂等性与分布式重试机制(极度关键)

幂等性(Idempotency)是指:一个操作执行一次和执行一百次,对系统状态产生的影响是相同的。 在复杂的网络环境或云原生架构中,网络超时是常态,调用失败了系统往往会自动重试。

缓存友好性(大幅提升性能)

浏览器、CDN 节点以及像 Nginx 这样的反向代理服务器,天生对 HTTP 方法有不同的缓存策略。

契合云原生基础设施与监控

在微服务、网关(如 K8s Ingress)以及现代 AIOps 运维监控体系中,底层基础设施是高度依赖 HTTP 标准的。

HTTP有几种请求类型

GET(查)

幂等

POST(增)

非幂等

PUT(改 – 全量替换)

PUT 的语义是替换(Replace)目标资源。

DELETE(删)

幂等

PATCH(改 – 局部打补丁,现代 RESTful 极力推荐的第 5 种)

PATCH 的语义是给资源打补丁(Modify/Patch)

OPTIONS(查 – 并非用来操作业务资源,而是供浏览器和跨域网关进行能力探测,属于基础设施层面的必要补充)

OPTIONS 是一种极其特殊且极其重要的 HTTP 请求方法。它的核心作用是:“投石问路”(查询服务器的通信选项和能力)。

对于前端和后端开发者来说,你遇到 OPTIONS 请求 99% 都是因为一个场景:CORS(跨域资源共享)的预检请求(Preflight Request)

为什么会有 OPTIONS 预检请求?

当你的前端应用(比如运行在 http://localhost:5173)试图向不同域名的后端接口(比如 http://api.yourdomain.com)发送一个复杂请求时,浏览器出于安全保护机制,不会直接把你的实际请求发出去

所谓“复杂请求”,通常包括:

这时候,浏览器的行为是:

  1. 暗中拦截:浏览器悄悄拦截你的真实请求。
  2. 发送 OPTIONS 投石问路:浏览器自动向相同的后端 URL 发送一个 OPTIONS 请求。这个请求不带 body 数据,而是带上几个特殊的 Header(比如问服务器:Access-Control-Request-Method: PUT,我能用 PUT 吗?)。
  3. 服务器响应:如果你的后端配置了允许跨域(CORS),就会在响应头里告诉浏览器(比如 Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS,可以,你发吧)。
  4. 发送真实请求:浏览器收到允许的许可后,才会把那个真正的 PUTPATCH 请求发出去。

这就是为什么你在浏览器的 Network(网络)面板里,明明代码里只写了一个请求,却经常会看到两个挨着的网络请求记录:一个是 OPTIONS,紧接着才是你的实际请求。

总结OPTIONS 不是用来传业务数据的,它是浏览器和服务器之间为了跨域安全进行的一次握手谈判。

发送 OPTIONS 的好处是什么?(既然慢,为什么还要发?)

前面我们提到 OPTIONS 预检请求会让第一次跨域变慢,很多人觉得这完全是“脱裤子放屁”。但实际上,它是为了保护后端服务器不被恶意攻击

请想象这样一个危险的场景:

  1. 你登录了某个银行网站 bank.com,浏览器里存了银行的登录 Cookie。
  2. 你不小心点开了一个黑客的恶意网站 evil.com
  3. 黑客的网站在后台偷偷用 JavaScript 向 bank.com/api/transfer 发送了一个 DELETE 请求(或者一个带特殊指令的 PUT 请求),试图破坏你的账户。

如果没有 OPTIONS 预检机制: 浏览器会傻乎乎地把这个 DELETE 请求发给银行服务器。虽然银行服务器处理完后,浏览器根据同源策略会拦截响应,不让黑客网站看到结果,但服务器上的执行已经完成了,数据已经被破坏了!

有了 OPTIONS 预检机制(好处体现):

  1. 浏览器发现 evil.com 试图向 bank.com 发送高风险的 DELETE 请求。
  2. 浏览器主动拦截,先发一个没有危害的 OPTIONS 去问银行服务器:“evil.com 想发 DELETE,你允许吗?”
  3. 银行服务器一看,我只允许 bank.com 的请求,于是拒绝了 OPTIONS
  4. 浏览器收到拒绝后,直接把那个真实的 DELETE 请求掐死在摇篮里,根本不往外发。

总结 OPTIONS 的好处:它赋予了浏览器在“危险动作发生之前”就踩刹车的能力,保护了那些没有防范意识的老旧服务器免受跨域的跨站请求伪造(CSRF)类攻击,确保不会产生意外的数据修改(副作用)

先发送options,再发送真实请求,请求变慢的问题。

直观上来说,它确实会让请求变慢。

因为在发送真实的业务请求之前,浏览器必须先和服务器进行一次完整的 HTTP 通信(也就是 OPTIONS 请求)。这在网络术语中叫做增加了一个 RTT(Round Trip Time,往返时延)

假设你的前端和后端服务器之间的网络单程延迟是 50 毫秒:

  1. OPTIONS 请求发过去(50ms)+ 服务器同意并返回(50ms)= 耗时 100ms。
  2. 真实的 POSTPUT 请求再发过去(50ms)+ 服务器处理并返回数据(比如 50ms)= 耗时 100ms。

原本只需要 100ms 就能完成的操作,因为跨域预检,总耗时变成了 200ms。如果网络环境较差,或者跨国访问,这个延迟就会非常明显,用户会感觉到明显的卡顿。

那么,业界是如何解决这个“变慢”的问题的?

为了防止每一个接口调用都遭受这种性能惩罚,浏览器和现代架构主要有以下几种应对策略:

浏览器的终极武器:预检缓存(Max-Age)

浏览器非常聪明,它知道每次都问服务器“我能不能发 PUT”很蠢。所以,HTTP 协议提供了一个专门的响应头:Access-Control-Max-Age

避免触发预检:使用“简单请求”

如果你的请求完全符合“简单请求”的严苛条件,浏览器就不会发 OPTIONS,而是直接发真实请求。 但这在现代开发中极难做到,因为简单请求要求:

既然现在企业级开发(比如你的停车管理系统)几乎全是用 JSON 交互,并且必然会带 Token 进行权限校验,所以触发预检几乎是不可避免的

终极架构方案:彻底消灭跨域(同源部署)

既然跨域(CORS)会带来这些麻烦,最一劳永逸的方法就是让前端和后端在同一个域下,彻底不跨域

在现代云原生架构中,我们极少让前端直接去请求后端的真实域名,而是通过网关或反向代理来统一入口。比如在 Kubernetes 环境下,你可以利用 Ingress 或 K3s 自带的 Traefik 来做路由转发:

这样做的结果是:对于浏览器而言,前端代码和后端接口都在同一个域名下。浏览器认为这是同源请求,安全得很,于是彻底关闭了 CORS 机制,再也不会发送任何 OPTIONS 预检请求,性能达到最优。

总结来说:OPTIONS 确实会造成首次请求的延迟,但在良好的服务端缓存配置(Max-Age)或优秀的网关架构(同源反向代理)下,这种性能损耗可以被完美抹平。

HEAD

HEAD 请求本质上就是“没有响应体的 GET 请求”。

当你向服务器发送 GET 请求时,服务器会返回响应头(Headers)和响应体(Body,比如真实的 JSON 数据或图片文件)。 而当你发送 HEAD 请求时,服务器只返回响应头(Headers),绝对不会返回 Body。

它的核心使用场景是:在不消耗大量带宽的情况下,获取资源的元信息(Meta-information)。

TRACE

作用:它是一个“回显(Echo)”服务。你发给服务器什么请求,服务器就把你发的东西原封不动地在响应体里返回给你。

场景:主要用于网络诊断。你的请求在到达最终服务器之前,可能会经过很多代理服务器(Proxy、网关)。使用 TRACE,你可以看到最终服务器收到的请求是什么样子的,从而排查到底是哪个中间节点篡改了你的 Header。

注意:在生产环境中,通常会禁用 TRACE 请求,因为它可能会引发一种叫 XST(跨站追踪)的安全漏洞。

CONNECT

作用:要求代理服务器将连接转换为透明的 TCP/IP 隧道。

场景:它几乎专门用于代理服务器(Proxy)环境下的 HTTPS 穿透。当你在公司内网通过代理上网时,你的浏览器会向代理服务器发送 CONNECT 请求,代理服务器帮你和外部目标网站建立一条盲目的数据通道,然后你的浏览器才开始在这条通道里进行加密的 HTTPS 握手。

架构师

Q:你觉得怎么才算一个合格的架构师,或者成长之路是什么

A:架构师也许要很多技能,但自己觉得架构师一个重要的点就是呆过适当多的公司,并且在公司里呆过一段时间,熟悉公司的流程,然后公司这段时间也有不断新来的人,这样才能在尽可能短的时间内与别人充分的交流,去陈纳新。当然他自己也要有爱学精神,不断钻研新的技术与趋势。

service、serviceImpl

问题:java项目为什么要拆分为service,serviceImpl ,这都是一个人写的,不就重复了么,写一个就行了。

Spring 框架的核心机制:AOP 代理

这是在 Spring/SpringBoot 项目中最实际的一个原因。Spring 的核心特性之一是 AOP(面向切面编程),你平时用的 @Transactional(事务控制)、@Cacheable(缓存)、以及自定义的权限校验注解,底层都是通过 AOP 实现的。

面向接口编程与依赖倒置原则(SOLID)

在软件工程中,有一条非常重要的原则:依赖于抽象(接口),而不是依赖于具体实现

定义规范与契约(团队协作)

虽然现在是你一个人写,但在企业级开发中,往往是前后端分离或多人协作:

分布式系统与微服务调用(RPC)

如果你以后接触到微服务架构(比如 Spring Cloud Feign 或 Dubbo),接口的作用就极其关键了。

RPC抄过来的,接口打包成jar依赖给别人。自己负责维护Impl实现,大厂都是这么干的。不过单体或者SpringCloud就没必要了,你分析的都很对。可能是没有大厂的命,都得了大厂的病。

其实接口层没有也没事,但接口层也有他的好处,就是多人维护一个项目的时候,接口层相当于一个简单的功能说明,也就是所对应的实现类对外开放了多少口子。
像一些大的项目,实现类代码都好几千行了,里面也包含了大量不对外的私有方法,当你要找一个功能或者了解一个实现类有什么功能的时候就会很乱。但接口层只有对外接口的声明,就显得很清晰

为什么内存比磁盘块,为什么SSD比机械硬盘快

内存比磁盘更靠近CPU,所以读写数据会更快,比内存更快的是CPU自带的三级缓存。SSD是直接电的速度在运行,而机械硬盘读取数据要靠磁盘转,所以SSD比机械硬盘快。

为什么k8s里最小调度单位是pod,而不是容器

pod里面不会仅有一个容器的,有时候是容器组,比方说用了istio会多两个容器,用了knative会多一个容器,所以pod是容器组的概念,他们共用网络,存储等,所以最小调度单位不是容器而是pod。

自述

自我描述和突出点。面试别人自己问,还不如让对方主动说,让他自己主动说做了什么,以及项目中的闪光点,这不仅考验面试者的表达能力,也考验面试者的总结能力。如果自己想不到,那么主动问也没能问出啥,自己可以适当的提问。

退出移动版