假設我們已經建立了PostgreSQL雙向複製 ,最好檢查一下中斷的情況,以及如何利用 PostgreSQL 驅動程式的本機故障轉移功能。
我們將衝突解決策略更改為last_update_wins。這樣,在每個資料庫中的兩個同時更新之間,具有最大提交時間戳的更新將被選中。
listen_addresses = '*' port = 5432 max_connections = 20 shared_buffers = 128MB temp_buffers = 8MB work_mem = 4MB wal_level = logical max_wal_senders = 3 track_commit_timestamp = on shared_preload_libraries = 'pglogical' pglogical.conflict_resolution = 'last_update_wins'
|
我們需要透過新的變化來啟動組合服務:
docker compose up
請注意,根據程式語言和驅動程式的不同,此功能可能並不總是可用。概念是,當您配置連線池以建立與資料庫的連線時,您可以配置兩個主機。第一個主機將是主主機,而輔助主機將是主主機離線後進行故障轉移的主機。故障轉移可以互換,本質上驅動程式會嘗試找到第一個可用的主機。
1、Python
Python 和驅動程式psycopg2提供此功能。我們將使用 flask api 實現一個應用程式。
該應用程式將提供兩個端點:
一個用於獲取員工的工資,一個用於將工資增加 :
from flask import Flask from psycopg2.pool import SimpleConnectionPool app = Flask(__name__) postgreSQL_pool = SimpleConnectionPool(1, 20, user="postgres", password="postgres", host="localhost,localhost", port="5432,5431", database="postgres", options="-c search_path=test_schema") @app.route('/employee/<employee_id>/salary/increment', methods=['POST']) def increment_salary(employee_id): conn = postgreSQL_pool.getconn() cur = conn.cursor() cur.execute(""" UPDATE employee SET salary=salary + %s WHERE id = %s; """, (1, employee_id)) conn.commit() cur.close() postgreSQL_pool.putconn(conn) return '', 204 @app.route('/employee/<employee_id>/salary') def index(employee_id): conn = postgreSQL_pool.getconn() cur = conn.cursor() cur.execute(""" SELECT salary FROM employee WHERE id=%s; """, employee_id) salary = cur.fetchone()[0] cur.close() postgreSQL_pool.putconn(conn) return str(salary), 200
|
讓我們以 SimpleConnectionPool 為例,我們可以看到兩個用逗號分隔的主機(它是 localhost,因為它是我們正在執行的本地 docker compose),並且在埠部分,相應的主機埠用逗號分隔。
我們可以執行應用程式
flash run
在另一個終端上發出使用 curl 的呼叫
$ curl -X POST http://localhost:5000/employee/1/salary/increment $ curl http://localhost:5000/employee/1/salary
|
總體來說,工資會增加,我們應該在獲取請求中看到這一點。現在讓我們關閉一個資料庫
docker compose stop postgres-b
此操作後的第一次呼叫將會失敗,但連線將重新初始化並指向輔助主機。
% curl http://localhost:5000/employee/1/salary <!doctype html> <html lang=en> <title>500 Internal Server Error</title> <h1>Internal Server Error</h1> <p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p> % curl http://localhost:5000/employee/1/salary 1254.23
|
2、 Spring Boot
相同的功能適用於其他驅動程式。以 Spring Boot 應用程式上的 Java 驅動程式配置為例。
spring.datasource.url=jdbc:postgresql://localhost:5432,localhost:5431/postgres?currentSchema=test_schema spring.datasource.username=postgres spring.datasource.password=postgres
|
在 jdbc url 上我們新增兩個用逗號分隔的主機localhost:5432,localhost:5431然後我們可以實現具有相同功能的應用程式。
package com.egkatzioura.psqlfailover.repository; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class EmployeeRepository { private final JdbcTemplate jdbcTemplate; public EmployeeRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public void incrementSalary(Long employeeId, float increment) { jdbcTemplate.update("UPDATE employee SET salary=salary+? WHERE id=?",increment, employeeId); } public Float fetchSalary(Long employeeId) { return jdbcTemplate.queryForObject("SELECT salary FROM employee WHERE id=?",new Object[]{employeeId},Float.class); } }
|
import com.egkatzioura.psqlfailover.repository.EmployeeRepository; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class EmployeeController { private final EmployeeRepository employeeRepository; public EmployeeController(EmployeeRepository employeeRepository) { this.employeeRepository = employeeRepository; } @PostMapping("/employee/{id}/salary/increment") public void incrementSalary(@PathVariable Long id) { employeeRepository.incrementSalary(id,1f); } @GetMapping("/employee/{id}/salary") public Float fetchSalary(@PathVariable Long id) { return employeeRepository.fetchSalary(id); } }
|
由於複製,更改應該已經到達另一個資料庫。您可以以迴圈方式啟動和重新啟動組合服務。更改將被複制,因此每次發生故障轉移時資料都會在那裡。當我們啟動和停止資料庫時docker compose stop postgres-b,我們可以使用 curl 發出請求:
$ curl -X POST http://localhost:8080/employee/1/salary/increment $ curl http://localhost:8080/employee/1/salary
|
最終,Java 驅動程式可以更優雅地處理故障轉移。在故障轉移期間,它不會在第一個請求時失敗,而是會先連線到另一臺主機並返回結果。就是這樣。您在 PostgreSQL 上設定了雙向複製,並設法利用驅動程式功能將故障轉移到不同的主機。
希望您玩得開心!