读写分离中间件 -- Amoeba 的安装和配置

2015-04-08 09:19:14   最后更新: 2015-04-12 23:26:11   访问数量:3080




之前搭建了读写分离系统:

MySQL 分布式主从读写分离架构及实战

对于这样主库用来读写,从库只读的系统,怎样决定每一个 sql 读写的真实库呢?在应用程序中主动判断显然是不现实的

目前的解决方案主要有以下几种:

  1. 通过应用程序实现,实现困难,增加了代码耦合
  2. 通过 mysql-proxy 实现,这种实现性能不高,可扩展性不强,而其又是 lua 脚本编写,开发环境中熟悉的人少
  3. 自己开发接口,这个方案需要具备一定实战经验,且开发成本高、门槛高
  4. 开源项目 Amoeba,Amoeba 是目前较好的 mysql 读写分离中间件,具有负载均衡、高可用性、sql过滤、读写分离、可路由相关的query到目标数据库,并且安装配置非常简单,本文即对此进行安装、配置和介绍

 

Amoeba 是阿里巴巴公司的开源项目,主要优势有:

  • 实现读写分离,并具有负载均衡、sql 过滤等功能
  • 降低 db 与客户端连接数
  • 配置基于 xml,配置简单,安装方便
  • 提供数据切分规则并降低切分规则给数据库带来的影响

 

但 Amoeba 暂不支持事务

 

由于 Amoeba 是基于 java 编写的,所以我们首先要安装 jdk

ORACLE java SE下载最新版本的 jdk

 

下载后执行:

sudo mkdir /usr/lib/jvm sudo tar -zxvf jdk-8u40-linux-x64.tar.gz -C /usr/lib/jvm

 

 

然后修改 /etc/profile 添加:

export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_40 export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH

 

 

然后执行:

source /etc/profile

 

让配置立即生效

 

  • 注:执行 java -version 如果出现 bash: ./java: cannot execute binary file 错误,不要怀疑,你搞错了系统的版本,你的系统是 32 位,而你下载的 jdk 是 64 位版本

 

执行下列命令,让 java 成为系统默认命令:

sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.8.0_40/bin/java 300 sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.8.0_40/bin/javac 300 sudo update-alternatives --config java

 

 

Amoeba SourceForge 主页上下载最新的 Amoeba for mysql 安装包

下载后执行:

unzip amoeba-mysql-3.0.5-RC-distribution.zip sudo mv amoeba-mysql-3.0.5-RC /usr/lib/amoeba/.

 

 

Amoeba 的配置文件在 /usr/lib/amoeba/amoeba-mysql-3.0.5-RC/conf 目录中:

  • amoeba.xml:主配置文件,配置所有数据源以及 Amoeba 自身的参数,我们只需要对这个配置文件进行配置即可
  • rule.xml:配置所有 Query 路由规则的信息
  • functionMap.xml:配置用于解析Query 中的函数所对应的Java 实现类
  • rullFunctionMap.xml:配置路由规则中需要使用到的特定函数的实现类

 

amoeba.xml 配置

<?xml version="1.0" encoding="gbk"?> <!DOCTYPE amoeba:configuration SYSTEM "amoeba.dtd"> <amoeba:configuration xmlns:amoeba="http://amoeba.meidusa.com/"> <proxy> <!-- service class must implements com.meidusa.amoeba.service.Service --> <service name="Amoeba for Mysql" class="com.meidusa.amoeba.mysql.server.MySQLService"> <!-- port --> <property name="port">8066</property> <!-- bind ipAddress --> <property name="ipAddress">192.168.1.140</property> <property name="connectionFactory"> <bean class="com.meidusa.amoeba.mysql.net.MysqlClientConnectionFactory"> <property name="sendBufferSize">128</property> <property name="receiveBufferSize">64</property> </bean> </property> <property name="authenticateProvider"> <bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator"> <property name="user">amoeba</property> <property name="password">amoeba</property> <property name="filter"> <bean class="com.meidusa.toolkit.net.authenticate.server.IPAccessController"> <property name="ipFile">${amoeba.home}/conf/access_list.conf</property> </bean> </property> </bean> </property> </service> <runtime class="com.meidusa.amoeba.mysql.context.MysqlRuntimeContext"> <!-- proxy server client process thread size --> <property name="executeThreadSize">128</property> <!-- per connection cache prepared statement size --> <property name="statementCacheSize">500</property> <!-- default charset --> <property name="serverCharset">utf8</property> <!-- query timeout( default: 60 second , TimeUnit:second) --> <property name="queryTimeout">60</property> </runtime> </proxy> <!-- Each ConnectionManager will start as thread manager responsible for the Connection IO read , Death Detection --> <connectionManagerList> <connectionManager name="defaultManager" class="com.meidusa.toolkit.net.MultiConnectionManagerWrapper"> <property name="subManagerClassName">com.meidusa.toolkit.net.AuthingableConnectionManager</property> </connectionManager> </connectionManagerList> <!-- default using file loader --> <dbServerLoader class="com.meidusa.amoeba.context.DBServerConfigFileLoader"> <property name="configFile">${amoeba.home}/conf/dbServers.xml</property> </dbServerLoader> <queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter"> <property name="ruleLoader"> <bean class="com.meidusa.amoeba.route.TableRuleFileLoader"> <property name="ruleFile">${amoeba.home}/conf/rule.xml</property> <property name="functionFile">${amoeba.home}/conf/ruleFunctionMap.xml</property> </bean> </property> <property name="sqlFunctionFile">${amoeba.home}/conf/functionMap.xml</property> <property name="LRUMapSize">1500</property> <property name="defaultPool">write_serv</property> <property name="writePool">write_serv</property> <property name="readPool">read_serv</property> <property name="needParse">true</property> </queryRouter> </amoeba:configuration>

 

 

我们绑定了 192.168.1.140 端口 8066,用户名 amoeba 密码 amoeba,当 amoeba 运行起来后,我们使用这些参数就可以登录到 amoeba 虚拟的 mysql 了

接下来的 defaultPool、writePool、readPool 三个节点就完成了负载均衡和读写分离的配置,他们的配置就在 configFile 节点中

 

dbServers.xml

在 amoeba.xml 中,为了更好的进行模块区分,我们将真实 server 的配置放到了 dbServers.xml 文件中

<?xml version="1.0" encoding="gbk"?> <!DOCTYPE amoeba:dbServers SYSTEM "dbserver.dtd"> <amoeba:dbServers xmlns:amoeba="http://amoeba.meidusa.com/"> <!-- Each dbServer needs to be configured into a Pool, If you need to configure multiple dbServer with load balancing that can be simplified by the following configuration: add attribute with name virtual = "true" in dbServer, but the configuration does not allow the element with name factoryConfig such as 'multiPool' dbServer --> <dbServer name="abstractServer" abstractive="true"> <factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory"> <property name="connectionManager">${defaultManager}</property> <property name="sendBufferSize">64</property> <property name="receiveBufferSize">128</property> <!-- mysql port --> <property name="port">3306</property> <!-- mysql schema --> <property name="schema">techlog</property> <!-- mysql user --> <property name="user">amoeba</property> <property name="password">amoeba</property> </factoryConfig> <poolConfig class="com.meidusa.toolkit.common.poolable.PoolableObjectPool"> <property name="maxActive">500</property> <property name="maxIdle">500</property> <property name="minIdle">1</property> <property name="minEvictableIdleTimeMillis">600000</property> <property name="timeBetweenEvictionRunsMillis">600000</property> <property name="testOnBorrow">true</property> <property name="testOnReturn">true</property> <property name="testWhileIdle">true</property> </poolConfig> </dbServer> <dbServer name="se1" parent="abstractServer"> <factoryConfig> <!-- mysql ip --> <property name="ipAddress">192.168.1.140</property> </factoryConfig> </dbServer> <dbServer name="se2" parent="abstractServer"> <factoryConfig> <!-- mysql ip --> <property name="ipAddress">192.168.1.141</property> </factoryConfig> </dbServer> <dbServer name="se3" parent="abstractServer"> <factoryConfig> <!-- mysql ip --> <property name="ipAddress">192.168.1.142</property> </factoryConfig> </dbServer> <dbServer name="write_serv" virtual="true"> <poolConfig class="com.meidusa.amoeba.server.MultipleServerPool"> <!-- Load balancing strategy: 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA--> <property name="loadbalance">1</property> <!-- Separated by commas,such as: server1,server2,server1 --> <property name="poolNames">se1</property> </poolConfig> </dbServer> <dbServer name="write_serv" virtual="true"> <poolConfig class="com.meidusa.amoeba.server.MultipleServerPool"> <!-- Load balancing strategy: 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA--> <property name="loadbalance">1</property> <!-- Separated by commas,such as: server1,server2,server1 --> <property name="poolNames">se1,se2,se2,se3,se3</property> </poolConfig> </dbServer> </amoeba:dbServers>

 

在这里,我们看到,由于三台 server 的 mysql 端口、用户名、密码、所用数据库都相同,所以在这里直接给 dbServer 节点添加了 parent 属性,继承了公共域和值

定义好真实 server 的 IP、端口、用户名、密码、数据库等信息,我们就要开始配置 write_serv 和 read_serv 了

 

write_serv 节点中,我们只加入了 master se1,

read_serv 节点中,我们加入了 se1,se2,se2,se3,se3,为什么要这么写呢?

 

amoeba 负载均衡有三种模式,在 <property name="loadbalance"> 节点中配置 1、2、3 来区分

  1. 1 -- ROUNDROBIN: 轮询 pollNames 执行命令
  2. 2 -- WEIGHTBASED: 根据当前活动连接数,活动连接少的优先
  3. 3 -- HA: 仅使用活动节点,只有当活动节点不可用时才使用备用节点

 

我们这里使用了最简单实用的 ROUNDROBIN 模式,因此通过重复写入 server 名来为 server 加权,这样让写库执行读操作的几率小一些

 

授予权限

当然了,接下来我们需要让 amoeba 能够有权限访问和操作三台服务器上指定的数据库

分别在三台服务器中执行:

grant all on techlog.* to amoeba@192.168.1.140 identified by 'amoeba'; flush privileges;

 

 

激动人心的时刻到了,我们用 root 权限执行 /usr/lib/amoeba/amoeba-mysql-3.0.5-RC/bin/launcher

 

执行成功,amoeba 服务已经在后台乖乖运行着去了

 

此时,我们通过运行命令:

mysql -uamoeba -pamoeba -h192.168.1.140 -P8066

就可以登录 amoeba 提供的虚拟 mysql 客户端了:

 

 

表创建、删除与数据写入、删除

为了测试 amoeba 是不是真的做到了读写分离,我们首先创建一个表,顺便也可以创建 amoeba 创建表是否可以做到

create table test ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `server_name` varchar(10) NOT NULL, PRIMARY KEY (`id`) )ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

 

创建表后,我们在三台服务器上查看,全部都已经有了 test 表,此时我们写入一条数据

insert into test (server_name) values ('master_se1');

 

 

全部更新完成,没有问题

 

数据读取

我们首先在两台 slaver 上分别执行 update 操作,更新 test 表中的数据,使其分别为 slaver_se2 和 slaver_se3

然后我们在 amoeba 虚拟客户端上执行:

select * from test;

 

经过反复执行,我们得到下面的结果:

 

 

可以看到,amoeba 以轮询的方式分别在三台服务器上取数据,并且是按照我们设定的访问比例进行的

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤

 






技术帖      mysql      sql      技术分享      存储      server      opensource      数据库      db      ha      读写分离      jdk      主从      阿里      阿里巴巴      负载平衡     


京ICP备15018585号