2024年3月

  • 产生一个rsakey
ssh-keygen -t rsa -b 2048 -f admin -C "admin"

注意:已有的linux里面的b可能是4096,交换机无法识别。

  • 通过web界面登录交换机管理界面,从文件管理中上传admin.pub

2024-03-29T06:12:49.png

  • 新增本地admin账号
local-user admin
 password cipher $c$3$6nilO+wOdgvkfAwZszmWLhLWOvV4fF5Is4IR
 authorization-attribute level 3
 service-type ssh telnet terminal
 service-type ftp
 service-type web
  • 导入刚刚web页面上传的admin.pub
public-key peer admin import sshkey flash:/admin.pub
  • 配置admin账号使用public登录ssh,配置root使用密码登陆过
 ssh server enable
 ssh user root service-type all authentication-type password
 ssh user admin service-type all authentication-type publickey assign publickey admin work-directory flash:/
  • 参考

https://www.cnblogs.com/kscnchina/archive/2013/02/04/2892430.html

昨天的这个时候,一个同事过来找俺。大概意思是他们做嵌入式的,正在自动化测试模块的一些功能。这个嵌入式模块会通过无线路由器连接互联网。而他们需要对这个模块所连接的无线路由器的互联网访问能力进行控制。

说了半天,俺也是听的云里雾里。最后他说他找了一根可以控制的网线。

2024-03-28T09:38:21.png
https://item.taobao.com/item.htm?abbucket=4&id=620647499054&ns=1&spm=a21n57.1.0.0.634d523cHHVZ5w

他发的图更牛逼,在网线的中间真的有一个开关灯的那种开关。看的俺眼珠子差点没掉出来。

连呼牛逼。同事可以通过在这种网线上加一个模块控制开关。然后同事就去采购了。他从OSI的物理层解决了这个需求。

思考了一天。今天尝试使用paramiko连接交换机去解决。

最后还好实现了。

2024-03-28T09:41:21.png

#!/bin/env python3.7
import paramiko
import time
import sys

# 使用示例
hostname = "xxx"
username = "xxx"
password = "xxx"

def execute_commands_on_switch(hostname, username, password, commands):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    try:
        ssh.connect(hostname=hostname, username=username, password=password,allow_agent=False,look_for_keys=False)

        shell = ssh.invoke_shell()

        for command in commands:
            shell.send(command + "\n")
            time.sleep(1)  # 等待一段时间,让命令执行完毕

        output = shell.recv(10000).decode('utf-8')
        print(output)

    except paramiko.AuthenticationException:
        print("Authentication failed. Please check your credentials.")
    except paramiko.SSHException as ssh_exception:
        print(f"SSH exception occurred: {ssh_exception}")
    finally:
        ssh.close()


# 定义关闭接口的函数
def port_start(hostname, username, password):
    commands = [
        "sys",
        "interface giga 1/0/7",
        "undo shutdown",
        "quit",
        "quit"
    ]
    execute_commands_on_switch(hostname, username, password, commands)

# 定义恢复接口启动的函数
def port_stop(hostname, username, password):
    commands = [
        "sys",
        "interface giga 1/0/7",
        "shutdown",
        "quit",
        "quit"
    ]
    execute_commands_on_switch(hostname, username, password, commands)

if __name__ == "__main__":
    if len(sys.argv) > 1:
        action = sys.argv[1]
        if action == 'start':
            port_start(hostname,username,password)
        elif action == 'stop':
            port_stop(hostname,username,password)
        else:
            print("Invalid argument, please specify 'start' or 'stop'")
    else:
        print("No action specified, please provide an argument (either 'start' or 'stop')")

一、背景

同事发来一个图片,说是日志显示,一个服务推送数据到opentsdb得到了http 500返回码。
经过排查,确实如此,而且还不少。
2024-03-27T06:47:16.png

二、现象

排查现象,有波动,怀疑是跟并发量有关系。

但是,查询昨日正常与异常数据,前者2万左右,后者几百。不像是并发量问题。

三、猜测

(1)内存问题

之前CDH集群报内存不足,赶紧进行了内存扩容。
但是,问题依旧。
2024-03-27T06:47:40.png

四、定位

研发仔细分析日志,并通过本地调试opentsdb接口,发现是某一个id出现了问题。遇到了opentsdb的三字节问题。

  • 什么是三字节问题
    其实“三字节问题”是 OpenTSDB 中一个具体的实现细节。在 OpenTSDB 中,每一个唯一的时间序列都会有一个与之对应的 ID,这个 ID 是由一个特定的编码算法生成的,长度为 3 字节。因此,对于每一个唯一的度量/标签组合,OpenTSDB 只能生成最多 2^24(即约 1677 万)个不同的 ID。如果你的时间序列数量超过了这个上限,OpenTSDB 就无法再生成新的 ID,你将无法添加新的时间序列。 换句话说,OpenTSDB 的“三字节问题”指的是对每一个度量/标签的组合,OpenTSDB 只能处理最多约 1677 万个唯一的时间序列。这是一个极端的情况,一般不会出现。然而,对于一些大规模、长期的数据收集任务,可能会达到这个上限。 如果你遇到了这个问题,如我前面提到的,你可能需要清理一部分数据,或者调整你的数据存储策略和时间序列设计。在不改动 OpenTSDB 代码的情况下,这些可能是目前解决这个问题的可行方案。如果有更多疑问,可以继续提问。
  • 如果删除了三年前的数据,那么ID会有么?

删除数据并不会直接释放其对应的时间序列 ID,因为 ID 一旦生成会被保留,以保证时间序列的唯一性。即使对应的数据点被删除,但在 OpenTSDB 中,这个 ID 依然会被保留,因此删除数据并不能解决时间序列 ID 耗尽的问题。 在 OpenTSDB 的设计中,没有直接的办法来重新生成或复用已使用过的时间序列 ID。这是为了保证每一个时间序列的唯一性和一致性。因此,“三字节问题”实际上是 OpenTSDB 的一个设计限制。 如果你遇到了这个问题,可能需要考虑采用其他策略进行优化,比如重新考虑和设计你的标签和度量的组织方式;或者利用 OpenTSDB 提供的数据预处理功能,将某些数据进行整合,从而减少时间序列的数量。 这是一个复杂的问题,需要根据你具体的应用场景和需求来进行解决。如果有更多的疑问或需要更深入的讨论,请随时提问。

五、解决(2024年4月19日)

当时间来到4月中旬,客户着急使用,可是已经没有了uid分配。

(1)测试环境测试

  • 查看当前\x00的tagv的值

    hbase shell
    scan 'tsdb-uid', {LIMIT => 3, REVERSED => false}
    

2024-04-19T03:20:00.png

  • 删除\x00的tagv
    \x00的tagv消失

    deleteall 'tsdb-uid',"\x00",'id:tagv'
    scan 'tsdb-uid', {LIMIT => 3, REVERSED => false}

2024-04-19T03:20:43.png

  • 新建一个tagv用于打数据。出现了\x00的tagv

2024-04-19T03:21:36.png

但是写入数据错误 (should never happen!!!)。估计是新数据需要新增tagv uid的时候,当前tsdb-uid表中tagv uid已经分配了出去。所以本次分配失败,返回给客户端500。
下次客户端继续发送put请求,继续请求分配tagv,tsdb继续将tagv的uid增长+1。

2024-04-19T03:21:57.png

  • 写一个脚本,一直写数据

虽然仍然报“should never happen!!!”,但是tsdb-uid表中的tagv uid的值在一直增长。使用如下程序继续。这个程序会打到一个之前没有的uid,

#!/bin/bash

while true
do
    current_time=$(date +%s)
    current_time_ms=$((current_time * 1000))
    current_second=$(date +%S)

    data=$(cat <<EOF
[
    {
        "metric": "jacky",
        "value": "$current_second",
        "timestamp": "$current_time_ms",
        "tags": {
            "dvid": "109",
            "type": "1",
            "did": "123456789012345609"
        }
    }
]
EOF
)

    echo "Sending data to OpenTSDB..."
    response=$(curl -X POST http://192.168.124.194:4242/api/put --header "Content-Type: application/json" -d "$data" -w "%{http_code}" -s)
    status_code=$(echo "$response" | tail -n1)
    response_body=$(echo "$response" | sed '$d')

    if [[ "$status_code" -eq 200 || "$status_code" -eq 204 ]]; then
        echo "Data sent successfully. Server responded with status code: $status_code"
    else
        echo "Failed to send data. Server responded with status code: $status_code"
        echo "Response body: $response_body"
    fi

    sleep 1
done
  • 随着上面程序的运行,tagv这个地方的value一直在增加。等到删除tagv \x00之前的值。也许当该值增加到之前的\0C\D4M应该就可以了 。

2024-04-19T03:23:04.png

  • 到了D4M的下一个D4N,数值停止了。

2024-04-19T03:23:14.png

  • we are in !
    2024-04-19T03:23:25.png
  • 开始正常写入数据
    2024-04-19T03:23:38.png

(2)处理线上

  • 查看uid使用情况

    [root@cdhslave3 ~]# tsdb uid grep tagv . | wc -l
    16776889
    [root@cdhslave3 ~]# tsdb uid grep metrics . | wc -l
    334181
    [root@cdhslave3 ~]# tsdb uid grep tagk . | wc -l
    7
    
  • 分析tagv 数量16776889的组成
# 大于等于4小于14字节的一共是16487127
data.tagv.less_than_14_bytes.txt 15488132
data.tagv.less_than_7_bytes.txt 998995

# 应该是正常的数量
data.tagv.less_than_4_bytes.txt 1000
data.tagv.非数字.txt 5445
data.tagv.more_than_14_bytes.txt 283317
  • 分析出来tagv的值后,组成命令
tsdb uid delete tagv 2357129 ;echo deleted!;
tsdb uid delete tagv 23571293 ;echo deleted!;
tsdb uid delete tagv 23571305 ;echo deleted!;
tsdb uid delete tagv 2357131 ;echo deleted!;
tsdb uid delete tagv 23571311 ;echo deleted!;
tsdb uid delete tagv 2357132 ;echo deleted!;
tsdb uid delete tagv 23571329 ;echo deleted!;
tsdb uid delete tagv 23571337 ;echo deleted!;
tsdb uid delete tagv 235713378 ;echo deleted!;
tsdb uid delete tagv 23571344 ;echo deleted!;
  • 因为命令比较长,所以实用split分离成15个文件,每个服务器跑3个。

(3)加急处理

按照每天60万的速度,删除1600多万的数据需要一个月左右。但是,用户等不及。

那就再想想办法,删除最近的10万数据先。

  • 下载所有tsdb-uid表的数据。
cd /tmp
cat <<'EOF'>commands.txt
scan 'tsdb-uid', {REVERSED => false}
exit
EOF

hbase shell commands.txt > data.all.txt
  • 分离出来tagv
cat data.all.txt | grep tagv > data.all.tagv.txt
  • 刷新,找到大约30~40多万行,第一个有问题的数据的位置。
    2024-04-19T03:29:09.png
  • 找到这行数据
└─$ cat data.all.tagv.txt| grep -n "value=319338$"
219835: \x03Z\xBD column=name:tagv, timestamp=1689656981443, value=319338
  • 从上面这行开始,往后看10万行
sed -n '219835,319835p' data.all.tagv.txt | awk '{print $(NF)}' | awk -F\= '{print $2}' | sort -n > data.all.tagv.result.txt
  • 修改成删除命令
vi data.all.tagv.result.txt

sed -i 's:^:tsdb uid delete tagv :g'
sed -i 's:$:;echo deleted!'

2024-04-19T03:29:55.png
然后放到线上去执行。

  • 删除\x00 tagv的值

当研发把异常代码注释掉之后,开始删除\x00。随着新数据metric+tags到达,将申请uid,然后促使\x00的tagv value开始递增变化。

hbase shell
deleteall 'tsdb-uid',"\x00",'id:tagv'
scan 'tsdb-uid', {LIMIT => 3, REVERSED => false}
  • 第二天一大早查看删除情况
    2024-04-19T03:30:55.png
  • 查看uid情况
    2024-04-19T03:31:04.png
  • 模拟数据打入,居然成功了。
    可能是opentsdb内部机制,实现了uid回收。
    2024-04-19T03:31:24.png

(4)剩余的异常uid删除

现在1600多万的异常uid还在以每天60左右的速度删除。当删除之后,基本上opentsdb数据库中基于tagv的value异常值将得到极大缓解。

创造者

Davide Libenzi是一位在计算机科学领域有着深厚研究和实践经验的专家,尤其在Linux内核和网络编程方面有着突出的贡献。他是Epoll(Linux内核中用于处理大规模并发连接的I/O多路复用机制)的创造者,这个技术在许多高并发服务器应用程序中都有着广泛应用。

Davide Libenzi在他的职业生涯中还参与了许多其它重要的项目。他一直在Linux社区中积极贡献代码,改进Linux内核,提高了Linux在高性能计算、高并发网络处理等方面的能力。

在开发Epoll的同时,他也发表了一些关于操作系统设计、网络编程和高性能计算的论文,为开发者社区提供了非常有价值的参考。

总的来说,Davide Libenzi是一位在Linux以及网络编程领域有深远影响力的科学家和工程师。

为什么创造epoll

Davide Libenzi创造了Epoll以解决在现有I/O多路复用技术中存在的一些关键问题。

其中,最关键的问题是传统的I/O多路复用技术(如select和poll)所存在的性能问题。它们在处理大量并发连接时,性能会线性下降,特别是处理上千甚至上万的并发连接时,性能会严重降低。

因此,Epoll是为了解决这个问题被引入的。它采用了一种全新的方式来监视和管理大量的文件描述符,可以高效的处理大量并发连接,且不会因为文件描述符的数量增加而导致性能下降。

此外,Epoll引入了边缘触发方式,提供了更加灵活的文件描述符状态通知方式,以适应在不同的应用场景中。

综合来看,Epoll的创造是为了解决在处理大量并发连接时的性能问题,以及提供一种更加灵活的文件描述符状态通知方式,从而满足在不同应用环境中的需求。

关键特性:

  1. 高性能和可扩展性:传统的 I/O 多路复用技术如select和poll,随着监控的文件描述符(File descriptor)数量增多,性能会急剧下降,特别是在大规模并发连接下,它们的效能会受到严重影响。但是,Epoll可以高效地处理大量并发连接,而且它的性能并不会随着监控的文件描述符的数量增加而下降,因此具有很好的可扩展性。
  2. 边缘触发(ET)和水平触发(LT): Epoll支持两种触发模式,边缘触发和水平触发。边缘触发只会通知事件的状态变更(由未就绪变为就绪),而水平触发则会反复通知,直到该事件的就绪状态被清除。这使得Epoll在不同的应用场景中具有更大的灵活性。
  3. 一次性创建,多次使用:在使用Epoll之前,只需要创建一次Epoll实例。然后,可以多次使用该实例,从而避免了频繁创建和销毁实例的开销,提高了程序的运行效率。
  4. 占用资源少:Epoll中,创建后的实例是全局的,所有的socket在其上共享,这就使得Epoll所占用的系统资源较少,特别是在处理大量连接请求时。

这些特性使得Epoll在处理大量并发连接时表现出极高的性能,因此在许多需要处理大量客户端连接的服务器中,Epoll成为了关键的技术选择。

哪个内核引入

Epoll是在 Linux 2.5.44 内核版本中首次引入的,作为一个实验性的功能。之后在Linux 2.6版本中正式成为内核的一部分,且被广泛地应用并接受。因此,我们常常会听到,Epoll是Linux 2.6及其后续版本中的一个关键特性。

如何理解epoll

Epoll是用于处理大量并发连接的高效I/O多路复用技术,是Linux操作系统专门为解决C10K问题(即同时处理大量并发连接)而产生的。它是Linux 2.6内核引入的新的事件处理机制,相比于传统的select和poll,它的性能更高。

以下是Epoll的一些基础理解:

  1. 高效: Epoll可以识别出活跃的socket,这使得Epoll在处理大量并发连接时具有很高的效率。它避免了传统的select和poll方法需要对每个socket进行检查的问题。
  2. 可扩展性: Epoll的另一个优点是它不会随连接数增加而导致性能下降。这是因为Epoll使用一个事件表来跟踪活跃的socket,而不是每次都对所有socket进行检查。
  3. 工作方式: 你需要使用epollcreate来创建一个Epoll的实例。然后用epollctl添加、修改或删除你关心的事件,并用epoll_wait来等待事件的发生。

总的来说,理解Epoll的关键是明白它是如何利用事件表来高效地处理大量并发连接的。

更深层次的理解可能需要您有一定的计算机网络和操作系统知识,以便更好地理解Epoll的工作机制和它如何处理内核和socket之间的通信。

什么是中断?

中断是计算机系统中的一种机制,它允许处理器(CPU)暂停当前正在执行的任务,转而去处理一个紧急事件或者一个更高优先级的任务。中断可以由硬件设备产生,也可以由软件产生,其目的是让计算机能够响应外部或内部的事件,提高计算机系统的效率和响应速度。

硬件中断(Hardware Interrupts)

硬件中断通常由外部设备触发,例如:

  • 键盘输入:用户按键时,键盘会向CPU发送一个中断信号,CPU停止当前任务,转而处理按键事件。
  • 鼠标移动或点击
  • 网络数据包到达
  • 硬盘I/O操作完成

硬件中断允许设备在需要CPU注意时立即获得响应,无需等待CPU主动轮询检查设备状态,大大提高了效率。

软件中断(Software Interrupts)

软件中断,又称为异常或陷阱(trap),是由执行特定指令的结果触发的,用于实现系统调用、处理异常情况等。例如:

  • 系统调用:当应用程序需要请求操作系统提供的服务时(如文件操作、网络通信等),会通过触发软件中断来实现。
  • 异常处理:如除零错误、访问无效内存地址等,会触发软件中断,由操作系统接管处理。

中断处理

当中断发生时,CPU会立即暂停当前执行的任务(中断当前的指令序列),保存当前任务的状态,然后转而执行与该中断相关的中断处理程序。一旦中断被处理完成,CPU会恢复之前被中断的任务,继续执行。

中断机制使得计算机能够更加高效地管理多任务和响应外部事件,是现代操作系统并发处理的基础。

kernel_interrupt 是硬中断么?

kernel_interrupt 不直接指一个特定的硬中断或软中断,但它通常与操作系统中断处理机制相关。在操作系统中,中断可以分为两大类:硬中断(Hardware Interrupts)和软中断(Software Interrupts)。

  • 硬中断通常由硬件设备产生,如键盘输入、网络包接收等。当硬件设备需要CPU注意时,它会发送一个信号到CPU,这个信号称为硬中断。硬中断会导致当前CPU执行流被打断,以处理该硬件事件。
  • 软中断(也称为软件中断或软件信号),是由软件层触发的中断,用于处理诸如网络数据包处理、延迟的文件系统操作等任务。软中断可以由硬中断处理程序触发,以延迟执行一些不紧急的任务,或者由软件主动触发。

当我们提到kernel_interrupt时,我们可能是在讨论与内核中断处理相关的代码或函数,这包括对硬中断和软中断的响应。在具体的上下文中,它可能涉及到硬中断的处理,软中断的调度,或者两者的一些结合。

因此,如果你的问题是想了解kernel_interrupt是否仅指硬中断,答案是不一定。它取决于kernel_interrupt的具体使用场景和上下文。在操作系统的内核代码中,处理中断的机制通常既包括硬中断的直接响应,也包括软中断或任务队列等机制来延迟处理或分发任务。

软中断一般包括哪些,给出一些示例吧

软中断(Softirqs)和任务队列(Tasklets)是Linux内核中处理中断的机制,它们用于在中断上下文之外执行中断服务例程的一部分工作。这样做可以减少在硬件中断处理程序中花费的时间,从而允许系统更快地响应其他硬件中断。软中断和任务队列通常用于处理不需要立即完成的任务,如数据包处理、块设备操作等。下面是一些软中断的示例:

  1. 网络传输(NET_TX_SOFTIRQ)

    • 用于处理网络设备的传出数据包。当网络设备驱动有数据要发送时,它会触发这个软中断来处理发送队列中的数据包。
  2. 网络接收(NET_RX_SOFTIRQ)

    • 用于处理接收到的网络数据包。当网络设备接收到数据包,并且硬件中断处理程序将数据包放入接收队列后,这个软中断被触发以进一步处理这些数据包。
  3. 块设备软中断(BLOCK_SOFTIRQ)

    • 用于处理块设备的请求,如硬盘IO操作。这个软中断会处理完成的IO请求,比如数据读写操作的后处理。
  4. 任务队列软中断(TASKLET_SOFTIRQ)

    • 用于执行"任务队列"中的任务。任务队列是一种机制,允许将任务推迟到稍后在软中断上下文中执行。这是一种用于安排延迟执行的简单方法。
  5. 调度器软中断(SCHED_SOFTIRQ)

    • 用于执行调度器的工作,比如选择下一个要运行的进程。这个软中断确保系统能够及时重新调度,以响应运行在系统上的不同进程的需求。
  6. RCU软中断(RCU_SOFTIRQ)

    • 用于执行读-拷贝更新(Read-Copy-Update,RCU)同步机制的回收工作。RCU是一种避免在读多写少的数据结构上使用锁的机制,它通过延迟释放来保证数据结构的一致性。

这些软中断的实际使用和触发方式取决于系统的具体需求和硬件设备的行为。通过这种机制,Linux内核可以有效地处理各种硬件中断产生的任务,同时保持系统的响应性和性能。

昨天晚上需要停电,把办公区的PC服务器进行了关机。今早启动,有些服务起不来,一看日志,原来是redis运行异常。

查看日志,报告内存错误。

*** Preparing to test memory region 7fe9b524c000 (4096 bytes)
.O.O.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E
!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!

=== REDIS BUG REPORT END. Make sure to include from START to END. ===

       Please report the crash by opening an issue on github:

           http://github.com/redis/redis/issues

  If a Redis module was involved, please open in the module's repo instead.

  Suspect RAM error? Use redis-server --test-memory to verify it.

  Some other issues could be detected by redis-server --check-system

使用命令redis-server --test-memory 4096
2024-03-22T03:11:54.png
2024-03-22T03:13:32.png

修了半天没修完。

算了,re do cluster。

./create-cluster start
./create-cluster create

需要分析

嵌入式开发是指为非通用计算机硬件创建软件的过程,这类硬件通常专为特定任务或功能而设计。由于嵌入式开发可以包括从简单的微控制器程序到复杂的操作系统和应用程序的开发,因此所需的笔记本电脑规格可以根据具体任务而大不相同。不过,可以提供一些一般性的指南:

处理器(CPU):强大的处理器可以加速编译过程,特别是对于大型项目或使用高级语言(如C++或Java)的情况。Intel Core i5或i7,或者是AMD Ryzen 5或7系列是不错的选择。

内存(RAM):嵌入式开发工作往往需要同时运行多个应用程序,如代码编辑器、编译器和模拟器。因此,较大的RAM有助于提高多任务处理能力。建议至少8GB,如果是更复杂的项目,16GB或更多将更为合适。

存储(硬盘):SSD(固态硬盘)由于其更快的读写速度,对于加速编译过程和程序启动大有裨益。至少256GB的SSD是起步,如果项目较大,512GB或更多会更好。

操作系统兼容性:根据你的目标平台和开发环境,选择一个兼容你工具链的操作系统很重要。Linux是许多嵌入式开发项目的首选,因此确认笔记本电脑支持或预装了Linux,或者可以轻松安装Linux,将是一个加分项。当然,Windows和macOS也支持大量的开发工具。

端口和连接性:嵌入式开发常常需要将笔记本电脑连接到各种硬件设备,比如调试器、程序员、原型板等。因此,多种类型的端口(如USB-A、USB-C/Thunderbolt、甚至串行端口适配器)的可用性是需要考虑的因素。

图形处理能力:虽然大多数嵌入式开发不需要强大的图形处理能力,但如果你的项目涉及到复杂的用户界面或者需要进行图形渲染,那么具备合适图形处理单元(GPU)的笔记本电脑会是必需的。

根据你的具体需求和预算,可以在以上指标中做出权衡。如果你的项目相对简单,或者你刚开始接触嵌入式开发,可能不需要顶级配置的笔记本电脑。但对于更复杂的开发工作,选择配置更高的机型将能提供更为流畅和高效的开发体验。

需求定位

基本参数,i5cpu/8GB内存/1TB SSD,品牌选择dell或者联想。

方案

方案一 戴尔(DELL) latitude 5420

参数:14英寸/酷睿i5-1145G7/8G/512G
价格:4599元
地址:https://item.jd.com/100054866499.html#crumb-wrap
2024-03-22T06:18:17.png

方案二 联想(Lenovo)ThinkBook 14

参数:14英寸/13代i5-13500H/16G/1T
价格:4399元
地址:https://item.jd.com/100062061094.html
2024-03-22T06:18:40.png

注意哦,各种urlencode以及signature。对querystring,参数不能少,顺序不能乱。

#!/bin/bash
set -e
#================================================================================================================#
# 功能:用于更新阿里云域名IP,实现DDNS功能
#
# 在 http://www.gebi1.com/forum.php?mod=viewthread&tid=287344&page=1&_dsign=8f94f74c 提供的脚本文件基础上修改的。
# ghui, modified 12/2/2019
# 在 N1 debian Buster with Armbian Linux 5.3.0-aml-g12 手动执行/定时任务(crontab)执行测试通过
#================================================================================================================#
#
# 使用方法:
#
# 方法1. 外部参数
# 修改源码,将对应参数 修改为$1,$2,$3,$4,$5,$6 
# aliddns.sh <aliddns_ak> <aliddns_sk> <aliddns_subdomain> <aliddns_domain> <aliddns_iptype> <aliddns_ttl>
# 示例(A 代表 IPv4,AAAA 代表 IPv6): 
# 执行:aliddns.sh "xxxx" "xxx" "test" "mydomain.site" "A" 600
# 执行:aliddns.sh "xxxx" "xxx" "test" "mydomain.site" "AAAA" 600
#
# 方法2. 内部参数
# 修改源码,将$1,$2,$3,$4,$5,$6 替换为对应参数
# 
# 示例: 
# aliddns_ak="xxxx"
# aliddns_sk="xxx"
# aliddns_subdomain="test"
# aliddns_domain="mydomain.site"
# aliddns_iptype="A"
# aliddns_ttl=600 
# 执行:aliddns.sh
#
#================================================================================================================#
#--------------------------------------------------------------
# 参数
#
# (*)阿里云 AccessKeyId 
aliddns_ak=xxx
# (*)阿里云 AccessKeySecret 
aliddns_sk=xxx
# (*)域名:test.mydomain.com 
aliddns_subdomain=test #'test'
aliddns_domain=xxx.com #'mydomain.com'
# (*)ip地址类型:'A' 或 'AAAA',代表ipv4 和 ipv6
aliddns_iptype=A # 'A' 或 'AAAA',代表ipv4 和 ipv6
# TTL 默认10分钟 = 600秒 
aliddns_ttl=600 #"600"
#--------------------------------------------------------------
machine_ip=""
ddns_ip=""
aliddns_record_id=""
if [ "$aliddns_subdomain" = "@" ]
then
  aliddns_name=$aliddns_domain
else
  aliddns_name=$aliddns_subdomain.$aliddns_domain
fi
now=`date`
echo "**************************************************"
echo "$now"
echo "$aliddns_name"
function getMachine_IPv4() {
    echo $(/usr/bin/wget -qO- -t1 -T2 http://ip.3322.net)
}
function getMachine_IPv6() {
    ipv6=`ip addr | grep "inet6.*global" | grep -v "deprecated" | awk '{print $2}' | awk -F"/" '{print $1}' | sed -n '1,1p'`
    echo $ipv6
}
function getDDNS_IP() {
    current_ip=`nslookup -query=$aliddns_iptype $aliddns_name | grep "Address" | grep -v "#53" | awk '{print $2}'`
    echo $current_ip
}
function urlencode() {
    # urlencode <string>
    out=""
    while read -n1 c
    do
        case $c in
            [a-zA-Z0-9._-]) out="$out$c" ;;
            *) out="$out`printf '%%%02X' "'$c"`" ;;
        esac
    done
    echo -n $out
}
function enc() {
    echo -n "$1" | urlencode
}
function send_request() {
    local args="AccessKeyId=$aliddns_ak&Action=$1&Format=json&$2&Version=2015-01-09"
    local hash=$(echo -n "GET&%2F&$(enc "$args")" | openssl dgst -sha1 -hmac "$aliddns_sk&" -binary | openssl base64)
    curl -s "http://alidns.aliyuncs.com/?$args&Signature=$(enc "$hash")"
}
function get_recordid() {
    grep -Eo '"RecordId":"[0-9]+"' | cut -d':' -f2 | tr -d '"'
}
function query_recordid() {
    send_request "DescribeSubDomainRecords" "SignatureMethod=HMAC-SHA1&SignatureNonce=$timestamp&SignatureVersion=1.0&SubDomain=$aliddns_name&Timestamp=$timestamp&Type=$aliddns_iptype"
}
function update_record() {
    send_request "UpdateDomainRecord" "RR=$aliddns_subdomain&RecordId=$1&SignatureMethod=HMAC-SHA1&SignatureNonce=$timestamp&SignatureVersion=1.0&TTL=$aliddns_ttl&Timestamp=$timestamp&Type=$aliddns_iptype&Value=$(enc $machine_ip)"
}
function add_record() {
    send_request "AddDomainRecord&DomainName=$aliddns_domain" "RR=$aliddns_subdomain&SignatureMethod=HMAC-SHA1&SignatureNonce=$timestamp&SignatureVersion=1.0&TTL=$aliddns_ttl&Timestamp=$timestamp&Type=$aliddns_iptype&Value=$(enc $machine_ip)"
}
if [ "$aliddns_iptype" = 'A' ]
then
    echo "ddns is IPv4."
    machine_ip=`echo "$(getMachine_IPv4)"`
    echo "machine_ip = $machine_ip"
    aliddns_record_id=$aliddnsipv4_record_id
else
    echo "ddns is IPv6."
    machine_ip=`echo "$(getMachine_IPv6)"`
    echo "machine_ip = $machine_ip"
    aliddns_record_id=$aliddnsipv6_record_id
fi
ddns_ip=`echo "$(getDDNS_IP)"`
echo "ddns_ip = $ddns_ip"
if [ "$machine_ip" = "" ]
then
    echo "machine_ip is empty!"
    exit 0
fi
if [ "$machine_ip" = "$ddns_ip" ]
then
    echo "skipping\n"
    exit 1
fi
echo "start update..."
timestamp=`date -u "+%Y-%m-%dT%H%%3A%M%%3A%SZ"`
if [ "$aliddns_record_id" = "" ]
then
    aliddns_record_id=`query_recordid | get_recordid`
    echo "----------------" $aliddns_record_id "\n"
    
    if [ "$aliddns_iptype" = 'A' ]
    then
        aliddnsipv4_record_id=$aliddns_record_id
    else
        aliddnsipv6_record_id=$aliddns_record_id
    fi
fi
#add support */%2A and @/%40 record
if [ "$aliddns_record_id" = "" ]
then
    echo "add record starting"
    aliddns_record_id=`add_record | get_recordid`
    if [ "$aliddns_record_id" = "" ]
    then
        echo "aliddns_record_id is empty. \n"
    else
        if [ "$aliddns_iptype" = 'A' ]
        then
            aliddnsipv4_record_id=$aliddns_record_id
        else
            aliddnsipv6_record_id=$aliddns_record_id
        fi
        echo "added record $aliddns_record_id \n"
    fi
else
    echo "update record starting"
    update_record $aliddns_record_id
    echo "updated record $aliddns_record_id \n"
fi

参考:https://developer.aliyun.com/article/1095952