MaxWell的学习

什么是MaxWell?

MaxWell守护进程是读取MySQL的二进制日志,将更新操作写入到消息队列,例如Kafka,RabbitMQ,Redis的订阅发布的应用。

官网上的示例1,在数据库test的表maxwell插入了一条数据,那么你在名为maxwell的topics会接收到一条消息,含有数据库、表、时间戳、更新类型等字段。

mysql> insert into `test`.`maxwell` set id = 1, daemon = 'Stanislaw Lem';
  maxwell: {
    "database": "test",
    "table": "maxwell",
    "type": "insert",
    "ts": 1449786310,
    "xid": 940752,
    "commit": true,
    "data": { "id":1, "daemon": "Stanislaw Lem" }
  }

示例2,更新语句也是类似的。不同的是,更新的消息会多一个字段old,记录字段尚未被更新之前的数据。

 mysql> update test.maxwell set daemon = 'firebus!  firebus!' where id = 1;
  maxwell: {
    "database": "test",
    "table": "maxwell",
    "type": "update",
    "ts": 1449786341,
    "xid": 940786,
    "commit": true,
    "data": {"id":1, "daemon": "Firebus!  Firebus!"},
    "old":  {"daemon": "Stanislaw Lem"}
  }

生产者

MaxWell支持多种生产者,官网自带配置文档。以Kafka为例,默认会把消息发送到名为maxwell的topics,你也可以自定义配置

踩坑

在MaxWell启动后,Kafka的生产者可以进行高性能的配置。如果添加以下的配置

kafka.acks = 1
kafka.compression.type = snappy
kafka.retries=0

消息会以snappy的方式进行压缩。什么是snappy压缩方式
在采用Composer包nmred/kafka-php消费消息时,遇到MaxWell生产的消息无法读取。
后来定位到是该Composer包不支持snappy压缩的消息,更换为php-rdkafka的扩展即可。

参考链接

  1. Kafka 0.9 Configuration Best Practices
  2. Manually Installing the extension

配置系统的思考

配置系统在整个软件的生命周期里,有着很重要的角色。例如:

在新公司上班,第一天需要配置开发环境。。。

多环境部署,开发、测试、预发布、生产不同环境的配置。。。

项目的数据库,缓存,负载均衡,Nginx各种服务的配置。。。

做这么多年的开发,说来好笑,一直在跟配置系统打交道,但却没有花时间思考配置系统存在的意义。那么如何在目前Linux+Nginx+MySQL+PHP的构架下,尝试合适自己的配置系统呢?

常见的配置系统

既然不知道什么样的配置系统适合自己,那么我们可以看看别人家,是如何使用配置系统的。

Phalcon

Phalcon框架是我使用时间较长的开发框架,先看看它内部的配置如何实现。

<?php

use Phalcon\Config;

$config = new Config(
    [
        'test' => [
            'parent' => [
                'property'  => 1,
                'property2' => 'yeah',
            ],
        ],
    ]
);

echo $config->get('test')->get('parent')->get('property');  // displays 1
echo $config->test->parent->property;                       // displays 1
echo $config->path('test.parent.property');                 // displays 1

#甚至你可以注入配置到di
use Phalcon\Di\FactoryDefault;
use Phalcon\Config;

// Create a DI
$di = new FactoryDefault();

$di->set(
    'config',
    function () {
        $configData = require 'config/config.php';

        return new Config($configData);
    }
);

实现的原理是根据传入的数组转换Phlacon\Config对象的属性。支持不同类型配置器,例如ini、Json、Php、yaml等,你可以理解为Phlacon\Config是一个工厂模式,生产不同的类型的配置实例。

Magento

这是一个流行的开源电子商务框架。在1.x的框架版本都是采用xml去实现的。那时候发现xml做配置,非常“灵活”。注意,我这里用一个非常的形容词,这意味调试开发成本会比较高。那时候我的时间大部分都花费在xml文件里查找对应的配置。幸好,官方终于在2.x版本优化过来了,终于可以解脱了。

The Magento 2 deployment configuration replaces local.xml in Magento 1.x.

config.php 代码实例:

return array (
  'modules' =>
  array (
    'Magento_Core' => 1,
    'Magento_Store' => 1,
    'Magento_Theme' => 1,
    'Magento_Authorization' => 1,
    'Magento_Directory' => 1,
    'Magento_Backend' => 1,
    'Magento_Backup' => 1,
    'Magento_Eav' => 1,
    'Magento_Customer' => 1,
...
  ),
);

json

JSON文件格式是一个伟大的发明。简单的key和value的形式,就解决大部分配置文件,主要应用在不需要复杂逻辑配置,比如Composer、NPM等。

# composer.json
{
    "config": {
        "preferred-install": {
            "my-organization/stable-package": "dist",
            "my-organization/*": "source",
            "partner-organization/*": "auto",
            "*": "dist"
        }
    }
}
# package.josn

{ "name": "ethopia-waza",
  "description": "a delightfully fruity coffee varietal",
  "version": "1.2.3",
  "devDependencies": {
    "coffee-script": "~1.6.3"
  },
  "scripts": {
    "prepare": "coffee -o lib/ -c src/waza.coffee"
  },
  "main": "lib/waza.js"
}

YAML

在GitLab中CI构建配置文件是采用YML。原因是在构建的过程需要存储复杂业务流程,整体逻辑较多,YML适合应用于这类场景。

# see https://gitlab.com/help/ci/yaml/README.md#jobs
# Select image
image: geekwho/code:php
stages:
  - phplint
  - phpfixer
  - build
  - test
  - deploy

# php lint
phplint:
  stage: phplint
  script:
  - bash bin/phplint.sh
  only:
  - master
  - prelease
  - dev

# php code style
phpfixer:
  stage: phpfixer
  script:
  - bash bin/phpfixer.sh
  only:
  - master
  - prelease
  - dev

# unit test
test:
  stage: test
  script:
  - bash bin/test.sh
  only:
  - master
  - prelease
  - dev
  allow_failure: true

PHP数组

最简单的配置系统可以直接采用PHP自带的数组。数组的实现本身也是hash table 。

<?php
// 直接返回数组元素
return [
    'database' => [
      'adapter'     => 'Mysql',
      'host'        => 'localhost',
      'username'    => 'root',
      'password'    => '',
      'dbname'      => '@@name@@',
      'charset'     => 'utf8',
    ],
    'version' => '1.0',

    /**
     * if true, then we print a new line at the end of each execution
     *
     * If we dont print a new line,
     * then the next command prompt will be placed directly on the left of the output
     * and it is less readable.
     *
     * You can disable this behaviour if the output of your application needs to don't have a new line at end
     */
    'printNewLine' => true,
];
// 可以在配置里做一些逻辑判断
<?php

$dsn = 'mysql:host=localhost;dbname=yii2basic';
return [
    'class' => 'yii\db\Connection',
    'dsn' => $dsn,
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',
];
// 甚至还可以使用静态变量
<?php

class Config {
    public static $db = 'mysql';
    public static $user = 'root';
    public static $passwd = '';
    public static $host = 'locahost';

}

Bash

甚至有时候在Bash环境也需要添加部分文件,我是这样做的。

# @Author: geekwho
# @Date:   2018-09-18 11:11:11
# @Last Modified by:   geekwho
# @Last Modified time: 2018-09-17 11:11:11
# 配置文件
project="wiki"

domain=".xbc.me"

user="geekwho"

host="your host"

port="your port"

dest=""

# 然后在你需要使用变量的地方进行加载
source ./.config.sh

环境变量

很多时候我们需要判断的环境变量,比如dev、stg、pre、pro等不同环境。

项目目录

这类方式就是直接在当前项目下建一个隐藏文件,类似 .env.dev 代表当前的环境。不好的地方,因为在当前项目下,容易被误修改或删除,导致环境判断异常。

php.ini

直接在php.ini添加自定义的环境变量,例如env=dev。那么在bash环境下,还需要单独去做一次环境变量的解析。

/etc

直接在系统文件下建立一个环境变量的文件。例如/etc/.env.dev。这类操作需要运维配置设置对应环境的变量文件。

两者结合

如何把环境变量和配置系统结合起来呢?

根据环境变量独立配置文件

推荐的配置目录结构如下:

app
    config
        dev
            config.php
        test
            config.php
        pre
            config.php
        prd
            config.php
// 实例
<?php

class Config {
    public static $db = 'mysql';
    public static $user = 'root';
    public static $passwd = '';
    public static $host = 'locahost';

}

All in ONE

全部环境变量全部都写同一个文件里。

#app/config/db.php
<?php

class Config {
    public static $db = [
      'dev' => 'mysql',
      'test' => 'mysql',
      'pre' => 'mysql',
      'prd' => 'mysql',
    ];
    public static $user_dev = [
      'dev' => 'root',
      'test' => 'root',
      'pre' => 'root',
      'prd' => 'root',
    ];
    public static $passwd = [
      'dev' => '',
      'test' => '',
      'pre' => '',
      'prd' => '',
    ]
    public static $hos = [
      'dev' => 'localhost',
      'test' => 'localhost',
      'pre' => 'localhost',
      'prd' => 'localhost',
    ]

}

参考链接

  1. phalcon config
  2. magento config
  3. composer config
  4. npm config
  5. GitLabv CI YAML
  6. PHP Array
  7. LaraDock

堆排序

具体实现

GitHub版本库的堆排序。

算法实现

实现

该算法按照二叉树的原理。
完全二叉树:深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中,序号为1至n的节点对应时,称之为完全二叉树

 原数组 52,85,6,3,57,27,77,37,96,52
        0   1 2 3 4  5  6  7  8  9
              52
            /   \
           /     \
          85      6
         / \     / \
        /   \   /   \
        3   57 27   77
       / \  /
      6  3 25
 实现堆的排序,在起始数组为0的情况下:
 1. 数组下标i的左子节点在位置  (2*i+1)
 2. 数组下标i的右节点在位置    (2*i+2)
 3. 数组下标i的父节点在位置    floor(i-1)/2

 最大堆
               96
             /    \
            /      \
           85      77
          / \      / \
         /   \    /   \
         57  52  37   27
        / \  /
       25 6  3

 最小堆
               3
             /    \
            /      \
           6       25
          / \      / \
         /   \    /   \
        27   37  52   57
       / \   /
      77 85 96

难点

  1. 数组元素个数为n,则从最后一个父元素(最后一个非叶子节点)开始访问,即$i = floor($n/2)-1
  2. 从根元素开始访问,再访问左节点,再到右结点,称为先序遍历。
  3. 最大的堆,完成排序之后,是从小到大的数组。因为建立最大堆,是最大的在堆顶,但是第二次循环之后,
  4. 最大的被置换到数组最后一个数组。
  5. 最小堆,完成排序的数组是从大到小的数组。

耗时分析

机器配置 ThinkPad T440s CPU i5-4300U Memory 8GB
maxheap num 1000 sort cost time is 0.011452198028564 s
minheap num 1000 sort cost time is 0.0060770511627197 s
maxheap num 10000 sort cost time is 0.092055082321167 s
minheap num 10000 sort cost time is 0.090849161148071 s
maxheap num 100000 sort cost time is 1.2427570819855 s
minheap num 100000 sort cost time is 1.1724309921265 s

算法分析

最坏时间复杂度 О(nlog n)
最优时间复杂度 О(nlog n)
平均时间复杂度 О(nlog n)
空间复杂度   О(n)

参考链接

  1. 堆排序
  2. 堆排序(Heap Sort)算法学习