docker mysql orchestrator高可用构建

使用到这个项目的构建脚本:

https://github.com/pondix/docker-mysql-proxysql

但是有个bug,需要修改一下conf/orchestrator/Dockerfile

FROM debian:stretch

MAINTAINER Nikolaos Vyzas nick@proxysql.com

WORKDIR /usr/local/orchestrator

#deb文件需要放在Dockerfile同级目录下
COPY orchestrator_3.1.4_amd64.deb /usr/local/orchestrator/
EXPOSE 3000
WORKDIR /usr/local/orchestrator
RUN apt update && apt -y install wget && apt -y install ./orchestrator_3.1.4_amd64.deb && mkdir /var/lib/orchestrator && rm -rf /var/lib/apt/lists/*

CMD /usr/local/orchestrator/orchestrator http

用Docker实现MySQL ProxySQL读写分离

ProxySQL是一个高性能的MySQL中间件,能够代理数百台MySQL服务器,支持数十万的并发连接。ProxySQL代理MySQL并提供读写分离,查询重写和数据分片等功能。
这篇文章主要介绍用Docker Compose编排用ProxySQL实现MySQL集群上读写分离的实践过程。
Docker Compose编排中使用了一个现有镜像,就是breeze2/proxysql
本次实践源码上传在GitHub的breeze2/mysql-proxy-docker

Docker Compose编排

这个编排主要实现一主两从的一个MySQL集群和一个ProxySQL代理,ProxySQL代理MYSQL集群的数据请求并且进行读写分离。

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
L–mysql-proxy-docker //主目录
L–scripts //本地(Docker宿主)使用的一些脚本
L–mysql_set_users_and_repls.sh //设置各个数据库账号和开启主从复制
L–…
L–services //需要build的服务(目前是空)
L–volumes //各个容器的挂载数据卷
L–mysql_node0
L–mysql_node1
L–mysql_node2
L–proxysql
L–share //各个容器共享的目录
L–scripts //各个容器共用的一些脚本
L–parameters.env //账号密码等环境参数
L–docker-compose.yml //编排配置

docker-compose.yml

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
version: “2”
services:

master:
image: mysql:5.7
container_name: mysql_node0
restart: always
mem_limit: 256m
networks:
net1:
ipv4_address: 10.6.0.10
ports:
– “3306”
volumes:
– “./volumes/share/:/root/share/”
– “./volumes/mysql_node0/lib/:/var/lib/mysql/”
– “./volumes/mysql_node0/conf/:/etc/mysql/conf.d/”
env_file:
– ./parameters.env

slave1:
image: mysql:5.7
container_name: mysql_node1
restart: always
depends_on:
– master
mem_limit: 256m
networks:
net1:
ipv4_address: 10.6.0.11
ports:
– “3306”
volumes:
– “./volumes/share/:/root/share/”
– “./volumes/mysql_node1/lib/:/var/lib/mysql/”
– “./volumes/mysql_node1/conf/:/etc/mysql/conf.d/”
env_file:
– ./parameters.env
slave2:
image: mysql:5.7
container_name: mysql_node2
depends_on:
– master
restart: always
mem_limit: 256m
networks:
net1:
ipv4_address: 10.6.0.12
ports:
– “3306”
volumes:
– “./volumes/share/:/root/share/”
– “./volumes/mysql_node2/lib/:/var/lib/mysql/”
– “./volumes/mysql_node2/conf/:/etc/mysql/conf.d/”
env_file:
– ./parameters.env

proxy:
image: breeze2/proxysql:1.4.3
container_name: proxysql
depends_on:
– master
– slave1
– slave2
restart: always
mem_limit: 256m
networks:
net1:
ipv4_address: 10.6.0.9
ports:
– “127.0.0.1:60320:6032”
– “60330:6033”
volumes:
– “./volumes/proxysql/conf:/etc/proxysql”
entrypoint: “proxysql -f -c /etc/proxysql/pr.cnf”
env_file:
– ./parameters.env

networks:
net1:
driver: bridge
ipam:
config:
– subnet: 10.6.0.0/16
gateway: 10.6.0.1

这里配置了四个容器服务,一个breeze2/proxysql,负责代理各个数据库;三个mysql,其中一个是主库,另外两个是从库。每个容器服务都指定了静态IP,即使服务重启也不会出现IP错乱问题。proxysql容器的6032是提供管理服务的端口,只对Docker宿主机本地IP开放,而6033是代理数据请求的端口,可以对Docker宿主机网络IP开放。

环境参数

parameters.env

1
2
3
4
MYSQL_ROOT_PASSWORD=123456
MYSQL_DATABASE=testing
MYSQL_User=testing
MYSQL_PASSWORD=testing

数据库配置

这里简单的配置一下各个数据库数据复制备份相关的参数
主库,mysql_node0/conf/my.cnf

1
2
3
4
5
6
7
8
9
10
[mysqld]
server-id=1
gtid-mode=on
enforce-gtid-consistency=true
log-bin=mysql-bin
binlog-do-db=testing
binlog-ignore-db=mysql
replicate-do-db=testing
replicate-ignore-db=mysql
expire_logs_days=7

从库,mysql_node1/conf/my.cnf

1
2
3
4
5
6
7
8
9
10
[mysqld]
server-id=2
gtid-mode=on
enforce-gtid-consistency=true
log-bin=mysql-bin
binlog-do-db=testing
binlog-ignore-db=mysql
replicate-do-db=testing
replicate-ignore-db=mysql
expire_logs_days=7

从库,mysql_node2/conf/my.cnf

1
2
3
4
5
6
7
8
9
10
[mysqld]
server-id=3
gtid-mode=on
enforce-gtid-consistency=true
log-bin=mysql-bin
binlog-do-db=testing
binlog-ignore-db=mysql
replicate-do-db=testing
replicate-ignore-db=mysql
expire_logs_days=7

ProxySQL配置

proxysql/conf/pr.cnf

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
datadir=”/tmp”
# 管理平台参数
admin_variables =
{
admin_credentials=”admin2:admin2″
mysql_ifaces=”0.0.0.0:6032″
refresh_interval=2000
}
# mysql全局参数
mysql_variables =
{
threads=4
max_connections=2048
default_query_delay=0
default_query_timeout=36000000
have_compress=true
poll_timeout=2000
# interfaces=”0.0.0.0:6033;/tmp/proxysql.sock”
interfaces=”0.0.0.0:6033″
default_schema=”information_schema”
stacksize=1048576
server_version=”5.5.30″
connect_timeout_server=3000
# make sure to configure monitor username and password
# https://github.com/sysown/proxysql/wiki/Global-variables#mysql-monitor_username-mysql-monitor_password
monitor_username=”pr_muser”
monitor_password=”pr_mpass”
monitor_history=600000
monitor_connect_interval=60000
monitor_ping_interval=10000
monitor_read_only_interval=1500
monitor_read_only_timeout=500
ping_interval_server_msec=120000
ping_timeout_server=500
commands_stats=true
sessions_sort=true
connect_retries_on_failure=10
}
# mysql用户参数
mysql_users =
(
{
username = “pr_auser”
password = “pr_apass”
default_hostgroup = 0
}
)
# mysql服务器参数,10.6.0.10是主库放在0组,其他是从库放在1组
mysql_servers =
(
{
address = “10.6.0.10”
port = 3306
weight = 1
hostgroup = 0
max_connections = 50
},
{
address = “10.6.0.11”
port = 3306
weight = 2
hostgroup = 1
max_connections = 100
},
{
address = “10.6.0.12”
port = 3306
weight = 2
hostgroup = 1
max_connections = 150
}
)
# mysql请求规则,以下配置是读时加锁的请求发给0组,普通读取的请求发给1组,其他默认发给0组(上面的default_hostgroup)
mysql_query_rules:
(
{
rule_id=1
active=1
match_pattern=”^SELECT .* FOR UPDATE$”
destination_hostgroup=0
apply=1
},
{
rule_id=2
active=1
match_pattern=”^SELECT”
destination_hostgroup=1
apply=1
}
)

实际运行

在主目录下执行docker-compose up -d构建并运行整个Docker服务。

开启主从复制

在主目录下执行:

1
$ sh ./scripts/mysql_set_users_and_repls.sh

实际上是调用了挂载在数据库容器里的一些脚本:

1
2
3
volumes/share/scripts/msyql_grant_proxysql_users.sh #设置给proxysql用的账号,pr_auser和pr_muser
volumes/share/scripts/msyql_grant_slave.sh #主库设置给从库用的账号
volumes/share/scripts/msyql_grant_slave.sh #从库开始数据复制

脚本里的执行命令都很简单,一看就明。

开启ProxySQL代理

构建整个服务的时候,proxysql会先挂载主目录下的./volumes/proxysql/conf/pr.cnf到容器内/etc/proxysql/pr.cnf,然后执行proxysql -f -c /etc/proxysql/pr.cnf,所以这里的ProxySQL是按照pr.cnf里面的配置开启MySQL代理服务的,请仔细阅读上面ProxySQL配置。若有需要在ProxySQL运行的过程中修改配置,可以登录ProxySQL的管理系统操作。

ProxySQL管理系统

在Docker宿主机上登录ProxySQL管理系统(Docker宿主机需要装有MySQL Client):

1
$ mysql -u admin2 -padmin2 -h 127.0.0.1 -P60320

在ProxySQL管理系统上添加一个mysql_user(注意这个testing账号是各个数据库都已建立的,具体查看上面环境参数):

1
mysql> INSERT INTO mysql_users(username, password, default_hostgroup) VALUES (‘testing’, ‘testing’, 2);

确认是否已添加:

1
2
3
4
5
6
7
8
mysql> SELECT * FROM mysql_users;
+———-+———-+——–+———+——————-+—————-+—————+————————+————–+———+———-+—————–+
| username | password | active | use_ssl | default_hostgroup | default_schema | schema_locked | transaction_persistent | fast_forward | backend | frontend | max_connections |
+———-+———-+——–+———+——————-+—————-+—————+————————+————–+———+———-+—————–+
| pr_auser | pr_apass | 1 | 0 | 0 | | 0 | 0 | 0 | 1 | 1 | 10000 |
| testing | testing | 1 | 0 | 2 | NULL | 0 | 1 | 0 | 1 | 1 | 10000 |
+———-+———-+——–+———+——————-+—————-+—————+————————+————–+———+———-+—————–+
2 rows in set (0.00 sec)

把当前修改(MEMORY层)加载到正在运行的ProxySQL(RUNTIME层):

1
mysql> LOAD MYSQL USERS TO RUNTIME;

在Docker宿主机上确认ProxySQL是否已加载最新配置:

1
2
3
4
5
6
7
$ mysql -u testing -ptesting -h 127.0.0.1 -P60330
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 5.5.30 (ProxySQL Admin Module)


若想ProxySQL重启后依然是当前配置,要把当前修改(MEMORY层)保存到ProxySQL的Sqlite数据库里(DISK层):

1
mysql> SAVE MYSQL USERS TO DISK;

ProxySQL配置系统分三层,分别是MEMORY层、RUNTIME层和DISK层。ProxySQL管理系统操作的是MEMORY层,当前ProxySQL运行的是RUNTIME层,保存在ProxySQL本地Sqlite数据库里的是DISK层,详情请阅读文档ProxySQL Configuration

SysBench测试工具

SysBench是一个脚本化、多线程的基准测试工具,经常用于评估测试各种不同系统参数下的数据库负载情况。
SysBench的使用教程可以参考sysbench 0.5使用手册
这里使用SysBench-v1.0.9来对ProxySQL进行测试。

SysBench Test Prepare

首先,做测试准备:

1
2
3
4
5
6
7
8
$ sysbench /usr/local/Cellar/sysbench/1.0.9/share/sysbench/oltp_read_write.lua –threads=5 –max-requests=0 –time=36 –db-driver=mysql –mysql-user=pr_auser –mysql-password=’pr_apass’ –mysql-port=60330 –mysql-host=127.0.0.1 –mysql-db=testing –report-interval=1 prepare
sysbench 1.0.9 (using bundled LuaJIT 2.1.0-beta2)

Initializing worker threads…

Creating table ‘sbtest1’…
Inserting 10000 records into ‘sbtest1’
Creating a secondary index on ‘sbtest1’…

注意,/usr/local/Cellar/sysbench/1.0.9/share/sysbench/oltp_read_write.lua文件可以在SysBench安装包里找到,执行命令后,会在主库testing数据库里生成一个sbtest1表并插入一些数据,
在从库里一样可以看到testing数据库下有sbtest1表,说明主从复制已生效。

SysBench Test Run

然后,开始读写测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ sysbench /usr/local/Cellar/sysbench/1.0.9/share/sysbench/oltp_read_write.lua –threads=5 –max-requests=0 –time=36 –db-driver=mysql –mysql-user=pr_auser –mysql-password=’pr_apass’ –mysql-port=60330 –mysql-host=127.0.0.1 –mysql-db=testing –report-interval=1 run
sysbench 1.0.9 (using bundled LuaJIT 2.1.0-beta2)

Running the test with following options:
Number of threads: 5
Report intermediate results every 1 second(s)
Initializing random number generator from current time


Initializing worker threads…

Threads started!

[ 1s ] thds: 5 tps: 51.66 qps: 1087.83 (r/w/o: 769.92/209.62/108.29) lat (ms,95%): 144.97 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 5 tps: 61.26 qps: 1229.13 (r/w/o: 862.60/243.01/123.52) lat (ms,95%): 142.39 err/s: 1.00 reconn/s: 0.00
[ 3s ] thds: 5 tps: 60.85 qps: 1237.04 (r/w/o: 867.92/247.41/121.71) lat (ms,95%): 121.08 err/s: 0.00 reconn/s: 0.00
[ 4s ] thds: 5 tps: 67.07 qps: 1332.44 (r/w/o: 931.01/267.29/134.15) lat (ms,95%): 127.81 err/s: 0.00 reconn/s: 0.00

查看结果

登录ProxySQL管理系统,查看统计结果:

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
$ mysql -u admin2 -padmin2 -h 127.0.0.1 -P60320
mysql> select * from stats_mysql_query_digest limit\G;
*************************** 1. row ***************************
hostgroup: 0
schemaname: testing
username: pr_auser
digest: 0xE365BEB555319B9E
digest_text: DELETE FROM sbtest1 WHERE id=?
count_star: 2564
first_seen: 1508313300
last_seen: 1508313336
sum_time: 1923227
min_time: 149
max_time: 39773
*************************** 2. row ***************************
hostgroup: 0
schemaname: testing
username: pr_auser
digest: 0xFB239BC95A23CA36
digest_text: UPDATE sbtest1 SET c=? WHERE id=?
count_star: 2566
first_seen: 1508313300
last_seen: 1508313336
sum_time: 2016454
min_time: 158
max_time: 53514

*************************** 13. row ***************************
hostgroup: 1
schemaname: testing
username: pr_auser
digest: 0xDBF868B2AA296BC5
digest_text: SELECT SUM(k) FROM sbtest1 WHERE id BETWEEN ? AND ?
count_star: 2570
first_seen: 1508313300
last_seen: 1508313336
sum_time: 7970660
min_time: 216
max_time: 56153
*************************** 14. row ***************************
hostgroup: 1
schemaname: testing
username: pr_auser
digest: 0xAC80A5EA0101522E
digest_text: SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c
count_star: 2570
first_seen: 1508313300
last_seen: 1508313336
sum_time: 10148202
min_time: 272
max_time: 58032
14 rows in set (0.00 sec)

可以看到读操作都发送给了hostgroup=1组,写操作都发送给了hostgroup=0组,说明读写分离已生效,读写分离配置请仔细阅读上面ProxySQL配置mysql_query_rules部分

此致

到此,用Docker实现MySQL ProxySQL读写分离已完成。另外ProxySQL提供的查询重写功能,其实是利用mysql_query_rules配置,对接收到的查询语句进行正则替换,再传递给数据库服务器,详情请阅读文档ProxySQL Configuration中的“MySQL Query Rules”部分;而数据分片功能,在真实数据分片的基础上,再结合mysql_query_rules配置,重写query到正确的主机、数据库或表上,详细内容可以阅读MySQL Sharding with ProxySQL

redis集群搭建

一、准备工具

安装docker(来自官网

(1) 安装所需的软件包

$ sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2
复制代码

(2) 使用以下命令来设置稳定的存储库

$ sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
复制代码

(3) 安装docker ce(docker社区版)

$ sudo yum install docker-ce
复制代码

(4) 启动docker

$ sudo systemctl start docker
复制代码

在docker库获取redis镜像(截至2019-03-27,最新版为5.0.4)

$ docker pull redis
复制代码

至此,docker上redis cluster所有工具准备完毕,我们在命令行上输入docker images,就可以查看到已经安装的镜像

二、 集群搭建

创建Redis 容器

#####(1)创建redis配置文件(redis-cluster.tmpl)

我在路径/home下创建一个文件夹redis-cluster,在路径/home/redis-cluster下创建一个文件redis-cluster.tmpl,并把以下内容复制过去。(注:路径可自定义,我用的是/home/redis-cluster

port ${PORT}                                       ##节点端口
protected-mode no                                  ##开启集群模式
cluster-enabled yes                                ##cluster集群模式
cluster-config-file nodes-${PORT}.conf 
                     ##集群配置名
cluster-node-timeout 5000                          ##超时时间
cluster-announce-ip 192.168.XX.XX                  ##实际为各节点网卡分配ip  先用上网关ip代替
cluster-announce-port ${PORT}                      ##节点映射端口
cluster-announce-bus-port 1${PORT}                 ##节点总线端口
appendonly yes                                     ##持久化模式
复制代码

通过命令,可查看

[root@node-01 redis-cluster]# cat /home/redis-cluster/redis-cluster.tmpl
port ${PORT}
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 192.168.1.221
cluster-announce-port ${PORT}
cluster-announce-bus-port 1${PORT}
appendonly yes
复制代码

备注:此模版文件为集群节点通用文件  其中${PORT} 将读取命令行变量  ip则根据网卡分配ip进行替换  以保证节点配置文件除端口以及ip 全部一致。

(2) 创建自定义network
$ docker network create redis-net
复制代码

docker network ls ##查看网卡信息

[root@node-01 conf]# docker network ls 
NETWORK ID NAME DRIVER SCOPE 7f804fa5fbd2 redis-net bridge local
复制代码

备注:创建redis-net虚拟网卡 目的是让docker容器能与宿主(centos7)桥接网络 并间接与外界连接

(3) 查看redis-net虚拟网卡网关ip
[root@node-01 conf]# docker network inspect redis-net | grep "Gateway" | grep --color=auto -P '(\d{1,3}.){3}\d{1,3}' -o 172.21.0.1
复制代码

备注:docker network inspect network-name 显示 network-name对应配置信息         (gerp 过滤网关配置行 并筛选ip ) 可人工

(4) 在/home/redis-cluster下生成conf和data目标,并生成配置信息
 $ for port in `seq 7000 7005`; do \
  mkdir -p ./${port}/conf \
  && PORT=${port} envsubst < ./redis-cluster.tmpl > ./${port}/conf/redis.conf \
  && mkdir -p ./${port}/data; \
  done
复制代码

共生成6个文件夹,从7000到7005,每个文件夹下包含data和conf文件夹,同时conf里面有redis.conf配置文件

(5) 创建6个redis容器
$ ip=2
for port in `seq 7000 7005`; do \
docker run -d -p ${port}:${port} -p 1${port}:1${port} \
-v /Users/kevin/Docker/redis-cluster/${port}/conf/redis.conf:/usr/local/etc/redis/redis.conf \
-v /Users/kevin/Docker/redis-cluster/${port}/data:/data \
--restart always --name redis-${port} --net redis-net --ip 172.20.0.${ip} \
 redis redis-server /usr/local/etc/redis/redis.conf; \
 let ip++;
done

复制代码
备注:命令译为  循环7010 - 7015  运行redis 容器
docker  run            运行
-d                          守护进程模式
--restart always     保持容器启动
--name redis-710* 容器起名
--net redis-net    容器使用虚拟网卡
-p                        指定宿主机器与容器端口映射 701*:701*
-P                        指定宿主机与容器redis总线端口映射 1701*:1701*
--privileged=true -v /home/redis-cluster/701*/conf/redis.conf:/usr/local/etc/redis/redis.conf
付权将宿主701*节点文件挂载到容器/usr/local/etc/redis/redis.conf 文件中
--privileged=true -v /home/redis-cluster/${port}/data:/data \
付权将宿主701*/data目录挂载到容器/data目录中
--sysctl net.core.somaxconn=1024 redis redis-server /usr/local/etc/redis/redis.conf;
容器根据挂载的配置文件启动 redis服务端
复制代码
6、通过命令docker ps可查看刚刚生成的6个容器信息
[root@node-01 redis-cluster]# docker ps
CONTAINER ID        IMAGE                                         COMMAND                  CREATED             STATUS                PORTS                                                              NAMES
15c479074b87        redis                                         "docker-entrypoint..."   10 seconds ago      Up 9 seconds          0.0.0.0:7005->7005/tcp, 6379/tcp, 0.0.0.0:17005->17005/tcp         redis-7005
45bec33c2c35        redis                                         "docker-entrypoint..."   11 seconds ago      Up 10 seconds         0.0.0.0:7004->7004/tcp, 6379/tcp, 0.0.0.0:17004->17004/tcp         redis-7004
45482d9e5bb8        redis                                         "docker-entrypoint..."   11 seconds ago      Up 10 seconds         0.0.0.0:7003->7003/tcp, 6379/tcp, 0.0.0.0:17003->17003/tcp         redis-7003
f633d5c767c9        redis                                         "docker-entrypoint..."   11 seconds ago      Up 10 seconds         0.0.0.0:7002->7002/tcp, 6379/tcp, 0.0.0.0:17002->17002/tcp         redis-7002
eefc0d49fedf        redis                                         "docker-entrypoint..."   11 seconds ago      Up 10 seconds         0.0.0.0:7001->7001/tcp, 6379/tcp, 0.0.0.0:17001->17001/tcp         redis-7001
58b311e5dbcb        redis                                         "docker-entrypoint..."   12 seconds ago      Up 11 seconds         0.0.0.0:7000->7000/tcp, 6379/tcp, 0.0.0.0:17000->17000/tcp         redis-7000  
复制代码
7、查看容器分配ip
[root@node-01 conf]# docker network inspect redis-net
[
{
    "Name": "redis-net",
    "Id": "7f804fa5fbd2473f1e6e9783ac52aa980c355e3253b15a28e10f8793c46fc39b",
    "Created": "2019-03-27T12:59:14.45569686+08:00",
    "Scope": "local",
    "Driver": "bridge",
    "EnableIPv6": false,
    "IPAM": {
        "Driver": "default",
        "Options": {},
        "Config": [
            {
                "Subnet": "172.21.0.0/16",
                "Gateway": "172.21.0.1"
            }
        ]
    },
    "Internal": false,
    "Attachable": false,
    "Containers": {
        "449c38916b4ef919d1b1828da3f69e0f5256eb2023204f25f69fbb8c449f2163": {
            "Name": "redis-7005",
            "EndpointID": "5c89e6fdd0e983d6d2b950c9bc41616920265dda93f2852650ca5e9da1da22b1",
            "MacAddress": "02:42:ac:15:00:07",
            "IPv4Address": "172.21.0.7/16",
            "IPv6Address": ""
        },
        "600aa2f84dfbe72a19caf5dc684cb7691e89a43753f70d74355468b7659caa23": {
            "Name": "redis-7000",
            "EndpointID": "75873081222c7a2eedc5f3ee639df5cc7645790012dd72e34c97fd97dcb5cf47",
            "MacAddress": "02:42:ac:15:00:02",
            "IPv4Address": "172.21.0.2/16",
            "IPv6Address": ""
        },
        "78aeae8b2d381d3789848f45f766dd01d7c0d5443505920decf41fce751f765d": {
            "Name": "redis-7002",
            "EndpointID": "a70d816cd9e3650b1d10507a85fa85c0c2e143ba99c0974fb000ec87ad5b173d",
            "MacAddress": "02:42:ac:15:00:04",
            "IPv4Address": "172.21.0.4/16",
            "IPv6Address": ""
        },
        "7ecd2e1372a2bc15cb89fad856ab410757b49936f662ce170990c04e1861037d": {
            "Name": "redis-7004",
            "EndpointID": "f8cdb522601e9667ec06dc0dc0d39b46331b3d24cbea1155b00b73e600eca8c7",
            "MacAddress": "02:42:ac:15:00:06",
            "IPv4Address": "172.21.0.6/16",
            "IPv6Address": ""
        },
        "bd8254045c66bc6569c9a585b58cb028e864b6c7f16d836009debd3a3350913c": {
            "Name": "redis-7003",
            "EndpointID": "6a25ff0046e6d6575579cdf08bccbb2a62b29460e3932de3f8e476bb908fdc9c",
            "MacAddress": "02:42:ac:15:00:05",
            "IPv4Address": "172.21.0.5/16",
            "IPv6Address": ""
        },
        "cfef6ca3aeea9d3b4fa527538fbe2a2f79aac25d52921b4edc31839b6f330887": {
            "Name": "redis-7001",
            "EndpointID": "98a36b61bc6f50f78444a6cefa5e9999f5366e72425602511bc95f602ef0dc93",
            "MacAddress": "02:42:ac:15:00:03",
            "IPv4Address": "172.21.0.3/16",
            "IPv6Address": ""
        }
    },
    "Options": {},
    "Labels": {}
}
]
复制代码

备注:每个容器对应分配了ip 需要修改节点配置文件redis.conf与其对应并重启容器。

(8) 修改宿主挂载目录配置文件
vim /home/redis-cluster/700*/conf/redis.conf ##编辑ip为查询分配的ip 并esc+:wq退出

port 7000
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.21.0.2
cluster-announce-port 7000
cluster-announce-bus-port 17000
appendonly yes
复制代码

启动集群

进入一个节点
docker exec -it redis-7000 bash
复制代码
使用Redis 5创建集群,redis-cli只需键入:
redis-cli --cluster create 172.20.0.2:7000 172.20.0.3:7001 172.20.0.4:7002 172.20.0.5:7003 172.20.0.6:7004 172.20.0.7:7005 --cluster-replicas 1
复制代码
至此,集群已经创建好了。
[root@node-01 data]# docker exec -it redis-7000 bash
root@600aa2f84dfb:/data# redis-cli --cluster create 172.21.0.2:7000  172.21.0.3:7001  172.21.0.4:7002  172.21.0.5:7003  172.21.0.6:7004  172.21.0.7:7005 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.21.0.6:7004 to 172.21.0.2:7000
Adding replica 172.21.0.7:7005 to 172.21.0.3:7001
Adding replica 172.21.0.5:7003 to 172.21.0.4:7002
M: 0d403d866c4bdfa90f3a1e21b2a33356474014ee 172.21.0.2:7000
   slots:[0-5460] (5461 slots) master
M: ab4bf4833926dd7ffca5f2d34ef754466730e7d7 172.21.0.3:7001
   slots:[5461-10922] (5462 slots) master
M: 846f087ee5885f17d5a5c05d2eb2ba470441f700 172.21.0.4:7002
   slots:[10923-16383] (5461 slots) master
S: e44c2b96f1446b1860fe582547634b12a461407d 172.21.0.5:7003
   replicates 846f087ee5885f17d5a5c05d2eb2ba470441f700
S: 4efa0a9b7b4b159807900efa5f86410e55c1d2b3 172.21.0.6:7004
   replicates 0d403d866c4bdfa90f3a1e21b2a33356474014ee
S: 8a616ee30a7104bcd341d57af825c6b5b30176ff 172.21.0.7:7005
   replicates ab4bf4833926dd7ffca5f2d34ef754466730e7d7
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 172.21.0.2:7000)
M: 0d403d866c4bdfa90f3a1e21b2a33356474014ee 172.21.0.2:7000
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: e44c2b96f1446b1860fe582547634b12a461407d 172.21.0.5:7003
   slots: (0 slots) slave
   replicates 846f087ee5885f17d5a5c05d2eb2ba470441f700
M: ab4bf4833926dd7ffca5f2d34ef754466730e7d7 172.21.0.3:7001
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 8a616ee30a7104bcd341d57af825c6b5b30176ff 172.21.0.7:7005
   slots: (0 slots) slave
   replicates ab4bf4833926dd7ffca5f2d34ef754466730e7d7
M: 846f087ee5885f17d5a5c05d2eb2ba470441f700 172.21.0.4:7002
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 4efa0a9b7b4b159807900efa5f86410e55c1d2b3 172.21.0.6:7004
   slots: (0 slots) slave
   replicates 0d403d866c4bdfa90f3a1e21b2a33356474014ee
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
复制代码
连接客户端,查看主从信息
root@600aa2f84dfb:/data# redis-cli -c -p 7000     
127.0.0.1:7000> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.21.0.6,port=7004,state=online,offset=3556,lag=1
master_replid:ef97265d34769b68096786c3e802fb3a13968fe9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:3556
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:3556
复制代码

其它注意事项

宿主主机对外开放ip
for port in `seq 7000 7005`; do \  
firewall-cmd --zone=public --add-port=${port}/tcp --permanent  
done
#重新载入
firewall-cmd --reload
复制代码

#####需要暂停容器并删除容器 以便重复6步骤

for port in `seq 7000 7005`; do \
  docker stop redis-${port};
  docker rm redis-${port};
done

完美解决 MacOS 下不能 ping Docker 容器的问题 | MacOS 使用 ssh 链接

今天下午从docker hubpullCentOS。跑起来后装了ssh,然后在宿主机上ping不通CentOScontainer…..这是多么囧的事情…

  • 对于docker for mac不能ping容器官网给的解释是
    ….
    对于以上解释通过端口映射可以解决docker for mac连接容器的问题,但是有些情况下是不需要端口的怎么办?以上说法对我毫无卵用。带着问题,Google了下据说有个openVPN可以解决这个问题。
  • 解决问题
    搜了好一阵子,大多都没有详细说明,最后在GitHub上搜到一个叫 docker-mac-network 的项目。嗯,很是牛逼。

项目README大致翻译

使用OpenVPN来搞定。让你从macOS访问docker for mac

Quickstart

访问docker网络:

安装Tunnelblick  这个软件是OpenVPNmacOS的客户端。

  • 运行 docker-compose up 因为该项目需要生成一些keys,所以第一次启动会耗费点时间。
  • 双击生成的 docker-for-mac.ovpn 文件,或者直接在你的终端运行该文件把它添加到Tunnelblick。 该文件会在项目根目录生成。
  • 使用Tunnelblick链接你新添加的docker-for-mac

现在你可以从你的macOS访问doker里面的网络了。

Implementation notes

该配置由两个服务组成,它们都是基于Alpine

openvpn

使用OpenVPN docker镜像 kylemanna/openvpn.

服务端和客户端配置文件都自动的由helpers/run.sh来生成, 该脚本运行在镜像中,只能通过调整该脚本的配置来访问你docker for mac的网络。

该服务使用网络在TCP 1194端口上跑着,这说明它可以在vm上访问所有的docker网络。

只有172.16.0.0/20 私有网络被配置生成器路由到了Docker for Mac。主机上没有设置DNS服务。

镜像OpenVPN的配置在目录 (/etc/openvpn/*)中,对应宿主的文件系统的./config/,这样便于自定义。

proxy

自打有docker for mac开始,宿主机就不能访问容器的网络。我们使用TCP代理。此镜像使用socat将端口13194转发到OpenVPN容器上。

使用过程

  1. 把项目从GitHubClone下来,放到某个文件夹中。
  2. 修改run.sh脚本
#!/bin/shdest=${dest:-docker.ovpn}if [ ! -f "/local/$dest" ]; then
    echo "*** REGENERATING ALL CONFIGS ***"
    set -ex    #rm -rf /etc/openvpn/*
    ovpn_genconfig -u tcp://localhost
    sed -i 's|^push|#push|' /etc/openvpn/openvpn.conf    echo localhost | ovpn_initpki nopass
    easyrsa build-client-full host nopass
    ovpn_getclient host | sed '
        s|localhost 1194|localhost 13194|;
    s|redirect-gateway.*|route 172.17.0.0 255.255.0.0|; # 这里要配置你container的ip和子网掩码
    ' > "/local/$dest"fi# Workaround for https://github.com/wojas/docker-mac-network/issues/6/sbin/iptables -I FORWARD 1 -i tun+ -j ACCEPTexec ovpn_run
  1. 运行docker-compose.yml。打开终端cd到项目的根目录。输入docker-compose up
    运行后自动创建两个容器,一个为代理容器一个为openvpn服务端容器
  2. 查看项目根目录,会发现有生成的docker-for-mac.ovpn文件
    docker-for-mac.ovpn
  3. 编辑docker-for-mac.ovpn文件,这里是个坑,需要加一个comp-lzo yes属性。
client
nobind
dev tun
remote-cert-tls server

remote localhost 13194 tcp<key>-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6AzE2XEmBxBkX
0PajWJQkLOFTb6pgydCrzpV5/9wSL1mgOGg7X2pA080LgGB8GVoSj+hyOdnPc5SQ
P/JKuEqHDeGmNR3mdROXPBJzEheFtpQP+01bmQgTi8aEFllof+M8N6XdZKbWCMIr
Oa6kHFJTM7S5lC7zqZCQ/Pg0yi81EZWdzanCYT/orB90RTvBMa4R8Y/YOFdtKZip
YVPAokNVu/Vb7Lvwmqhu84RBqeJ9BPsAZuFuSLu9l0HsqZZisAzer05aOAld5SgU
6YsaimXLsff+wuOssM2LXohbHdE11bEN286SvOvecDj3/QfYDzLlVMq9OLX3TPep
dQ2WPZ0zAgMBAAECggEAB3NTSEsgApuZKbQL/PTyUhSeHdpuJ5xNyGPo5s8RwIS3
AUzhF+e1eM1C/D/zHWVijzXGaLTafY2ymKiHCukgyxIKRL8B6JYt57PXRVanLIro
mU9GhPG3OhkyJV5DzIS61FGiSiDa8d298T9JOHeN8Jk0lu5Rhx72lGgdhPfINkbh
i+fcPXTuPpeNKxwcOcV7MQdJOCir1gVwRaHoh3b/1/hBfllUMla0IcVDrOByq5j9
NFvFXp3cIwHtpxXi9HIXjYg50tIpKsZVA1My1X7zSgw4Uy3DkYOCtGfTd3ZPkAZg
M49WrkXzwfS4xCGlptYcXKpMuRK+4CGn3DY62/TuwQKBgQDfTcdCSEjp9XBpp15v
qlfWiHCm2w9qqQj30Bu78vflvJRLHEXgAK5jn5i/oKBDn9P65XT5iMG73IilBtDr
ZqLHxf6bii+3giNk1r7Ij8dABea2DZoDAiXw7gBYeWrShwFoEK1sAOQLpXfKyahg
D5Ri73MzWB6X8NgonTSWgMLCEwKBgQDVP5nxiit6w15ztlisHO0OyI4NOXCz8JJP
/psbCLafn/jO9MVUtD+CGzB/LaenPyN2wgFwS2rW1aP0eQ4CblhdBsR2DPyFQn+Q
kFvlfECViPyqctuQ3Vg5whUnd2uB6s/41Kf5SCesDmc+moq0agCYfcPPC26T8YjN
TitLqugcYQKBgQCLHO1Rwa6K8jjB2V7fugNWx1FG4ZnNHNlVaPSeNlNxb/YChO6T
ClmZ/scuOmcdOejZmTEyc5wd6NnH1WM6A4rNYe+n/oKTlCNwosfgMpIytHpyWC7m
SORSIDNe6uU9mpWpuxoO+s8W4lKqps6aaZpLuFjQV8lXZteDFcy5JiYuuQKBgQCL
JfFB1zL/7DPZtYau6EEXgG6Q2oZcJAKVCEIVpd3k0vH7uWR6HRPDxhIjKptXWq+9
cmKV7+BLRySxBbzKgU5PhXb0wQyVuAqdPlV4rVdHPBgC8Oes44IKTkaXdilEoQAM
Eld5Jkgc0Vk1VIplpzW0S3xVwdPp3ZWywesmeOL0gQKBgHVjNnrAM6KnmtcC2ESt
JanCQtU3IHtBE9ReCGBEZ9lR0jCqvFbivv3CNhOr5O+S1V3kgIKQPB76+zmYl/0C
RYwauaLkzOEbi6UunmNT39Mqdcr7PyoaPp2Wk9PD0tM2/ehNu6MLh41MX1w1Fd46
O77pYRHWuGOfZTPdshxOfPgf
-----END PRIVATE KEY-----</key><cert>-----BEGIN CERTIFICATE-----
MIIDQzCCAiugAwIBAgIQMH0suH0s9/Tt3dojdz/UTDANBgkqhkiG9w0BAQsFADAU
MRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTgwOTE2MTEwMTA3WhcNMjgwOTEzMTEw
MTA3WjAPMQ0wCwYDVQQDDARob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAugMxNlxJgcQZF9D2o1iUJCzhU2+qYMnQq86Vef/cEi9ZoDhoO19qQNPN
C4BgfBlaEo/ocjnZz3OUkD/ySrhKhw3hpjUd5nUTlzwScxIXhbaUD/tNW5kIE4vG
hBZZaH/jPDel3WSm1gjCKzmupBxSUzO0uZQu86mQkPz4NMovNRGVnc2pwmE/6Kwf
dEU7wTGuEfGP2DhXbSmYqWFTwKJDVbv1W+y78JqobvOEQanifQT7AGbhbki7vZdB
7KmWYrAM3q9OWjgJXeUoFOmLGoply7H3/sLjrLDNi16IWx3RNdWxDdvOkrzr3nA4
9/0H2A8y5VTKvTi190z3qXUNlj2dMwIDAQABo4GVMIGSMAkGA1UdEwQCMAAwHQYD
VR0OBBYEFA3QP+B02XYogxyjbFYYGBfhHqj5MEQGA1UdIwQ9MDuAFB8KyCaZfFiQ
aSxHMmMj00QD1kqioRikFjAUMRIwEAYDVQQDDAlsb2NhbGhvc3SCCQCGOlhuj5Vr
KzATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQEL
BQADggEBALhv25L/dmTOszEwYn0pDtdkTwzIsCoD5tx1bQutlkvWHjG65md32Mfx
B60Sqo5f/Rc7ZwB4+4kKW/3gt0KaqLPbyWGLeH9yJNu/6VDMwvHJC07YxpzhLyHL
taq9VQDWS5FQUkC9oYsEZzxeaadcCQpNp1fdx4586fVYGJ2/dGDeqE52ZqMDNRSF
I1DurZlaKVsbkFdrm0UAlqBWZDDHlSImkd+uKwXwqQdnkxiPzCoW0dpMfw+7DvuV
Muq3759LtYmZbDFh8dUDRZ830WAwGzv9yhPpOg58G0dNyMTQJo59/p2Ea4NHHlBv
TqfXQM1at9leSpR5ZFkqDbIbYoxUuxk=
-----END CERTIFICATE-----</cert><ca>-----BEGIN CERTIFICATE-----
MIIDLzCCAhegAwIBAgIJAIY6WG6PlWsrMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xODA5MTYxMTAwMDhaFw0yODA5MTMxMTAwMDhaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAOvgs+KzBgg5Bz+dALH8uFsfBk2QklaeeK0O5H63qjXqujICy/MGxXAiqdo6
UqLTca9psvhhR0QeXEnZ2daBrw3XuGZ/nw9lmJZ/S/MOfJWTerYaj1ZttPjnlOFS
muWKouNam33mJF2vkGbYUoEdb3qqEoZTd3DBEnRAKLzE0v7evAbv7XW7kh+w7W+D
4RsalXLaUqquP/BYcOAWB733ZBIjpmEglA0u2ZtF9tXK/HT2d3dkxbrdRrF2WLwg
fcE53mb8/ayf3qu7nRj0NaqqYb8R9R5fcQnzMCi5ZZ/Dudbcc5/6NEu5G1PTCyTs
Z9o/FZO1KF3q7TR0FzYMkTMGF20CAwEAAaOBgzCBgDAdBgNVHQ4EFgQUHwrIJpl8
WJBpLEcyYyPTRAPWSqIwRAYDVR0jBD0wO4AUHwrIJpl8WJBpLEcyYyPTRAPWSqKh
GKQWMBQxEjAQBgNVBAMMCWxvY2FsaG9zdIIJAIY6WG6PlWsrMAwGA1UdEwQFMAMB
Af8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQC2APlsYTKtque4IF/C
nFRcY5J5Btj7SuyLUjkVp6HxengmwI5pDeubMvv9EeVZ0lAzobQ988P4vGkNGRde
QKGNkwjpsvNj+kyYCWhke8eYuxOXRZCNwp19kJjxhCN/i/adLhff0cXDfrMbX0Zv
W5WNgbrPBlk3TEn5XRHR/yp+UFNwL4e2ZovYFy3hBAuRfIdUF1wdZIdrjsHEMGBe
vHfKSebhvyPgAGAi91vU14M99xERqrvM09pMfMHF9xYhiiQkcZtVzaauZE0d3m+o
VK4HQZg2UjxfLUo5kU1Ou0E3cYHkDx3eEF/X/F5SyoWPTa2FfrK0j5/CE32vhlck
szzU
-----END CERTIFICATE-----</ca>key-direction 1<tls-auth>#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
e33646344fdba3b929ea6e07f1558bdb
080136f61bd0a0611bb1a952802d47b2
eb14d5aae9d25c4ab3614d495876e815
3ce7c2f9f67dda2183bec7928dfb792f
003cb36d5a57fd9e0df23c2fd4c7ae05
6a8b1ecb6c3697a78d3c3a67c16b98ad
167258c57aa3dd674c5a735d3df4b5ab
1610a676bd97beaadab10fd82f82c9e7
2b1efe35f96ff237b366e057d20b3a18
cdc095510df43bcfa638853efde91a67
93507615eceb77f2de619d614ca64901
2273a9074153b531dde8ac10f6d2cdd9
f73e8cba9548f2dc853b8c4178beed68
5d15b81da6121f7dc3c0f25c7008955d
5f9c6264d47bac43f6983ad8af8aba5b
8fa4ac4611109613e1a9d876620bdd83
-----END OpenVPN Static key V1-----</tls-auth>comp-lzo yes
route 172.17.0.0 255.255.0.0
  1. 把该文件添加到Tunnelblick,然后连接该网络。
    连接后的样子
  2. 测试
    docker container centos 网络

宿主机,完美搞定



作者:阿波罗程序猿

Python慢,为啥还有大公司用?

Instagram 的视频可以给我们哪些启示呢?

  • Python + Django 的组合完全可以负载用户数以 10 亿记的服务,如果你正准备开始一个项目,放心使用 Python 吧!
  • 完善的单元测试对于复杂项目是非常有必要的。如果没有那『成千上万的单元测试』。很难想象 Instagram 的迁移项目可以成功进行下去。
  • 开发者和同事也是你的产品用户,利用好他们。用他们为你的新特性发布前多一道测试。
  • 完全基于主分支的开发流程,可以给你更快的迭代速度。前提是拥有完善的单元测试和持续部署流程。
  • Python 3 是大势所趋,如果你正准备开始一个新项目,无需迟疑,拥抱 Python 3 吧!

作者:我爱学python
链接:https://www.jianshu.com/p/e18e01ad7ad9
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

composer配置使用代理

composer加速,使用http代理,执行:
export http_proxy=”http://127.0.0.1:1087″
export https_proxy=”http://127.0.0.1:1087″

取消代理:
unset http_proxy

laravel 使用workerman提供http服务

  • 背景

公司项目使用的是laravel框架,开发完后压测一直不过,期间开启过opcache扩展加速(不属于本篇内容,不在此赘述),后决定采用workerman或swoole加速laravel应用,本篇以workerman为例。

首先大家应该知道laravel每次收到请求都会把框架核心代码加载一遍,使用workerman提升性能的关键是把框架核心代码缓存起来,这样就不需要每次重新加载了,以此来提升性能(swoole同理)

  • composer安装workerman
#更改composer源
composer config -g repo.packagist composer https://packagist.phpcomposer.com
#下载workerman包
composer require workerman/workerman
  • 创建启动命令
  1. 生成 WorkermanCommand 文件
php artisan make:command WorkermanCommand

    执行以上命令行会在 app/Console/Commands/ 目录下生成 WorkermanCommand.php 文件,对文件做如下修改。

<?php

namespace App\Console\Commands;

use Workerman\Worker;
use Illuminate\Console\Command;

class WorkermanCommand extends Command
{
    private $server;
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'wk {action}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Start a Workerman server.';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        global $argv;
        $arg = $this->argument('action');
        $argv[1] = $argv[2];
        $argv[2] = isset($argv[3]) ? "-{$argv[3]}" : '';
        switch ($arg) {
            case 'start':
                $this->start();
                break;
            case 'stop':
                break;
            case 'restart':
                break;
            case 'reload':
                break;
            case 'status':
                break;
            case 'connections':
                break;
        }
    }

    private function start()
    {
        // 创建一个Worker监听20002端口,不使用任何应用层协议
        $this->server = new Worker("http://0.0.0.0:1234");
        // 启动4个进程对外提供服务
        $this->server->count = 4;
        $handler = \App::make('handlers\WorkermanHandler');
        // 连接时回调
        $this->server->onConnect = [$handler, 'onConnect'];
        // 收到客户端信息时回调
        $this->server->onMessage = [$handler, 'onMessage'];
        // 进程启动后的回调
        $this->server->onWorkerStart = [$handler, 'onWorkerStart'];
        // 断开时触发的回调
        $this->server->onClose = [$handler, 'onClose'];
        // 运行worker
        Worker::runAll();
    }
}

在这个自定义命令还引用了其他的类文件,如

$handler = \App::make('handlers\WorkermanHandler');

所以,需要创建一个 WorkermanHandler.php 的文件来处理对应的操作

2、创建 WorkermanHandler.php

创建 app/handlers/WorkermanHandler.php

<?php

namespace handlers;


class WorkermanHandler
{
    public function onWorkerStart($worker)
    {
        //加载index文件的内容
        require __DIR__ . '/../../vendor/autoload.php';
        require_once __DIR__ . '/../../bootstrap/app.php';
    }

    // 处理客户端连接
    public function onConnect($connection)
    {
        echo "new connection from ip " . $connection->getRemoteIp() . "\n";
    }

    // 处理客户端消息
    public function onMessage($connection, $data)
    {
        // 向客户端发送hello $data
        //server信息
        if (isset($data->server)) {
            foreach ($data->server as $k => $v) {
                $_SERVER[strtoupper($k)] = $v;
            }
        }

        //header头信息
        if (isset($data->header)) {
            foreach ($data->header as $k => $v) {
                $_SERVER[strtoupper($k)] = $v;
            }
        }

        //get请求
        if (isset($data->get)) {
            foreach ($data->get as $k => $v) {
                $_GET[$k] = $v;
            }
        }

        //post请求
        if (isset($data->post)) {
            foreach ($data->post as $k => $v) {
                $_POST[$k] = $v;
            }
        }

        //文件请求
        if (isset($data->files)) {
            foreach ($data->files as $k => $v) {
                $_FILES[$k] = $v;
            }
        }

        //cookies请求
        if (isset($data->cookie)) {
            foreach ($data->cookie as $k => $v) {
                $_COOKIE[$k] = $v;
            }
        }

        ob_start();//启用缓存区

        //加载laravel请求核心模块
        $kernel = app()->make(\Illuminate\Contracts\Http\Kernel::class);
        $laravelResponse = $kernel->handle(
            $request = \Illuminate\Http\Request::capture()
        );
        $laravelResponse->send();
        $kernel->terminate($request, $laravelResponse);

        $res = ob_get_contents();//获取缓存区的内容
        ob_end_clean();//清除缓存区

        //输出缓存区域的内容
        $connection->send($res);
    }

    // 处理客户端断开
    public function onClose($connection)
    {
        echo "connection closed from ip {$connection->getRemoteIp()}\n";
    }
}

3.使用composer自动加载

        "classmap": [
            "app/Handlers"
        ],

执行

composer dump-autoload -o

至此基本配置写完了,下边试试启动一下

php artisan wk start
----------------------------------------- WORKERMAN -----------------------------------------
Workerman version:3.5.19          PHP version:7.2.11
------------------------------------------ WORKERS ------------------------------------------
proto   user            worker          listen                 processes    status
tcp     xuce            none            http://0.0.0.0:1234    4             [OK]
---------------------------------------------------------------------------------------------

说明启动成功了!!!

linux下postfix配置smtp服务

系统一般都自带了postfix,保险起见,安装下

yum -y install postfix

安装sasldb、saslauthd

#提供smtp的虚拟账户和密码服务 sasldb2包含在saslauthd当中yum -y install cyrus-sasl cyrus-sasl-lib cyrus-sasl-plain  cyrus-sasl-devel#当前mta查看alternatives --display mta#设置mtaalternatives --set mta /usr/sbin/sendmail.postfix#再次查看mtaalternatives --display mta#输出结果最后一行会有类似如下的提示:mta即设置完毕#Current `best' version is /usr/sbin/sendmail.postfix.

postfix开机启动设置,因为采用sasldb2提供postfix的smtp账户和密码,无需saslauthd服务运行,故而saslauthd服务不用启动—看吧,使用sasldb2还减少资源占用:

systemctl enable postfix

postfix配置文件在/etc/postfix目录下,主要修改的配置文件为:/etc/postfix/main.cf,配置说明详见代码段:

vi /etc/postfix/main.cf #vi编辑postfix配置文件#找到如下配置项酌情修改#######postfix主机名,修改成你的域名 此项需要添加A记录并指向postfix所在主机公网IPmyhostname = mail.jjonline.com.cn#域名mydomain = jjonline.com.cn#本机postfix的邮箱域名后最 此项默认值使用myhostname#此处使用了前项mydomain 也就是说本机postfix邮箱后缀为:@jjonline.com.cnmyorigin = $mydomain#指定postfix系统监听的网络接口 此处必须是localhost或127.0.0.1或内网ip#若注释或填入公网ip 服务器的25端口将对公网开放#默认值为all 即监听所有网络接口inet_interfaces = all#网络协议 这里ipv4即可inet_protocols = ipv4#指定postfix接收邮件时收件人的域名,换句话说,也就是你的postfix系统要接收什么样的邮件。#此项配置中$myhostname表示postfix接受@$myhostname为后缀的邮箱的邮件 逗号分割支持指多项#此项默认值使用myhostnamemydestination = $mydomain, localhost.$mydomain, localhost#此项制定接收邮件的规则 可以是hash文件 此项对本次配置无意义 可以直接注释local_recipient_maps =#指定你所在的网络的网络地址mynetworks = 42.121.107.189, 10.200.9.xxx, 127.0.0.1#请依据实际情况修改#指定MUA通过smtp连接postfix时返回的header头信息#原始配置附带有postfix版本号 去掉即可,此项酌情处理,可以先不管smtpd_banner = JJonline.Cn ESMTP Server下面的配置默认是没有的,需要在配置文件最后加上#SMTP Config#指定postfix兼容MUA使用不规则的smtp协议--主要针对老版本的outlook 此项对于本次配置无意义broken_sasl_auth_clients = yes#指定可以向postfix发起SMTP连接的客户端的主机名或ip地址smtpd_client_restrictions = permit_sasl_authenticated#此处permit_sasl_authenticated意思是允许通过sasl认证(也就是smtp链接时通过了账号、密码效验的用户)的所有用户smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, #发件人在执行RCPT TO命令时提供的地址进行限制规则 此处照搬复制即可reject_unauth_destination#指定postfix使用sasl验证 通俗的将就是启用smtp并要求进行账号、密码效验smtpd_sasl_auth_enable = yes#指定SMTP认证的本地域名 本次配置可以使用 smtpd_sasl_local_domain = '' 或干脆注释掉 默认为空smtpd_sasl_local_domain = $mydomain#取消smtp的匿名登录 此项默认值为noanonymous smtp若能匿名登录危害非常大 此项请务必指定为noanonymoussmtpd_sasl_security_options = noanonymous#指定通过postfix发送邮件的体积大小 此处表示5Mmessage_size_limit = 5242880######

查看postfix配置文件的所有配置项:

postconf #不带任何命令参数 即可输出所有postfix配置项以及默认值

五、sasldb2建立smtp用户和密码

某种意义上来讲:smtp的账户密码建立也就是建立邮箱账户(类似jjonline@jjonline.cn的邮箱地址)。

#配置postfix启用sasldb2作为smtp的账号秘密效验方式#编辑通过sasl启用smtp账号密码效验的配置vi /etc/sasl2/smtpd.conf #vi写入或编辑内容如下:#####pwcheck_method: auxpropauxprop_plugin: sasldbmech_list: plain login CRAM-MD5 DIGEST-MD5######这里需要注意的是:这个配置文件的位置是64位机器上的,32位机器应该在:/usr/lib/sasl2/smtpd.conf#创建smtp账号saslpasswd2 -c -u `postconf -h mydomain` test #回车会要求输入密码,连续两次#表示创建test@$mydomain的邮箱账号(也是smtp的账号)和密码#本例就是创建test@jjonline.com.cn账号和密码#此处注意的是smtp登录用的账号并不是单纯的用户名 而是整个邮箱地址字符串#假设此处设置的smtp账号test@jjonline.com.cn密码为test123 下方测试时要用到#查看sasldb2的用户和密码sasldblistusers2#此命令进用户查看sasldb的用户情况#此命令回车后会输出诸如这样的内容:test@jjonline.com.cn: userPassword#每次添加smtp用户完毕之后需重启postfix或reload

测试postfix配置文件并启动postfix

#没有问题的话会返回着色[ok]字样#启动postfixsystemctl restart postfix #更改sasldb2数据的权限,让postfix可以读取chmod 755 /etc/sasldb2

六、测试smtp

http://tool.chinaz.com/Tools/Base64.aspx 这个网站可以直接base64加密

telnet测试时需要用到,账号和密码都需要添加加密的字符串

我是直接在windows上操作的

telnet 192.168.2.182 25#输入auth login 进行登录操作auth login334 VXN1cm5hbWU6 #会显示出这个#输入账号为base64的加密字符串dGVzdEBqdW1wc2VydmVyLmNvbQ==#输入密码为base64的加密字符串0dGVzdDEyMw==235 2.7.0 Authentication seccessful  #认证成功后出来的东西

如果失败先查看日志 /var/log/maillog 大概知道是哪步没操作

自此,自建postfix并启用smtp和smtp的虚拟账号完成;

七、域名解析记录设置

postfix配置完毕,想要通过这台postfis邮件服务器发送的邮件被其他邮箱服务商所接受,还需要做A记录、txt记录还mx记录,请适配你自己的域名和ip地址。

postfix用客户端外发游戏出现Relay access denied,拒绝发信

解决办法:

编辑/etc/postfix/main.cn,找到

smtpd_recipient_restrictions=permit_sasl_authenticated, 
    permit_mynetworks, 
    reject_unauth_destination,

修改成

smtpd_recipient_restrictions = permit_sasl_authenticated, 
        permit_mynetworks, 
        reject_unauth_destination,
	reject_non_fqdn_sender,
        reject_non_fqdn_recipient, 
        reject_unknown_recipient_domain

MongoDB 分片的原理、搭建、应用

原文:http://blog.51cto.com/13643643/2148825

什么是分片

高数据量和吞吐量的数据库应用会对单机的性能造成较大压力,大的查询量会将单机的CPU耗尽,大的数据量对单机的存储压力较大,最终会耗尽系统的内存而将压力转移到磁盘IO上。
MongoDB分片是使用多个服务器存储数据的方法,以支持巨大的数据存储和对数据进行操作。分片技术可以满足MongoDB数据量大量增长的需求,当一台MongoDB服务器不足以存储海量数据或者不足以提供可接受的读写吞吐量时,我们就可以通过在多台服务器上分割数据,使得数据库系统能存储和处理更多的数据。

MongoDB分片优势

分片为应对高吞吐量与大数据量提够了方法

  • 使用分片减少了每个分片需要处理的请求数,因此,通过水平扩展,群集可以提高自己的存储容量。比如,当插入一条数据时,应用只需要访问存储这条数据的分片。
  • 使用分片减少了每个分片村存储的数据
    分片的优势在于提供类似线性增长的架构,提高数据可用性,提高大型数据库查询服务器的性能。当MongoDB单点数据库服务器存储成为瓶颈、单点数据库服务器的性能成为瓶颈或需要部署大型应用以充分利用内存时,可以使用分片技术。

    MongoDB分片群集的组成

  • Shard:分片服务器,用于存储实际的数据块,实际生产环境中一个shard server 角色可以由几台服务器组成一个Peplica Set 承担,防止主机单点故障。
  • Config Server:配置服务器,存储了整个分片群集的配置信息,其中包括chunk信息。
  • Routers:前端路由,客户端由此接入,且让整个群集看上去像单一数据库,前端应用可以透明使用。

环境准备

  • 系统版本:CenTos 7
  • 软件版本:MongoDB4.0
  • 关闭防火墙及selinux
    systemctl stop firewalld.service
    setenforce 0
IP:172.16.10.26 IP:172.16.10.27 IP:172.16.10.29
mongos(27017) mongos(27017) mongos(27017)
config(30000) config(30000) config(30000)
shard1主节点(40001) shard1副节点(40001) shard1仲裁节点(40001)
shard2仲裁节点(40002) shard2主节点(40002) shard2副节点(40002)
shard1副节点(40003) shard1仲裁节点(40003) shard1主节点(40003)

部署MongoDB分片群集

群集部署的搭建思路,利用三台服务器,分别安装mongodb数据库,每台服务器创建五个实例(mongos、configs、shard1、shard2、shard3)。三台不同的服务器上的相同名称的实例,创建为一个复制集,分别包括主节点,副节点,仲裁节点。mongos不需创建复制集,config不需指定主副节点及仲裁节点,但是要创建复制集。三台服务器的操作步骤略有差别,但是大多是都是重复操作,步骤完全一致。

安装MongoDB数据库

安装支持软件和mongodb

yum install openssl-devel -y
tar zxf mongodb-linux-x86_64-rhel70-4.0.0.tgz -C /usr/local
mv /usr/local/mongodb-linux-x86_64-rhel70-4.0.0 /usr/local/mongodb  //解压即完成安装

创建数据存储目录和日志存储目录

路由服务器不存储数据,因此就不需要创建数据存储目录,只需创建config、shard1、shaed2、shard3即可,日志文件创建完成之后还需要给予权限。

mkdir -p /data/mongodb/logs/
mkdir /etc/mongodb/
mkdir /data/mongodb/config/
mkdir /data/mongodb/shard{1,2,3}
touch /data/mongodb/logs/shard{1,2,3}.log
touch /data/mongodb/logs/mongos.log
touch /data/mongodb/logs/config.log
chmod 777 /data/mongodb/logs/*.log

创建管理用户,修改目录权限

useradd -M -u 8000 -s /sbin/nologin mongo
chown -R mongo.mongo /usr/local/mongodb
chown -R mongo.mongo /data/mongodb

设置环境变量

echo "PATH=/usr/local/mongodb/bin:$PATH" >> /etc/profile
source /etc/profile

系统内存优化

ulimit -n 25000
ulimit -u 25000
sysctl -w vm.zone_reclaim_mode=0
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag  //*注意*这些优化都是临时的,重启失效

部署配置服务器

创建配置文件

#vim /etc/mongodb/config.conf
pidfilepath = /data/mongodb//logs/config.pid           //pid文件位置
dbpath = /data/mongodb/config/                             //数据文件存放位置
logpath = /data/mongodb//logs/config.log               //日志文件位置
logappend = true                 
bind_ip = 0.0.0.0                                                     //监听地址
port = 30000                                                           //端口号
fork = true 
replSet=configs                                                      //复制集名称
configsvr = true
maxConns=20000                                                  //最大连接数

将配置文件发送到其他服务器

scp /etc/mongodb/config.conf root@172.16.10.27:/etc/mongodb/
scp /etc/mongodb/config.conf root@172.16.10.29:/etc/mongodb/

MongoDB(4.0)分片——大数据的处理之道

启动config实例

mongod -f /etc/mongodb/config.conf  //三台服务器操作一致

配置复制集(任一台操作即可)

mongo --port 30000                    //建议三台服务器都进入数据库,方便查看角色变更
config={_id:"configs",members:[{_id:0,host:"172.16.10.26:30000"},{_id:1,host:"172.16.10.27:30000"},{_id:2,host:"172.16.10.29:30000"}]}    //创建复制集
rs.initiate(config)                //初始化复制集

MongoDB(4.0)分片——大数据的处理之道
MongoDB(4.0)分片——大数据的处理之道

部署shard1分片服务器

创建配置文件

#vim /etc/mongodb/shard1.conf
pidfilepath = /data/mongodb//logs/shard1.pid
dbpath = /data/mongodb/shard1/
logpath = /data/mongodb//logs/shard1.log
logappend = true
journal = true
quiet = true
bind_ip = 0.0.0.0
port = 40001
fork = true
replSet=shard1
shardsvr = true
maxConns=20000

将配置文件发送到其他服务器

scp /etc/mongodb/shard1.conf root@172.16.10.27:/etc/mongodb/
scp /etc/mongodb/shard1.conf root@172.16.10.29:/etc/mongodb/

启动shard1实例

mongod -f /etc/mongodb/shard1.conf  //三台服务器操作一致

配置shard1复制集

在shard分片服务器的创建中,需要注意的点是,不是在任一台服务器上创建都能成功的,如果选择在预先设置为仲裁节点的服务器上创建复制集会报错。以shard1分片服务器为例,可以在172.16.10.26和172.16.10.27服务器上创建复制集,在172.16.10.29上创建则会失败,因为在复制集创建之前,172.16.10.29已经被设置为仲裁节点。
MongoDB(4.0)分片——大数据的处理之道

mongo --port 40001    //建议三台服务器都进入数据库,方便查看角色变更
use admin
config={_id:"shard1",members:[{_id:0,host:"172.16.10.26:40001",priority:2},{_id:1,host:"172.16.10.27:40001",priority:1},{_id:2,host:"172.16.10.29:40001",arbiterOnly:true}]}
rs.initiate(config)

部署shard2分片服务器

创建配置文件

#vim /etc/mongodb/shard2.conf
pidfilepath = /data/mongodb//logs/shard2.pid
dbpath = /data/mongodb/shard2/
logpath = /data/mongodb//logs/shard2.log
logappend = true
journal = true
quiet = true
bind_ip = 0.0.0.0
port = 40002
fork = true
replSet=shard2
shardsvr = true
maxConns=20000

将配置文件发送到其他服务器

scp /etc/mongodb/shard2.conf root@172.16.10.27:/etc/mongodb/
scp /etc/mongodb/shard2.conf root@172.16.10.29:/etc/mongodb/

启动shard2实例

mongod -f /etc/mongodb/shard2.conf  //三台服务器操作一致

配置shard复制集(非仲裁节点服务器)

mongo --port 40002    //建议三台服务器都进入数据库,方便查看角色变更
use admin
config={_id:"shard2",members:[{_id:0,host:"172.16.10.26:40002",arbiterOnly:true},{_id:1,host:"172.16.10.27:40002",priority:2},{_id:2,host:"172.16.10.29:40002",priority:1}]}
rs.initiate(config)

部署shard3分片服务器

创建配置文件

#vim /etc/mongodb/shard3.conf
pidfilepath = /data/mongodb//logs/shard3.pid
dbpath = /data/mongodb/shard3/
logpath = /data/mongodb//logs/shard3.log
logappend = true
journal = true
quiet = true
bind_ip = 0.0.0.0
port = 40003
fork = true
replSet=shard3
shardsvr = true
maxConns=20000

将配置文件发送到其他服务器

scp /etc/mongodb/shard3.conf root@172.16.10.27:/etc/mongodb/
scp /etc/mongodb/shard3.conf root@172.16.10.29:/etc/mongodb/

启动shard3实例

mongod -f /etc/mongodb/shard3.conf  //三台服务器操作一致

配置shard复制集(非仲裁节点服务器)

mongo --port 40003    //建议三台服务器都进入数据库,方便查看角色变更
use admin
config={_id:"shard3",members:[{_id:0,host:"172.16.10.26:40003",priority:1},{_id:1,host:"172.16.10.27:40003",arbiterOnly:true},{_id:2,host:"172.16.10.29:40003",priority:2}]}
rs.initiate(config);

部署路由服务器

创建配置文件

pidfilepath = /data/mongodb/logs/mongos.pid
logpath=/data/mongodb/logs/mongos.log
logappend = true
bind_ip = 0.0.0.0
port = 27017
fork = true
configdb = configs/172.16.10.26:30000,172.16.10.27:30000,172.16.10.29:30000
maxConns=20000

将配置文件发送到其他服务器

scp /etc/mongodb/mongos.conf root@172.16.10.27:/etc/mongodb/
scp /etc/mongodb/mongos.conf root@172.16.10.29:/etc/mongodb/

启动mongos实例

mongos -f /etc/mongodb/mongos.conf  //三台服务器操作一致*注意*这里是“mongos”而非“mongod”

启用分片功能

mongo    //因为默认端口即是27017,所以此处不接端口号
mongos> use admin
mongos> sh.addShard("shard1/172.16.10.26:40001,172.16.10.27:40001,172.16.10.29:40001")
mongos> sh.addShard("shard2/172.16.10.26:40002,172.16.10.27:40002,172.16.10.29:40002")
mongos> sh.status()           //查看群集状态
//此处先添加两各分片服务器,还有一个,待会添加

MongoDB(4.0)分片——大数据的处理之道

测试服务器分片功能

设置分片chunk大小

mongos> use config
switched to db config
mongos> db.settings.save({"_id":"chunksize","value":1})   //设置块大小为1M是方便实验,不然就需要插入海量数据
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "chunksize" })

模拟写入数据

mongos> use python
switched to db python
mongos> show collections
mongos> for(i=1;i<=50000;i++){db.user.insert({"id":i,"name":"jack"+i})}
//在python库的user表中循环写入五万条数据
WriteResult({ "nInserted" : 1 })

启用数据库分片

mongos>sh.enableSharding("python")
//数据库分片就有针对性,可以自定义需要分片的库或者表,毕竟也不是所有数据都是需要分片操作的

MongoDB(4.0)分片——大数据的处理之道

为表创建的索引

创建索引的规则是不能一致性太高,要具有唯一性,例如序号,比如性别这一类重复性太高的就不适合做索引

mongos> db.user.createIndex({"id":1})   //以”id“为索引

MongoDB(4.0)分片——大数据的处理之道

启用表分片

mongos> sh.shardCollection("python.user",{"id":1})

MongoDB(4.0)分片——大数据的处理之道

查看分片情况

mongos> sh.status()
--- Sharding Status --- 
            ···省略内容
  shards:
        {  "_id" : "shard1",  "host" : "shard1/172.16.10.26:40001,172.16.10.27:40001",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "shard2/172.16.10.27:40002,172.16.10.29:40002",  "state" : 1 }
            ···省略内容
                        chunks:
                                shard1  3
                                shard2  3
                        { "id" : { "$minKey" : 1 } } -->> { "id" : 9893 } on : shard1 Timestamp(2, 0) 
                        { "id" : 9893 } -->> { "id" : 19786 } on : shard1 Timestamp(3, 0) 
                        { "id" : 19786 } -->> { "id" : 29679 } on : shard1 Timestamp(4, 0) 
                        { "id" : 29679 } -->> { "id" : 39572 } on : shard2 Timestamp(4, 1) 
                        { "id" : 39572 } -->> { "id" : 49465 } on : shard2 Timestamp(1, 4) 
                        { "id" : 49465 } -->> { "id" : { "$maxKey" : 1 } } on : shard2 Timestamp(1, 5) 

手动添加分片服务器,查看分片情况是否发生变化

mongos> use admin
switched to db admin
mongos> sh.addShard("172.16.10.26:40003,172.16.10.27:40003,172.16.10.29:40003")
mongos> sh.status()
--- Sharding Status --- 
           ···省略内容
  shards:
        {  "_id" : "shard1",  "host" : "shard1/172.16.10.26:40001,172.16.10.27:40001",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "shard2/172.16.10.27:40002,172.16.10.29:40002",  "state" : 1 }
        {  "_id" : "shard3",  "host" : "shard3/172.16.10.26:40003,172.16.10.29:40003",  "state" : 1 }
           ···省略内容
                        chunks:
                                shard1  2
                                shard2  2
                                shard3  2
                        { "id" : { "$minKey" : 1 } } -->> { "id" : 9893 } on : shard3 Timestamp(6, 0) 
                        { "id" : 9893 } -->> { "id" : 19786 } on : shard1 Timestamp(6, 1) 
                        { "id" : 19786 } -->> { "id" : 29679 } on : shard1 Timestamp(4, 0) 
                        { "id" : 29679 } -->> { "id" : 39572 } on : shard3 Timestamp(5, 0) 
                        { "id" : 39572 } -->> { "id" : 49465 } on : shard2 Timestamp(5, 1) 
                        { "id" : 49465 } -->> { "id" : { "$maxKey" : 1 } } on : shard2 Timestamp(1, 5) 

服务器又对数据进行重新分片,当你再次移除一个分片服务器,此时又会对数据再次进行分片处理,MongoDB对数据的处理非常灵活

PHP Prime Number Function

//快速计算某个值以内的质数
function getPrimeNumbers($n)
{
    $sieve = [];
    for($i = 1; $i <= $n; $i++) {
        $sieve[$i] = $i;
    }

    $i =2;
    while($i * $i <= $n) {
        if(isset($sieve[$i])) {
            $k = $i;
            while ($k * $i <= $n) {
                unset($sieve[$k * $i]);
                $k++;
            }
        }
        $i++;
    }
    return $sieve;
}