crontab 每小时运行一次

先给出crontab的语法格式

对于网上很多给出的每小时定时任务写法,可以说绝大多数都是错误的!比如对于下面的这种写法:

00 *   * * *  #每隔一小时执行一次
00 */1 * * *  #与上面是相同的任务

这种是正确的吗?对照最前面给出的语法格式来看,这个定时任务在0分、每小时、每天、每月、每星期执行,看起来似乎很合理啊?前一个小时的0分和后一个小时的0分不就是相隔一个小时吗?这样的话不就是每隔一个小时执行吗?再仔细看看,好像有点不对劲,为什么是每小时的0分?如果我在11:55创建了定时任务,希望每小时执行一次,那么接下来这个定时任务是在12:55执行,还是在12:00执行?很显然,它是每小时的0分执行,也就是在12:00执行。

似乎没区别?那么请思考下,每隔3小时执行如何写?

网上给出的写法如下:

0 */3 * * * #错误的每隔3小时执行一次写法

这种也是错误的写法,这种能充分展示前面一小时执行一次带来的迷惑感,这种写法其实是每整3小时的0分执行一次。也就是说,它会在每天的3点、9点、12点...等3的整数倍时间点来执行,并不是真正意义上每隔3个小时执行,如同前面类似,如果你在11:56创建这种每3小时的定时任务,那么它会在12点就开始执行,而不是14:56执行。请看如下验证:

11:56分创建上面所谓的“每3小时执行一次”的定时任务:

4分钟后而不是3小时后,crontab执行了该定时任务:

事实上,对于这种 "0 /3 " 这种分钟时间点固定,小时按每小时或者每3小时的写法,都是从1点开始算倍数,比如每一小时就是1点、2点、3点,而每2小时就是2点、4点、6点,每3小时就是3点、6点、9点等等,以此类推。

除此之外,网上还有人给出这种的每小时执行一次的定时任务写法:

* */1 * * * #错误的每隔一小时执行一次,事实上每分钟执行一次

这种比前面那种还不如,它压根不是每小时执行,而是每分钟执行!为什么?因为分钟要求的是每分钟执行,而小时却要求每一个小时执行,这2个分明是冲突的时间策略。最终以分钟为准,所以它是每分钟执行一次。

真正的每隔一小时、每隔105分钟执行的定时任务应该如何写呢?如下:

*/60  * * * * #每60分钟即每小时执行一次
*/105 * * * * #每105分钟执行一次

原文链接:crontab每小时运行一次

Ubuntu18.04 永久修改 DNS

使用过Ubuntu18.04的朋友们应该会遇到域名无法解析的问题,这个时候要设置dns,但是比较恶心的是,18版本不能简单地修改/etc/resolv.conf,因为一旦重启,就恢复默认值了。
也不能修改 /etc/resolvconf/resolv.conf.d/base,因为无法保存修改。

解决办法:

sudo vi /etc/systemd/resolved.conf

如图所示,把DNS取消注释,添加DNS,保存退出,重启即可。

原文链接:Ubuntu18.04 永久修改 DNS

Ubuntu 18.04 修改 DNS

编辑以下文件:

sudo vim /etc/systemd/resolved.conf

修改如下:


[Resolve]
 
DNS=119.29.29.29

保存退出后

重新启动服务:

systemctl restart systemd-resolved.service

MacOS 使用 SSHFS 将远程文件挂载到本地

SSHFS允许您使用SFTP安装远程文件系统。 大多数SSH服务器默认支持并启用此SFTP访问,因此SSHFS使用起来非常简单,服务器端无需执行任何操作。

在 MacOS 中使用 SSHFS 需要 FUSE 的支持

用于 macOS 的 FUSE 允许您通过第三方文件系统扩展 macOS 的本地文件处理功能。 它是 MacFUSE 的继任者,已被许多产品用作软件构建块,但不再维护;

安装

  • 下载安装

你可以从官网中下载最新版的 FUSE for macOSSSHFS 直接安装。

  • HomeBrew

也可以使用 HomeBrew 安装

$ brew cask install osxfuse
$ brew install sshfs

使用

  • 新建本地挂载点
$ mkdir local-file
  • 挂载
$ sshfs user@hostname:/absolute/path/to/document local-file

远程的地址最好使用绝对路径。此时打开该文件夹就可以访问远程文件了。

  • 卸载挂载文件
umount local-file

Link: MacOS 使用 SSHFS 将远程文件挂载到本地

isKindOf 和 isMemberOf 的区别

在iOS 开发中,在遇到需要判断一个对象是否属于某个类的时候,我们经常会用到 isKindOfisMemberOf 这两个方法。但往往我们稍不注意,就会将他们两个用混了。虽然有时候的结果是对的,但对于程序的健壮性来说却不是一个好的事情了。所以我们在平常的程序开发中,一定要做到代码所写的和心里所想的完全一致。这样才能保证我们的程序的质量。今天我们就来好好梳理下这两个判断方法的区别。

  • iskindOf 是用来判断一个对象是否为某个类以及该类的父类的对象
  • isMemberOf 只能用来判断一个对象是否当前类的对象

所以如果我们需要判断一个对象是否为某两个类的对象,只能使用 isMemberOf 来进行判断,这样才能保证不管以后这两个类怎么变,都可以得到正确的结果。

isKindOf 应该在什么时候使用呢?

排除上面那一种用法,那就是判断 一个对象是否属于某个类或属于某个类的父类 这一种情况了。

以上是我在实际编程中对这两个方法的一点理解。

通过 Jenkins 和 gogs 自动发布 Hugo 博客到 VPS

Hugo 博客是使用 Go 语言编写的静态博客引擎,可以快速将 markdown 博文转换成 HTML 文件。

环境:Ubuntu 16.4 + Nginx

Jenkins 的安装

  • . 安装 Jenkins :
wget -q -O - https://jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins
  • . 通过命令进行升级:
sudo apt-get update
sudo apt-get install jenkins
  • . 通过命令来启动、停止Jenkins:
sudo service jenkins start   # 启动Jenkins
sudo service jenkins stop    # 停止Jenkins
sudo service jenkins restart # 重启Jenkins
  • . 访问Jenkins:
http://localhost:8080
  • . 查看Jenkins 文件路径:
配置文件:/etc/default/jenkins
安装路径:/var/lib/jenkins
日志路径:/var/log/jenkins

Jenkins 安装好后,默认端口是8080,如果发现使用上述路径 ( http://localhost:8080 ) 无法访问Jenkins,可能是因为其他的程序占用了该端口,需要修改为其他的端口:

修改文件:/etc/default/jenkins

HTTP_PORT=8081

然后重新启动Jenkins :

sudo service jenkins restart

然后根据Jenkins 提示进行配置

使用 Nginx 进行方向代理

在 Nginx 配置目录: /etc/nginx/conf.d 添加配置文件:

server {
    listen       443;
    server_name  Jenkins;
    debug_http;
    client_max_body_size 60M;
    client_body_buffer_size 512k;
    location / {
        proxy_pass
        http://localhost:8081;
        proxy_redirect  off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    ssl_certificate /etc/letsencrypt/live/Jenkins/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/Jenkins/privkey.pem; # managed by Certbot
}
server {
    listen       80;
    server_name  Jenkins;

    if ($host = Jenkins) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
}

Jenkins 是指你自己的域名

我这里将所有HTTP 请求重定向到了 HTTPS

现在我们可以通过 https://Jenkins 来访问 Jenkins 了。

安装 Jenkins 插件:

这里我们需要安装两个插件:

Gogs plugin
Publish Over SSH

登录 Jenkins ,在 系统管理 --> 插件管理 中搜索上述两个插件并安装

Jenkins 持续集成配置:

在进行配置之前,我们需要添加两个凭证:

在 Jenkins 主页点击 凭证 --> 系统 --> 全局凭证 进入凭证列表页面,点击 添加凭证 ,我们需要添加两个凭证:

Username with password        # 用来登录 Gogs
SSH Username with private key # 用来登录 VPS

配置 Jenkins 持续集成:

  • 添加任务,配置如下:

    • Gogs Webhook 下选择 Use Gogs secret
    • Source Code Management 下选择 Git 并在 Repository URL 填写 git 地址,在 Credentials 选择我们上面配置的凭证
    • Build 下添加两个命令:
rm -rf public
hugo
  • Post-build Actions 添加在上面添加的登录 VPS 的凭证,并在 Source files 添加:
public/**
  • Exec command 添加需要在你服务器执行的命令,也可以不填,默认文件会复制到 SSH Server 服务器配置的目录下。

以下是图片配置截图:

Gogs Webhook

Source Code Management

Build

Post-build Actions

添加 Gogs 钩子:

在 Gogs 博客的 git 项目 下,点击 仓库设置 --> 管理 WEB 钩子 添加一个钩子,以便在你 push 后可以通知 Jenkins 来自动生成博客,并将博客部署到 VPS。

  • 推送地址 下添加钩子地址:
https://<Jenkins Server Address>/gogs-webhook/?job=<Jenkins 任务名称>
  • 数据格式 下选择 application/json ,保存。

可以在底部点击测试推送,查看 Jenkins 是否可以收到推送并进行自动部署到 VPS 。

That's All.

SDWebImage 加载图片失败后,不重新加载的解决

今天测试发现一个 bug,说在弱网的情况下,列表图片会加载失败,等网络好了后,重新下拉刷新,加载失败的列表图片,也无法进行加载。
根据测试反馈信息,我觉得应该是 SDWebImage 在图片加载失败后,给图片链接做了标记,不再重新加载了。于是查看 SDWebImage 源码,发现了 SDWebImageRetryFailed 这个枚举值,查看解释,恍然大悟。都是使用时没有仔细查看源码的锅,以后多注意,使用一个第三方的库,就得好好了解下他的源码:

/**
 * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
 * This flag disable this blacklisting.
 */
SDWebImageRetryFailed = 1 << 0,

AppCode 快捷操作

快捷键:

  • Command + O 根据类名跳转文件
  • Command + Option + O 根据文件名快速跳转
  • Command + F12 显示代码结构(可搜索跳转)
  • option + F7 查看该方法在那些地方被调用
  • Command + Shift + F 全局搜索文字
  • Shift + esc 关闭下边调试窗口
  • Command + ➡️ 光标跳到当前行代码末尾
  • Command + ⬅️ 光标跳转到当前行代码开始
  • Command + 4 打开或关闭调试窗口
  • Command + E 最近编辑的文件
  • Control + Shift + R 运行

配置篇

设置编辑时代码自动换行

  • Command + , 打开 Preference 界面,如图设置即可。

Link:
AppCode skills

iOS 数据精度及大数的处理

在 iOS 开发中,我们很容易遇到使用 CGFloat 来标示浮点数,但这样的表示会造成精度失真。这时我们可以使用NSDecimalNumber来处理这个问题。

NSDecimalNumberNSNumber的子类,可以处理大数运算及数据的精度问题。

大数相乘可能导致的问题

我们先上一段代码:

NSString *priceStr = @"";
NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString:priceStr];
NSDecimalNumber *countNum = [NSDecimalNumber decimalNumberWithString:stringWithNSInteger(NSIntegerMax)];
number = [number decimalNumberByMultiplyingBy:countNum];

在这段代码中,number的值为:NaN,即:not a number ,非数值;
而countNum 是一个最大的整数,
最后,将NaN和最大的整数相乘,导致了overflow的crash。

解决方案:

//定义数值处理的行为
NSDecimalNumberHandler *roundUp = [NSDecimalNumberHandler
                                      decimalNumberHandlerWithRoundingMode:NSRoundBankers
                                      scale:2
                                      raiseOnExactness:NO
                                      raiseOnOverflow:NO
                                      raiseOnUnderflow:NO
                                      raiseOnDivideByZero:NO];
    
NSString *priceStr = @"";
NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString:priceStr];
NSDecimalNumber *countNum = [NSDecimalNumber decimalNumberWithString:stringWithNSInteger(NSIntegerMax)];

//使用数据处理行为的约定来进行运算,防止crash
number = [number decimalNumberByMultiplyingBy:countNum withBehavior:roundUp];

上面这个例子不会crash了,但是最终number的值为NaN,需要后续的业务逻辑进行判断处理;

NSDecimalNumberHandler 用到的参数,其中:

NSRoundBankers

枚举,截断的方式;完整的定义如下:

// Rounding policies :
// Original
//    value 1.2  1.21  1.25  1.35  1.27
// Plain    1.2  1.2   1.3   1.4   1.3
// Down     1.2  1.2   1.2   1.3   1.2
// Up       1.2  1.3   1.3   1.4   1.3
// Bankers  1.2  1.2   1.2   1.4   1.3

typedef NS_ENUM(NSUInteger, NSRoundingMode) {
    NSRoundPlain,   // Round up on a tie
    NSRoundDown,    // Always down == truncate
    NSRoundUp,      // Always up
    NSRoundBankers  // on a tie round so last digit is even
};

scale

小数点后面的位数(精度)

raiseOnExactness

The exception raised if there is an exactness error.

raiseOnOverflow

是否抛出溢出错误,如果为YES,则APP会捕获溢出错误,这会导致APPcrash;

The exception raised on overflow.

raiseOnUnderflow

The exception raised on underflow.

raiseOnDivideByZero

The exception raised on divide by zero.

补充知识:

  • 判断一个数值是否为NaN可以使用系统方法:isnan(x);注:x为数值类型,不是NSDecimalNumber,更不是NSNumber;
  • 如果要判断一个NSDecimalNumber 是否为 NAN ,则使用下面的方法:
if([number isEqualToNumber:NSDecimalNumber.notANumber]){
    NSLog(@"number is nan");
}else{
    NSLog(@"number:%@",number);
}

参考链接:iOS 两个数相乘导致 NSDecimalNumber overflow exception 错误的分析及解决