Coding的痕迹

一位互联网奔跑者的网上日记

0%

背景

最近在开发“上应小风筝”的电费统计功能后端接口。前端突然反馈说数据有问题:前端页面中计算得到的平均值和电费排名中的消费量不一致。(如图,图上 0.05 即为均值,但此图为事后截,无太大问题)

电费统计页(事后截图)

去查看后端返回的数据,发现最后 8 小时电费情况始终为 0 元,而与实际金额不符。因此怀疑是时区问题的锅。

阅读全文 »

   数据库备份是实际运维中必须进行的操作,然而有时因为没空或者懒,便忽视了这项工作,有时甚至造成损失。

背景

   今天早上对本博客的 nodejsnpm 进行升级时,apt 提示部分包可以使用 apt autoremove 命令删除。为了减少系统体积,便执行了。因为注意到执行前系统内存占用在 600M 左右,执行后内存占用在 240M 左右,便感觉到不对劲。于是首先便想到了同一台服务器上的数据库,执行:

1
2
root@Aliyun:~# systemctl status postgres
Unit postgres.service could not be found.

   想着出事了,博客上运行的 PostgreSQL 不翼而飞。由于用户量不大,开始不慌不忙地进行恢复工作。

阅读全文 »

  银行家算法(Banker’s Algorithm)是一个避免死锁(Deadlock)的算法。

背景

  在银行中,客户申请贷款的数量是有限的,每个客户在第一次申请贷款时要声明完成该项目所需的最大资金量,在满足所有贷款要求时,客户应及时归还。银行家在客户申请的贷款数量不超过自己拥有的最大值时,都应尽量满足客户的需要。在这样的描述中,银行家就好比操作系统,资金就是资源,客户就相当于要申请资源的进程。(维基百科

  察看银行家算法的历史,它于 1965 年由 Dijkstra 和学生所设计,打算应用到 THE 系统 中的。作为那个时代的产物,这个算法或许当时在避免死锁上取得了很好的效果,而技术发展日新月异,需求的变化和性能的差异使得它今天的价值主要体现在教学和思想的传播了吧。

阅读全文 »

Rust 使用 tesseract 识别验证码

近期需要将验证码识别功能集成到一个 Rust 项目中,验证码图片大概这样:

验证码图片示例

观察

首先观察图片,发现前景文字和背景图片颜色深度差别较大,可以对图片进行二值化处理。使用 Photoshop 可以看到,转为灰度图像后,在 0 ~ 255 内,前景和背景大约以 130 为界。使用 “天若OCR” 截图、识别了若干张图片,在较大截图区域的情况下,识别率可以达到 100%,说明这个验证码不复杂。由于在线 API 有数量等限制,最终将目光锁定在了 tesseract 上。

阅读全文 »

这是一篇 PostgreSQL 13 在 Linux 上安装的笔记,便于日后查询使用。数据库配置于 Debian 10 (Buster) 上,在配置的过程中,你可以参考官方文档

阅读全文 »

   最近最少使用算法,或 最近最久未使用算法 (Least Recently Used, LRU)是一种常用的页面置换算法(当然也可以用于文件缓存等情景)。再实际情况中,如某些页面长时间未被访问,则它们在将来还可能会长时间不会访问。算法基于这个推论,选择最近最久未使用的页面予以淘汰。

  我们认为 “最近使用” 指的是读/写相应的内存页(或值),然后以一个例子(来自参考资料1)看看:

LRU过程示意

阅读全文 »

   在被 rust-postgresql 折磨之后,选择了 diesel 库,这才了解到对象关系映射(Object Relational Mapping, ORM) ,可以像使用本地变量和函数一样操作关系型数据库。这有点像非关系型数据库(如MongoDB)操作方式的意味,不过不知道孰先孰后。

初步认识

   diesel 提供了一个叫 diesel_cli 的工具,用来将 MySQL、PostgreSQL 或 Sqlite 上的数据表结构转换成代码,类似于 (schema.rs),或代为执行一些 SQL 语句:

1
2
3
4
5
6
7
8
9
10
// A simple schema.rs
table! {
verifications (id) {
id -> Int4,
uid -> Int4,
login_type -> Int4,
account -> Varchar,
credential -> Nullable<Varchar>,
}
}
阅读全文 »

   晚上在写一段多线程程序时,在C++标准库中发现了这样的写法,让我很惊讶:

1
this_thread::sleep_for(2s);

Ctrl + Click 点进去看,函数的声明是这样的:

1
2
3
_NODISCARD constexpr chrono::seconds operator"" s(unsigned long long _Val) noexcept /* strengthened */ {
return chrono::seconds(_Val);
}

  像是重载了双引号的写法,翻了翻 cppreference ,原来叫用户定义字面量(字面值)。

阅读全文 »

   和使用 http 类似,测试代码可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
use actix_web::client::Client;

#[actix_rt::main]
async fn main() {
let mut client = Client::default();

// Create request builder and send request
let response = client.get("https://sunnysab.cn")
.send().await.unwrap();

println!("{:?}", response);

}
阅读全文 »

   借一个 Actix-web::Client 官方的例子,可以这样发送一个 HTTP 请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
use actix_web::client::Client;

#[actix_rt::main]
async fn main() {
let mut client = Client::default();

// Create request builder and send request
let response = client.get("http://sunnysab.cn")
.send().await.unwrap();

println!("{:?}", response);

}
阅读全文 »