<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>蜂巢 on 豆子技术站</title><link>https://blog.91demo.top/categories/%E8%9C%82%E5%B7%A2/</link><description>Recent content in 蜂巢 on 豆子技术站</description><generator>Hugo -- 0.155.1</generator><language>zh-cn</language><lastBuildDate>Wed, 11 Mar 2026 08:00:00 +0800</lastBuildDate><atom:link href="https://blog.91demo.top/categories/%E8%9C%82%E5%B7%A2/index.xml" rel="self" type="application/rss+xml"/><item><title>解决 Cloudreve 无法通过 HTTPS 连接自签名证书 MinIO</title><link>https://blog.91demo.top/cloudreve-s3/</link><pubDate>Wed, 11 Mar 2026 08:00:00 +0800</pubDate><guid>https://blog.91demo.top/cloudreve-s3/</guid><description>&lt;p&gt;这是一次真实事件案例改编。用户A在部署 Cloudreve 存储策略时，集成了用户内网环境下的 MinIO S3 服务。但是当用户上传资料时发现Cloudreve无法工作，无法将资源上传到MinIO中。&lt;/p&gt;
&lt;p&gt;我也是第一次接触Cloudreve，经过搜索发现&lt;a href="https://github.com/cloudreve/cloudreve"&gt;Cloudreve&lt;/a&gt;是一个自托管文件管理与共享系统，支持多存储提供商。那么现在的问题就变成了Cloudreve如何配置使用了自定义证书的S3服务Minio。&lt;/p&gt;
&lt;p&gt;由于用户的Minio服务使用了DDNS映射到公网，我首先想到的是，使用可以通过mc访问到用户的Minio服务。经过初步排查：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;确认内网穿透链路正常。&lt;/li&gt;
&lt;li&gt;使用 MinIO 客户端工具 mc 测试，发现必须带上 &amp;ndash;insecure 参数才能正常访问，明确了核心矛盾在于 SSL 证书验证失败。&lt;/li&gt;
&lt;li&gt;尝试将证书放置在自定义目录并使用 mc &amp;ndash;config-dir 指定，可以连接成功，但 Cloudreve 还是无法正常工作，无法直接复用该配置，且即便在同目录下放置证书也未能生效。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;经过搜索和分析，知道需要将自定义 CA 证书导入系统信任库，才能使 Cloudreve（Go 环境）能够原生识别。那么现在的问题就变成了如何在 CentOS 系统中安装并激活自定义证书？&lt;/p&gt;
&lt;p&gt;经过一番折腾摸索，终于搞定，这里记录一下历程。&lt;/p&gt;
&lt;h3 id="1检查证书是否pem格式"&gt;1，检查证书是否PEM格式？&lt;/h3&gt;
&lt;p&gt;Go 程序通常识别PEM格式的证书。所以需要先检查自定义证书是否是PEM格式？可以使用以下命令检查证书文件（如public.crt）：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cat public.crt
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;如果开头是 &amp;mdash;&amp;ndash;BEGIN CERTIFICATE&amp;mdash;&amp;ndash;，则是 PEM 格式。&lt;/li&gt;
&lt;li&gt;如果是一堆乱码，则是 DER 格式。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果是DER格式，需要转换为PEM格式，转换的命令如下：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;openssl x509 -inform der -in public.crt -out public.pem
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="2放置证书"&gt;2，放置证书&lt;/h3&gt;
&lt;p&gt;在CentOS Linux中，需要将证书文件放在/etc/pki/ca-trust/source/anchors/目录下，并且后缀必须为.crt。如果文件名为public.pem，需要改为public.crt。这是血的经验和教训。&lt;/p&gt;
&lt;p&gt;首先确认文件是否放置正确？&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ls /etc/pki/ca-trust/source/anchors/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出中应包含我们的证书，如果没有，需要重新放置。&lt;/p&gt;
&lt;h3 id="3激活证书"&gt;3，激活证书&lt;/h3&gt;
&lt;p&gt;当放置好证书后，必须进行激活，否则系统不认。可以执行以下命令激活，让系统重新扫描目录并生成全局信任束，依次执行：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;update-ca-trust extract
update-ca-trust
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="4验证证书是否激活成功"&gt;4，验证证书是否激活成功&lt;/h3&gt;
&lt;p&gt;我们需要确认是否激活成功？确认的方法就是检查系统全局证书合集&lt;code&gt;ca-bundle.crt&lt;/code&gt;是否已包含你的证书信息：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;tail -n 20 /etc/pki/tls/certs/ca-bundle.crt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以查看最近的20行中是否包含自己的证书，如果还没有成功，则执行命令追加：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cat public.crt | sudo tee -a /etc/pki/tls/certs/ca-bundle.crt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;最后，使用 grep 再次搜索证书中关键的信息&lt;/p&gt;</description></item><item><title>记一次 Windows DLL 动态链接库并供第三方多语言调用</title><link>https://blog.91demo.top/go-dll/</link><pubDate>Thu, 29 Jan 2026 18:00:00 +0800</pubDate><guid>https://blog.91demo.top/go-dll/</guid><description>&lt;p&gt;我使用go写了一个http转WebSocket服务，这是一个命令行程序，双击可以直接运行，今天，有个新的需求，需要将这个命令行程序转为dll，然后供第三方使用。&lt;/p&gt;
&lt;h2 id="1改造程序"&gt;1，改造程序&lt;/h2&gt;
&lt;p&gt;这个命令行程序很小，核心逻辑就是启动了一个HTTP服务，然后接收连接，然后通过WebSocket将内容转发出去。所以，在调整为dll时，仅仅需要导出两个函数即可。&lt;/p&gt;
&lt;p&gt;1，StartServer，启动http服务，监听端口地址。因为不能阻塞，所以需要在监听服务时使用go方法启动一个携程。&lt;br&gt;
2，StopServer，关闭http服务，需要提供给第三方，当它关闭时，需要调用它，释放监听地址等资源。&lt;/p&gt;
&lt;p&gt;下面开始改造代码：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;package main
import &amp;#34;C&amp;#34; // 必须导入 C 包
import (
// ... 原有导入 ...
)
// 保持原有的全局变量和函数逻辑 (例如transDataGet, connWs 等)
// 需要注意下面的//export这中间不能有空格，否则无法导出头文件
//export StartServer
func StartServer() {
// 将原本 main 函数里的逻辑放在这里
gin.SetMode(gin.ReleaseMode)
r := gin.New()
// ... 注册路由 ...
r.Run(&amp;#34;127.0.0.1:9988&amp;#34;)
}
// 必须保留一个空的 main 函数
func main() {}
// 其它参考StartServer，如果需要导出，一定要添加//export
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="2编译命令"&gt;2，编译命令&lt;/h2&gt;
&lt;p&gt;我是在windows环境，需要安装cgo环境，我安装了Mingw-w64支持C编译环境，执行以下命令：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;go build -buildmode=c-shared -o trans.dll main.go
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;执行后会生成trans.h和trans.dll两个文件。&lt;/p&gt;
&lt;h2 id="3验证dll"&gt;3，验证dll&lt;/h2&gt;
&lt;p&gt;除了一些可以查看dll的工具外，还可以使用第三方语言进行测试。例如我这里使用Python。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;import ctypes
import time
lib = ctypes.CDLL(&amp;#34;./trans.dll&amp;#34;)
lib.StartServer()
print(&amp;#34;服务已在后台启动...&amp;#34;)
time.sleep(10) # 模拟第三方程序运行
lib.StopServer()
print(&amp;#34;服务已关闭&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="4再次优化代码"&gt;4，再次优化代码&lt;/h2&gt;
&lt;p&gt;当实际测试的时候，我发现它会阻塞调用者，为了解决这个问题，我们需要再次改造程序。将启动服务改为非阻塞模式。将Gin的启动逻辑放入协程，并利用http.Server提供的Shutdown方法来实现优雅退出。&lt;/p&gt;</description></item><item><title>记一次因直接使用unwrap导致Rust上传工具崩溃问题</title><link>https://blog.91demo.top/upart-rs/</link><pubDate>Mon, 01 Jan 2024 18:00:00 +0800</pubDate><guid>https://blog.91demo.top/upart-rs/</guid><description>&lt;p&gt;最近，更新了上传文章工具 rust 版本，这个版本使用的库为 nwg，即 native-windows-gui 库，该库支持老的 Windows GUI。&lt;/p&gt;
&lt;p&gt;昨天，当我准备在另一台电脑上录制视频时，发现运行不起来，GUI 界面闪现一下，就退出了。以为版本太老，我先升级了 Rust 版本到最新版本（😭，不应该的原因，固定版本反而更可靠稳定一些）。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;rustup update
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;升级完成后，cargo clean ,再 cargo run 重新运行。&lt;/p&gt;
&lt;p&gt;郁闷，还是报错。&lt;/p&gt;
&lt;p&gt;将 main.rs 文件中该行注释，&lt;code&gt;#![windows_subsystem = &amp;quot;windows&amp;quot;]&lt;/code&gt;，可以在命令行中查看报错信息，提示是找不到文件。我想了下，只有图片是文件，然后我将图片的路径修改，发现修改之后，编辑器还提示错误，找不到文件。重新改回去后，编辑器提示错误消失。说明不是这里的错误。&lt;/p&gt;
&lt;p&gt;我真的晕了。从网上查找问题原因，没有找到此类问题的解决方法，郁闷。在查找时，发现了另一个 Windows 官方支持的 rust 库，就叫 windows，打算有时间了用这个库重写一下。&lt;/p&gt;
&lt;p&gt;今天，我又查看了下另一台电脑上的这个项目，发现可以正常运行，将 rust 升级到最新版本后，还是可以运行。在看到配置文件后，我才恍然大悟，原来是查找的是这个配置文件。我赶紧扒拉代码确认，发现确实是这个问题。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;let conf = Ini::load_from_file(&amp;#34;conf.ini&amp;#34;).unwrap();
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上面是使用文件的地方，找不到文件 panic。我这里优化了一下，让错误提醒的更明显一些。这样我就瞬间能知道问题原因了。&lt;/p&gt;
&lt;p&gt;优化后的代码如下：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;let conf = Ini::load_from_file(&amp;#34;conf.ini&amp;#34;).expect(&amp;#34;please config conf.ini file&amp;#34;);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;有些问题其实很简单，但由于时间长了，还是会一头懵，如果报错信息提示完善一点，会很快定位到问题。&lt;/p&gt;</description></item><item><title>构建高可靠 APK 上传工具，彻底终结网络超时与手误引发的生产事故</title><link>https://blog.91demo.top/apkup-rs/</link><pubDate>Mon, 02 Jan 2023 12:40:16 +0800</pubDate><guid>https://blog.91demo.top/apkup-rs/</guid><description>&lt;p&gt;记录一个未公开的 Rust 实战小工具，分享如何通过技术手段解决网络超时与手误操作带来的生产事故。&lt;/p&gt;
&lt;p&gt;在自学 Rust 的那段日子里，我除了开发开源项目，还为日常工作量身定制了一些不公开的内部工具。其中最令我印象深刻的，是一个看似简单的 &lt;strong&gt;APK 文件上传小工具&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;虽然它只是一个 Rust + &lt;code&gt;native-windows-gui&lt;/code&gt; (NWG) 编写的单一界面应用，但它解决的两个核心生产问题，至今仍对我有着深远的影响。&lt;/p&gt;
&lt;h2 id="工具画像极简与高效"&gt;工具画像：极简与高效&lt;/h2&gt;
&lt;p&gt;这个工具的界面极其克制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;交互区&lt;/strong&gt;：一个醒目的文件拖拽控件，文案提示“请把 APK 文件拖拽到这里”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;表单区&lt;/strong&gt;：版本号、Version Code、更新描述、是否强制升级（开关）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行区&lt;/strong&gt;：一个大大的“上传”按钮。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它的核心逻辑非常纯粹：校验必填项 -&amp;gt; 调用内部上传 API -&amp;gt; 上传文件 -&amp;gt; 返回成功或失败的对话框。&lt;/p&gt;
&lt;h2 id="故事一消失的-60-秒与隐藏的超时限制"&gt;故事一：消失的 60 秒与隐藏的超时限制&lt;/h2&gt;
&lt;p&gt;工具上线初期，一切运行平稳。由于办公室带宽充足，APK 的上传时间通常维持在 40 秒左右。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;事故发生&lt;/strong&gt;：&lt;br&gt;
APK 在集成广告后，经常会发生失败的情况，客户端弹出“超时”的报错。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查过程&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;我首先检查了后端服务，发现后端接口完全没有超时限制，服务器日志显示连接是被客户端主动断开的。&lt;/li&gt;
&lt;li&gt;多次重复上传文件使用有线和无线对比，经排查是文件变大，由于公司无线网络波动，上传速度时好时坏，就会偶尔出现失败的情况。对比排查，发现失败的情况上传时间都超过了 60 秒。&lt;/li&gt;
&lt;li&gt;回到 Rust 代码中，我发现当时为了追求简洁，直接使用了 HTTP 客户端的默认配置。而这个默认配置的 &lt;strong&gt;Timeout 为 60 秒&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：&lt;br&gt;
在那个瞬间我意识到，工具不能只考虑“理想状态”。我手动将本地超时时间放宽至 5 分钟，并增加了状态提示。上传时不会再出现超时的情况了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;教训&lt;/strong&gt;：&lt;strong&gt;永远不要依赖默认的超时设置&lt;/strong&gt;，尤其是在处理文件上传这类长耗时操作时，本地的容错范围必须根据业务场景精细化配置。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="故事二包名校验把人为失误挡在门外"&gt;故事二：包名校验——把人为失误挡在门外&lt;/h2&gt;
&lt;p&gt;这是这个工具最有价值的一次功能进化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;事故发生&lt;/strong&gt;：&lt;br&gt;
有一次，同事不小心将应用 A 的打包产物当作应用 B 上传到了官网，导致用户下载后发现软件张冠李戴。这属于严重的生产事故。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查过程&lt;/strong&gt;：&lt;br&gt;
这个客户端软件调用内部API后，会自动将上传的文件名修改为固定的软件下载文件名，显示在网站上。经测试，如果将应用A的打包产物使用应用B上传，在官网就会出现相同的情况。解决这个方法也非常简单，就是上传的时候小心细致一点，应用A使用A客户端软件，应用B使用B客户端软件。但是人总会犯错，尤其是在面对多个长相相似的 &lt;code&gt;.apk&lt;/code&gt; 文件时。通过肉眼观察文件名来区分应用，是极其不可靠的。&lt;/p&gt;</description></item></channel></rss>