rss
type: channel
link: https://www.sharklj.com/
description: Just So So ...
items: 宝塔+certd实现SSL证书自动申请踩坑
items: 工具聚合
items: BitCask论文翻译/笔记
items: 从零实现KV存储-2
items: 从零实现KV存储-1
items: Joern安装
items: 六六
items: Handsome8.3主题美化记录
items: 事记
items: Handsome主题美化记录

宝塔+certd实现SSL证书自动申请踩坑
type: item
date: 2026-01-19T22:38:00+08:00
description: 写在前面网站是在宝塔里维护的，网站的ssl证书每个季度就会过期还得手动申请太麻烦了，就想着搞个自动申请，本以为十几分就搞定结果搞了两个多小时。以下是踩坑记录，其余能在官方文档中找到的操作不再赘述。certd在宝塔里的安装过程省略，着重说一下在certd中配置宝塔-网站证书部署，也就是下面的的第二个阶段这个阶段的配置里需要填一个宝塔面板的地址，正常理解的就是应该把宝塔页面的浏览器地址填上去，结果一直报错，卡在这里很久查了半天，找到这句话：在绝大多数 Linux 环境（CentOS/Ubuntu）的 Docker 中，容器访问宿主机都使用这个固定 IP：http://172.17.0.1:8888所以我就试着把http://172.17.0.1:8888这个地址当作certd访问宝塔的地址填上去测了一下，结果真的通了然后按照测试按钮下面的结果把172.20.0.2加入宝塔api的白名单就搞定了

工具聚合
type: item
date: 2024-02-19T22:27:00+08:00
description: 一些好用的AI工具[tabs] [tab name="AI画图" active="true"] [link] [item name="Midjourney" link="https://www.midjourney.com/home" desc="AI画图Top1" /] [/link] [/tab][tab name="Prompt"] [link] [item name="Ask Prompts" link="https://www.askprompts.com/" desc="ChatGPT快捷指令" /] [/link] [/tab] [tab name="AI导航"] [link] [item name="AIVESA" link="https://www.naviai.cn/" desc="AI工具聚合网站" /] [/link] [/tab][tab name="图片"] [link] [item name="免扣图片网" link="https://miankoutupian.com/" desc="AI免费抠图网站" /] [/link] [/tab] [tab name="Github"] [link] [item name="一些实用工具集合" link="https://www.kdocs.cn/l/cdH3liGF360p" desc=" " /] [/link] [/tab][tab name="自媒体"] [link] [item name="小红书文案AI生成" link="https://ai.xiaohongshu.live/zh?f=wz.aidaohang.com.cn" desc=" " /] [/link] [/tab][/tabs]

BitCask论文翻译/笔记
type: item
date: 2024-01-11T22:18:00+08:00
description: 翻译论文来源：bitcask-intro.pdf (riak.com)背景介绍Bitcask的起源与Riak分布式数据库的历史紧密相连。在Riak的K/V集群中，每个节点都使用了可插拔的本地存储；几乎任何结构的K/V存储都可以用作每个主机的存储引擎。这种可插拔性使得Riak的处理能够并行化，从而可以在不影响代码库其他部分的情况下改进和测试存储引擎。有很多类似的本地K/V存储系统，包括但不限于Berkeley DB、Tokyo Cabinet和Innostore。在评估此类存储引擎时，我们想实现的目标包括：读取或写入每个项目的低延迟高吞吐量，尤其是在写入随机项目的传入流时处理比RAM大得多的数据集的能力，无退化故障友好性，在快速恢复和不丢失数据方面都很好易于备份和恢复相对简单、可理解（因而可支持）的代码结构和数据格式访问负载大或容量大时的可预测行为允许在Riak中轻松默认使用的许可证实现其中一些目标并不困难，但是想实现所有目标就不那么容易了。现有的本地K/V存储系统（包括但不限于作者编写的系统）均未达到上述所有目标。当我们在与Eric Brewer讨论这个问题时，他关于哈希表日志合并的关键见解是：这样做可能会比LSM树更快或更快。这导致我们以新的视角探索了20世纪80年代和90年代首次开发的日志结构化文件系统所使用的一些技术。这次探索导致了Bitcask的诞生，它是一个能够完全实现上述所有目标的存储系统。虽然Bitcask最初是为了给Riak使用而诞生，但是它的设计很通用，因此也可以作为其他应用程序的本地K/V存储。模型描述active data file最终采用的模型在概念上非常简单。Bitcask实例是一个目录，我们强制规定在给定时间内，只有一个操作系统进程可以打开该Bitcask进行写入。您可以将该进程有效地视为“数据库服务器”。在任何时候，该目录中都有一个文件由服务器进行写入操作。当该文件达到一定大小时，它将被关闭，并创建一个新的活动文件。[font color="#FFA500"]一旦文件被关闭，无论是出于有意还是由于服务器退出，它都被视为不可变的，并且永远不会被再次打开进行写入。[/font]活动文件，也就是上文提到的active data file，只能以追加的方式写入，这意味着顺序写入的同时不需要磁盘寻址。文件中的每个键值对entry的格式如下：每次写入时，都只是向active data file追加一个新的entry。删除操作只是写入一个特殊的墓碑值（可以理解为是一个特殊标记），它将在下一次合并时被删除。因此，Bitcask数据文件无非是这些entry的线性序列：keydir在active data file中完成追加操作后，接着去内存中更新一个名为keydir的数据结构。keydir是一个哈希表（在本论文中它是一个哈希表，也可以是其他数据结构），它将Bitcask中的每个key映射到一个固定大小的结构，这个结构记录了这个key写在哪个文件、该键在该文件中的偏移量以及大小。一开始我觉得上面这张图就是对bitcask中哈希表存储内容的正确理解，但是后来觉得下面这个图才是，因为哈希表的value存储的应该是entry的信息，而不是entry中value的信息。原论文中的图有比较大的迷惑性。数据写入与读取数据的写入其实在上面两节已经介绍过了，为了方便理解记忆就再总结一下。写入很简单，就是往bitcask中追加一条entry，然后更新keydir（原子操作），将刚刚新增的entry的信息存储起来，就像下面这样：数据的读取流程则是先拿着key去keydir中取出相应的entry信息，然后根据entry中提供的信息去data file中取出key对应的value，就像下面这样：数据合并因为bitcask删除的数据的方式是通过追加一条相同key的entry实现的，所以文件的size会越来越大，就需要定期的合并文件，合并的过程是这样的：先遍历所有的old data file，将所有的有效数据进行合并，如果有多个entry含有相同的key，则只保留最新的entry，有点像Redis中的AOF，合并完成后，old data file会变成merge data file，且数量也会减少，例如10个old data file 合并成5个merge data file。因为bitcask是在内存中构建索引，也就是之前提到的keydir，构建keydir需要在启动的时候扫描所有的data file，如果数据量很大，那么构建索引的过程就会很耗时，为了解决这个问题，bitcask在合并数据的时候还会为每个merge data file生成一个hint file，这个hint file中存储的也是一堆entry，这些entry的格式和data …

从零实现KV存储-2
type: item
date: 2024-01-06T23:09:00+08:00
description: 前置知识本节主要是对标准文件IO进行一些简单的封装。代码实现项目结构|-bitcask |--fio//新增 |---file_io.go //新增 |---io_manager.go//新增 |--data |---log_record.go |--index |---btree.go |---index.goIO接口定义新增文件：fio/io_manager.gopackage fio const FilePermCode = 0644 // IOManager 文件IO管理器 type IOManager interface { // Read 从文件指定位置读取数据 Read(b []byte, offset int64) (int, error) // Write 写入数据到文件指定位置 Write(b []byte) (int, error) // Sync 将存放在内存缓冲区的数据持久化到磁盘中 Sync() error // Close 关闭文件 Close() error }IO接口实现以下代码就是对标准文件IO进行简单的封装，方法的相应注释也都一并复制了，虽然是英文单但都很简单，应该不难看懂。新增：fio/file_io.gopackage fio import "os" // FileIO 对标准文件系统的一些接口进行简单封装 type FileIO struct { //系统文件描述符 fd *os.File } // NewFileIO 初始化标准文件IO func NewFileIO(filename string) (*FileIO, error) { fd, err := os.OpenFile( filename, // 以读写模式的模式打开文件，如果文件不存在则创建， // 如果文件已存在则以追加的方式进行写入 os.O_RDWR|os.O_CREATE|os.O_APPEND, FilePermCode) if err != nil { return nil, err } return &FileIO{fd: fd}, nil } // Read 从文件指定位置读取数据 func (fio *FileIO) Read(b []byte, offset int64) (int, error) { // ReadAt reads len(b) bytes from the File starting at byte offset off. // It returns the number of bytes read and the error, if any. // ReadAt always returns a non-nil error when n < len(b). // At end of file, that error is io.EOF. return fio.fd.ReadAt(b, offset) } // Write 写入数据到文件指定位置 func (fio *FileIO) Write(b []byte) (int, error) { // Write writes len(b) bytes to the File. // It returns the number of bytes written and an error, if any. // Write returns a non-nil error when n != len(b). return fio.fd.Write(b) } // Sync 将存放在内存缓冲区的数据持久化到磁盘中 func (fio *FileIO) Sync() error { // Sync commits the current contents of the file to stable storage. // Typically, this means flushing the file system's in-memory copy // of recently written data to disk. return fio.fd.Sync() } // Close 关闭文件 func (fio *FileIO) Close() error { return fio.fd.Close() }单元测试工具函数，用于删除测试文件func destroyFile(path string) { if err := os.RemoveAll(path); err != nil{ panic(err) } }FileIOfunc TestNewFileIOManager(t *testing.T) { path := filepat…

从零实现KV存储-1
type: item
date: 2024-01-06T11:51:00+08:00
description: 前置知识TODOBitcaskTODO代码实现项目结构|-bitcask |--data |---log_record.go |--index |---btree.go |---index.go数据存放要把数据存放到磁盘中，就需要通过一个数据结构来记录这个位置信息。data/log_record.go// LogRecordPos 数据内存索引，记录数据在磁盘中的位置 type LogRecordPos struct { Fid uint32 // 文件id，表示数据存放在哪个文件中 Offset int64 // 表示将数据存放在了文件中的哪个位置 }索引接口实现索引接口是为了方便以后使用不同的数据结构作为索引，只要该数据结构实现此接口即可生效，不需要额外改动。index/index.go// Indexer 抽象索引接口，如果要实现不同的索引结构，只需要实现该接口即可 type Indexer interface { // Put 往索引中 添加key对应的索引位置信息 Put(key []byte, pos *data.LogRecordPos) bool // Get 根据key获取索引位置信息 Get(key []byte) *data.LogRecordPos // Delete 根据key删除索引位置信息 Delete(key []byte) bool }索引实现我们使用BTree作为索引结构，google提供了一个BTree库，直接导入即可：github.com/google/btree。在实现索引接口之前，还需要对导入的BTree进行简单的封装：data/btree.go// BTree 索引数据结构，封装了google的btree库 type BTree struct { tree *btree.BTree lock *sync.RWMutex } // NewBTree 初始化一个BTree对象 func NewBTree() *BTree { return &BTree{ //这里的degree指的是BTree的度，也就是每个节点最多有多少个子节点 tree: btree.New(32), //lock: &sync.RWMutex{}, lock: new(sync.RWMutex), } }接口实现：index/btree.gofunc (bt *BTree) Put(key []byte, pos *data.LogRecordPos) bool { it := &Item{key: key, pos: pos} bt.lock.Lock() bt.tree.ReplaceOrInsert(it) bt.lock.Unlock() return true } func (bt *BTree) Get(key []byte) *data.LogRecordPos { it := &Item{key: key} bt.lock.RLock() value := bt.tree.Get(it) if value == nil { return nil } bt.lock.RUnlock() return value.(*Item).pos } func (bt *BTree) Delete(key []byte) bool { it := &Item{key: key} //会返回被删除的值，如果btree中没有这个值，则返回nil oldItem := bt.tree.Delete(it) if oldItem == nil { return false } return true }在Put方法中，我们调用了BTree中自带的ReplaceOrInsert方法：// ReplaceOrInsert adds the given item to the tree. If an item in the tree // already equals the given one, it is removed from the tree and returned. // Otherwise, nil is returned. // // nil cannot be added to the tree (will panic). func (t *BTree) ReplaceOrInsert(item Item) Item { if item == nil { panic("nil item being added to BTree") } if t.root == nil { t.root = t.cow.newNode() t.root.items = append(t.root.items, item) …

Joern安装
type: item
date: 2023-01-20T19:58:00+08:00
description: Joern安装之全网最详细教程写在前面网上很多关于jorn的安装教程都是几年前的了，而且joern对包的版本要求非常严格，就导致joern的安装异常麻烦。本人异常“幸运”的碰到了，历时三天终于安装成功。文章记录了本人安装joern的全过程，其中包括在安装过程中遇到的问题以及解决问题的方法，希望可以帮助到大家。所需要的各种包已经整理好放在最后。环境要求这里的环境要求尽量保持一致，否则会导致莫名其妙的问题。java 1.7python 2.7neo4j-community-2.1.8(2.1.5也可)，文末提供的安装包自带gremlin插件。关于java和neo4j的安装我这里就不细说了，网上教程一大堆，也不难，只给出安装neo4j的链接。开始安装确保jdk安装成功且环境已经配置完成；确保neo4j可以正常启动。安装joernwget https://github.com/fabsx00/joern/archive/0.3.1.tar.gztar xfzv 0.3.1.tar.gz这一步就是去那个链接下载joern的压缩包，然后解压。如果你有WinScp类似的工具，你可以直接复制这个链接去浏览器里下载，然后把解压后的文件夹放到linux就行。下载joern需要的依赖包cd joern-0.3.1wget http://mlsec.org/joern/lib/lib.tar.gztar xfzv lib.tar.gz这一步跟上一步一样，也是去那个链接里下载依赖包的压缩包，然后解压。注意要确保解压后的lib文件夹位于joern的目录下。lib文件夹中是各种各样的jar包，也就是说jar包的位置是：joern/lib/jar包下载antsudo apt-get install antsudo ant倒数第二行出现BUILD SUCCESSFUL即表示安装成功，如下图所示，其中红框中的路径下一步要用。建立连接alias joern = 'java -jar 红框中的路径'就是把第三步红框中的路径拼接在alias joern = 'java -jar后面，别忘了单引号。安装py2neo 2.0sudo pip install py2nep==2.0执行上面的代码大概率汇报错，因为2.0的包实在是过于久远，以至于pip已经抛弃了它。此时只能去github上下载，一定要下载2.0的包。https://github.com/py2neo-org/py2neo/tags?after=py2neo-2.0.7把下载好的安装包放在linux任意位置，进入py2neo的根目录，执行下面的代码：sudo python2 setup.py install安装python-joernsudo apt-get install python-setuptools python-devwget https://github.com/fabsx00/python-joern/archive/0.3.1.tar.gztar xfzv 0.3.1.tar.gzcd python-joern-0.3.1sudo python2 setup.py install这一步安装了两个东西，一定要按照上面的顺序安装。第一个的安装基本不会出错。安装的第二个是python-joern本体，这一步如果出错了，可能是因为py2neo的版本不对，也可能是某个包下载出错，譬如我这里的错误：最后一行这里提示出现了invalid syntax错误，然后很多同学就是去google或者百度invalid syntax关键字，去找解决方法，一顿操作猛如虎，结果哼哧哼哧搜了半天都是驴唇不对马嘴的答案。咱们要善于发现发现问题的本质，发挥主观能动性，不能是Terminal它说啥就是啥，怎么办呢？找到出现错误的第一行：上面的代码一看真的头皮发麻，我一开始也是这样，不想去读代码，反正就是赖。但是你稍微读一下就会发现下面的信息第一个红框肯定是正常信息，因为执行的时候没报错。咱们找错误肯定先找出错的第一行，首先定位关键字Traceback，用来跟踪错误出处的。一看到Traceback这个关键字就知道错误的起点在这部分，然后往上看，发现Reading.... Downloading...啥的，最后发现pyparsing这一看就是一个python包呀，也就是说它在下载这个包的时候出错了。那怎么办？去github上下载下来然后自己手动安装pyparsing版本那么多，咱们要下载哪一个版本呢？看上图箭头指出来的信息：searching for pyparsing != 3.0.5 ,>=2.0.2这里的!=和>=这个两个符号就很有意思，尤其是这个>=，打个比方：你给你女朋友买了个火腿肠(3.0.5)，然后她说：这>=棒棒糖(2…

六六
type: item
date: 2022-10-14T21:24:00+08:00

Handsome8.3主题美化记录
type: item
date: 2022-02-26T17:27:00+08:00
description: [scode type="green" size=""]尝试了很多教程，有些美化操作因为主题版本不适配而失效，所以这里只记录适用于handsome8.3版本的美化操作有本文未提到的且适用于8.3版本的美化教程欢迎在评论区中留言。本文会持续更新。[/scode][tabs][tab name="外观" active="true"]此栏美化教程需要在主题设置/开发者设置中自行添加js或者css代码，以及对主题源代码进行改动，若日后更新主题，可能需要重复美化过程，比较麻烦。个人更推荐使用插件。[collapse title="一、 添加额外表情包" status="false"]用压缩包内的0w0.js文件替换usr/themes/handsome/usr下的同名文件将压缩包中另外两个文件夹：weibo和paopao放入/usr/themes/handsome/assets/img/emotion目录下若感觉表情包尺寸太大或太小，可将下面的代码填入开发者设置/自定义css中自行调节大小，个人推荐25px左右.emotion-weibo { width: 100px; } .emotion-tieba { width: 100px; } .comment-content-true img.emotion-weibo { max-width: 100%!important; } .comment-content-true img.emotion-tieba { max-width: 100%!important; }[button color="info" icon="glyphicon glyphicon-download-alt" url="https://pan.baidu.com/s/1rm7RSUhrV5yRO0EHK1csvg" type=""]百度云[/button]提取码：[/collapse][collapse title="二、 给左上角头像添加特效" status="false"]在开发者设置/css中添加以下代码即可旋转特效，可通过totate字段调节旋转角度img-full {width: 100px;border-radius: 50%;animation: light 4s ease-in-out infinite;transition: 0.5s;}.img-full:hover {transform: scale(1.15) rotate(720deg);}礼花特效@keyframes light { 0% { box-shadow: 0 0 4px #f00; } 25% { box-shadow: 0 0 16px #0f0; } 50% { box-shadow: 0 0 4px #00f; } 75% { box-shadow: 0 0 16px #0f0; } 100% { box-shadow: 0 0 4px #f00; } }[/collapse][collapse title="三、 去除首页顶部博客名称" status="false"]进入/usr/themes/handsome/index.php文件，删除以下代码 <?php $this->options->title(); ?> [/collapse][collapse title="四、 彩色标签云" status="false"]在开发者设置/java script中添加以下代码 let tags = document.querySelectorAll("#tag_cloud-2 a"); let colorArr = ["#000000", "#000000", "#000000", "#000000", "#000000", "#000000"]; tags.forEach(tag => { tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)]; tag.style.backgroundColor = tagsColor; }); let tags = document.querySelectorAll("#tag_cloud-2 a"); let colorArr = ["#ADD8E6", "#ADD8E6", "#ADD8E6", "#ADD8E6", "#ADD8E6", "#ADD8E6"]; tags.forEach(tag => { tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)]; tag.style.backgroundColor = tagsC…

事记
type: item
date: 2022-02-26T16:43:00+08:00
description: [timeline title="每个人都是一本书" type="large" start="梦开始的地方" end=""][item date="2022-2-21"] 网站诞生啦 ::aru:proud:: [/item][item date="2022-2-28"]第一次骑电瓶车上路就被罚款了，没戴头盔、没上牌照 ::aru:crying:: [/item][/timeline]

Handsome主题美化记录
type: item
date: 2022-02-26T11:05:00+08:00
description: 添加贴吧表情包在开发者/自定义css中添加一下代码.emotion-weibo { width: 24px; } .emotion-tieba { width: 25px; } .comment-content-true img.emotion-weibo { max-width: 100%!important; } .comment-content-true img.emotion-tieba { max-width: 100%!important; } 2添加头像旋转特效.img-full { width: 100px; border-radius: 50%; animation: light 4s ease-in-out infinite; transition: 0.5s;}.img-full:hover {transform: scale(1.15) rotate(720deg);}@keyframes light {0% { box-shadow: 0 0 4px #f00; } 25% { box-shadow: 0 0 16px #0f0; } 50% { box-shadow: 0 0 4px #00f; } 75% { box-shadow: 0 0 16px #0f0; } 100% { box-shadow: 0 0 4px #f00; }}3文章左侧图标和评论头像悬停特效没搞懂啥意思css.img-circle { transition: all 0.3s;}.img-circle:hover {transform: rotate(360deg);}3首页文章卡片悬浮特效css.blog-post .panel:not(article) {transition: all 0.3s;}.blog-post .panel:not(article):hover {transform: translateY(-10px); box-shadow: 0 8px 10px rgba(73, 90, 47, 0.47);}4彩色标签云js let tags = document.querySelectorAll("#tag_cloud-2 a");let colorArr = ["#000000", "#000000", "#000000", "#000000", "#000000", "#000000"];tags.forEach(tag => {tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)]; tag.style.backgroundColor = tagsColor;}); let tags = document.querySelectorAll("#tag_cloud-2 a");let colorArr = ["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0"];tags.forEach(tag => {tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)]; tag.style.backgroundColor = tagsColor;}); let tags = document.querySelectorAll("#tag_cloud-2 a");let colorArr = ["#ADD8E6", "#ADD8E6", "#ADD8E6", "#ADD8E6", "#ADD8E6", "#ADD8E6"];tags.forEach(tag => {tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)]; tag.style.backgroundColor = tagsColor;}); let tags = document.querySelectorAll("#tag_cloud-2 a");let colorArr = ["#428BCA", "#AEDCAE", "#ECA9A7", "#DA99FF", "#FFB380", "#D9B999"];tags.forEach(tag => {tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)]; tag.style.backgroundColor = tagsColor;}); let tags = document.querySelectorAll("#ta…