Haproxy+多台MySQL从服务器(Slave) 实现负载均衡
本系統(tǒng)采用MySQL一主多從模式設(shè)計,即1臺 MySQL“主”服務(wù)器(Master)+多臺“從”服務(wù)器(Slave),“從”服務(wù)器之間通過Haproxy進行負載均衡,對外只提供一個訪問IP,當(dāng)程序需要訪問多臺"從"服務(wù)器時,只需要訪問Haproxy,再由Haproxy將請求分發(fā)到各個數(shù)據(jù)庫節(jié)點。
我們的程序可以有倆個數(shù)據(jù)源(DataSourceA,DataSourceB),一個(DataSourceA)直接連接主庫,另外一個(DataSourceB)連接Haproxy,當(dāng)需要寫入操作時可以使用DataSourceA,讀取時使用DataSourceB。
設(shè)計圖如下:
看到這里大家可能會有一個疑問,這個問題就是主從數(shù)據(jù)庫之間數(shù)據(jù)同步延時的問題!
因為大多數(shù)使用MySQL主從同步數(shù)據(jù)都是異步的,也就是說當(dāng)主庫的數(shù)據(jù)發(fā)生變化時并不能立即的更新從庫,這么做的目的也是為了更好的性能,那么設(shè)想一下,當(dāng)用戶新增一條記錄后立刻去從庫查詢,可能并不能查到剛剛新增的數(shù)據(jù),這豈不是很腦裂的問題~~~。
然而實際情況并不應(yīng)該是這樣的,我們也不應(yīng)該這樣去設(shè)計程序,我們就拿一個類似于CSND博客管理的系統(tǒng)來說,假設(shè)一個“博客系統(tǒng)”只有倆部分, 一部分是博客管理后臺,用戶可以在后臺新增,編輯,刪除博客。另一部分是門戶網(wǎng)站,負責(zé)展示所有用戶的博客信息,相對于這樣一個系統(tǒng)來說, 后臺管理模塊對數(shù)據(jù)庫的操作壓力不是很大,相反門戶網(wǎng)站讀取博客信息對數(shù)據(jù)庫的壓力很大,這也式一般互聯(lián)網(wǎng)產(chǎn)品的特點,而且最重要的一點是系統(tǒng)可以接受主從同步數(shù)據(jù)帶來的延遲,也就是說當(dāng)用戶在后臺新增一條博客時,前臺門戶網(wǎng)站并不能立即查詢到這條信息。一般都是再過一段時間后才會出現(xiàn)在首頁,因為大多數(shù)系統(tǒng)都有緩存設(shè)置,這樣正好給主從同步延遲帶來時間。
接著說上面的問題,有的同學(xué)可能會有這樣的疑問,就是后臺用戶在新增一條記錄后,一般都是立即查詢返回博客列表,按照上面說的豈不是查詢不到~~~,我覺得這個問題可以這么解決:
1、后臺用戶在進行 新增,查詢,編輯,刪除等操作時直接連接主庫,這樣無論什么操作都是實時的,因為后臺操作對數(shù)據(jù)庫的壓力不是很大,所以讀寫全部連接主庫應(yīng)該沒什么問題!?
2、門戶網(wǎng)站查詢博客列表時從 “從庫集群中查詢“,通過負載均衡技術(shù),解決了擴展性,高可用性等問題,同時門戶網(wǎng)站首頁也不需要實時查詢主庫中的數(shù)據(jù),因為網(wǎng)站本身一般都有緩存,也不是實時的。
上面的架構(gòu)設(shè)計只是拋磚引玉,大家有什么好想法也可以相互交流~
本文重點介紹的內(nèi)容有二點:
1、如何使用Haproxy給MySQL做負載均衡,提供相關(guān)的配置說明,健康檢查等等。
2、當(dāng)程序通過連接Haproxy代理之后,如何解決程序中連接池長連接失效的問題。
下面介紹如何安裝配置Haproxy~
1、首先進行負載均衡配置。
假設(shè)兩臺MySQL(slave)從服務(wù)器 ?A:192.168.1.191:3306 ? ? ?B:192.168.1.192:3306。
首先在linxu上安裝Haproxy,安裝過程略。。。。。。
安裝完畢后打開配置文件在/etc/haproxy/?haproxy.cfg,配置文件的路徑可能不用,別告訴我找不到~~~!
[html]?view plain?copy?print?
global??
????????maxconn?4096??
????????daemon??
????????chroot??????/var/lib/haproxy??
????????pidfile?????/var/run/haproxy.pid??
????????#debug??
????????#quiet??
????????user?haproxy??
????????group?haproxy??
???
defaults??
????????log?????global??
????????mode????http??
????????option??httplog??
????????option??dontlognull??
????????log?127.0.0.1?local0??
????????retries?3??
????????option?redispatch??
????????maxconn?2000??
????????#contimeout??????5000??
????????#clitimeout??????50000??
????????#srvtimeout??????50000??
????????timeout?http-request????10s??
????????timeout?queue???????????1m??
????????timeout?connect?????????10s??
????????timeout?client??????????1m??
????????timeout?server??????????1m??
????????timeout?http-keep-alive?10s??
????????timeout?check???????????10s??
???
listen??admin_stats?0.0.0.0:8888??
????????mode????????http??
????????stats?uri???/dbs??
????????stats?realm?????Global\?statistics??
????????stats?auth??admin:admin??
???
listen??proxy-mysql?0.0.0.0:23306??
????????mode?tcp??
????????balance?roundrobin??
????????option?tcplog??
????????option?mysql-check?user?haproxy?#在mysql中創(chuàng)建無任何權(quán)限用戶haproxy,且無密碼??
????????server?MySQL1?192.168.1.191:3306?check?weight?1?maxconn?2000??
????????server?MySQL2?192.168.1.192:3306?check?weight?1?maxconn?2000??
????????option?tcpka??
[html]?view plain?copy?print?
listen??admin_stats?0.0.0.0:8888?這個配置是監(jiān)控頁面,綁定到本機8888端口,賬號admin,密碼admin??
可以通過web的方式查看所有MySQL節(jié)點的使用情況, http://你的IP:8888/dbs 即可登錄監(jiān)控后臺。
如下圖:
[html]?view plain?copy?print?
listen??proxy-mysql?0.0.0.0:23306??
????????mode?tcp??
????????balance?roundrobin??
????????option?tcplog??
????????option?mysql-check?user?haproxy?#在mysql中創(chuàng)建無任何權(quán)限用戶haproxy,且無密碼??
????????server?MySQL1?192.168.1.191:3306?check?weight?1?maxconn?2000??
????????server?MySQL2?192.168.1.192:3306?check?weight?1?maxconn?2000??
????????option?tcpka??
[html]?view plain?copy?print?
</pre><pre?name="code"?class="html">proxy-mysql?0.0.0.0:23306?代理的端口。我們程序連接從庫集群時就訪問這個端口。??
[html]?view plain?copy?print?
balance?roundrobin?負載均衡方式,有很多種,可以去Google。??
[html]?view plain?copy?print?
option?mysql-check?user?haproxy?這里是配置健康檢查的,也是haproxy自帶的功能,<span?style="color:#ff6666;">需要在<span?style="font-family:?Arial,?Helvetica,?sans-serif;">mysql中創(chuàng)建無任何權(quán)限用戶haproxy,且無密碼</span></span>??
[html]?view plain?copy?print?
server?MySQL1?192.168.1.191:3306?check?weight?1?maxconn?2000?配置MySQL從庫節(jié)點,有多少配置多少就行了。??
有的同學(xué)可能不知道如何在MySQL中創(chuàng)建用戶,這里也給你寫好了。
用戶名為haproxy 且無密碼(重要) 否則haproxy無法檢測MySQL狀態(tài)。
CREATE USER 'haproxy'@'%' IDENTIFIED BY '';?
配置完成后啟動代理 service haproxy start ?如果用過yum方式安裝,應(yīng)該就能啟動了,如果是其它方式安裝,可能啟動方式不同,需要編寫腳本啟動,應(yīng)該不難自己研究一下~~~
然后讓我們寫個demo測試一下代理是否配置成功了沒!
[java]?view plain?copy?print?
public?static?void?main(String[]?args)?throws?Exception?{??
??????
??????
????Class.forName("com.mysql.jdbc.Driver");??
????Connection?conn?=?DriverManager.getConnection("jdbc:mysql://你的IP:23306/template?useUnicode=true",?"root",?"sql2008");??
??????
????for?(int?i?=?0;?i?<?100;?i++)?{??
????????PreparedStatement?pr?=?null;??
????????ResultSet?res?=?null;??
????????try?{??
?????????????pr?=?conn.prepareStatement("select?count(*)?from?sys_user");??
?????????????res?=?pr.executeQuery();??
????????????if(res.next())?{??
????????????????System.out.println(new?Date().toLocaleString()?+?"->"?+?res.getInt(1));??
????????????}??
????????}?catch?(Exception?e)?{??
????????????e.printStackTrace();??
????????????res.close();??
????????????pr.close();??
????????}??
??????????
????????Thread.sleep(25000);??
????}??
??????
????conn.close();??
}??
輸出結(jié)果如下:可以看到代理MySQL成功了,這時你可以隨機關(guān)掉一個MySQL節(jié)點的服務(wù),程序依然能夠正常的執(zhí)行,說明負載均衡也成功了。
[html]?view plain?copy?print?
2015-8-28?10:09:27->7??
2015-8-28?10:09:52->7??
2015-8-28?10:10:17->7??
2015-8-28?10:10:42->7??
2015-8-28?10:11:07->7??
小小的激動有沒有~有沒有~。于是乎我們就把程序中數(shù)據(jù)源的配置改造一下,讓它連接haproxy即可。
<property name="jdbcUrl" value="jdbc:mysql://你的IP:23306/template?useUnicode=true" /> .
是不是以為大功告成了,如果你就這樣配置的話,等程序運行起來它就會給你一個大大的surprise
其實這里面是有坑的~~~~,且聽我細細道來。
一般的情況下,我相信大家在直接連接MySQL的時候幾乎都用到了連接池。
以我的配置為例:
[html]?view plain?copy?print?
??<bean?id="dataSource"?class="com.mchange.v2.c3p0.ComboPooledDataSource"?destroy-method="close">??
<property?name="driverClass"?value="com.mysql.jdbc.Driver"?/>??
<property?name="jdbcUrl"?value="jdbc:mysql://你的IP:23306/你的數(shù)據(jù)庫名稱?useUnicode=true"?/>??
<property?name="user"?value="xx"?/>??
<property?name="password"?value="yy"?/>??
<property?name="initialPoolSize"?value="5"?/>??
<property?name="minPoolSize"?value="5"?/>??
<property?name="maxPoolSize"?value="30"?/>??
<property?name="maxIdleTime"?value="0"?/>??
<property?name="idleConnectionTestPeriod"?value="30"?/>??
<property?name="acquireIncrement"?value="3"?/>??
<property?name="automaticTestTable"?value="C3p0TestTable_NotDelete"?/>??
<property?name="autoCommitOnClose"?value="false"?/>??
lt;/bean>??
其它的參數(shù)這里不解釋,大家可以查詢C3P0配置信息,網(wǎng)上很多。
這里只說一個:
[html]?view plain?copy?print?
idleConnectionTestPeriod=30?這個參數(shù)是配置連接池?每隔多少時間去檢查池內(nèi)鏈接的有效性,單位秒。??
[html]?view plain?copy?print?
我這里設(shè)置成30秒,那么C3P0會每隔30秒?把連接池內(nèi)所有的空閑連接拿出來挨個發(fā)一個測試SQL語句,已確定這個鏈接的有效性。??
以前我們的數(shù)據(jù)源是直接連接MySQL數(shù)據(jù)庫的,在正常的情況下MySQL是不會斷開這個鏈接的。
但是我們現(xiàn)在連接的是haproxy,也就是說我們程序的連接(Connection)是與haproxy建立的,這里的坑在于這個連接是會被haproxy斷掉的,這樣的話你連接池內(nèi)的鏈接就變成了無效鏈接,在下次需要查詢數(shù)據(jù)庫時還需要重新創(chuàng)建連接,而且程序由于拿到的連接是無效鏈接,還有可能報錯。
那么haproxy與我們程序之間的連接超時時間在哪設(shè)置呢?
[html]?view plain?copy?print?
timeout?client??????????1m??#這個參數(shù)配置程序與haproxy的鏈接超時時間??
timeout?server??????????1m??<span?style="font-family:?Arial,?Helvetica,?sans-serif;">#這個參haproxy與mysql鏈接超時時間</span>??
這里的超時時間不是指連接過程的超時時間,而是指連接上以后,多少時間內(nèi)沒有心跳,操作這個時間就認為超時,然后斷開連接。
寫的可能有些啰嗦,我們看個例子開說明一下:
[java]?view plain?copy?print?
public?static?void?main(String[]?args)?throws?Exception?{??
??????
??????
????Class.forName("com.mysql.jdbc.Driver");??
????Connection?conn?=?DriverManager.getConnection("jdbc:mysql://你的IP:23306/template?useUnicode=true",?"root",?"sql2008");??
??????
????for?(int?i?=?0;?i?<?100;?i++)?{??
????????PreparedStatement?pr?=?null;??
????????ResultSet?res?=?null;??
????????try?{??
?????????????pr?=?conn.prepareStatement("select?count(*)?from?sys_user");??
?????????????res?=?pr.executeQuery();??
????????????if(res.next())?{??
????????????????System.out.println(new?Date().toLocaleString()?+?"->"?+?res.getInt(1));??
????????????}??
????????}?catch?(Exception?e)?{??
????????????e.printStackTrace();??
????????????res.close();??
????????????pr.close();??
????????}??
??????????
????????Thread.sleep(60000);??
????}??
??????
????conn.close();??
}??
我上面配置的是?timeout client 1m ,也就是說客戶端連接到haproxy后 1分鐘之內(nèi)沒有數(shù)據(jù)請求即為超時,就會斷掉鏈接:
[html]?view plain?copy?print?
<pre?name="code"?class="java"><pre?name="code"?class="java"?style="font-size:18px;">第一次查詢沒有問題:??
Thread.sleep(60000); 我把間隔設(shè)置為60秒,第二次查詢與第一次查詢間隔60秒就會報錯,因為超時了。
[java]?view plain?copy?print?
那如果我把間隔改為?<span?style="font-family:?Arial,?Helvetica,?sans-serif;">Thread.sleep(50000);?50秒,就不會報錯。</span>??
結(jié)論就是
[html]?view plain?copy?print?
idleConnectionTestPeriod?的時間一定要小于?<span?style="font-size:18px;?background-color:?rgb(240,?240,?240);">timeout?client的時間。這樣C3P0會在Haproxy斷掉鏈接之前發(fā)送一次“心跳”過去,保持鏈接的有效性。</span>??
[html]?view plain?copy?print?
<span?style="font-size:18px;?background-color:?rgb(240,?240,?240);">而且?</span><span?style="font-family:?Arial,?Helvetica,?sans-serif;">timeout?client與?</span><span?style="font-family:?Arial,?Helvetica,?sans-serif;">timeout?server?盡量保持一致,已達到最佳效果。</span>??
本文轉(zhuǎn)自yzy121403725 51CTO博客,原文鏈接:http://blog.51cto.com/lookingdream/1828380,如需轉(zhuǎn)載請自行聯(lián)系原作者
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎
總結(jié)
以上是生活随笔為你收集整理的Haproxy+多台MySQL从服务器(Slave) 实现负载均衡的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于随机森林模型的心脏病患者预测及可视化
- 下一篇: 基于Java毕业设计智慧后勤系统源码+系