0%

搭建squid代理服务器

首先在代理服务器上安装squid
yum install squidapt install squid
编辑
/etc/squid/squid.conf
在上面增加一条acl定义

1
acl trustedhost src 47.103.123.187/32

在下面适当的位置增加一条允许访问

1
2
3
4
#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#
http_access allow trustedhost

确保squid服务器处于运行状态

1
service squid status

如果服务没有运行则启动即可

1
service squid start

如果已经启动则重新加载配置

1
squid -k rec

squid默认运行在3128端口,有必要的话把它通过网关路由器或通过ssh隧道映射到公网。

在Ubuntu上配置apt的代理

编辑/etc/apt/apt.conf.d/proxy.conf文件
添加条目

1
2
Acquire::http::Proxy "http://proxy-IP-address:proxyport/";
Acquire::https::Proxy "http://proxy-IP-address:proxyport/";

在CentOS上配置yum的代理

编辑/etc/yum.conf文件
添加条目

1
http_proxy=http://proxy-IP-address:proxyport

Linux上

在Linux上我们可以很方便地实现端口转发,比如通过SSH-Tunnel,使用

1
ssh -CfNg -L 监听IP:监听端口:目标IP:目标端口 localhost

甚至可以通过目标局域网暴露出来的一个ssh端口转发到另一个局域网的内部的服务器上,更详细的参考另一篇之前写的文章ssh-tunnel

通过ssh开启的端口转发在系统重启后需要重新建立,而且如果需要建立很多端口转发这样不方便管理,这时候我们可以使用rinetd工具

apt install rinetd会以服务的形式安装rinetd

service rinetd status可以查看运行状态

配置文件在/etc/rinetd.conf

修改配置后可以通过service rinetd reload重新加载配置而不必重启服务

日志文件在/var/log/rinetd.log

Windows上

在Windows上其实也有个内置的工具可以完成端口转发,而且也很方便

开启端口转发

1
netsh interface portproxy add v4tov4 listenaddress=监听IP listenport=监听端口 connectaddress=目标IP connectport=目标端口

其中监听IP可以填0.0.0.0表示接收来着任意网卡接口的数据。

查看端口转发

1
netsh interface portproxy show v4tov4

重启后也会保留设置,除非主动关闭

关闭端口转发

1
netsh interface portproxy delete v4tov4 listenaddress=本地IP listenport=本地端口

Ubuntu

1
cat /var/log/syslog | grep cron

修改rsyslog配置

1
vim /etc/rsyslog.d/50-default.conf

解开注释

1
cron.* /var/log/cron.log

重启rsyslog

1
service rsyslog restart

查看crontab日志

1
tail -f /var/log/cron.log

CentOS

1
vim /var/log/cron
1
vim /var/spool/mail/root

获取当前日期

1
date "+%Y-%m-%d"

获取当前时间

1
date "+%H:%M:%S"

获取1天前的日期

1
date -d"-1 day" "+%Y-%m-%d"

获取1个月前的日期

1
date -d"-1 months" "+%Y-%m-%d"

获取1小时前的日期时间

1
date -d"-1 hours" "+%Y-%m-%d %H:%M:%S"

备份命令

1
mongodump -h <ip>:<port> -d <数据库名> -u root -p pass -o <备份目录> [--gzip]

例如

1
2
TRADE_DATE=`date "+%Y%m%d"`
mongodump -h 127.0.0.1:27017 -d ftresearch -u root -p pass -o /var/lib/mongodb/backup/$TRADE_DATE --gzip

其中--gzip表示压缩备份文件,对于压缩

还原备份

还原前我们先启动一个新的MongoDB的容器,把容器的27017端口映射到宿主机的47017。

1
docker run -dit -p 47017:27017 --name=mongo_test -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=pass mongo:3.6.2

执行还原命令

1
mongorestore -h <ip>:<port> -d <数据库名> -u root -p pass <备份目录> --drop --gzip

例如

1
mongorestore -h 127.0.0.1:47017 -u root -p pass /var/lib/mongodb/backup/20230709 [--drop] [--gzip]

其中--drop的意思是如果要还原的数据库已存在,则先删除。
--gzip表示还原的是压缩备份,如果备份时没加--gzip参数则还原时也不加。

在NTP(Network Time Protocol)中,ntpdate是一个命令行工具,用于将系统时钟与网络上的NTP服务器进行同步。它可以通过向NTP服务器发送请求并根据响应调整本地时钟来实现时间同步。

CentOS上通常自带ntpdate,如果找不到ntpdate命令可以通过下面命令安装:

1
yum install -y ntpdate

Ubuntu上通常没有自带ntpdate,可以通过下面命令安装:

1
apt install ntpdate

常用的同步命令格式

1
ntpdate [-u] <server>

-u 参数表示使用非特权(unprivileged)模式进行时间同步。当以非特权模式运行时,ntpdate不需要超级用户权限来执行同步操作。这对于普通用户来说很有用,因为他们通常没有足够的权限来使用特权模式。

通过使用 -u 参数,ntpdate会使用一个高位端口(1024以上)来发送NTP请求,而不是使用标准的NTP端口(123号端口)。在大多数操作系统中,只有特权用户才能使用低位端口,因此非特权模式是普通用户进行时间同步的常见选择。

常用的ntp服务器

1
2
3
4
5
cn.pool.ntp.org  中国开源免费NTP服务器
ntp1.aliyun.com 阿里云NTP服务器
ntp2.aliyun.com 阿里云NTP服务器
time1.aliyun.com 阿里云NTP服务器
time2.aliyun.com 阿里云NTP服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Screen {
public:
template<typename... Arg>
void print(int x, int y, const char* fmt, Arg&&... arg) {
move_to(x, y);
printf(fmt, forward<Arg>(arg)...);
}

void move_to(int x, int y) {
printf("\033[%d;%dH", x+1, y+1); // 移动光标
}

void hide_cursor() {
printf("\033[?25l"); // 隐藏光标
}

void show_cursor() {
printf("\033[?25h"); // 显示光标
}

void clear() {
printf("\033[2J\n"); // 清屏
}
};

BusyWaitableCondition是忙寻版的可等待条件

WaitableCondition是睡眠版的可等待条件

两者实现相同功能,只是忙寻版会占用一个cpu core,而睡眠版会陷入睡眠在需要时才被唤醒继续执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

#include <iostream>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <thread>
#include <future>

using namespace std;

class BusyWaitableCondition {
public:
void Wait() {
while(!_flag);
}

void Notify() {
_flag = true;
}

void Reset() {
_flag = false;
}

private:
std::atomic<bool> _flag = false;
};

class WaitableCondition {
public:
void Wait() {
std::unique_lock<std::mutex> lock(_mutex);
if(!_flag) {
_cv.wait(lock, [this](){ return _flag; });
}
}

void Notify() {
do {
std::lock_guard<std::mutex> lock(_mutex);
_flag = true;
} while(false);
_cv.notify_all();
}

void Reset() {
std::lock_guard<std::mutex> lock(_mutex);
_flag = false;
}

private:
std::mutex _mutex;
std::condition_variable _cv;
bool _flag = false;
};

int main() {
WaitableCondition c1;
auto fut1 = std::async(std::launch::async, [&]{
c1.Wait(); // 等待条件1满足才继续执行
cout << "Hello 1" << endl;
});
auto fut2 = std::async(std::launch::async, [&]{
c1.Wait(); // 等待条件1满足才继续执行
cout << "Hello 2" << endl;
});

this_thread::sleep_for(chrono::seconds(1));
cout << "1" << endl;
cout << "2" << endl;
cout << "3" << endl;
cout << "4" << endl;
cout << "5" << endl;
c1.Notify(); // 通知条件1满足了

return 0;
}

上面的例子中有定义Reset方法,但是没有演示,它的作用是将条件设置为否,以便可以再次等待,然后可以重新触发。

如果只需要等待一个一次性事件而不需要Reset方法那么可以用std::promise+std::future进行更简单的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class WaitableEvent {
public:
WaitableEvent(): _fut(_promise.get_future()) {

}

void Wait() const {
_fut.wait();
}

void Notify() {
_promise.set_value();
}

private:
std::promise<void> _promise;
std::future<void> _fut;
};

sscanf是C语言库函数,作用是从字符串中进行格式化解析。

例如在读文本文件时先用fgets把整行读入buffer再用sscanf进一步解析buffer的内容。

头文件

1
#include <stdio.h>
1
#include <cstdio>

函数原型

1
int sscanf(const char *str, const char *format, ...);

参数

  • str – 以’\0’结尾的字符串,解析的数据源。
  • format – 格式化字符串,描述解析的规则
  • args… – 对应format参数,提供需要读取的变量的地址

返回值

返回读取到的参数的个数。

当没有读入任何数据时,可能是因为走到了字符串的结尾,也可能是因为当前将要读入的字符串无法转换为指定的格式。

如果因为读到了字符串结尾则返回EOF,即-1,如果是因为其他原因没有读入任何参数,则返回0。

样例

1
2
3
4
5
6
int i;
float f;
double ff;
char buf[128];
char ch;
sscanf("123 3.14 2.71828 \t\r\n Hello x", "%d %f %lf %s %c", &i, &f, &ff, buf, &ch);

说明

%d表示读取一个整数

%f表示读取一个浮点数

%lf表示读取一个双精度浮点

%s表示读取一个字符串

%c 表示读取一个字符

在format中空格表示跳过任意多的空白字符(空白字符包括' ', '\t','\r','\n'

%d %f %lf %s这些格式自带跳过空白字符即使前面没有空格。而%c不会自动跳过空白字符。

高级用法

指定读取字符串的长度

1
2
char buf[5];
sscanf("Hello", "%4s", buf);

我们的buf只有5个字节大小,考虑到sscanf读取的字符串会在结尾加上’\0’,所以我们最多读取4个自己,可以在%s中间加上希望读取的长度。

这同样适用于%d %f %lf仍然表示最多读取的字符串长度,只是会在读取后会将字符串转换成相应类型的数据。

丢弃读取内容

%后面加上*可以丢弃当前%表示的格式读取到的内容,即后面的参数列表不需要传入接收此项的数据地址。

1
2
char buf[5];
sscanf("Hello", "%*4s %s", buf);

丢弃了4个字符,再跳过任意多空白字符,然后读入一个字符串,所以这时读入的buf里只有"o"

读取指定的字符

通过%[]指定读取的字符,可逐个列出或通过-指定范围,也可以指定多个范围。

1
2
3
char buf1[128];
char buf2[128];
sscanf("helloWorld", "%[a-z]%s", buf1, buf2);

读取除指定字符外的字符

%[...]类似,通过%[^...]可以反向指定需要读取的字符,即除了什么字符以外的所有字符。

非常适合读取用某种字符分割的字符串。

1
2
3
char buf1[128];
char buf2[128];
sscanf("Hello,World", "%[^,]%s", buf1, buf2);

获取当前读取到的位置

通过在format中需要确定的位置插入%n获取

1
2
3
4
5
char buf1[128];
int n1;
char buf2[128];
int n2;
sscanf("Hello,World", "%[^,]%n%s%n", buf1, &n1, buf2, &n2);

n1 = 5, n2 = 11,注意%n实际上不是表示前一个%读入了多少字符,而是表示当前已经读到的位置,但是如果有需要我们可以通过这个参数算出每个参数读入了多少字符。

综合应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const char* p = "Hello,World,123.45,, Hello world   ,ttt";
int n;
char buf[128] = {0};
int r;
while(*p != '\0' && (r = sscanf(p, "%127[^,]%n", buf, &n)) != EOF) {
if(r == 0) {
buf[0] = '\0';
n = 0;
}
printf("%s\n", buf);
p += n;
if(sscanf(p, "%*c%n", &n) == EOF) break;
p += n;
}

这里演示了读入以,分割的多字段的情况。其中有一点需要说明,while条件里的sscanf我们是期望读当前位置到下一个,前的字符串,而当r==0时,说明当前就指在下一个,上,此时读入了0个参数,而buf中会保留这之前的内容,所以将其手动置空,而n也同样没有被修改,所以我们也将其置0,以便后面的输出和移动可以用相同的代码统一处理。

通过这种方式sscanf完全可以替代strtok,而且避免了strtok使用了内部静态变量带来的线程安全问题。

CMakeList.txt中,LINK_DIRECTORIES和rpath-link都是用来指定链接库路径的,但是有区别,看来具体例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 设置库路径
LINK_DIRECTORIES(
${PROJECT_ROOT_DIR}/libs/kTrade/libs
${PROJECT_ROOT_DIR}/libs/kTrade/libs/ama_3_9_8 # (1)
)

# ...省略若干...

TARGET_LINK_LIBRARIES(AmaMD
dl Engine rt pthread
kTradeUtils
amaquote g3log
ama
-Wunused-function
-Wl,-rpath,'$ORIGIN'/libs/Ama,-rpath,'$ORIGIN'/libs,-rpath-link,${PROJECT_ROOT_DIR}/libs/kTrade/libs/ama_3_9_8 # (2)
)

在注释(1)处的目录下面有libama.so库和其依赖的若干.so
如果只加注释(1)处的路径而不加注释(2)处的-rpath-link参数,则会报错找不到通过ama间接依赖的一系列.so的问题,
如果只加注释(2)处的-rpath-link参数而不加注释(1)处的路径,则会报错找不到ama库本身的问题。

解决方案是要么两处都加上,要么只加(1)处的路径,但是需要在TARGET_LINK_LIBRARIES中写出所有通过ama间接依赖到的库名字,这样就都会通过(1)处的路径找到它们并使它们参与到链接中。
总之,直接链接的库只会通过(1)处指定的路径查找,而间接链接到的库只会通过(2)处指定的路径进行查找。