-
Notifications
You must be signed in to change notification settings - Fork 1
/
mysql.plugin.fifo.xml
267 lines (255 loc) · 9.09 KB
/
mysql.plugin.fifo.xml
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V5.0//EN"
"/usr/share/xml/docbook/schema/dtd/5.0/docbook.dtd" [
<!ENTITY article.author.xml SYSTEM "../common/article.author.xml">
<!ENTITY book.info.legalnotice.xml SYSTEM "../common/book.info.legalnotice.xml">
<!ENTITY book.info.abstract.xml SYSTEM "../common/book.info.abstract.xml">
]>
<article xml:base="http://netkiller.github.io/journal/"
xmlns="http://docbook.org/ns/docbook" xml:lang="zh-cn">
<articleinfo>
<title>数据库进程间通信解决方案</title>
<subtitle>数据库与其他第三方应用程序进程间通信解决方案</subtitle>
&article.author.xml;
<pubdate>$Date: 2013-12-16 13:34:20 +0800 (Thu, 16 May 2013) $</pubdate>
<releaseinfo>$Id$</releaseinfo>
&book.info.legalnotice.xml;
<abstract>
<para>你是否想过当数据库中的数据发生变化的时候出发某种操作?但因数据无法与其他进程通信(传递信号)让你放弃,而改用每隔一段时间查询一次数据变化的方法?下面的插件可以解决你的问题。</para>
<para>原文出处:<ulink url="http://netkiller.github.io/journal/mysql.plugin.fifo.html"/></para>
</abstract>
&book.info.abstract.xml;
<keywordset>
<keyword>mysql</keyword>
<keyword>plugin, udf</keyword>
<keyword>images</keyword>
</keywordset>
</articleinfo>
<section>
<title>背景</title>
<para>你是否有这样的需求:</para>
<para>你需要监控访问网站的IP,当同一个IP地址访问次数过多需要做出处理,例如拉黑,直接丢进iptables 防火墙规则连中。你的做法只能每个一段时间查询一次数据库,并且判断是否满足拉黑需求?</para>
<para>你是否需要监控某些数据发生变化,并通知其他程序作出处理。例如新闻内容修改后,需要立即做新页面静态化处理,生成新的静态页面</para>
<para>你使用数据库做队列,例如发送邮件,短信等等。你要通知发送程序对那些手机或者短线发送数据</para>
<para></para>
</section>
<section>
<title>解决思路</title>
<para>需要让数据库与其他进程通信,传递信号</para>
<para>例如,发送短信这个需求,你只要告诉发短信的机器人发送的手机号码即可,机器人永远守候那哪里,只要命令一下立即工作。</para>
<para>监控数据库变化的需求原理类似,我们需要有一个守护进程等待命令,一旦接到下达命令便立即生成需要的静态页面</para>
<para>这里所提的方案是采用fifo(First In First Out)方案,通过管道相互传递信号,使两个进程协同工作,这样的效率远比定时任务高许多。fifo是用于操作系统内部进程间通信,如果跨越操作系统需要使用Socket,还有一个新名词MQ(Message queue).</para>
<para>这里只做fifo演示, 将本程序改为Socket方案,或者直接集成成熟的MQ也是分分钟可以实现。</para>
</section>
<section>
<title>Mysql plugin</title>
<para> 我开发了几个 UDF, 共4个 function</para>
<variablelist>
<title>UDF</title>
<varlistentry>
<term>fifo_create(pipename)</term>
<listitem>
<para>创建管道.成功返回true,失败返回flase.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>fifo_remove(pipename)</term>
<listitem>
<para>删除管道.成功返回true,失败返回flase.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>fifo_read(pipename)</term>
<listitem>
<para>读操作.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>fifo_write(pipename,message)</term>
<listitem>
<para>写操作 pipename管道名,message消息正文.</para>
</listitem>
</varlistentry>
<!--
<varlistentry>
<term></term>
<listitem>
<para></para>
</listitem>
</varlistentry>
-->
</variablelist>
<para>有了上面的function后你就可以在begin,commit,rollback 直接穿插使用,实现在事物处理期间做你爱做的事。也可以用在触发器与EVENT定时任务中。</para>
</section>
<section>
<title>plugin 的开发与使用</title>
<para>编译UDF你需要安装下面的软件包</para>
<screen>
sudo apt-get install pkg-config
sudo apt-get install libmysqlclient-dev
sudo apt-get install gcc gcc-c++ make automake autoconf
</screen>
<para><ulink url="https://github.com/netkiller/mysql-fifo-plugin" /></para>
<para>编译udf,最后将so文件复制到 /usr/lib/mysql/plugin/</para>
<screen>
git clone https://github.com/netkiller/mysql-image-plugin.git
cd mysql-image-plugin
gcc -O3 -g -I/usr/include/mysql -I/usr/include -fPIC -lm -lz -shared -o fifo.so fifo.c
sudo mv fifo.so /usr/lib/mysql/plugin/
</screen>
<para>装载</para>
<screen>
create function fifo_create returns string soname 'fifo.so';
create function fifo_remove returns string soname 'fifo.so';
create function fifo_read returns string soname 'fifo.so';
create function fifo_write returns string soname 'fifo.so';
</screen>
<para>卸载</para>
<screen>
drop function fifo_create;
drop function fifo_remove;
drop function fifo_read;
drop function fifo_write;
</screen>
</section>
<section>
<title>插件如何使用</title>
<para>插件有很多种用法,这里仅仅一个例</para>
<screen>
CREATE TABLE `demo` (
`id` INT(11) NULL DEFAULT NULL,
`name` CHAR(10) NULL DEFAULT NULL,
`mobile` VARCHAR(50) NULL DEFAULT NULL
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
INSERT INTO `demo` (`id`, `name`, `mobile`) VALUES
(1, 'neo', '13113668891'),
(2, 'jam', '13113668892'),
(3, 'leo', '13113668893');
</screen>
<para>我们假设有一个demo这样的表,我使用shell写了一个守护进程用于处理数据库送过来的数据</para>
<screen>
<![CDATA[
#!/bin/bash
########################################
# Homepage: http://netkiller.github.io
# Author: neo <[email protected]>
########################################
NAME=demo
PIPE=/tmp/myfifo
########################################
LOGFILE=/tmp/$NAME.log
PIDFILE=/tmp/${NAME}.pid
########################################
function start(){
if [ -f "$PIDFILE" ]; then
exit 2
fi
if [ ! -f "$LOGFILE" ]; then
> ${LOGFILE}
fi
for (( ; ; ))
do
while read line
do
NOW=$(date '+%Y-%m-%d %H:%M:%S')
echo "[${NOW}] [OK] ${line}" >> ${LOGFILE}
done < $PIPE
done &
echo $! > $PIDFILE
}
function stop(){
[ -f $PIDFILE ] && kill `cat $PIDFILE` && rm -rf $PIDFILE
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
ps ax | grep ${0} | grep -v grep | grep -v status
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|status|restart}"
exit 2
esac
exit $?
]]>
</screen>
<para>启动守护进程</para>
<screen>
$ ./sms.sh start
$ ./sms.sh status
596 pts/5 S 0:00 /bin/bash ./sms.sh start
</screen>
<para>监控日志,因为守护进程没有输出,完成人户后写入日志。</para>
<screen>
$ tail -f /tmp/demo.log
</screen>
<para>开始推送任务</para>
<screen>
<![CDATA[
mysql> select fifo_write('/tmp/myfifo',concat(mobile,'\r\n')) from demo;
+-------------------------------------------------+
| fifo_write('/tmp/myfifo',concat(mobile,'\r\n')) |
+-------------------------------------------------+
| true |
| true |
| true |
+-------------------------------------------------+
3 rows in set (0.00 sec)
]]>
</screen>
<para>现在看看日志的变化</para>
<screen>
$ tail -f /tmp/demo.log
[2013-12-16 14:55:48] [OK] 13113668891
[2013-12-16 14:55:48] [OK] 13113668892
[2013-12-16 14:55:48] [OK] 13113668893
</screen>
<para>我们再将上面的例子使用触发器进一步优化</para>
<screen>
<![CDATA[
CREATE TABLE `demo_sent` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`mobile` VARCHAR(50) NOT NULL,
`status` ENUM('true','false') NOT NULL DEFAULT 'false',
`ctime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
CREATE DEFINER=`dba`@`%` TRIGGER `demo_after_insert` AFTER INSERT ON `demo` FOR EACH ROW BEGIN
insert into demo_sent(mobile,status) select new.mobile,fifo_write('/tmp/myfifo',concat(new.mobile,'')) as status;
END
]]>
</screen>
<para>测试</para>
<screen>
<![CDATA[
mysql> insert into demo(name,mobile) values('jerry','13322993040');
Query OK, 1 row affected (0.05 sec)
]]>
</screen>
<para>日志变化</para>
<screen>
$ tail -f /tmp/demo.log
[2013-12-16 14:55:48] [OK] 13113668891
[2013-12-16 14:55:48] [OK] 13113668892
[2013-12-16 14:55:48] [OK] 13113668893
[2013-12-16 14:55:48] [OK] 13322993040
</screen>
</section>
<section>
<title>部署相关问题</title>
<para>我们可以采用主从数据库,将任务放在专用的从库上执行</para>
<para>我们可以创建很多个管道,用于做不同的工作,例如插入一个任务,更新一个任务,发短信一个任务,处理模板与静态化一个任务等等。</para>
</section>
</article>