使用 PostgreSQL 16.1 + Citus 12.1 作為多個微服務的分散式 Sharding 儲存後端

為少發表於2023-12-26

image

在本教程中,我們將使用 PostgreSQL 16.1 + Citus 12.1 作為多個微服務的儲存後端,演示此類叢集的樣例設定和基本操作。

Citus 12.1 實驗環境設定

Docker 快速啟動 Citus 分散式叢集

docker-compose.yml

version: "3"

services:
  master:
    container_name: "${COMPOSE_PROJECT_NAME:-citus}_master"
    image: "citusdata/citus:12.1.1"
    ports: ["${COORDINATOR_EXTERNAL_PORT:-5432}:5432"]
    labels: ["com.citusdata.role=Master"]
    environment: &AUTH
      POSTGRES_USER: "${POSTGRES_USER:-postgres}"
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-citus}"
      PGUSER: "${POSTGRES_USER:-postgres}"
      PGPASSWORD: "${POSTGRES_PASSWORD:-citus}"
      POSTGRES_HOST_AUTH_METHOD: "${POSTGRES_HOST_AUTH_METHOD:-trust}"
  worker:
    image: "citusdata/citus:12.1.1"
    labels: ["com.citusdata.role=Worker"]
    depends_on: [manager]
    environment: *AUTH
    command: "/wait-for-manager.sh"
    volumes:
      - healthcheck-volume:/healthcheck
  manager:
    container_name: "${COMPOSE_PROJECT_NAME:-citus}_manager"
    image: "citusdata/membership-manager:0.3.0"
    volumes:
      - "${DOCKER_SOCK:-/var/run/docker.sock}:/var/run/docker.sock"
      - healthcheck-volume:/healthcheck
    depends_on: [master]
    environment: *AUTH
volumes:
  healthcheck-volume:

啟動擁有 3 個 worker 的叢集

docker-compose -p citus up --scale worker=3

image

檢視 worker 節點

docker exec -it citus_master psql -U postgres
SELECT master_get_active_worker_nodes();
 master_get_active_worker_nodes
--------------------------------
 (citus-worker-3,5432)
 (citus-worker-1,5432)
 (citus-worker-2,5432)
(3 rows)

微服務的 Citus 儲存後端實戰

Citus 官方示例原始碼

citus-example-microservices

在我們的示例中,我們將使用三個服務:

  • user service
  • time service
  • ping service

Distributed schemas(分散式模式)

分散式模式可以在 Citus 叢集中重新定位。系統可以在可用節點之間將它們作為一個整體重新平衡,從而允許有效地共享資源,而無需手動分配。

根據設計,微服務擁有自己的儲存層,我們不會對它們將建立和儲存的表和資料的型別做任何假設。但是,我們將為每個服務提供一個 schema,並假設它們將使用不同的 ROLE 連線到資料庫。當使用者連線時,他們的角色名稱將放在 search_path 的開頭,因此,如果 roleschema 名稱匹配,則不需要更改任何應用程式來設定正確的 search_path

連線到 Citus 協調器

docker exec -it citus_master psql -U postgres

為服務建立資料庫角色與密碼

CREATE USER user_service;
CREATE USER time_service;
CREATE USER ping_service;

在 Citus 中,模式有兩種分佈方式:

手動呼叫 citus_schema_distribute(schema_name) 函式:

CREATE SCHEMA AUTHORIZATION user_service;
CREATE SCHEMA AUTHORIZATION time_service;
CREATE SCHEMA AUTHORIZATION ping_service;

SELECT citus_schema_distribute('user_service');
SELECT citus_schema_distribute('time_service');
SELECT citus_schema_distribute('ping_service');

此方法還允許將現有的常規模式轉換為分散式模式。

只能分發不包含分散式表和引用表的模式。

另一種方法是啟用 citus.enable_schema_based_sharding 配置變數:

SET citus.enable_schema_based_sharding TO ON;

CREATE SCHEMA AUTHORIZATION user_service;
CREATE SCHEMA AUTHORIZATION time_service;
CREATE SCHEMA AUTHORIZATION ping_service;

這個變數可以在當前會話中修改,也可以在 postgresql.conf 中永久修改。將引數設定為 ON 時,預設情況下將分發所有建立的模式。

你可以列出當前分佈的模式:

select * from citus_schemas;
 schema_name  | colocation_id | schema_size | schema_owner
--------------+---------------+-------------+--------------
 user_service |             1 | 0 bytes     | user_service
 time_service |             2 | 0 bytes     | time_service
 ping_service |             3 | 0 bytes     | ping_service
(3 rows)

建立表

現在需要為每個微服務連線到 Citus 協調器。可以使用 \c 命令在現有的 psql 例項中切換使用者。

\c postgres user_service
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL
);
\d
                   List of relations
    Schema    |     Name      |   Type   |    Owner
--------------+---------------+----------+--------------
 public       | citus_schemas | view     | postgres
 public       | citus_tables  | view     | postgres
 user_service | users         | table    | user_service
 user_service | users_id_seq  | sequence | user_service
\c postgres time_service
CREATE TABLE query_details (
    id SERIAL PRIMARY KEY,
    ip_address INET NOT NULL,
    query_time TIMESTAMP NOT NULL
);
\c postgres ping_service
CREATE TABLE ping_results (
    id SERIAL PRIMARY KEY,
    host VARCHAR(255) NOT NULL,
    result TEXT NOT NULL
);

配置服務

citus-example-microservices 儲存庫包含 pingtimeuser 服務。它們都有一個我們要執行的 app.py

$ tree
.
├── LICENSE
├── README.md
├── ping
│   ├── app.py
│   ├── ping.sql
│   └── requirements.txt
├── time
│   ├── app.py
│   ├── requirements.txt
│   └── time.sql
└── user
    ├── app.py
    ├── requirements.txt
    └── user.sql

為了保障快速測試 Demo,這裡我為大家提供已經構建好的示例服務的 docker 映象,大家直接用就好。

在構建映象之前,我已經針對每個服務的 app.py 做了一定的修改,如下:

  1. user/app.py
db_config = {
    'host': 'citus_master',
    'database': 'postgres',
    'user': 'user_service',
    'port': 5432
}
...
app.run(host="0.0.0.0", port=5000)
  1. time/app.py
db_config = {
    'host': 'citus_master',
    'database': 'postgres',
    'user': 'time_service',
    'port': 5432
}
...
app.run(host="0.0.0.0", port=5000)
  1. ping/app.py
db_config = {
    'host': 'citus_master',
    'database': 'postgres',
    'user': 'ping_service',
    'port': 5432
}
...
app.run(host="0.0.0.0", port=5000)

當然,你也可以切換到每個 app 目錄,直接在各自的 python 環境中執行它們。

Docker 快速啟動 user 服務

docker run -d --name usersvc -p 6000:5000 \
         --network=citus_default \
         registry.cn-heyuan.aliyuncs.com/hacker-linner/citus-microsvc-user:1.0.0

Docker 快速啟動 time 服務

docker run -d --name timesvc -p 6001:5000 \
         --network=citus_default \
         registry.cn-heyuan.aliyuncs.com/hacker-linner/citus-microsvc-time:1.0.0

Docker 快速啟動 ping 服務

docker run -d --name pingsvc -p 6002:5000 \
         --network=citus_default \
         registry.cn-heyuan.aliyuncs.com/hacker-linner/citus-microsvc-ping:1.0.0

建立一些使用者

curl -X POST -H "Content-Type: application/json" -d '[
  {"name": "John Doe", "email": "john@example.com"},
  {"name": "Jane Smith", "email": "jane@example.com"},
  {"name": "Mike Johnson", "email": "mike@example.com"},
  {"name": "Emily Davis", "email": "emily@example.com"},
  {"name": "David Wilson", "email": "david@example.com"},
  {"name": "Sarah Thompson", "email": "sarah@example.com"},
  {"name": "Alex Miller", "email": "alex@example.com"},
  {"name": "Olivia Anderson", "email": "olivia@example.com"},
  {"name": "Daniel Martin", "email": "daniel@example.com"},
  {"name": "Sophia White", "email": "sophia@example.com"}
]' http://localhost:6000/users
{"message":"Users created successfully","user_ids":[1,2,3,4,5,6,7,8,9,10]}

列出已建立的使用者

curl http://localhost:6000/users

獲取當前時間

curl http://localhost:6001/current_time
{"current_time":"2023-12-25 06:19:28","ip_address":"192.168.65.1"}

baidu.com 執行 ping 命令:

curl -X POST -H "Content-Type: application/json" -d '{"host": "baidu.com"}' http://localhost:6002/ping
{"host":"baidu.com","result":"PING baidu.com (110.242.68.66): 56 data bytes\n64 bytes from 110.242.68.66: icmp_seq=0 ttl=63 time=56.996 ms\n64 bytes from 110.242.68.66: icmp_seq=1 ttl=63 time=84.375 ms\n64 bytes from 110.242.68.66: icmp_seq=2 ttl=63 time=99.899 ms\n64 bytes from 110.242.68.66: icmp_seq=3 ttl=63 time=130.946 ms\n--- baidu.com ping statistics ---\n4 packets transmitted, 4 packets received, 0% packet loss\nround-trip min/avg/max/stddev = 56.996/93.054/130.946/26.731 ms\n"}

探索資料庫

現在我們呼叫了一些 API 函式,資料已經儲存,我們可以檢查 citus_schemas 是否反映了我們所期望的:

docker exec -it citus_master psql -U postgres
select * from citus_schemas;
 schema_name  | colocation_id | schema_size | schema_owner 
--------------+---------------+-------------+--------------
 user_service |             1 | 32 kB       | user_service
 time_service |             2 | 32 kB       | time_service
 ping_service |             3 | 32 kB       | ping_service
(3 rows)

當我們建立模式時,我們沒有告訴 citus 在哪些 worker 機器上建立 schema。它已經自動為我們完成了這些。我們可以透過下面的查詢檢視每個 schema 所在的位置:

select nodename,nodeport, table_name, pg_size_pretty(sum(shard_size))
from citus_shards
group by nodename,nodeport, table_name;
    nodename    | nodeport |         table_name         | pg_size_pretty
----------------+----------+----------------------------+----------------
 citus-worker-1 |     5432 | ping_service.ping_results  | 32 kB
 citus-worker-2 |     5432 | user_service.users         | 32 kB
 citus-worker-3 |     5432 | time_service.query_details | 32 kB
(3 rows)

OK! 我們可以看到,各個微服務的後端儲存已經預設被分配到不同的 worker 機器節點了。

相關文章