JACIN Blog

深度的 AI 应用开发者

专注 AI 应用开发、Python 后端与工程实践,持续记录大模型落地、工具构建与真实项目经验。

183篇已发布
主题索引
查看全部分类
全部文章
183
服务器与部署
1 分钟

nginx 配置密码

这份配置记录面向那些 Docker 镜像本身没有账号密码、但又不适合直接开放给公网访问的服务,核心思路是在端口暴露和反向代理两层同时收紧入口。服务侧先在 docker-compose 的 ports 中使用 127.0.0.1:3033:8081 这种绑定方式,让容器端口只接受本机访问,避免外部绕过 Nginx 直接连到后端。随后在服务器上安装 apache2-utils,并通过 htpasswd -c /etc/nginx/.htpasswd admin 创建基础认证所需的账号密码文件。Nginx 配置部分则是在对应的 location / 代理块中加入 auth_basic 和 auth_basic_user_file,前者开启浏览器弹窗式的 Basic Auth,后者指定刚生成的密码文件。完成修改后执行 systemctl reload nginx 重新加载配置,即可让访问该反向代理路径的用户先通过密码验证。它适合临时保护管理后台、自用工具或轻量服务,但前提是请求确实都经过 Nginx,且后端端口不要再以 0.0.0.0 形式暴露。

nginx
部署搭建discourse
服务器与部署
6 分钟

部署搭建discourse

这份记录聚焦在更换 VPS 后重新部署 Discourse 时,如何基于官方 discourse_docker 仓库重新生成环境,而不是直接搬迁旧目录。配置核心集中在 containers/app.yml:只启用 Web 与限流模板,将容器 80 端口绑定到本机 127.0.0.1:37880,便于前面再由 Nginx 做反向代理,同时用 DISCOURSE_HOSTNAME 指定对外域名。数据库与缓存被拆分处理,PostgreSQL 通过外部地址连接,Redis 则放在 Docker 内网中,Discourse 需要通过 docker_args 加入同一个 mynetwork,Redis 未设置密码时可仅依赖内网访问。邮件服务也是部署前提之一,需要提前准备 SMTP 地址、端口、用户名、密码和 STARTTLS,否则 Discourse 的注册、通知等基础功能会受影响。文章特别指出反代 HTTPS 场景下要设置 DISCOURSE_FORCE_HTTPS,让容器内部仍走 HTTP 时也能生成正确的 HTTPS 外链,避免站点链接或资源地址异常。最后通过 after_code hook 安装 PostgreSQL 16 客户端,并强制修正 pg_dump、psql 软链接,用来匹配外部数据库版本,适合已经熟悉 Docker、Nginx 和基础服务拆分部署的站点维护者参考。

谷歌免费GCP配置防火墙
服务器与部署
6 分钟

谷歌免费GCP配置防火墙

这篇记录聚焦 GCP 免费服务器启用后的两类容易被忽略的问题:免费额度相关扣费项检查,以及面向个人小服务场景的防火墙收紧。费用部分强调先确认是否存在 snapshots 快照,若有需到 disks 相关页面处理;同时检查磁盘类型、容量是否符合标准型 30GB 的免费配置,并留意实例区域选择,否则即使实例本身来自免费套餐,也可能因快照、磁盘或区域不匹配产生费用。网络策略上,作者采用较激进的最小暴露思路,入站只放行自己使用的三个 IP,其他访问全部禁止,且明确该机器只是挂载小服务,不承担代理用途。出站控制没有直接在 GCP 防火墙里细分,而是在实例内通过 ipset 与 iptables 建立 CUSTOM_OUT_BLOCK 链,将中国、澳大利亚以及 Cloudflare、Fastly、Akamai 等网段批量导入 out_block_list,并在 OUTPUT 链首匹配后丢弃。脚本执行前需要先借助其他节点代理完成下载,因为封锁列表来源本身可能依赖 CDN;运行后日志显示规则已挂载,并累计屏蔽约 26130 个出站网段。它更适合已经会使用 GCP 免费实例、希望减少误扣费并按个人访问范围加固服务器的用户参考,不应直接当作通用云防火墙模板套用。

gcp
redis-发布与订阅机制-1
数据库与缓存
14 分钟

redis-发布与订阅机制-1

这篇笔记聚焦 Redis Pub/Sub 的发布订阅机制,核心定位是低延迟广播而非队列消费:发布者把消息写入 Channel,Redis 通过 SUBSCRIBE、PSUBSCRIBE 建立的订阅关系,将消息推送给所有当前在线的订阅连接。正文用 live_room:123 这类房间频道说明了订阅、发布、接收、取消订阅以及 PUBSUB 查询命令的基本用法,并给出 FastAPI 多进程房间广播示例:各节点订阅 live_room:*,收到消息后解析频道中的房间号,再调用本地 WebSocket 广播函数。文章特别强调每个订阅者在 Redis 端都是独立 TCP 长连接,发布时 Redis 执行 Fan-out,遍历同一频道下所有活跃连接并各发送一份完整消息,因此它不是“谁抢到谁处理”的负载均衡模型。需要注意的是,Pub/Sub 不持久化消息,也没有 ACK、重试、死信队列或回放能力,订阅连接断开、阻塞或不可读时就可能丢失该连接上的消息。文末将它与 RabbitMQ/Kafka 的队列、Topic 和消费者组机制对比,给出选型边界:在线聊天、游戏广播、房间通知等可容忍丢失的中小规模实时场景适合 Redis Pub/Sub;若要求可靠投递、复杂路由、审计回溯或海量吞吐,则应使用专业 MQ。

AOE网络-介绍
数据结构
11 分钟

AOE网络-介绍

这篇笔记围绕 AOE 网络梳理工程计划中的任务依赖与时间约束建模方法,说明它以有向无环图表示项目:顶点是事件,边是活动,边权是活动持续时间,源点到汇点的最长路径决定最短总工期。内容重点区分事件最早发生时间 ve、最迟发生时间 vl,以及活动的最早开始 E、最迟开始 L、最早完成时间和总时差,并给出正向取最大、逆向取最小的计算公式。拓扑排序部分解释了 Kahn 入度法和 DFS 法如何为依赖任务生成合法顺序,也说明在 AOE 网中正向推算最早时间本质上依赖拓扑序。关键路径求法采用 CPM 的“一次正推 + 一次逆推”:先从源点计算各事件 ve 和总工期,再从汇点反推 vl,最后用 E(i,j)==L(i,j) 判断关键活动。示例通过逐步计算 1 到 6 号事件的 ve、vl,展示活动 d 的最早开始和最迟开始都为 12,从而说明关键活动判定的具体过程。适合学习项目进度控制、DAG 任务编排、构建依赖分析或 Airflow、Prefect、Dagster 等调度框架底层思想的读者,用来建立从图论模型到关键路径计算的基础框架。

散列表-介绍
数据结构
13 分钟

散列表-介绍

散列表是一篇面向数据结构初学者的基础笔记,围绕“如何用哈希函数把关键字直接映射到存储位置”展开,解释它为什么能在平均情况下获得接近 O(1) 的插入、删除和精确查找效率。内容先定义哈希函数、散列表、装填因子等核心概念,再梳理开放定址法、链地址法、线性探测、二次探测、双重散列以及直接定址、除留余数、数字分析、平方取中等构造哈希函数的方法。文章通过与顺序表、链表、二叉搜索树对比,突出散列表适合快速精确查找,但本质上是以空间换时间,并不强调有序遍历或范围查询。示例部分用 H(key)=key%7、表长 11、线性探测插入 8 个关键字,逐步展示冲突处理,并计算成功查找平均长度为 9/8,失败查找平均长度为 6。这里特别提醒失败查找的统计起点由哈希函数值域决定,虽然物理槽位是 0 到 10,但 key%7 只会产生 0 到 6,不能把 7 到 10 也纳入平均。最后补充数据结构哈希与 MD5、SHA、CRC32 等成熟哈希函数的目标差异,并说明它们在数据分片、URL 缩短、文件去重、跨平台实现和安全需求中的使用边界。

数据结构
6 分钟

平衡二叉树AVL

这篇笔记面向正在学习数据结构和索引原理的读者,梳理 AVL 树作为自平衡二叉搜索树的基本定义、操作流程与应用价值。正文先用“任意结点左右子树高度差不超过 1”界定平衡条件,并引入高度和平衡因子 BF,说明 AVL 通过保持树高接近对数级来让查找、插入、删除稳定在 O(log n)。在基本操作部分,查找沿用普通 BST 的比较路径,不需要旋转;插入则先按 BST 规则放置新节点,再自底向上更新高度和 BF,一旦出现 |BF|>1 就在局部执行单旋或双旋。文章重点拆分 LL、RR、LR、RL 四种失衡形态:左左和右右分别用一次右旋或左旋修复,左右和右左则需要先处理子节点方向再旋转失衡节点。删除部分强调先按 BST 的叶子、单孩子、双孩子规则完成结构删除,再回溯检查平衡,且一次删除可能在多层触发旋转。最后,笔记把 AVL 的高度控制思想延伸到 B 树、B+ 树和 MySQL InnoDB、PostgreSQL、SQLite 等索引场景,帮助读者理解平衡树为什么适合动态有序且频繁增删的数据维护。

数据结构
4 分钟

哈夫曼树与哈夫曼编码

这篇笔记聚焦哈夫曼树与哈夫曼编码在无损压缩中的基本原理:根据符号出现次数或概率分配变长二进制码,让高频符号更短、低频符号更长,从而降低加权平均码长。正文把哈夫曼编码定位为前缀码中的平均码长最优方案,并用带权路径长度 WPL 公式说明其目标是寻找一棵使权值与码长乘积总和最小的满二叉树。构造部分给出典型流程:统计权值,将每个符号作为单节点树放入小根堆,反复合并权值最小的两棵树,直到得到唯一根树,再按左 0 右 1 的路径生成码字。示例使用 a 到 f 的频率演示树形结构、各字符码字以及 face 被拼接成比特串的过程,同时提醒左右孩子次序不同会导致具体码字不同,但总加权码长保持一致。文章还补充了哈夫曼树的结构性质与实现代价,包括 n 个叶子对应 2n-1 个节点、小根堆构造复杂度为 O(nlogn)、解码时从根按 0/1 走到叶子再回根继续。应用场景覆盖 ZIP、GZIP、PNG、MP3、JPEG、HTTP/2 HPACK、HTTP/3 QPACK、嵌入式存储和搜索索引等,适合数据结构学习者、后端与网络开发者理解压缩算法中“最优前缀码”的基础机制。

数据结构
1 分钟

关于“数据结构”类别

“数据结构”类别的核心作用是为站内相关内容提供清晰的归档边界,而不是展开某一种具体结构或算法实现。该说明需要帮助作者判断:一篇文章是否真正以数据组织方式、结构特性、使用场景或相关规则为中心,而不是仅在其他主题中顺带提及。它还强调分类应与既有类别保持区分,避免因为概念相邻、应用场景重叠或文章内容混杂而造成归类混乱。适合纳入该类别的内容,应围绕该分类存在的理由、承载的话题范围、与其他分类的差异以及后续维护规则展开。对于站点维护者而言,这类说明的价值在于统一写作和归档口径,减少重复分类、过细拆分或不必要的新建类别。读者也能据此快速理解该栏目关注的知识范围,并判断某篇技术笔记是否应被放入“数据结构”这一分类下。

Python 开发
10 分钟

Catch-All 路由

Catch-All 路由指用 FastAPI 的 `/{full_path:path}` 这类 `path` 类型路径参数匹配任意层级 URL 的兜底路由,常见于前端 SPA 路由回退、后台管理插件或静态资源兜底处理。文章先区分了默认 `str` 参数只匹配单段路径,而 `path` 会连同斜杠一起接收多段路径,因此 `/api/does-not-exist` 这类未命中精确接口的请求不会直接 404,而可能进入 fallback 处理函数。重点风险在路由注册顺序:FastAPI 按注册先后匹配,若 SQLAdmin 等插件提前注册了 catch-all,后续业务 router 可能被截获,导致本应进入 `/api/v1/**` 的请求落到兜底逻辑。文章还把这种顺序问题和浏览器 CORS 报错关联起来,说明 OPTIONS 预检请求在目标接口未注册、被覆盖或 catch-all 不支持 OPTIONS 时,可能得到 404/405 且缺少跨域响应头,最终表现为跨域失败。处理方式包括先注册所有业务接口,再注册 SQLAdmin 或其他兜底路由,并让 catch-all 使用 `api_route(..., methods=["GET", "POST", "OPTIONS"])` 显式接住 OPTIONS。若不希望业务逻辑处理预检,也可以单独添加 `@router.options("/{full_path:path}")` 返回空响应,保证预检请求有合法 handler。适合正在排查 FastAPI 路由“莫名其妙被命中”、后台插件干扰 API、或未命中接口却显示 CORS error 的后端开发者参考。

api-开发
Python 开发
13 分钟

接口等幂处理

接口等幂讨论的是 Web/API 在重复请求、自动重试或并发调用下如何避免重复修改系统状态,核心判断标准是同一操作执行多次后业务结果一致且不产生额外副作用。文章先用 HTTP 方法语义区分 GET、DELETE、PUT、POST 的等幂差异:查询、删除和“更新为固定状态”通常可视为等幂,而创建类 POST 容易产生重复资源,因此在支付、下单、扣积分等场景尤其需要防重。针对 PUT,文中强调理论语义和工程实现并不总是一致,即使请求体相同,也可能因 update_time、变更日志、审计表、MQ 消息、缓存刷新等动作破坏等幂。推荐做法是在更新前比较新旧数据,未变化时跳过写入和时间戳更新,或让日志、消息、缓存等副作用只在内容真正变更时触发。对于短时间重复提交或并发请求,文章给出基于 Redis 的幂等锁与状态缓存思路:用 idempotent key 标识一次操作,已处理则直接返回,未处理才执行业务逻辑。最后比较前端生成结构化 UUID 与后端令牌模式,认为完全随机 UUID 难以识别同一业务操作,而后端生成并下发幂等 key 更便于控制生命周期、缓存执行结果和保障支付、提交订单等关键接口的安全性。

api-开发
Python 开发
13 分钟

单例模式-介绍

这篇笔记以 Python 中的单例模式为对象,说明“一个类在程序运行期间只创建一个全局共享实例”的适用场景,包括配置管理、日志器、数据库连接池、线程池、缓存和 RedisClient 等需要复用状态或资源的对象。内容先对比懒汉式与饿汉式:前者在第一次使用时通过 `__new__` 创建实例,节省资源但在多线程并发判断 `_instance is None` 时可能产生多个对象;后者在模块加载时提前创建,写法简单且天然线程安全,但可能带来不必要的启动资源占用。针对懒汉式的并发问题,文章给出基于 `threading.Lock` 的双重检查锁实现,用外层判断减少加锁开销,用锁内二次判断避免多个线程排队后重复初始化。随后还展示了装饰器封装单例的函数式写法,并提醒其存在首次参数不可重设、默认线程不安全、只在进程内生效等边界。更 Pythonic 的方案是直接利用模块作为单例:`import` 会通过 `sys.modules` 缓存模块对象,使配置、全局状态或缓存变量在多个引用方之间共享。最后补充了 `threading.Lock` 的 acquire、release 和 `with lock` 用法,以及死锁、加锁粒度过大、共享状态污染和测试耦合等风险,适合正在学习 Python 设计模式和多线程资源保护的开发者。

设计模式
文章归档
183