李磊专栏

他山之石 可以攻玉
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

【Java】利用Gearman进行Mysql到Redis的复制

Posted on 2018-01-19 21:02  李磊(leige)  阅读(186)  评论(0)    收藏  举报

 转载 http://blog.csdn.net/fcly2013/article/details/50252411

开始的架构图已经有写得非常漂亮的。我这里就不重新再编辑了。java 版本实现job server和worker从第6点开始,红色部分是我遇到的一些坑。。。

1, 环境

CentOS 64位, MySQL, Redis, Java

mysql安装通过lnmp进行安装。所以默认安装路径为/usr/local/mysql

2, Redis简介

Redis是一个开源的K-V内存数据库,它的key可以是string/set/hash/list/...,因为是基于内存的,所在访问速度相当快。

3, Gearman简介

Gearman是一个开源的Map/Reduce分布式计算框架,具有丰富的client sdk,而且它支持MySQL UDF。

Gearman工作图

Gearman工作图

Gearman调用流程

Gearman调用流程

Gearman集群

从图中可以看出貌似Gearman的集群功能比较弱,Job Server之间没啥关系好像。
Gearman集群

4, MySQL - Redis配合使用方案

MySQL - Redis配合使用方案

首先我们以MySQL数据为主,将insert/update/delete交给MySQL,而select交给redis;
当有数据发生变化时,通过MySQL Trigger实时异步调用Gearman的UDF提交一个job给Job Server,当job执行的时候会去更新redis;从而保证redis与MySQL中的数据是同步的。

 

5, 软件安装

安装gearman

//安装依赖
$ yum install -y boost-devel gperf libevent-devel libuuid-devel

//下载gearman  2015-12-10来看都是最新版本
$ wget https://launchpad.net/gearmand/1.2/1.1.12/+download/gearmand-1.1.12.tar.gz
$ tar zxvf gearmand-1.1.12.tar.gz

//编译安装,指定mysqlclient的链接路径
$ ./configure
$ make
$ make install

//启动gearmand服务端 (启动之时,在/var/log/下创建gearmand.log日志文件。-l 指定日志文件  -d后台运行 -L 0.0.0.0 绑定到IPV4)
$ /usr/local/sbin/gearmand -L 0.0.0.0 -l /var/log/gearmand.log -d

//查看是否启动成功[root@iZ94uyronrjZ mysql]# ps -ef | grep gearman
root     31142     1  0 17:06 ?        00:00:00 /usr/local/sbin/gearmand -L 0.0.0.0 -l /znwx/logs/gearmand/gearmand.log -d
root     31174 30507  0 17:40 pts/0    00:00:00 grep gearman//查看是否安装成功,查看gearman版本
$ /usr/local/sbin/gearmand -V
 

 

 

5, MySQL UDF + Trigger同步数据到Gearman

安装lib_mysqludf_json

 

lib_mysqludf_json可以把MySQL表的数据以json数据格式输出

//下载lib_mysqludf_json(地址:https://github.com/mysqludf)
$ cd lib_mysqludf_json
先删除 lib_mysqludf_json.so
//编译 mysql_config 这是mysql的配置文件,可以 find /usr -name mysql_config 搜索下在什么位置
$ gcc $(/usr/local/mysql/bin/mysql_config  --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c

//拷贝lib_mysqludf_json.so到MySQL的plugin目录
//可以登陆MySQL,输入命令"show variables like '%plugin%'"查看plugin位置
$ cp lib_mysqludf_json.so /usr/lib64/mysql/plugin/

//演示lib_mysqludf_json功能
$ mysql -uname -hhost -ppwd
//首先注册UDF函数
mysql> CREATE FUNCTION json_object RETURNS STRING 
       SONAME "lib_mysqludf_json.so";
//json_array|json_members|json_values函数注册方式与json_object一样.
mysql> use test;
mysql> select * from user_list;
+------+----------+
| NAME | PASSWORD |
+------+----------+
| troy | pwd      |
+------+----------+
mysql> select json_object(name,password) as user from user_list;
+----------------------------------+
| user                             |
+----------------------------------+
| {"name":"troy","password":"pwd"} |
+----------------------------------+

安装gearman-mysql-udf

//下载 2015年12月10日已知最新版本
$ wget https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gz
$ tar zxvf gearman-mysql-udf-0.6.tar.gz
$ cd gearman-mysql-udf-0.6

//安装libgearman-devel
$ yum install libgearman-devel

//编译安装

//可以登陆MySQL,输入命令"show variables like '%plugin%'"查看plugin位置, mysql_config的配置文件,以及插件库所在路径,编译之后会在此路径生成.so文件

 

./configure --with-mysql=/usr/local/mysql/bin/mysql_config --libdir=/usr/local/mysql/lib/plugin/

$ make && make install //登录MySQL注册UDF函数 mysql> CREATE FUNCTIONgman_do_background RETURNS STRING SONAME "libgearman_mysql_udf.so"; mysql> CREATEFUNCTION gman_servers_set RETURNS STRING SONAME "libgearman_mysql_udf.so"; //函数gman_do|gman_do_high|gman_do_low|gman_do_high_background|gman_do_low_background|gman_sum注册方式类似,请参考gearman-mysql-udf-0.6/README //指定gearman job server地址 mysql> SELECTgman_servers_set('127.0.0.1:4730');

如果出现异常信息:
ERROR 1126 (HY000): Can't open shared library 'libgearman_mysql_udf.so' (errno: 11 libgearman.so.8: cannot open shared object file: No such file or directory)
表示系统找不到 libgearman.so 文件,一般so都在/usr/local/lib目录下,修改配置文件/etc/ld.so.conf,将/usr/local/lib目录加入进去即可:

$ cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
/usr/local/lib
$ /sbin/ldconfig -v | grep gearman*

 

 

MySQL Trigger调用Gearman UDF实现同步

DELIMITER $$
CREATE TRIGGER user_list_data_to_redis AFTER UPDATE ON user_list
  FOR EACH ROW BEGIN
    SET @ret=gman_do_background('MySQLToRedis', json_object(NEW.name as 'name', NEW.password as 'password')); 
  END$$
DELIMITER ;

 

 

6、这里才是重点:java实现job server 和worker或者单worker,网上都是一些php的。可恶啊。。。。。

我上代码了:

 

[java] view plain copy
 
  1. /** 
  2.  * mysql 同步到 redis 的工具类。连接远程gearman job server 
  3.  * 实现原理  
  4.  * mysql_udf  >>>>>  gearman job server>>>> gearm worker(本类) >>>>> redis 
  5.  * @author 堼宸落宇 2015年12月10日 
  6.  * 
  7.  */  
  8. @Component  
  9. public class MysqlToRedisWorker implements GearmanFunction{  
  10.     private static final Logger log = LoggerFactory.getLogger(MysqlToRedisWorker.class);  
  11.       
  12.     public static final String ECHO_FUNCTION_NAME = "MySQLToRedis";  
  13.       
  14.     @Autowired  
  15.     private Config config;  
  16.       
  17.     /** 
  18.      * 连接其他jobserver用的。 
  19.      */  
  20.     public void startWorker() {  
  21.         Gearman gearman = Gearman.createGearman();  //创建gearman对象,无论是client,worker都是由这个对象产生的  
  22.         log.info("MysqlToRedisWorkder connection:"+config.getEchoHost()+":"+config.getEchoPort()+",function:"+MysqlToRedisWorker.ECHO_FUNCTION_NAME);  
  23.         GearmanServer server = gearman.createGearmanServer(config.getEchoHost(), config.getEchoPort());  //创建gearman server,主要是server地址和端口  
  24.   
  25.         GearmanWorker worker = gearman.createGearmanWorker();  //正题来了,创建work节点。  
  26.         worker.setReconnectPeriod(2, TimeUnit.SECONDS);  //设置超时重连时间  
  27.         worker.setMaximumConcurrency(5);  //最大并发数  
  28.   
  29.         worker.addFunction(<span style="color:#cc0000;">ECHO_FUNCTION_NAME</span>, this);  //添加function方法  
  30.         worker.addServer(server);  //将work添加到server中  
  31.         log.info("MysqlToRedisWorkder is started!!!!");  
  32.     }  
  33.       
  34.     @Override  
  35.     public byte[] work(String func, byte[] data, GearmanFunctionCallback callback)  
  36.             throws Exception {  
  37.           
  38.         log.info("收到mysql的数据:::"+new String(data));  
  39.         System.out.println("@@@@@ " + new String(data));  //byte[] data是从client传递来的参数,我们将参数增加@@@@字符串后,原封不动返回  
  40.         return data;  
  41.     }  


说明以及问题:此类采用了gearman官网的java-gearman-service(地址:https://code.google.com/p/java-gearman-service/),目前release版本是0.6.6。java-gearman-servic.jar包中,即包括gearman server,还包括client和work客户端API,此包在谷歌上。我这里上传了一份:http://download.csdn.net/detail/u012386696/9343319.

 

问题:config类为spring注入的配置文件类,在worker.addFunction中,如果通过config类的属性,并且属性是从配置文件来的就会有问题。不知道为啥,写死就是OK的。此类连接远程的gearman job server.

 

jar包需要添加到本地jar仓库:

mvn install:install-file -Dfile=C:\software\java-gearman-service-0.6.6.jar -DgroupId=org.gearman.jgs -DartifactId=java-gearman-service -Dversion=0.6.6 -Dpackaging=jar

 

7、创建一个job server并注册worker,本地服务。

 

[java] view plain copy
 
  1. /** 
  2.  * 创建一个job server 服务器,并且注册一个worker。 
  3.  * @author <span style="font-family: tahoma, helvetica, arial;">堼宸落宇</span><span style="font-family: tahoma, helvetica, arial;">  2015年12月10日</span> 
  4.  * 
  5.  */  
  6. @Component  
  7. public class MysqlToRedisJobServer {  
  8.     private static final Logger log = LoggerFactory.getLogger(MysqlToRedisJobServer.class);  
  9.       
  10.     @Autowired  
  11.     MysqlToRedisWorker mysqlToRedisWorker;  
  12.     @Autowired  
  13.     Config config;  
  14.       
  15.     public void startJobServer() throws Exception{  
  16.          /* 
  17.          * Create a Gearman instance 
  18.          */  
  19.         Gearman gearman = Gearman.createGearman();  
  20.   
  21.         try {  
  22.   
  23.                 /* 
  24.                  * Start a new job server. The resulting server will be running in 
  25.                  * the local address space. 
  26.                  *  
  27.                  * Parameter 1: The port number to listen on 
  28.                  *  
  29.                  * throws IOException 
  30.                  */  
  31.                 GearmanServer server = gearman.startGearmanServer(config.getEchoPort());  
  32.                   
  33.                 if (server!=null) {  
  34.                     log.info("Start gearm jobServer with java !funcname:"+MysqlToRedisWorker.ECHO_FUNCTION_NAME+" port:"+config.getEchoPort());  
  35.                 }  
  36.                   
  37.                 /* 
  38.                  * Create a gearman worker. The worker poll jobs from the server and 
  39.                  * executes the corresponding GearmanFunction 
  40.                  */  
  41.                 GearmanWorker worker = gearman.createGearmanWorker();  
  42.   
  43.                 /* 
  44.                  *  Tell the worker how to perform the echo function 
  45.                  */  
  46.                 worker.addFunction(MysqlToRedisWorker.ECHO_FUNCTION_NAME, mysqlToRedisWorker);  
  47.   
  48.                 /* 
  49.                  *  Tell the worker that it may communicate with the given job server 
  50.                  */  
  51.                 worker.addServer(server);  
  52.   
  53.         } catch (IOException ioe) {  
  54.   
  55.                 /* 
  56.                  * If an exception occurs, make sure the gearman service is shutdown 
  57.                  */  
  58.                 gearman.shutdown();  
  59.   
  60.                 // forward exception  
  61.                 throw ioe;  
  62.         }  
  63.     }  


说明以及问题:这里创建一个job server,并设置监听端口。。

 

 

到这里就完成了java版本的mysql到redis的复制。可没看到复制到redis中啊。worker类的work方法就是从mysql_udf中传过来的,这里就随便怎么玩了,不是么。。。  备注  谨记 2天的成果