虚拟机安装群晖

虚拟机版本 VMware Workstation 16

  1. 兼容性选择 15.x
  2. Linux - 其他 Linux 4.x 内核 64 位
  3. 添加启动盘时选为 SATA
  4. 若启动后搜不到 ip, 可以尝试手动编辑虚拟机 vmx 配置文件,修改网卡为 e1000e

DSM 6.17 成功

1
2
3
4
参考:https://www.openos.org/threads/dsm-6-2-3-2020-12-27.29/ 
引导:1.02b
机型:DS3617+
引导方式 传统BIOS

DSM 6.23 成功

1
2
3
4
5
参考:https://www.openos.org/threads/dsm-6-2-3-2020-12-27.29/
引导: v1.04b
机型: DS3617+
引导方式: 传统BIOS
特殊处理:添加数据盘为 SATA 安装时提示找不到硬盘,切换成 SCSI 数据盘时可以正常安装

DSM 7.0.1 成功

1
2
3
4
参考:https://www.openos.org/threads/dsm-7-0-redpill-108.3536/
引导:211126 redpill-DS3615xs_7.0.1-42218_26.zip
机型 DS3615
引导方式 传统BIOS

Frp之后的群晖获取真实来访ip

群晖环境

  • 机型 DS3617xs
  • 版本 6.1.7
  • 开启 docker (镜像:nginx, stilleshan/frpc)
  • 开启 ssh

1. 修改群晖 nginx 配置

备份 DSM.mustache

1
cp /usr/syno/share/nginx/DSM.mustache{,.bak}

在第一个 server 块加入以下代码

1
2
set_real_ip_from 172.17.0.0/16;
real_ip_header X-Real-IP;

重启 nginx

1
nginx -s stop

2. 搭建 nginx 反代群晖

2.1 在 File Stationdocker 目录下新建 nginx 文件夹, 然后创建 default.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 443 ssl proxy_protocol;

real_ip_header proxy_protocol;
real_ip_recursive on;
set_real_ip_from 172.17.0.0/16;

ssl_certificate /etc/nginx/conf.d/cert;
ssl_certificate_key /etc/nginx/conf.d/key;

location / {
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
proxy_pass http://172.17.0.1:5000;
}
}

然后将 ssl 证书也导入 nginx 文件夹,并改名为 certkey

2.2 创建 nginx 容器

创建时选择 高级设置 > 存储空间 > 添加文件夹 ,选择 docker/nginx 文件夹,装载路径 设置为 /etc/nginx/conf.d ,然后 端口设置本地端口 设置为 50443容器端口 设置为 443类型 设置为 TCP ,然后正常创建即可

3. 创建 frpc 客户端

3.1 在 File Station 中 docker 目录下新建 frpc 文件夹, 然后创建 frpc.ini

1
2
3
4
5
6
7
8
9
[common]
// 你的 frp 服务器配置

[dsm]
type = tcp
local_port = 50443 // 上面创建的 nginx 的映射的本地端口
local_ip = 172.17.0.1
remote_port = 33333 // 看自己喜好选择端口即可
proxy_protocol_version = v2

3.2 创建 frpc 容器

创建时选择 高级设置 > 存储空间 > 添加文件,选择 docker/frpc/frpc.ini 文件,装载路径设置为 /frp/frpc.ini ,然后正常创建即可

4. 访问 https://你的frp服务器地址:33333 如能正常访问,应该就说明配置好了

这个问题是由php的imap扩展解析hostname引起的网络问题。

有两种解决办法:

1
2
3
4
5
6
7
// 1. 关闭imap扩展(不建议)
// 2. 修改hosts文件,使imap能正确的将hostname识别为127.0.0.1
// 2.1 获取hostname
hostname
// 2.2 将上一条命令得到的hostname写入hosts文件
127.0.0.1 localhost your-hostname
::1 localhost your-hostname

Terminal Tools

  • autojump
  • autosub
  • axel
  • brew
  • ccat
  • ffsend
  • Gogs
  • hexo
  • htop
  • iTerm
  • mycli
  • music-dl
  • ncdu
  • noti
  • oh-my-zsh
  • pgcli
  • prettyping
  • quick-look-plugins
  • sshpass
  • terminalizer
  • terminal-notifier
  • tldr
  • trans
  • wrk

Mac Tools

  • Alfred
  • Bartender
  • BetterZip
  • Betwixt
  • Caffeine
  • CheatSheet
  • Chrome
  • ClashX Pro
  • DataGrip
  • Expressions
  • Google Chrome
  • IntelliJ IDEA
  • iRamDisk
  • Stats(free) / iStat Menus
  • iTerm
  • Macs Fan Control
  • Moom
  • Motrix
  • Navicat Premium
  • NeteaseMusic
  • Numi
  • PhpStorm
  • Postman
  • Proxifier
  • PyCharm
  • ShadowsocksX-NG
  • SimplyRAR
  • Snap
  • Sourcetree
  • Syncthing
  • stretchly
  • The Unarchiver
  • Transmit
  • Typora
  • VMware Fusion

PHP多进程

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
// 转化为守护进程
function daemonize()
{
$pid = pcntl_fork();
if ($pid == -1)
{
die("fork(1) failed!\n");
}
elseif ($pid > 0)
{
//让由用户启动的进程退出
exit(0);
}

//建立一个有别于终端的新session以脱离终端
posix_setsid();

$pid = pcntl_fork();
if ($pid == -1)
{
die("fork(2) failed!\n");
}
elseif ($pid > 0)
{
//父进程退出, 剩下子进程成为最终的独立进程
exit(0);
}
}

daemonize();

// 设定子进程数量
$worker_num = 10;

// 设定接收到信号时操作
pcntl_signal(SIGINT, function () {
exit();
});

while (true) {
$pid = pcntl_fork();

//父进程和子进程都会执行下面代码
if ($pid == -1) {
exit("fork error");
} else if ($pid) {
// 在父进程执行
static $execute = 0;
$execute++;
if($execute >= $worker_num) {
pcntl_wait($status);
$execute--;
}
} else {
// 在子进程执行
// 业务逻辑放在这

exit();
}

// 信号处理函数在此执行
pcntl_signal_dispatch();
}

如何正确设置CRON定时任务

转载自火丁笔记,原文链接http://huoding.com/2016/12/12/573

相信很多人看了标题后都会纳闷:设置 CRON 定时任务有什么难的?不过请相信我,正确设置 CRON 真的不是一件简单的事情!各位看官不妨听我慢慢道来。

关于 CRON,出镜率最高的一个问题莫过于:为什么手动执行一切正常,放到 CRON 里就不执行呢?实际上此类问题多半是因为环境变量导致的,答案就在配置文件里:

catlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan, ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun, ...
# | | | | |
# * * * * * user-name command to be executed

比如说,你的 PHP 命令位于 /usr/loca/bin 目录,而你在 profile 里已经把这个目录加到了环境变量 PATH 里,不过 crontab 里可没有 /usr/loca/bin 目录,于是就出问题了。对付此类问题的方法很简单,那就是设置 CRON 的时候尽可能使用完整的全路径。

此外,有人喜欢直接在 /etc/crontab 里配置定时任务,这同样是十恶不赦的做法,多数时候,我们都应该使用 crontab -e 的方法来设置,原因是这样有语法检查。

如果本文的内容仅限于此类小菜,那么未免有些太对不起各位看官,下面上一道硬菜:设置一个 PHP 脚本,每分钟执行一次,怎么搞?听起来这分明就是一道送分题啊:

```* * * * * /path/to/php /path/to/file`
让我们设想如下情况:假如上一分钟的 A 请求还没退出,下一分钟的 B 请求也启动了,就会导致出现 AB 同时请求的情况,如何避免?答案是 flock,它实现了锁机制:

flock -xn /tmp/lock /path/to/php /path/to/file
让我们再来重放一下故障场景:假如上一分钟的 A 请求还没退出,下一分钟的 B 请求也启动了,那么 B 请求会发现 A 请求还没有释放锁,于是它不会执行。

看起来似乎完美解决了问题,不过让我们在加入一点特殊情况:假如因为某些无法预知的原因,导致脚本不能正常结束请求,进而导致不能正常释放锁,那么后续所有其它的 CD 等请求也都无法执行了,如何避免?答案是 timeout,它实现了超时控制机制:

timeout -s SIGINT 100 flock -xn /tmp/lock /path/to/php /path/to/file
让我们再来重放一下故障场景:假如上一分钟的 A 请求还没退出,下一分钟的 B 请求也启动了,那么 B 请求会发现 A 的请求还没有释放锁,于是它不会执行,不过下下分钟的 C 请求肯定能执行,因为在这之前,A 请求已经因为超时被 timeout 干掉了。

当然,无论是锁机制,还是超时控制禁止,我们都可以自己实现,不过既然系统已经提供了这样的功能,那么除非你对自己的编码能力有自信,否则还是使用系统的吧。

定制化GridView

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
GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
['class' => 'yii\grid\SerialColumn'],

[
'label' => '标题1',
'value' => function ($model) {
return $model->name;
}
],

[
'class' => 'yii\grid\ActionColumn',
'header' => '标题2',
'template' => '{add-child} {update} {delete}',
'buttons'=>[
'add-child' => function($url, $model, $key) {
return Html::a('添加商品', '#', [
'class' => 'layerAddChild',
'data-content-id' => $key
]);
},
'update' => function($url, $model, $key) {
return Html::a('编辑商品', '#', [
'class' => 'layerUpdateBrand',
'data-content-id' => $key
]);
},
'delete' => function($url, $model, $key) {
return Html::a('删除商品', '#', [
'class' => 'layerDeleteBrand',
'data-content-id' => $key
]);
},
],
],
],
]);