Bottledwater同步PostgreSQL中的数据变化到Kafka消息队列

当我们遇到需要捕获数据库中数据变化的时候,总是会想到通过消息队列来实现该需求,通过把数据变化发布到消息队列,来完成系统上下游的解耦。关心这些数据变化的应用可以从消息队列上获取这些数据。

Bottledwater-pg是针对PostgreSQL数据库的一种消息生产者,可以将PostgreSQL数据库的数据写入confluent Kafka,从而实时的分享给消息订阅者。支持PostgreSQL 9.4以及以上版本,支持全量快照,以及持续解析数据WAL日志中的增量数据并写入Kafka。每一张数据库表为一个topic。数据在使用decode从WAL取出后,使用Avro将数据格式化(通常格式化为JSON)再写入Kafka。

Bottledwater-pg有docker、源码编译、Ubuntu三种使用方式,本文以源码编译方式说明如何部署。

一. 环境说明

源端


PostgreSQL :postgresql-10.5

Kafka:kafka_2.11-2.3.0

Bottledwater-pg依赖以下软件包:

avro-c > =1.8.0

jansson

libcurl

librdkafka > =0.9.1

Bottledwater-pg可选以下软件包:

libsnappy

boost


另外编译要求较高的cmake版本,操作系统自带的cmake会出现编译错误,本文使用:

cmake-3.8.0

二. 安装前准备

2.1postgresql安装

配置用户和组

groupadd postgres

useradd postgres -g postgres

环境准备

yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel python-devel gcc-c++ openssl-devel cmake gcc* readline-devel

权限配置

mkdir /opt/postgres

chown -R postgres:postgres /opt/postgres/

配置环境变量

vi /etc/profile

#在文件末尾将以下环境变量添加进去

export PATH=/opt/postgres/bin:$PATH

export PGHOME=/opt/postgres

export PGDATA=/opt/postgres/data/

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PGHOME/lib/

export PATH=$PGHOME/bin:$PATH:$HOME/bin```

2.1.1安装过程

安装数据库

cd 下载好的压缩包存放路径

#解压文件

tar -zxvf postgresql-10.5.tar.gz

cd postgresql-10.5

#参数根据自己需求配置

./configure --prefix=/opt/postgres/ --with-python --with-libxml --with-libxslt make make install

****,安装过程内容太长就不截图了,从屏显的信息最后看到PostgreSQL installation complete. 就说明安装好了,如果报错大多数都是安装包问题或者依赖没下载,看下错误信息基本都能解决****

初始化数据库

su postgres

#初始化数据库的参数也是根据自己需要添加 ,可以通过--help查看

/opt/postgres/bin/initdb -D $PGDATA -E UTF8

#如果出现以下message就说明初始化成功了

********************************************************************* creating directory /opt/postgres/data ... ok creating subdirectories ... ok selecting default max_connections ... 100 selecting default shared_buffers ... 128MB selecting dynamic shared memory implementation ... posix creating configuration files ... ok running bootstrap script ... ok performing post-bootstrap initialization ... ok syncing data to disk ... ok WARNING: enabling "trust" authentication for local connections You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb. Success. You can now start the database server using: /opt/postgres/bin/pg_ctl -D /opt/postgres/data -l logfile start ********************************************************************

#启动数据库服务 #数据文件和日志文件的路径根据自己需求指定。

/opt/postgres/bin/pg_ctl -D $PGDATA -l /opt/postgres/server.log start

到此postgres服务就安装完毕了。

2.1.2使用数据库

进入数据库

su postgres

#进入数据库

[postgres@localhost postgres]

$ psql Type "help" for help.

postgres=#

修改数据库配置,允许其他服务器连接

#postgres安装好以后需要修改2个配置文件才能允许别的服务器访问。

cd /opt/postgres/data

vi postgresql.conf

#找到listen_addresses和port参数,修改如下,也可根据自己需求修改

listen_addresses = '*'

port = 5432

#根据自己的网段设置下放行的ip规则

vi pg_hba.conf 
# IPv4 local connections:

host   all    all    192.168.0.0/16    md5

配置好以后就可以使用postgres数据库了。

2.2编译安装cmake

(root用户操作)

# cd /opt # tar zxvf cmake-3.8.0.tar.gz

# cd cmake-3.8.0

# ./bootstrap

# make

# make install

# cmake -version

cmake version 3.8.0

CMake suite maintained and supported by Kitware ([kitware.com/cmake](http://kitware.com/cmake)).

2.3编译安装jansson

(root用户操作)

# cd /opt

# tar jxvf jansson-2.9.tar.bz2

# cd jansson-2.9

# ./configure

# make

# make install

# ls /usr/local/lib/pkgconfig jansson.pc

#export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH

2.4编译安装avro

(root用户操作)

# cd /opt

# yum install -y xz-*

# yum install -y zlib-devel.x86_64

# tar zxvf avro-src-1.8.1.tar.gz

# cd avro-src-1.8.1/lang/c

# mkdir build # cd build

# cmake .. -DCMAKE_INSTALL_PREFIX=/opt/avro -DCMAKE_BUILD_TYPE=Release -DTHREADSAFE=true

# make

# make test

# make install

导入库文件

# vi /etc/[ld.so](http://ld.so/).conf /opt/avro/lib # ldconfig

配置临时环境变量

# export LD_LIBRARY_PATH=/opt/avro/lib:$LD_LIBRARY_PATH

# export PKG_CONFIG_PATH=/opt/avro/lib/pkgconfig:/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH

2.5安装libcurl

(root用户操作)

# yum install -y libcurl-devel.x86_64

2.6编译安装librdkafka

(root用户操作)

# cd /opt # unzip librdkafka-master.zip

# cd librdkafka-master

# ./configure

# make

# make install

# ls /usr/local/lib/pkgconfig

jansson.pc   rdkafka.pc    rdkafka++.pc

2.7添加引用库

(root用户操作)

# vi /etc/[ld.so](http://ld.so/).conf.d/bottledwater.conf

/opt/avro/lib

/usr/local/lib

/opt/postgres/lib #初始化数据库位置的引用库

# ldconfig

2.8编译安装bottledwater-pg

(root用户操作)

# chown -R postgres:postgres /opt/

(postgres用户操作)

配置环境变量

$ vi ~/.bash_profile

# .bash_profile

# Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi

# User specific environment and startup programs export

PG_HOME=/opt/postgres #初始化数据库位置

export LD_LIBRARY_PATH=/opt/avro/lib:$LD_LIBRARY_PATH

export PKG_CONFIG_PATH=/opt/avro/lib/[pkgconfig:/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH](http://pkgconfig/usr/local/lib/pkgconfig:%24PKG_CONFIG_PATH)

PATH=$PG_HOME/bin:$PATH:$HOME/bin

export PATH

准备安装包

$ unzip bottledwater-pg-master.zip

$ cd bottledwater-pg-master

这里不知道是操作系统环境的问题还是开源软件本身的问题,需要修改源码包里的2处Makefile才能通过编译。


     PG_CFLAGS = -I$(shell pg_config --includedir) -I$(shell pg_config --includedir-server) -I$(shell pg_config --pkgincludedir)

修改kafka/Makefile

    PG_CFLAGS = -I$(shell pg_config --includedir) -I$(shell pg_config --includedir-server) -I$(shell pg_config --pkgincludedir)

    LDFLAGS=-L/usr/lib64 $(CURL_LDFLAGS) $(PG_LDFLAGS) $(KAFKA_LDFLAGS) $(AVRO_LDFLAGS) $(JSON_LDFLAGS)

编译并安装bottledwater-pg

$ make

$ make install

安装完成后会自动在PostgreSQL数据库扩展包目录下生成扩展库文件和扩展库控制文件。

$ ls /opt/postgres/lib/bottledwater*

/opt/postgres/lib/[bottledwater.so](http://bottledwater.so/)

$ ls /opt/postgres/share/extension/bottledwater*

/opt/postgres/share/extension/bottledwater--0.1.sql

/opt/postgres/share/extension/bottledwater.control

2.9 kafka安装

Step 1: 下载代码

下载kafka_2.11-2.3.0版本并且解压它。

tar -xzf kafka_2.11-2.3.0.tgz

cd kafka_2.11-2.3.0

Step 2: 启动服务

运行kafka需要使用Zookeeper,所以你需要先启动Zookeeper,如果你没有Zookeeper,你可以使用kafka自带打包和配置好的Zookeeper。

bin/zookeeper-server-start.sh config/zookeeper.properties

[2019-10-23 15:01:37,495] INFO Reading configuration from: config/zookeeper.properties (org.apache.zookeeper.server.quorum.QuorumPeerConfig) ... 现在启动kafka服务

bin/kafka-server-start.sh config/server.properties &

[2019-10-23 15:01:47,028] INFO Verifying properties (kafka.utils.VerifiableProperties) [2013-04-22 15:01:47,051] INFO Property socket.send.buffer.bytes is overridden to 1048576 (kafka.utils.VerifiableProperties) ...

2.9数据库配置

试验环境中PostgreSQL已有数据库msc

msc库中有一张表:

CREATE TABLE COMPANY (

ID INT PRIMARY KEY NOT NULL,

NAME TEXT NOT NULL,

AGE INT NOT NULL,

ADDRESS CHAR (50),

SALARY REAL,

JOIN_DATE DATE

);

修改数据库配置文件

$ vi /opt/postgres/data/postgresql.conf

max_worker_processes = 8 # 至少为8

wal_level = logical # 至少为logical,可以更高

max_wal_senders = 8 # 至少为8

wal_keep_segments = 256 # 至少为256

max_replication_slots = 4 # 至少为4

修改数据库白名单配置文件

  *这部分权限可能过大,可以精简

$ vi /opt/postgres/data//pg_hba.conf

local replication all trust

host replication all 127.0.0.1/32 trust

host replication all 0.0.0.0/0 md5

host all postgres 10.19.100.23/32 trust

重启数据库并对要监控的库创建bottledwater扩展

$ pg_ctl restart 
$ psql -U postgres -d mas -c "create extension bottledwater;"

CREATE EXTENSION

三 测试同步

$cd /opt/bottledwater-pg-master

假设一切都在localhost的默认端口上运行,则可以按以下方式启动bottledwater:

$./kafka/bottledwater --postgres=postgres://localhost

第一次运行时,它将创建一个名为的复制插槽bottledwater,为数据库创建一致的快照,然后将其发送到Kafka。(您可以使用--slot 命令行标志来更改复制插槽的名称。)快照完成后,它将切换为使用复制流。

当您不再希望运行瓶装水时,必须删除其复制插槽(否则最终将用完磁盘空间,因为开放的复制插槽会阻止WAL进行垃圾回收)。您可以通过psql 再次打开并运行以下命令来执行此操作:

select pg_drop_replication_slot('bottledwater');

第二次运行则发送到kafka中

$./kafka/bottledwater --postgres=postgres://[email protected]:5432/msc --broker=39.108.83.108:9092 -f json

[INFO] Writing messages to Kafka in JSON format [INFO] Created replication slot "bottledwater", capturing consistent snapshot "00000718-1". INFO: bottledwater_export: Table mas.mastest is keyed by index mastest_pkey [INFO] Snapshot complete, streaming changes from 0/1749F58.

向源端数据库写入数据

INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY,JOIN_DATE)

VALUES(6,'Paul',32,'Cal6',20000.00,'2001-08-13');

DELETE from COMPANY where ID= 1

UPDATE COMPANY set NAME='joker' WHERE ID= 6

目的端查看消息事件

查看topics列表

# bin/kafka-topics.sh --list --zookeeper localhost:2181

company

#./bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic company --from-beginning --property print.key=true

{"id": {"int": 1}} {"id": {"int": 1}, "name": {"string": "Paul"}, "age": {"int": 32}, "address": {"string": "California "}, "salary": {"float": 20000.0}, "join_date": {"Date": {"year": 2001, "month": 7, "day": 13}}}

{"id": {"int": 2}} {"id": {"int": 2}, "name": {"string": "Paul"}, "age": {"int": 32}, "address": {"string": "California "}, "salary": {"float": 20000.0}, "join_date": {"Date": {"year": 2001, "month": 7, "day": 13}}}

{"id": {"int": 3}} {"id": {"int": 3}, "name": {"string": "Paul"}, "age": {"int": 32}, "address": {"string": "California "}, "salary": {"float": 20000.0}, "join_date": {"Date": {"year": 2001, "month": 7, "day": 13}}}

null

null

{"id": {"int": 4}} {"id": {"int": 4}, "name": {"string": "Paul"}, "age": {"int": 32}, "address": {"string": "California "}, "salary": {"float": 20000.0}, "join_date": {"Date": {"year": 2001, "month": 7, "day": 13}}}

{"id": {"int": 5}} {"id": {"int": 5}, "name": {"string": "Paul"}, "age": {"int": 32}, "address": {"string": "California5 "}, "salary": {"float": 20000.0}, "join_date": {"Date": {"year": 2001, "month": 8, "day": 13}}}

{"id": {"int": 6}} {"id": {"int": 6}, "name": {"string": "Paul"}, "age": {"int": 32}, "address": {"string": "Cal6 "}, "salary": {"float": 20000.0}, "join_date": {"Date": {"year": 2001, "month": 8, "day": 13}}}

{"id": {"int": 1}} null

{"id": {"int": 6}} {"id": {"int": 6}, "name": {"string": "zyc"}, "age": {"int": 32}, "address": {"string": "Cal6 "}, "salary": {"float": 20000.0}, "join_date": {"Date": {"year": 2001, "month": 8, "day": 13}}}

{"id": {"int": 6}} {"id": {"int": 6}, "name": {"string": "joker"}, "age": {"int": 32}, "address": {"string": "Cal6 "}, "salary": {"float": 20000.0}, "join_date": {"Date": {"year": 2001, "month": 8, "day": 13}}}

说明:

1. 目的端Kafka配置文件中,Listener的配置不能用默认值localhost

2. 每一张PostgreSQL中的表都会被做为一个topic,每个topic是自动创建的不需要人工干预,即使后期新建的表也是如此。

3. 消息事件以“主键列 + 变更后的所有列”作为消息内容

4. 主从复制模式下,slave不能启动bottledwater,因为备库不产生wal日志

四bottledwater命令行选项

bottledwater客户端接受的各种命令行选项的参考,并带有指向相关文档区域的链接。如果这与的输出不一致bottledwater --help,则--help是正确的(请提交请求请求以更新此引用!)。

-d,--postgres=[postgres://user:pass@host:port/dbname](postgres://user:pass@hostport)(必需):PostgreSQL服务器的连接字符串或URI。

-s,--slot=slotname (默认值:bottledwater):[复制插槽的](https://github.com/confluentinc/bottledwater-pg#configuration)名称。该插槽是在首次使用时自动创建的。

-b,--broker=host1[:port1],host2[:port2]... (默认值:localhost:9092):Kafka代理主机/端口的列表,以逗号分隔。

-r,--schema-registry=[http://hostname:port](http://hostnameport/)(默认值:[http:// localhost:8081](http://localhost:8081/)):注册Avro模式的服务的URL。(仅用于 --output-format=avro。时省略--output-format=json。)

-f,--output-format=[avro|json] (默认值:avro):如何编码用于写入Kafka的消息。请参见[输出格式的](https://github.com/confluentinc/bottledwater-pg#output-formats)讨论。

-u,--allow-unkeyed:允许导出没有主键的表。[默认情况下不允许](https://github.com/confluentinc/bottledwater-pg#consuming-data)这样做,因为更新和删除需要主键来标识其行。

-p,--topic-prefix=prefix:字符串,以加在所有[主题名称之前](https://github.com/confluentinc/bottledwater-pg#topic-names)。例如,使用 --topic-prefix=postgres,来自表“ users”的更新将被写入主题“ postgres.users”。

-e,--on-error=[log|exit] (默认值:exit):如果出现暂时性错误(例如无法发布到Kafka),该怎么办。请参阅[错误处理的](https://github.com/confluentinc/bottledwater-pg#error-handling)讨论。

-x,--skip-snapshot:跳过采取[一致的快照](https://github.com/confluentinc/bottledwater-pg#configuration)的现有数据库的内容和刚开始流的任何新的更新。(忽略复制插槽是否已存在。)

-C,--kafka-config property=value:kafka制片设置全局配置属性(见[librdkafka文档](https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md))。

-T,--topic-config property=value:kafka生产者将主题配置属性(见[librdkafka文档](https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md))。

--config-help:打印Kafka配置属性列表。

-h,--help:打印此帮助文本。

你可能感兴趣的:(Bottledwater同步PostgreSQL中的数据变化到Kafka消息队列)