Ubuntu 系统下 Monit 监控 Mongodb

服务器上安装了 mongodb 数据库,为防止 mongodb 异常关闭,影响正常业务处理。需要使用 monit 监控 mongodb, 配置如下,比较简单。

在 /etc/monit/conf.d 增加 mongod 配置文件,内容如下即可:

1
2
3
4
5
6
7
check process mongod matching "/usr/bin/mongod"
   group database
   start program = "/usr/bin/sudo /bin/systemctl start mongod"
   stop  program = "/usr/bin/sudo /bin/systemctl stop mongod"
   if failed host 127.0.0.1 port 27017 protocol mongodb
     with timeout 10 seconds then restart
   if 5 restarts within 5 cycles then timeout

增加完了,运行 sudo monit reload 重新加载配置文件即可。

Golang 如何控制固定线程数

在 golang 里面大家应该都知道是 go 关键字调用一个函数,就开启了个线程。 如果一个循环,每个里面都调用 go 函数,那就会可能启动很多个线程, 如果想控制固定线程数来处理业务,该怎么写呢?

写过 golang 多线程的应该都知道,多线程之间可以通过管道通信协作,作为通信的数据。 我们可以通过 sync.WaitGroup 和管道配合来达到控制多线程的线程数。

代码

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
package main

import (
  "fmt"
  "sync"
  "time"
)

var MAX_THREADS = 5

func main() {
  jobs := make(chan int, 10)

  var wg sync.WaitGroup
    // 设置需要多少个线程阻塞
  wg.Add(MAX_THREADS)
    fmt.Println(time.Now())

    // 根据线程数控制启动多少个消费者线程
  for n := 0; n < MAX_THREADS; n++ {
      go worker(jobs, &wg)
  }

    // 生产者
  for i := 0; i < 10; i++ {
      jobs <- i
  }
    close(jobs)

    // 等待所有线程执行完毕的阻塞方法
  wg.Wait()
  fmt.Println(time.Now())
}
// 消费者
func worker(jobs <-chan int, wg *sync.WaitGroup) {
  for job := range jobs {
      fmt.Println(job)
      time.Sleep(1 * time.Second)
    }
    // 消费完毕则调用 Done,减少需要等待的线程
  wg.Done()
}

上面这个代码控制了 5 (MAX_THREADS) 个线程,你可以修改成 2 和 10 看下效果。

sync.WaitGroup 是一个线程控制组, 它能够一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine 执行完成。

它一共有 3 个方法,Add(),Done(),Wait()。

Add - 设置需要阻塞的线程数 Done - 代表单线程已经执行完毕 Wait - 等待所有线程执行完毕的阻塞方法

通过它就可以控制多少个消费者同时在处理了,而不是生成无限多个消费者。

使用 V2ray 中转服务器流量

简介

一般我们使用 v2ray 服务器,都是直接连接到这个服务器上,进行数据代理转发。就是常用的 客户端 => v2ray 服务器 模式, 其实可以 客户端 => v2ray 中转服务器1 => v2ray 中转服务器2 => v2ray 服务器, 可以设置 N 个服务器,但设置太多一定会影响访问速度,所以不宜设置太多。 本文仅限技术交流之用,大家请勿非法滥用。

我们来看看如何配置 v2ray 中转服务器。

v2ray

我推荐使用 docker 安装启动,非常简单, 中转服务器和目标服务器都需要安装。

1
sudo docker run -it -d -v /etc/v2ray:/etc/v2ray -p xxx:yyy v2ray/official v2ray -config=/etc/v2ray/config.json
  • xxx - 对外客户端开放的端口
  • yyy - docker 内部 v2ray 的端口

v2ray 中转服务器配置

我这边是一个 v2ray 中转服务器的配置

/etc/v2ray/config.json

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
80
81
82
83
84
85
86
87
88
89
90
91
92
{
    "inbound": {
        "port": yyy,          // docker 内部 v2ray 的端口
        "protocol": "vmess",  // 我这里使用的 vmess 协议,也可以使用其他协议
        "settings": {
            "clients": [
                {
                    "id": "uid-xxxxxxxxxxxxx", // vmess 协议需要配置的 uid
                    "alterId": 100
                }
            ]
        }
    },
    "outbound" : {
      "mux" : {
        "concurrency" : 8,
        "enabled" : true
      },
      "protocol" : "vmess", // 目标 v2ray 服务器的协议
      "settings" : {
        "vnext" : [
          {
            "users" : [
              {
                "id" : "uid-yyyyyyyyyyyyy", // 目标 v2ray 服务器的 vmess 协议需要配置的 uid
                "alterId" : 64  // 和目标服务器保持一致
              }
            ],
            "address" : "xxx.com", // 目前 v2ray 服务器的ip 地址或域名
            "port" : xxx_1  // 目标 v2ray 服务器的开放的对外端口
          }
        ]
      },
      "streamSettings": {
        "network": "tcp", // 记得配置 tcp
        "security": "none",
        "tlsSettings": {},
        "tcpSettings": {},
        "httpSettings": {},
        "kcpSettings": {
          "mtu": 1350,
          "tti": 50,
          "uplinkCapacity": 100,
          "downlinkCapacity": 100,
          "congestion": false,
          "readBufferSize": 2,
          "writeBufferSize": 2,
          "header": {
            "type": "none"
          }
        },
        "wsSettings": {},
        "quicSettings": {}
      }
    },
    "inboundDetour": [],
    "outboundDetour": [
        {
          "protocol": "blackhole",
          "settings": {},
          "tag": "blocked"
        }
      ],
    "routing": {
      "strategy": "rules",
      "settings": {
        "rules": [
          {
            "type": "field",
            "ip": [
              "0.0.0.0/8",
              "10.0.0.0/8",
              "100.64.0.0/10",
              "127.0.0.0/8",
              "169.254.0.0/16",
              "172.16.0.0/12",
              "192.0.0.0/24",
              "192.0.2.0/24",
              "192.168.0.0/16",
              "198.18.0.0/15",
              "198.51.100.0/24",
              "203.0.113.0/24",
              "::1/128",
              "fc00::/7",
              "fe80::/10"
            ],
            "outboundTag": "blocked"
          }
        ]
      }
    }
  }

v2ray 目标服务器配置

v2ray 服务器的配置

/etc/v2ray/config.json

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
{
  "log" : {                                 // [日志](../chapter_02/01_overview.md)
    "access": "/var/log/v2ray/access.log",  // 访问日志文件
    "error": "/var/log/v2ray/error.log",    // 错误日志文件
    "loglevel": "warning"                   // 错误日志等级
  },
  "inbound": {              // 主传入协议
    "port": yyy_2,          // docker 内部 v2ray 的端口
    "protocol": "vmess",    // 在这里使用 [VMess 协议](../chapter_02/protocols/vmess.md)
    "settings": {
      "clients": [
        {
          "id": "uid-xxxxxxxxxxx", // UUID
          "level": 1,       // 用户等级
          "alterId": 64     // 额外ID
        }
      ]
    }
  },
  "outbound": {             // 主传出协议
    "protocol": "freedom",  // 在这里使用“直接传出至互联网”
    "settings": {}
  },
  "outboundDetour": [       // 额外传出协议
    {
      "protocol": "blackhole",
      "settings": {},
      "tag": "blocked"
    }
  ],
  "routing": {                  // 路由设置
    "strategy": "rules",
    "settings": {
      "rules": [
        {
          "type": "field",      // 不允许客户端访问服务端的局域网地址,以提升安全性
          "ip": [
            "0.0.0.0/8",
            "10.0.0.0/8",
            "100.64.0.0/10",
            "127.0.0.0/8",
            "169.254.0.0/16",
            "172.16.0.0/12",
            "192.0.0.0/24",
            "192.0.2.0/24",
            "192.168.0.0/16",
            "198.18.0.0/15",
            "198.51.100.0/24",
            "203.0.113.0/24",
            "::1/128",
            "fc00::/7",
            "fe80::/10"
          ],
          "outboundTag": "blocked"
        }
      ]
    }
  }
}

启动例子

如果对客户端开放的是 10086 端口, 上面配置中的 yyy 为 8122, xxx_1 为 20180, yyy_2 为 8123

中转服务器

1
sudo docker run -it -d -v /etc/v2ray:/etc/v2ray -p 10086:8122 v2ray/official v2ray -config=/etc/v2ray/config.json

目标v2ray服务器

1
sudo docker run -it -d -v /etc/v2ray:/etc/v2ray -p 20180:8123 v2ray/official v2ray -config=/etc/v2ray/config.json

使用 Monit 监控 Rake Task

Monit

Monit是一个跨平台的用来监控Unix/linux系统(比如Linux、BSD、OSX、Solaris)的工具。Monit特别易于安装,而且非常轻量级(只有500KB大小),并且不依赖任何第三方程序、插件或者库。

Monit可以监控服务器进程状态、HTTP/TCP状态码、服务器资源变化、文件系统变动等等,根据这些变化,可以设定邮件报警、重启进程或服务。易于安装、轻量级的实现以及强大的功能,让Monit成为一个理想的后备监控工具。

Monit 常用命令

1
2
3
4
5
6
7
8
9
10
11
monit -t # 配置文件检测
monit # 启动monit daemon
monit -c /var/monit/monitrc # 启动monit daemon时指定配置文件
monit reload # 当更新了配置文件需要重载
monit status # 查看所有服务状态
monit status nginx # 查看nginx服务状态
monit stop all # 停止所有服务
monit stop nginx # 停止nginx服务
monit start all # 启动所有服务
monit start nginx # 启动nginx服务
monit -V # 查看版本

Rake Task

Rake Task 是 Rails 执行后台任务一个工具,可以执行一些定时或者轮询的后台任务。

如何配置 monit 来监控 rake task

1
2
3
4
5
6
check process xxx_task
    matching "xxx_task"
    start program = "/bin/bash -l -c 'source /home/deploy/.bashrc && cd /home/deploy/apps/xxx_project/current/ && RAILS_ENV=production /home/deploy/.rbenv/shims/bundle exec rake xxx_task &'"
    stop program  = "/bin/bash -l -c 'pgrep -f xxx_task | xargs kill -9'"
    if memory usage > 50% then restart
    if 9 restarts within 10 cycles then timeout

解释

  • source /home/deploy/.bashrc 用来加载环境变量。因为系统使用了 rbenv,所以必须先加载这个
  • pgrep -f xxx_task | xargs kill -9 杀死所有的这个任务主进程和子进程

在 Fish Shell 里面设置 Goenv

习惯了使用 rbenv 管理 Ruby 版本,现在使用 golang 了。就想使用上了 goenv 来方便管理 golang 版本。 我使用的是 fish shell,这边记录下如何设置 goenv 在 fish shell 环境下。

安装 goenv

mac 用户

1
brew install goenv

或者

1
git clone git@github.com:syndbg/goenv.git ~/.goenv

设置 config.fish

1
vim ~/.config/fish/config.fish

添加以下配置:

1
2
3
4
eval (goenv init - | source)
set -x PATH $HOME/.goenv/bin $PATH
set -gx PATH ‘/Users/grant/.goenv/shims’ $PATH
set -gx GOENV_SHELL fish

大功告成, 重开一个 shell 窗口,将自动加载 goenv。你可以随意安装 golang 版本了。

1
2
goenv install 1.10.3
goenv global 1.10.3

Rails 和 Wordpress 配合 Nginx 配置

由于项目需要公司主页需要使用 Wordpress,但是后台管理还是使用的 Rails 程序。 所以需要 nginx 修改配置才能达到效果。

Nginx 配置如下

Wordpress 是配置在 http://abc.com/www 子路径下。需要在 Rails 的主页处理的时候,自动跳转至此 URL。 wordpress 的服务器文件是放在 /home/deploy/apps/abc_home/www 下面。

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
server {
        listen 443;
        server_name  abc.com www.abc.com;
        ssl on;
        ssl_certificate   /etc/nginx/cert/xxx.pem;
        ssl_certificate_key  /etc/nginx/cert/xxx.key;
        ssl_session_timeout 5m;
        ssl_ciphers XXX;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        passenger_enabled on;
        passenger_min_instances 6;
        passenger_max_request_queue_size 300;
        rails_env    production;
        root         /home/deploy/apps/abc/current/public;


       location = /www { rewrite ^ /www/ last; }

       location ^~ /www/ {
          root /home/deploy/apps/abc_home/;
          index index.php;

          if (!-e $request_filename) {
            rewrite  ^(.*)$  /www/index.php?q=$1  last;
          }

          location ~ \.php(?|$) {
            include /etc/nginx/fastcgi_params;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
         }
      }
}

server {
        listen 80;
        server_name  abc.com www.abc.com;
        return 301 https://$server_name$request_uri;
}

如何部署 Rails 到 Ubuntu 14.04 服务器

由于经常开发 Rails 应用,也常常部署到 Ubuntu 服务器上,所以我就整理了 一篇比较完整的部署配置步骤,供大家以及自己以后参考。

环境系统说明

  • Ruby 2.3.1
  • Rails 4.2.6
  • Ubuntu 14.04 64 位

    配置步骤

1. 系统增加 deploy 用户

以 root 用户身份登入 ubuntu 系统,然后执行下面的命令增加具有 sudo 权限的 deploy 用户。

1
2
3
4
5
$ sudo adduser deploy
$ sudo adduser deploy sudo
$ visudo
# 将 sudo 用户改成运行 sudo 无需密码
% sudo   ALL=(ALL) NOPASSWD:ALL

2. 把你的 SSH 公钥加入到 authorized_keys

这一步是为了以后你自己本地使用 cap 部署代码准备的,这样的话,每次部署无需输入密码。

1
2
3
4
5
## 切换到 deploy 用户
$ su deploy
$ mkdir .ssh
$ vim .ssh/authorized_keys
# 粘贴你的 ssh 公钥,然后保存

3. 安装 Ruby 需要的依赖库

Ruby 安装的时候会需要用到一些系统的依赖库,所以提前装好。

1
2
3
4
$ sudo apt-get update
$ sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev
libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev
python-software-properties libffi-dev nodejs

4. 安装 Ruby

我这里使用的是 Rbenv 安装 Ruby, 我这里使用的是 2.3.1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ cd
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ exec $SHELL

$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
$ echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
$ exec $SHELL

$ rbenv install 2.3.1
$ rbenv global 2.3.1
$ ruby -v
$ gem install bundler

5. 安装 Nginx + Passenger

我使用 Nginx + passenger 来运行 Rails 应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
$ sudo apt-get install -y apt-transport-https ca-certificates

# Add Passenger APT repository
$ sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger trusty main > /etc/apt/sources.list.d/passenger.list'
$ sudo apt-get update
$ sudo apt-get install -y nginx

$ sudo vim /etc/nginx/nginx.conf
  # 这行取消注释
  include /etc/nginx/passenger.conf;

$ sudo vim /etc/nginx/passenger.conf
  passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;
  passenger_ruby /home/deploy/.rbenv/shims/ruby; # 设置 Rbenv

6. 安装 postgres

安装 postgres 的时候,需要注意将 pg_hba.conf 里面的配置改成 md5。

1
2
3
4
5
6
7
8
9
10
$ sudo apt-get install postgresql postgresql-contrib libpq-dev
$ sudo su - postgres
$ vim /etc/postgresql/9.3/main/pg_hba.conf
local   all             all                           md5
$ service postgresql restart
# 新建 app_db_user 的数据库用户名,请牢记数据库密码,待会需要配置到 database.yml 里面去
$ createuser --interactive -P blog_db_user
# 新建 blog_production 数据库
$ createdb blog_production -Ublog_db_user
$ exit

7. 代码部署

我是把代码放到 /home/deploy/apps/ 下面的,这里可以随意改动,不需要按照我这个配置。

1
2
3
4
5
6
7
8
$ cd ~
$ mkdir -p apps/blog/shared/config
$ cd apps/blog/shared/config
# 创建一些 application.yml database.yml secrets.yml

# 在服务器上生成公钥配置到项目的 deploy key 里面去
$ ssh-keygen
$ cat ~/.ssh/id_rsa.pub

服务器配置完成后,本地需要使用 capistrano 来自动部署代码,下面自己本机电脑上的操作。

Gemfile

1
2
3
4
5
6
7
8
group :development do
  gem 'capistrano', '~> 3.5'
  gem 'capistrano-rbenv', '~> 2.0'
  gem 'capistrano-bundler', '~> 1.1'
  gem 'capistrano-rails', '~> 1.1'
  gem 'capistrano-passenger', '~> 0.2'
  gem 'capistrano-sidekiq'
end

bundle 完成之后, 运行 bundle exec cap install 会生成默认的 Capfile, deploy.rb 和 config/deploy 目录下的各个环境的配置文件。

Capfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Load DSL and set up stages
require "capistrano/setup"

# Include default deployment tasks
require "capistrano/deploy"

# Include tasks from other gems included in your Gemfile
# require 'capistrano/rvm'
# 因为使用的是 rbenv
require 'capistrano/rbenv'
# require 'capistrano/chruby'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/passenger'
# 因为使用了 sidekiq,部署的时候会自动重启 sidekiq
require 'capistrano/sidekiq'

# 因为使用了 whenever,部署的时候会自动重新生成定时任务
require "whenever/capistrano"

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }

config/deploy.rb

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
# config valid only for current version of Capistrano
lock '3.5.0'

set :application, 'blog'
set :repo_url, 'git@github.com:grant/blog.git'

set :rbenv_type, :user # or :system, depends on your rbenv setup
set :rbenv_ruby, '2.3.1'

# Default branch is :master
ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp

set :rails_env, 'production'

# 设置成代码的目录
set :deploy_to, '/home/deploy/apps/blog'

# Default value for :linked_files is []
set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/secrets.yml', 'config/application.yml')

# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'private/sdf_files', 'vendor/bundle', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/system', 'public/uploads')

# Default value for keep_releases is 5
# set :keep_releases, 5
set :whenever_identifier, -> { "#{fetch(:application)}_#{fetch(:stage)}" }

# 下面是自动做 db:seed 在部署完成后
namespace :deploy do
  after :restart, :clear_cache do
    on roles(:web), in: :groups, limit: 3, wait: 10 do
      # Here we can do anything such as:
      within release_path do
        with rails_env: fetch(:rails_env) do
          execute :rake, 'db:seed'
        end
      end
    end
  end
end

config/deploy/production.rb

1
server 'grantcss.com', user: 'deploy', roles: %w(web app db)

这个时候,在本地运行下面的命令,代码会自动部署到服务器上的 /home/deploy/apps/blog 目录

1
$ cap production deploy

8. 配置 nginx 启动 Rails 应用

将这个应用配置到 nginx 里面,然后删除默认的配置,重启 nginx 即可。设置的域名确保 A 地址指向你的服务器了。 至此,一个 Rails 应用部署配置完成了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ssh deploy用户 登录到服务器
$ cd /etc/nginx/sites-available/
$ vim blog
server {
    listen 80;
    server_name blog.grantcss.com;
    passenger_enabled on;
    rails_env    production;
    root         /home/deploy/apps/blog/current/public;
}

$ sudo ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled/blog
$ sudo rm /etc/nginx/sites-enabled/default
$ sudo service nginx restart

如何在 Rails 中使用 PostgresSQL 表分区

当你在开发中遇到这样一种场景,需要收集单个设备的数据信息,这个这个设备每隔 10 秒就会发一次数据 到 Rails 服务器,服务器需要把每次来的数据收集起来,以便以后数据分析使用,但这个设备会很多,几千, 几万,甚至几十万都是有可能的。

那一个设备的一天的数据量就是 10 * 60 * 60 * 24 = 864000 条,一年的就是 3 个亿,如果 1 万个设备的话, 就是 1 万亿条数据,看起来数据量有点恐怖,如果放在单表里面,势必随着数据量的增大,查询,插入等数据操作 的性能都会受到很大影响。

这个时候,有什么好的方法可以解决这个问题呢? 我当时正好使用的是 PostgresSQL,于是查了些资料,很多人 推荐表分区来解决此问题,那让我们来看看如何使用这一特性吧。

什么是表分区

在 PG 里面就是一个主表,会有很多的子表继承主表,字段和主表一样。一般都是建立一个主表,里面是空,然后每个分区都去继承它。无论何时,都应保证主表里面是空的。

PostgresSQL 分区是把逻辑上的一个大表分割成物理上的几块。分区不仅能带来性能的提升,还能带来管理和维护上的方便。

分区的好处是:

  • 查询性能可以得到极大提升。
  • 更新的性能也可以得到提升,因为表的每块的索引要比在整个数据集上的索引要小。如果索引不能全部放在内存里,那么在索引上的读和写都会产生更多的磁盘访问。
  • 批量删除可以用简单的删除某个分区来实现。
  • 可以将很少用的数据移动到便宜的、转速慢的存储介质上。

表在多大情况下才考虑分区呢? PostgresSQL官方给出的建议是:当表本身大小超过了机器物理内存的实际大小时(the size of the table should exceed the physical memory of the database server),可以考虑分区。

Rails 中如何实现分区

Gemfile

因为本人使用的是 Rails 4.2 所以 Gemfile 配置如下, partitioned 是分区的主 gem, activerecord-redshift-adapter 是 partitioned 依赖的 gem ,为了兼容 rails 4 设置的版本。

1
2
gem "partitioned", github: "dkhofer/partitioned", branch: "rails-4-2"
gem 'activerecord-redshift-adapter',  github: "arp/activerecord-redshift-adapter", branch: "rails4-compatibility"

创建分区规则

因为我需要以 created_at 字段按月分区,所以需要继承 Partitioned::ByMonthlyTimeField 类。

1
2
3
4
5
6
7
8
9
10
11
class PartitionedByCreatedAtMonthly < Partitioned::ByMonthlyTimeField
  self.abstract_class = true

  def self.partition_time_field
    :created_at
  end

  partitioned do |partition|
    partition.index :id, unique: true
  end
end

指定表使用此分区规则

如果你想指定哪张表使用此分区规则,只需要这个表的 model 继承 PartitionedByCreatedAtMonthly, 而不是继承 ActiveRecord::Base。

1
2
class DeviceLog < PartitionedByCreatedAtMonthly
end

创建好 5 年的分区表

使用 migration 来提前创建分区表,这时候数据库会新建这样的分区表:”device_logs_partitions”.“p201607”, “device_logs_partitions”.“p201608”, “device_logs_partitions”.“p201609”…

1
2
3
4
5
6
7
8
9
10
11
12
class CreatePartitionedTables < ActiveRecord::Migration
  def up
    # 创建分区表专用的名字为 device_logs_partitions 的模式
    DeviceLog.create_infrastructure
    dates = DeviceLog.partition_generate_range(Date.today, Date.today + 5.year)
    DeviceLog.create_new_partition_tables(dates)
  end

  def down
    DeviceLog.delete_infrastructure
  end
end

测试

上面的配置完成后,运行 rake db:migrate,使用 DeviceLog 模型创建一条记录,你会发现创建的数据会根据 created_at 的值自动创建到对应的分区表里面去。

1
2
### 假设是今天 20170731 号运行的
DeviceLog.create(:log => 'xxx')

会看到如下的插入 SQL

1
INSERT INTO device_logs_partitions.p201607 (log,...) values ('xxx',...;

如何查询分区表的数据呢

因为所有数据都到分区表了,所以 gem 的作者不建议直接使用 DeviceLog.find 方法,取而代之的是下面的查询方法, 这样的话只会在分区表里面查询。

1
2
3
DeviceLog.from_partition(today).find(1)
DeviceLog.find(:first, conditions: {created_at:
  @select_date.beginning_of_day..@select_date.end_of_day})

其他分区规则

至此基本分区表用法就是这样,这个 gem 其实还提供其他的分区规则,具体用法类似:

1
2
3
4
5
6
Partitioned::ById
Partitioned::ByForeignKey
Partitioned::ByDailyTimeField
Partitioned::ByMonthlyTimeField
Partitioned::ByWeeklyTimeField
Partitioned::ByYearlyTimeField

总结

这个 gem 确实比自己在 PG 里面设定分区规则要方便很多,有类似需求的可以尝试使用下。

如何 Docker 化你的 Rails 应用

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中, 然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

最近有项目需要打包快速部署到不同的服务器的需要,如果和以前一样,从安装 Ruby 开始,然后数据库,依赖的一些库(node), nginx 配置等,一步一步搞的话,又慢又麻烦, 于是就想尝试使用 Docker 部署看看。

这里就不解释 Docker 的一些基本教程和使用方法了,需要学习的可以去 https://www.gitbook.com/book/yeasy/docker_practice/ 看看。 未完待续…

Rails 5 中的迅捷开发模式

对于大型项目代码来说,Rails 5 增加了一个迅捷开发模式。

正如我们所知道的,在开发(development)模式时,如果代码发生了变化, 当一个请求到来时, Rails 会重新加载整个程序。这样做的方法已经发展了很多年。

很长一段时间的 Rails 简单地在每一个请求无条件地重新加载。

Rails 的 3.2 改善了这个处理,增加了文件系统监视器,通过遍历代码树的请求检查每个文件的修改时间(mtimes)。

这棵树的遍历,是每个请求才会发生,而不是显示每个页面视图都会做。尤其是遍历应用的每个 assets 文件,虽然遍历整个应用程序树一次可能不会花很多时间,但取决于你 assets 文件的数量和代码规模。

Rails 5 是增加了一个事件驱动的文件监视系统。当有新的文件变化的操作系统调用 Rails 的异步处理来设置标志。当收到请求时,该标志会被设定。

这个监视器默认是关闭的,应用程序可以在 Gemfile 增加如下配置:

1
2
3
group :development do
  gem 'listen', '~> 3.0.4'
end

在 Linux 和 Mac OS X 安装没有什么特别依赖,但是在 BSD 和 Windows 安装的话,需要安装些依赖。

即使事件驱动监视器被启用,Rails Console 里想加载新的代码,仍然需要手动 reload, 因为如果后台自动改变已经实例化的对象会让人感到惊讶。

相比遍历整个应用程序树,这个监视系统会加快开发模式里的请求 Response 速度,特别是对于大型项目来说。