基于JDBC的負(fù)載均衡
Hologres從 V1.3版本開始,支持在JDBC配置多個只讀從實(shí)例以支持簡單的負(fù)載均衡。本文為您介紹如何基于JDBC實(shí)現(xiàn)負(fù)載均衡。
背景信息
Hologres中一個主實(shí)例可以綁定多個只讀從實(shí)例,實(shí)例與實(shí)例之間共享存儲,但是計(jì)算資源是互相隔離的,從而實(shí)現(xiàn)讀寫分離高可用部署,詳情請參見主從實(shí)例讀寫分離部署(共享存儲)。
Hologres支持使用JDBC配置多個只讀從實(shí)例,實(shí)現(xiàn)簡單的負(fù)載均衡,如下圖所示:
您使用JDBC配置多個只讀從實(shí)例可以實(shí)現(xiàn)以下功能:
將查詢請求隨機(jī)分配到您的只讀從實(shí)例,避免單個只讀從實(shí)例負(fù)載過高。
查詢請求按照順序連接只讀從實(shí)例,極大程度避免因?yàn)橹蛔x從實(shí)例故障導(dǎo)致服務(wù)不可用,存在以下情況:
當(dāng)只讀從實(shí)例1連接失敗時,JDBC會自動嘗試連接只讀從實(shí)例2。
當(dāng)只讀從實(shí)例1和只讀從實(shí)例2連接失敗時,JDBC會自動嘗試連接只讀從實(shí)例3。
只有當(dāng)只讀從實(shí)例1、只讀從實(shí)例2和只讀從實(shí)例3均連接失敗時,系統(tǒng)才會提示連接失敗。
從Hologres V2.0.10版本開始,targetServerType
參數(shù)支持更加豐富的取值,擴(kuò)展負(fù)載均衡使用的場景。
使用說明
前提條件
已購買多個Hologres只讀從實(shí)例并綁定至Hologres主實(shí)例,詳情請參見配置共享存儲的主從實(shí)例。
已下載42.3.2以上版本的Postgres JDBC驅(qū)動,詳情請參見JDBC。
命令格式
JDBC配置多個只讀從實(shí)例需要在連接URL中將多個只讀從實(shí)例的Endpoint:Port
信息用半角逗號(,
)分隔,命令格式如下:
jdbc:postgresql://<Endpoint1>:<Port1>,<Endpoint2>:<Port2>,<Endpoint3>:<Port3>.../<DBNAME>?user=<AccessKey ID>&password=<AccessKey Secret>&targetServerType=any&loadBalanceHosts=<value>[&hostRecheckSeconds=<value>]
參數(shù)說明
參數(shù) | 描述 |
Endpoint | Hologres實(shí)例的網(wǎng)絡(luò)地址。 進(jìn)入Hologres管理控制臺的實(shí)例詳情頁獲取網(wǎng)絡(luò)地址。 |
Port | Hologres實(shí)例的端口。 進(jìn)入Hologres管理控制臺的實(shí)例詳情頁獲取端口。 |
DBNAME | Hologres創(chuàng)建的數(shù)據(jù)庫名稱。 |
AccessKey ID | 當(dāng)前阿里云賬號的AccessKey ID。 您可以單擊AccessKey 管理,獲取AccessKey ID。 |
AccessKey Secret | 當(dāng)前阿里云賬號的AccessKey Secret。 您可以單擊AccessKey 管理,獲取AccessKey Secret。 |
targetServerType | 允許連接到指定狀態(tài)的只讀從實(shí)例。取值為 僅Hologres V2.0.10及以上版本支持如下取值:
JDBC會根據(jù)GUC參數(shù)
|
loadBalanceHosts | 指定嘗試連接只讀從實(shí)例的順序,取值如下:
|
hostRecheckSeconds | 可連接Endpoint列表的緩存時間,默認(rèn)為 若您希望調(diào)整該緩存的時間,可以修改
|
關(guān)于更多JDBC配置說明,詳情請參見JDBC的手冊。
使用示例
如下示例會將查詢隨機(jī)分發(fā)到三個只讀從實(shí)例上,并且當(dāng)其中一個實(shí)例連接失敗時,JDBC會自動嘗試切換到另一個實(shí)例連接。
import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class hatest { public static void main(String[] args) { // 設(shè)置Hologres實(shí)例的連接endpoint,只讀從實(shí)例1 String endpoint1 = "hgpostcn-cn-wxxxxxxxx01-cn-shanghai.hologres.aliyuncs.com:80"; // 設(shè)置Hologres實(shí)例的連接endpoint,只讀從實(shí)例2 String endpoint2 = "hgpostcn-cn-wxxxxxxxx02-cn-shanghai.hologres.aliyuncs.com:80"; // 設(shè)置Hologres實(shí)例的連接endpoint,只讀從實(shí)例3 String endpoint3 = "hgpostcn-cn-wxxxxxxxx03-cn-shanghai.hologres.aliyuncs.com:80"; // 設(shè)置待連接的數(shù)據(jù)庫名 String dbname = "postgres"; String jdbcUrl = "jdbc:postgresql://" + endpoint1 + "," + endpoint2 + "," + endpoint3 + "/" + dbname; Properties properties = new Properties(); // 設(shè)置連接數(shù)據(jù)庫的用戶名 properties.setProperty("user", "xxxx"); //設(shè)置連接數(shù)據(jù)庫的密碼 properties.setProperty("password", "xxxx"); // 配置targetServerType,此處配置為any,表示可以對任意endpoint發(fā)送請求 properties.setProperty("targetServerType", "any"); // 配置LoadBalance策略,此處配置true,表示開啟LoadBalance properties.setProperty("loadBalanceHosts", "true"); // 配置hostRecheckSeconds時間,此處配置為10秒 properties.setProperty("hostRecheckSeconds", "10"); try { Class.forName("org.postgresql.Driver"); Connection connection = DriverManager.getConnection(jdbcUrl, properties); PreparedStatement preparedStatement = connection.prepareStatement("show hg_frontend_endpoints;" ); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { ResultSetMetaData rsmd = resultSet.getMetaData(); int columnCount = rsmd.getColumnCount(); Map map = new HashMap(); for (int i = 0; i < columnCount; i++) { map.put(rsmd.getColumnName(i + 1).toLowerCase(), resultSet.getObject(i + 1)); } System.out.println(map); } } catch (Exception exception) { exception.printStackTrace(); } } }
如下示例可以將查詢輪詢100次分發(fā)到2個實(shí)例上。
import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class hatest { public static void main(String[] args) { int x = 1; while( x <= 100 ){ // 設(shè)置Hologres實(shí)例的連接endpoint,主實(shí)例 String endpoint1 = "hgpostcn-cn-wxxxxxxxx04-cn-hangzhou.hologres.aliyuncs.com:80"; // 設(shè)置Hologres實(shí)例的連接endpoint,只讀從實(shí)例 String endpoint2 = "hgpostcn-cn-wxxxxxxxx05-cn-hangzhou.hologres.aliyuncs.com:80"; // 設(shè)置待連接的數(shù)據(jù)庫名 String dbname = "postgres"; String jdbcUrl = "jdbc:postgresql://" + endpoint1 + "," + endpoint2 + "/" + dbname ; Properties properties = new Properties(); // 設(shè)置連接數(shù)據(jù)庫的用戶名 properties.setProperty("user", "xxx"); // 設(shè)置連接數(shù)據(jù)庫的密碼 properties.setProperty("password", "xxx"); // 配置targetServerType,此處配置為any,表示可以對任意endpoint發(fā)送請求 properties.setProperty("targetServerType", "any"); // 配置LoadBalance策略,此處配置true,表示開啟LoadBalance properties.setProperty("loadBalanceHosts", "true"); // 配置hostRecheckSeconds時間,此處配置為10秒 properties.setProperty("hostRecheckSeconds", "10"); try { Class.forName("org.postgresql.Driver"); Connection connection = DriverManager.getConnection(jdbcUrl, properties); PreparedStatement preparedStatement = connection.prepareStatement("show hg_frontend_endpoints;" ); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { ResultSetMetaData rsmd = resultSet.getMetaData(); int columnCount = rsmd.getColumnCount(); Map map = new HashMap(); for (int i = 0; i < columnCount; i++) { map.put(rsmd.getColumnName(i + 1).toLowerCase(), resultSet.getObject(i + 1)); } System.out.println(map); } } catch (Exception exception) { exception.printStackTrace(); } x++; } } }
此時觀察兩個實(shí)例的監(jiān)控信息,可以看到兩個實(shí)例的連接數(shù)基本一致,查看實(shí)例的監(jiān)控信息請參見查看監(jiān)控指標(biāo)。
實(shí)例hgpostcn-cn-wxxxxxxxx04的監(jiān)控信息。
實(shí)例hgpostcn-cn-wxxxxxxxx05的監(jiān)控信息。