学海无涯

centos7 与 centos stream 发布版本流程对比

Centos7 的替代方案

CentOS 7 是一个开源操作系统。CentOS(Community ENTerprise Operating System)是基于 Red Hat Enterprise Linux (RHEL) 的一个社区版本,旨在提供一个稳定和可靠的企业级操作系统。用户可以自由地下载、使用、修改和分发它的源代码。本人个人服务器也是长期采用此系统,**CentOS 7 Linux 将于 Jun 30th, 2024 EOF(结束生命周期).**

梳理了一下对应的替代方案。

centos OS stream

结论:不建议,根据下面发布流程,Centos stream 优先享受新特性,但是随之,centos stream 也成为了 RHEL 版本的灰度测试,且centos stream 不再提供小版本,导致稳定性不可控。 centos7 与 centos stream 发布版本流程对比

优点 缺点
接近 RHEL:提供新特性,提前体验 相对不稳定性:可能引入不可预见的问题
滚动更新:频繁进行版本更新 缺乏历史小版本:不再提供 7.X 的小版本
快速反馈:可测试新特性并提供反馈 长时间支持有限:不明确的支持期
活跃开发社区:吸引更多开发者参与 社区反应:用户对替代传统 CentOS 的不满
无成本使用:适合成本敏感用户 学习曲线:需要适应滚动更新模型

RHEL Linux

适合用户:

  • 不差钱
  • 对稳定性要求极高
  • 需要购买专业 Linux 维护服务
  • 金融行业

Rocky Linux

  • Rocky Linux 与 RHEL 和 CentOS 的关系

Rocky Linux 是一个基于 Red Hat Enterprise Linux (RHEL) 的开源操作系统,旨在提供一个稳定和免费替代品。它的创建是由于 CentOS 项目转变为 CentOS Stream,这一变化引发了部分用户的不满,因为 CentOS Stream 更加接近于 RHEL 的开发版,而不是稳定的发行版。

F**K ZZZQ - 记录那些因政治正确而改名的技术

0x00

请允许我首先给出一个和善而不失礼貌的微笑 :) 以纪念因为这些莫名其妙的事而浪费的时间。

在今年早些时候乔治-弗洛伊德(George Floyd)的惨死和BLM抗议活动之后,科技公司希望通过放弃master、slave、blacklist和whitelist等非包容性术语来表达对黑人社区的支持。

Github

主分支从 master 改为 main

适用范围: 目前新建的项目已修改,老项目不做变更

Redis

目前的变化

  • 将 master-slave 架构的描述改为 master-replica
  • 为 SLAVEOF 提供别名 REPLICAOF,所以仍然可以使用 SLAVEOF,但多了一个选项
  • 保持继续使用 slave 来对 INFO 和 ROLE 进行回应,现在目前看来,这仍然是一个重大的破坏性变更

计划中的变化

  • 编写一个 INFO 的替代品
  • 在内部替换很多东西,因为技术原因,如果作了改动,许多 PR 也会无法应用,所以必须在某些地方进行大变动

目前的变化适用于 redis 5.0, 5.0以前不做变更,5.0以后可能还会有变更(6.0目前没有大变更)

Python

master process 变更为 parent process

适用范围: python 3.8及其以上

记录一次celery消费失败的问题排除

0x00

昨天发完UAT后,今天惯例点进UAT看看服务的情况,突然发现flower监测的celery竟然有半数以上的失败!

开始排查

马上查看这个queue的日志,确实是有一堆失败的。

当前这个queue的业务是从redis里把数据取出来写入minio里落盘,但是涉及的数据均为几十k的数据,讲道理不应该会失败。

查询得到这几个失败的任务redis key的插入时间为2020-12-28 15:17:48,而消费的时间却是2020-12-29 17:17:21

这里就出现了一个问题,业务逻辑上当一个key塞入redis中后,马上会把落盘任务推到队列里,一般来说不会积攒这么久。但是此处竟然积攒到了一天以上才开始消费,而此处也因为我们设置的redis单key最大过期时间为24小时,所以导致落盘任务失败,并且数据丢失了。

发现问题

那么到底是什么任务积攒在队列里积攒了这么久。。?经过和同事分析发现,因为此次发版前还没有上线深度学习的功能,所以只分配了两个通用消费者。当启动几个深度学习任务时,这么点消费者完全没有办法应付之后的任务了,导致简单的几十k数据落盘任务都需要积攒天级以上的时间才能完成。

所以,深度学习任务单独开个queue分流不阻塞其他任务,就解决了此次问题。。(感觉好蠢,浪费了一个上午

解决群晖使用百度云docker镜像提示登陆过期的问题

0x00

群晖中使用的百度套件是基于VNC + Linux版百度云打包的docker镜像实现的,但是作者因为稳定性原因通过修改检测更新的hosts方法将百度云版本锁定在3.0.1.2版本。可是这在部分机器上不生效,那么我们反之道而行,自己更新容器里的百度云版本。

解决方案

  1. 去百度云官网下载.deb版百度云: 下载地址

  2. 将下好的镜像放到群晖已挂载的baidudownload文件夹里

  3. 进入容器中的baidudownload目录

  4. 执行 sudo dpkg -i baidunetdisk_3.4.1_amd64.deb

  5. 将hosts文件中127.0.0.1 update.pan.baidu.com这一行注释掉

然后群晖中的百度云套件就不报登陆过期的问题了

群晖中使用破解版 emby

0x00

本文基本来源 Yukino’s 理想乡emby 破解版完全食用方法

但是文中没有提供 docker 版的安装方法和相关配置!!所以这里完善一下!

套件版方案使用起来很舒服,但是需要 fan 墙刮削。docker 版可以以一种巧妙的方式刮削,但是配置起来麻烦。

套件版 emby 破解

  1. 因为使用 emby 套件源,在国内难以访问,所以我们从官网上下载 emby 并安装

  2. 安装好后关闭 emby

  3. 使用脚本破解 wget https://neko.re/wp-content/uploads/simple-file-list/NyaaHost.sh ; sh NyaaHost.sh (再次鸣谢Yukino’s 理想乡)

  4. 进入并配置 emby

  5. 使用 Chrome 安装 URLRedirector(Chrome, Firefox) 插件,添加用户规则 原始地址 https://mb3admin.com ,目标地址 https://crackemby.neko.re ,然后确认并保存,别忘了勾选重定向。

  6. Chrome 访问 emby 地址,emby 高级版秘钥里随便填个字符串点保存就破解成功了~!

Docker 版 emby 安装及破解

先附上容器启动命令

docker create \
–name=emby \
–device=/dev/dri/renderD128:/dev/dri/renderD128 \
–device=/dev/dri/card0:/dev/dri/card0 \
-p 
yukinococo/emby_crack:unix-x64

Python 中的上下文管理

0x00

当我们执行语句块前需要一些准备动作,在执行完成之后又需要执行一些收尾动作。比如:文件读写后需要关闭,数据库读写完毕后需要关闭连接,资源加锁解锁等情况。对于这种情况 python 提供了上下文管理的概念,可以通过上下文管理器处理代码块执行前的准备动作,以及执行后的收尾动作。

使用 with 语句

先来看看不使用上下文管理器的情况

f = open("log.txt", "w")

try:
    f.write("hello")
finally:
    f.close()

使用上下文管理器

with open("log.txt", "w") as f:
    f.write("hello")

当结束语句的时候,Python 会自动的帮我们调用 f.close()方法

With 语句内部帮我们做了什么呢?

  1. with 后面的 open("log.txt", "w") 语句返回对象的__enter__方法会被调用,并把__enter__的返回值赋值给as后面的变量

  2. with执行完之后,会调用前端返回对象的__exit__方法。

让我们写个类测试一下:

class ContextTest:
    def __enter__(self):
        print("进入enter!")
        return "Foo"

    def __exit__(self, *args, **kwargs):
        print("进入exit!")


def get_sample():
    return ContextTest()


with get_sample() as sample:
    print(f"Sample: {sample}")

运行结果:

进入enter!
Sample: Foo
进入exit!

自己实现一个上下文管理器

通过__enter__和__exit__实现

根据上面 with 语句的原理,我们自己使用类实现一个支持 with 语句的打开文件的类

设计架构

Spark vs Dask Python生态下的计算引擎

本文基于Gurpreet Singh大佬在 Spark+AI SUMMIT 2020 的公开课编写

0x00

对于 Python 环境下开发的数据科学团队,Dask 为分布式分析指出了非常明确的道路,但是事实上大家都选择了 Spark 来达成相同的目的。Dask 是一个纯 Python 框架,它允许在本地或集群上运行相同的 Pandas 或 Numpy 代码。而 Spark 即时使用了 Apache 的 pySpark 包装器,仍然带来了学习门槛,其中涉及新的 API 和执行模型。鉴于以上陈述,我们下面将对比这两个技术方案。

Spark vs Dask

首先先上Dask和Spark的架构设计图~ 设计架构

生态

Dask 对于 Python 生态中的 Numpy、Pandas、Scikit-learn等有很好的兼容性,并且在low level api中提供了延迟执行的方法。

Spark 是独立于 Python 生态的另一个项目,但如果是在 JVM 环境下开发,并且十分需要使用 Spark SQL 等特性,可以考虑使用Spark。

性能

Dask 中的 dataframe 基本上由许多个 pandas 的 dataframe 组成,他们称为分区。但是因为 Dask 需要支持分布式,所以有很多 api 不完全和 pandas 中的一致。并且在涉及到排序、洗牌等操作时,在 pandas 中很慢,在 dask 中也会很慢。除此之外,dask 几乎都是遵循 pandas 设计的。

在线、离线激活鉴权实战

任务目标

实现一个客户端通过给与的指定激活码,激活仅限当前机器使用具体软件的功能。客户端可能处于能连接互联网无法连接互联网两种情况。同时均要实现在指定时间使权限过期的功能,激活码在使用时才开始计时。

在线激活模式

这个就非常简单了,使用最简单的哈希加盐就可以实现了,与正常 REST 接口的哈希加盐验证只有,验证服务器端需要预先存好设置的激活码这一步。

激活码有效期就通过一个取巧的方法,将有效期的秒数转换成16进制,埋藏在序列号的指定段上。 在将当前时间和过期时间一起加盐,实现用户无法通过修改或锁定系统时间破解我们的系统。

当然,激活后,也要每隔一段时间验证一下是否过期,并且同时更新验证的时间戳,防止用户修改或锁定系统时间。

可以将鉴权放在请求前检验登录情况的装饰器里,每次更新只更新到flush里,在其他操作commit的时候再更新到数据库,防止一个请求里多个commit会导致数据无法回滚。

比较有风险的一点就是,用户可能会抓包服务端发到客户端的鉴权结果请求,然后伪造成功信息实现越过真实的鉴权结果,所以我们要设置一些比较特殊的字符串成功信息的字符串。或者可以实现类似银行电子狗,客户端-服务端均会随时间产生的相同动态加密校验码,与成功或失败信息加密后,再返回给客户端,保证中间过程即时被抓包,也无法破解。以下伪代码不考虑此过程

# 激活部分client伪代码

def online_active():
	# 前端传递激活码
	serial_number = form.serial_number.data

	# 获取本机硬件信息
	device_info = get_device_info()

	# 盐
	secret = config.secret

	# 记录激活码过期时间

	expire_time = int(serial_number[6:], 16) * (60 * 60 * 24)
	expire_timestamp = now_timestamp + expire_time

	# 硬件信息加盐生成的指纹
	signature = md5(device_info, expire_timestamp, now_timestamp, secret)

	# 向激活服务器发起验证请求
	if verify_license(serial_number, device_info, expire_timestamp, now_timestamp, signature):

		# 写入数据库
		insert_active()
		return True
	else:
		return False
# 激活部分server伪代码

def verify_license(serial_number, device_info, expire_timestamp, now_timestamp, signature):

	# 验证是否存在对应的激活码
	if serial_number in db:
		# 与客户端一致,只要验证指纹是否一致就行了
		if signature == md5(device_info, expire_timestamp, now_timestamp, secret):
			return True

	return False
# 客户端检测权限是否仍然有效
def is_active():
	# 验证激活设备是否改变,同时验证了激活时间是否被篡改

	timestamp_or_false = is_active_equipment()

	if not expire_timestamp_or_false:
		return False

	# 获取下面每次鉴权写入数据库的时间last_timestamp,防止用户篡改系统时间,躲避过期机制
	expire_timestamp, last_timestamp = expire_timestamp_or_false

	# 是否因为超过过期时间而过期
	if now_time > expire_timestamp:
		return False

	# now_time 为当前系统时间
	# 防止用户锁定时间
	if last_timestamp >= now_time:
		return False

	# 更新数据库中的now_timestamp并重新生成指纹
	now_timestamp = now_time
	signature = md5(something)

	# 写入数据库
	insert_active()
	return True



# 客户端检测是否设备是否改变

def is_active_equipment():
	# 其实就是重复哈希加盐鉴权的过程,只不过是将验证客户端指纹、验证服务器指纹改为了,
	# 客户端指纹、客户端数据库指纹鉴权了

	db_signature, serial_number, expire_timestamp, now_timestamp = get_db_signature()

	# 获取本机硬件信息
	device_info = get_device_info()

	# 盐
	secret = config.secret

	# 硬件信息加盐生成的指纹
	signature = md5(device_info, expire_timestamp, now_timestamp, secret)

	if signature == db_signature:
		return expire_timestamp, now_timestamp
	return False

离线激活模式

这里逻辑就比较恶心,但是还是类似上面在线激活的思想。只不过从客户端-激活服务器改为了客户端-手机-激活服务器

Redis 主从复制 哨兵模式实战

0x00

项目中因为并发不是很高一直偷懒用的单节点 Redis ,但是有很多大 key 写入的场景,这样会影响读性能,于是准备做主从复制,顺便做一下哨兵模式。

Redis 主从复制配置

这里很简单,只需要配置slaveof <masterip> <masterport>参数即可实现。

注: Redis 5.0 版本后建议使用replicaof代替slaveof。 因为奇妙的政治正确原因,迫使作者修改了配置名,虽然slaveof仍能使用,但是只是暂时的兼容方案。

修改后重启 Redis 查看配置显示, 表示成功

# Replication
role: slave
master_host: 192.168.14.130
master_port: 6379
master_link_status: up
...

Redis 哨兵模式配置

必要配置如下

port 26379

# 当前哨兵绑定的ip,一般为本机ip
bind 192.168.2.210

# 设置master节点为 192.168.14.130 6379 上的redis,
# 别名为redis-master,当两个哨兵同意故障转移就会执行
# 一般设置N/2+1(N为哨兵总数)
sentinel monitor redis-master 192.168.14.130 6379 2

# 认为master节点断线所需的毫秒(SDOWN)
sentinel down-after-milliseconds redis-master 5000

# 如果在 180000 毫秒内 master 节点没有恢复,则认为是真正宕机
# 当下一次检测宕机 master 节点恢复后,则并入 slave 中
sentinel failover-timeout redis-master 180000

# 当 master 宕机后,最多可以多少个节点对新 master 进行同步
# 数字越小完成故障转移的时间越长
sentinel parallel-syncs redis-master 2

配置成功后显示如下

算法题基本框架 - Python实现

二叉树

DFS

二叉搜索树的中序遍历即为这棵树叶子节点值从小到大的排列

def dfs(root: TreeNode):
	if not root:
		return

	# 先序遍历
	dfs(root.left)
	# 中序遍历
	dfs(root.right)
	# 后续遍历

BFS

 from collections import deque

def bfs(root: TreeNode, target: TreeNode):
    queue = deque()
    # 记录访问的节点避免走回头路
    vistied = []

    # 起点在循环外入队
    queue.append(root)
    vistied.append(root)
    step = 0

    while queue:
        # 先记录此时队列长度,避免将新入队元素算入本次循环
        length = len(queue)
        for i in range(length):
            cur = queue.popleft()

            # 判断是否到达终点(此处需改成符合题意的条件)
            if cur is target:
                return step

            # 将cur相邻节点入队,adj()泛指cur相邻节点
            for x in cur.adj():
                if x not in vistied:
                    queue.append(x)
                    vistied.append(x)
        step += 1

二叉堆

数组表示(从0开始)的二叉堆有如下性质: