Home | 簡體中文 | 繁體中文 | 雜文 | Search | ITEYE 博客 | OSChina 博客 | Facebook | Linkedin | 知乎專欄 | Email

第 8 章 數據與應用程序間通信

目錄

8.1. 管道通信
8.1.1. 背景
8.1.2. 解決思路
8.1.3. Mysql plugin
8.1.4. plugin 的開發與使用
8.1.5. 插件如何使用
8.1.6. 部署相關問題
8.2. 消息隊列
8.2.1. 背景
8.2.2. 應用場景
8.2.3. Mysql plugin
8.2.4. plugin 的開發與使用
8.2.5. 插件如何使用
8.3. 資料庫與外界檔案
8.3.1. 背景
8.3.2. 解決思路
8.3.3. 解決方案
8.3.4. plugin 的開發與使用
8.3.5. 在事務中使用該插件
8.3.6. 通過觸發器調用圖片處理函數
8.4. Socket 方式
8.4.1. UDP

本章講解數據與應用程序間通信,這裡會涉及到

8.1. 管道通信

你是否想過當資料庫中的數據發生變化的時候出發某種操作?但因數據無法與其他進程通信(傳遞信號)讓你放棄,而改用每隔一段時間查詢一次數據變化的方法?下面的插件可以解決你的問題。

8.1.1. 背景

你是否有這樣的需求:

你需要監控訪問網站的IP,當同一個IP地址訪問次數過多需要做出處理,例如拉黑,直接丟進iptables 防火牆規則連中。你的做法只能每個一段時間查詢一次資料庫,並且判斷是否滿足拉黑需求?

你是否需要監控某些數據發生變化,並通知其他程序作出處理。例如新聞內容修改後,需要立即做新頁面靜態化處理,生成新的靜態頁面

你使用資料庫做隊列,例如發送郵件,短信等等。你要通知發送程序對那些手機或者短線發送數據

8.1.2. 解決思路

需要讓資料庫與其他進程通信,傳遞信號

例如,發送短信這個需求,你只要告訴發短信的機器人發送的手機號碼即可,機器人永遠守候那哪裡,只要命令一下立即工作。

監控資料庫變化的需求原理類似,我們需要有一個守護進程等待命令,一旦接到下達命令便立即生成需要的靜態頁面

這裡所提的方案是採用fifo(First In First Out)方案,通過管道相互傳遞信號,使兩個進程協同工作,這樣的效率遠比定時任務高許多。fifo是用於操作系統內部進程間通信,如果跨越操作系統需要使用Socket,還有一個新名詞MQ(Message queue).

這裡只做fifo演示, 將本程序改為Socket方案,或者直接整合成熟的MQ也是分分鐘可以實現。

8.1.3. Mysql plugin

我開發了幾個 UDF, 共4個 function

UDF

fifo_create(pipename)

創建管道.成功返回true,失敗返回flase.

fifo_remove(pipename)

刪除管道.成功返回true,失敗返回flase.

fifo_read(pipename)

讀操作.

fifo_write(pipename,message)

寫操作 pipename管道名,message消息正文.

有了上面的function後你就可以在begin,commit,rollback 直接穿插使用,實現在事物處理期間做你愛做的事。也可以用在觸發器與EVENT定時任務中。

8.1.4. plugin 的開發與使用

編譯UDF你需要安裝下面的軟件包

sudo apt-get install pkg-config
sudo apt-get install libmysqlclient-dev

sudo apt-get install gcc gcc-c++ make automake autoconf
		

https://github.com/netkiller/mysql-fifo-plugin

編譯udf,最後將so檔案複製到 /usr/lib/mysql/plugin/

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/
		

裝載

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';
		

卸載

drop function fifo_create;
drop function fifo_remove;
drop function fifo_read;
drop function fifo_write;
		

8.1.5. 插件如何使用

插件有很多種用法,這裡僅僅一個例

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');
		

我們假設有一個demo這樣的表,我使用shell寫了一個守護進程用於處理資料庫送過來的數據

		
#!/bin/bash
########################################
# Homepage: http://netkiller.github.io
# Author: neo <netkiller@msn.com>
########################################
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 $?
		
		

啟動守護進程

$ ./sms.sh start
$ ./sms.sh status
  596 pts/5    S      0:00 /bin/bash ./sms.sh start
		

監控日誌,因為守護進程沒有輸出,完成人戶後寫入日誌。

$ tail -f /tmp/demo.log
		

開始推送任務

		
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)
		
		

現在看看日誌的變化

$ 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
		

我們再將上面的例子使用觸發器進一步優化

		
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
		
		

測試

		
mysql> insert into demo(name,mobile) values('jerry','13322993040');
Query OK, 1 row affected (0.05 sec)		
				
		

日誌變化

$ 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
		

8.1.6. 部署相關問題

我們可以採用主從資料庫,將任務放在專用的從庫上執行

我們可以創建很多個管道,用於做不同的工作,例如插入一個任務,更新一個任務,發短信一個任務,處理模板與靜態化一個任務等等。