Amoeba 实现 MySQL 读写分离

[TOC]

一、读写分离原理

读写分离(Read/Write Splitting),基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理 SELECT 查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。

二、阿里云 ECS 上使用 Amoeba 框架配置 MySQL 的读写分离

1. 前提:MySQL 数据库主从复制已经配置成功

假设配置成功后信息如下:
Master:120.77.219.39
Slave:172.18.247.212
amoeba:120.77.219.39
这里是将 amoeba 安装在 Master 所在服务器上.
当然也可以将 amoeba 配置在第三台服务器上

2. 配置amoeba

2.1 下载 jdk for linux(如果已经安装请跳过此步骤)
此处下载的为jdk-8u144-linux-x64.tar.gz
使用tar命令解压到/usr/share/jdk1.8里
然后配置环境变量:
用Vim编辑器打开/etc/profile 
在profile文件末尾加入: 
export JAVA_HOME=/usr/share/jdk1.8 
export PATH=$JAVA_HOME/bin:$PATH 
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar 

如果使用apt安装的openjdk,只需要在~/.bashrc文件中配置一下环境变量:
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export JRE_HOME=${JAVA_HOME}/jre
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH 
export CLASSPATH=$CLASSPATH:.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib

环境变量配置完毕,最好重启服务器。或者使用source命令。
2.2 下载并配置 amoeba

此处下载的版本是:amoeba-mysql-3.0.5-RC ,将压缩包解压到 /usr/share/amoeba

(1) 配置 dbServer.xml

编辑文件:

vi ${AMOEBA_HOME}/conf/dbServers.xml

配置如下内容:

<?xml version="1.0" encoding="gbk"?>

<!DOCTYPE amoeba:dbServers SYSTEM "dbserver.dtd">
<amoeba:dbServers xmlns:amoeba="http://amoeba.meidusa.com/">
    
    <!-- 抽象的 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>
            
            <!-- 主从库 -->
            <property name="schema">yz_test</property>
            
            <!-- 访问主从库的用户(主从节点上的用户名和密码尽量建成一样的) -->
            <property name="user">yzyz</property>
            
            <!-- 主从库的密码 -->
            <property name="password">123</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。名称可以自定义,这里定义为:writedb -->
    <dbServer name="writedb"  parent="abstractServer">
        <factoryConfig>
            <!-- 写库的IP,一般在一主多从的情况下,将 master 设置为写库 -->
            <property name="ipAddress">120.77.219.39</property>
        </factoryConfig>
    </dbServer>
    
    <!-- 读库,继承自抽象 dbServer。读从库可能会有多个,自定义名称,以便区分 -->
    <dbServer name="readslave1"  parent="abstractServer">
        <factoryConfig>
            <!-- 读库的IP,一主多从情况下,将 slave 设置为读库 -->
            <property name="ipAddress">101.201.66.147</property>
        </factoryConfig>
    </dbServer>
    
    <!-- 这是一个虚拟的 dbServer,相当于 dbServer 组,将读库的 IP 统一放到这个组内 -->
    <dbServer name="multiReadSlavePool" virtual="true">
        <poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
            <!-- 负载调度策略: 1=ROUNDROBIN 复制均衡 , 2=WEIGHTBASED 基于权重, 3=HA -->
            <property name="loadbalance">1</property>
            
            <!-- 将多个读库的dbServer名称放在一起,以半角逗号分隔: server1,server2... -->
            <property name="poolNames">readslave1</property>
        </poolConfig>
    </dbServer>
        
</amoeba:dbServers>

需要修改三处:

​ (1)MySQL 数据库连接的用户名和密码

​ (2)写库 IP,读库 IP

​ (3)多个读库的 dbServer 组

(2) 配置 amoeba.xml

编辑文件:

vi ${AMOEBA_HOME}/conf/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">
            <!-- Amoeba 访问端口 -->
            <property name="port">8066</property>
            
            <!-- bind ipAddress -->
            <!-- 如果不指定绑定IP ,将对所有的 IP 扫描 -->
            <!-- 
            <property name="ipAddress">127.0.0.1</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">
                    
                    <!-- 访问 amoeba 的用户名,远端通过 8066 端口访问 amoeba 服务,
                         并使用此处配置的用户名和密码登录 amoeba -->
                    <property name="user">amoebaroot</property>
                    <!-- 访问 amoeba 的密码 -->
                    <property name="password">Cs123456</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>
        
        <!-- Amoeba 默认的池:一般情况下配置为 dbServers.xml 中对应的 master  -->
        <property name="defaultPool">writedb</property>
        
        <!-- writePool 写库 dbServer  -->
        <property name="writePool">writedb</property>
        <!-- readPool 读库 dbServer 组 -->
        <property name="readPool">multiReadSlavePool</property>
        
        <property name="needParse">true</property>
    </queryRouter>
</amoeba:configuration>

(3) 配置 amoeba 运行环境 JVM 相关参数

amoeba 在启动时,可能会报出 JVM 内存不足的问题,我们可以配置它的 JVM 运行环境的堆存大小。

编辑文件:

${AMOEBA_HOME}/jvm.properties

JVM_OPTIONS 后面的值中的 -Xss196k 修改为 -Xss256k

  1. /usr/share/amoeba/bin 目录赋予"执行"权限:chmod +x bin/*
  2. 在安全组中添加 8066/8066 端口的外网访问权限。
  3. 进入 /usr/share/amoeba/bin ,使用 ./launcher start 命令启动amoeba。

jvm.properties 中的其他配置说明(注意:在实际环境中,不要保留中文注释):

# app名字
APP_NAME=Amoeba-MySQL

# app版本号
APP_VERSION=3.0.0-beta

# 日志输出路径,log4j中可引用参数 ${project.output}

APP_OUTPUT_PATH=$PROJECT_HOME/logs

# 应用程序的 PID 文件存放路径,    默认存放在: ${project.home}/${APP_NAME}.pid
#APP_PID_PATH=/temp/logs/$APP_NAME

# 控制台输出到日志文件

APP_CONSOLE_LOG=$APP_OUTPUT_PATH/console.log


# 程序相关的配置参数

#APP_OPTIONS="-DmyParam=value1 -DmyParam2=value2"

# 启动参数  

#APP_ARGS="args0 "


# JVM相关的参数,包括内存配置、垃圾回收策略

JVM_OPTIONS="-server -Xms256m -Xmx1024m -Xss256k -XX:PermSize=16m -XX:MaxPermSize=96m"


# 应用程序忽略的信号列表,以逗号分割,程序shutdown的信号为15(可用 kill -15 pid 可让程序文明的shutdown,请不要在这儿填15)

IGNORE_SIGNALS=1,2

3. 添加阿里云 ECS 安全组端口 8066

在阿里云 ECS 安全组端口中添加 8066 端口:

入方向
协议:自定义 TCP
端口范围:8066/8066
授权类型:地址段访问
授权对象:0.0.0.0/0

4. 启动 amoeba

启动命令:

cd ${AMOEBA_HOME}/bin
./launcher start 

如果之前启动了 amoeba,还没有来得及关闭,那么这次启动会提示 8066 端口被占用。我们可以查看是哪个进程占用了该端口,然后 kill 该进程 PID:

lsof -i:8066  # 查看 8066 端口被哪个进程占用

COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
java    15009 root   65u  IPv4  76545      0t0  TCP *:8066 (LISTEN)

## 找到该进程的 PID ,杀掉就可以了
kill -9 15009

## 然后再次启动 amoeba
cd ${AMOEBA_HOME}/bin
./launcher start

查看 amoeba 是否在运行:

netstat -unlpt | grep java

tcp        0      0 0.0.0.0:8066            0.0.0.0:*               LISTEN      15038/java
tcp        0      0 127.0.0.1:8005          0.0.0.0:*               LISTEN      11394/java
tcp        0      0 0.0.0.0:8009            0.0.0.0:*               LISTEN      11394/java
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      11394/java

如果能看到 8066 端口处于 LISTEN 状态,就说明 amoeba 已经启动完成。

5. 停止 amoeba

${AMOEBA_HOME}/bin/shutdown

6. 测试 amoeba

  1. 使用 amoeba 所在的 IP,端口号 8066,amoeba.xml 中配置的用户名和密码,远程连接 amoeba。远程连接成功,说明 amoeba 启动成功。
  2. 连接 amoeba,写 SQL 语句查询和添加,分别看看主库和从库是否都有数据,如果有,说明写入成功。
  3. 然后停止 slave 的 MySQL 服务,再测试查询和添加,能添加、不能查询,说明 写库 master 正常,读库 slave 离线。
  4. 然后启动 slave ,停止 master ,再测试查询和添加,能查询、不能添加,说明 读库 slave 正常,写库 master 离线。

7. 常见错误

1、如果提示 Unknown system variable 'language' 错误,将 mysql 的包换成 5.1.24 试试。
2、提示无法打开 console.log,就将logs文件夹权限修改一下,比如:chmod 777 logs
3、提示 address in Use,查找占用 8066 端口的进程,kill -9 杀掉,再次启动 amoeba

参考 青衫lys 的文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260