首页 > 数据库 > 数据库开发技术 > 高可用: 体验使用Odyssey连接池(一)

关于 Odyssey ,它是一种相对高级的多线程 PostgreSQL 连接池。 内部采用异步的多线程体系结构,使用事件驱动的异步方式来实现 内部逻辑,但是对外提供的接口看起来仍像是同步的过程方式,而没有采用回调的方式来实现,便于开发人员使用。
从支持程度来讲,它的多线程方式比pgbouncer似乎更胜一筹,单个应用能更好的利用CPU,并且可以支持更大规模的并发。
最近在总结PG的一些连接池的使用时,体验了一把Odyssey(奥德赛)连接池。顺便说一句,俄国的工程师水平还是相当不错的。可是看到网上的文档极少,除了github上有些不算很细致的介绍以外。就萌生了写下这篇短文的想法。
基本架构是下图这样的:
Instance: 应用程序的入口点,用于系统初始化,读取配置文件,准备日志收集,解析命令行选项,启动System线程和worker线程。
System: 用于启动Router, cron和console子系统。
router是核心组件,用于处理客户端的注册和转发客户端的请求。它会根据到来的请求,进行连接信息匹配,然后dispatch到worker_pool中的工作线程里头。同时,确保连接的限制和客户端池的排除是有效的。有处理隐含的cancel请求,因为到服务器池的请求必须要与一个client key相匹配。
Router 是以典型的请求-应答模式工作的:客户端(来自worker线程)发送一个请求消息到router, 然后等待答复。这有可能会成为一个“热点”(但目前来看还不是问题)。
Cron: 周期性的服务组件,例如,用来周期性检查连接是否过期,数据库配置是否过时等等
Worker和Worker池: Worker线程主是是监听连接队列上的消息,如果有新的连接事件,它会创建一个前端的例程,并处理整个例程的整个生命周期。值得一提的是,每个worker线程,可以处理几千个客户端例程。(这是事件模式的优点,不是?) 而worker池则用于维护一个worker线程的线程池。相当于所有的worker线程都是从池中生成和取出的。便于统一管理。
我们试一下CentOS7.9下边的PostgreSQL14.7,里头安装Odyssey连接池。
1)、获取源码并编译
它的基本依赖库(包),有cmake3 (注:必须是3)、openssl、以及postgresql-server-dev。
1sudo yum install cmake3.x86_64
2sudo ln -s /usr/bin/cmake3 /usr/bin/cmake
3
4git clone https://github.com/yandex/odyssey
5
6[05:16:31-postgres@centos1:/pgccc/soft] cd odyssey
7[05:16:31-postgres@centos1:/pgccc/soft/odyssey]
8
9mkdir -p build
10cd build
11[05:16:31-postgres@centos1:/pgccc/soft/odyssey/build]
12
13cmake -DCMAKE_BUILD_TYPE=Release ..
14make
15
16[05:16:06-postgres@centos1:/pgccc/soft/odyssey/build]$ make
17[ 0%] Built target libkiwi
18[ 1%] Built target libmachinarium
19[ 1%] Built target build_libs
20[ 41%] Built target odyssey
21[ 98%] Built target odyssey_test
22[100%] Built target odyssey_stress
2)、在编译上述包以后,相应的可执行文件都不在系统路径里头,我们需要进相应的复制以完成安装。
1# root用户
2sudo -s
3[05:20:49-root@centos1:/pgccc/soft/odyssey/build]$ mkdir -p /usr/odyssey/{bin, conf}
4[05:25:24-root@centos1:/pgccc/soft/odyssey/build]$ cp -pv ../config-examples/odyssey-dev.conf /usr/odyssey/conf
5‘../odyssey-dev.conf’ -> ‘/usr/odyssey/conf/odyssey-dev.conf’
6[05:26:12-root@centos1:/pgccc/soft/odyssey/build]$ cp -r ../scripts /usr/odyssey/
7
8#建一个链接到基本路径
9[05:27:18-root@centos1:/pgccc/soft/odyssey/build]$ ln -v -s /usr/odyssey/bin/odyssey /usr/bin/odyssey
10‘/usr/bin/odyssey’ -> ‘/usr/odyssey/bin/odyssey’
11
12#看看基本结构
13[05:26:47-root@centos1:/pgccc/soft/odyssey/build]$ tree /usr/odyssey
14/usr/odyssey
15├── bin
16│ └── odyssey
17├── conf
18│ └── odyssey-dev.conf
19└── scripts
20 ├── centos
21 │ └── odyssey.spec
22 ├── debian
23 │ ├── changelog
24 │ ├── config
25 │ ├── control
26 │ ├── init
27 │ ├── install
28 │ ├── logrotate
29 │ └── rules
30 ├── install_ci.sh
31 ├── pgsql_benchmark.sh
32 ├── pgsql_check.sh
33 ├── pgsql_pgbouncer_benchmark.sh
34 └── systemd
35 ├── odyssey.service
36 └── odyssey@.service
37
38#最后将相应目录权限完全赋给postgres用户
39chown -R postgres:postgres /usr/odyssey
3)、试试启动odyssey连接池进程
1# 使用postgres用户, 以后台进程的方式启动一下
2nohup odyssey /usr/odyssey/conf/odyssey-dev.conf &
3
4[05:49:04-postgres@centos1:/pgccc/soft/odyssey]$ ps -ef | grep odyssey | grep -v grep
5postgres 22378 22025 0 05:48 pts/2 00:00:00 odyssey: version 2060 listening and accepting new connections
我们能看到它在监听2060端口。
这个时候,因为我们没有对配置文件做任何修改,所以,还是无法通过连接odyssey连接池来连接到后端的数据库。
打开配置文件:/usr/odyssey/conf/odyssey-dev.conf, 修改并加入数据库连接相关环节。我在下边修改的部分前边加了提示:
1pid_file "/tmp/odyssey.pid"
2#updated. 设置为no daemonize,这样方便调试
3daemonize no
4
5#updated. 修改socket dir, 与PG保持一样
6unix_socket_dir "/var/run/postgresql"
7unix_socket_mode "0644"
8
9log_format "%p %t %l [%i %s] (%c) %m\n"
10#updated. 让日志信息输出到屏幕
11log_to_stdout yes
12
13log_syslog no
14log_syslog_ident "odyssey"
15log_syslog_facility "daemon"
16
17log_debug yes
18log_config yes
19log_session yes
20log_query yes
21log_stats yes
22stats_interval 60
23log_general_stats_prom yes
24log_route_stats_prom no
25promhttp_server_port 7777
26
27workers "auto"
28resolvers 1
29
30readahead 8192
31
32cache_coroutine 0
33
34coroutine_stack_size 16
35
36nodelay yes
37
38keepalive 15
39keepalive_keep_interval 75
40keepalive_probes 9
41
42keepalive_usr_timeout 0
43
44listen {
45 host "*"
46 port 6432
47 backlog 128
48 compression yes
49 tls "disable"
50}
51
52
53storage "postgres_server" {
54 type "remote"
55# host "[localhost]:5555,localhost"
56#updated. 要想支持本地连接,只用127.0.0.1
57 host "127.0.0.1"
58 port 5555
59 target_session_attrs "read-write"
60}
61
62#updated. 完整的数据库连接信息
63#因为auth是"none", 所以不用提供password这一部分信息
64database "mydb" {
65 user "mydb" {
66 authentication "none"
67 storage "postgres_server"
68
69 pool "session"
70 server_lifetime 3600
71
72 log_debug yes
73 pool_discard yes
74
75 quantiles "0.99,0.95,0.5"
76 client_max 1007
77 }
78}
79
80database "db1" {
81 user "user1" {
82 authentication "none"
83
84 storage "postgres_server"
85
86 pool "transaction"
87 pool_ttl 10000
88 server_lifetime 10000
89 pool_size 10
90
91 log_debug yes
92 pool_discard no
93 pool_smart_discard yes
94 pool_reserve_prepared_statement yes
95
96 quantiles "0.99,0.95,0.5"
97 client_max 1007
98 }
99}
100
101database default {
102 user default {
103 authentication "none"
104
105 storage "postgres_server"
106
107 pool "session"
108 pool_size 0
109
110 pool_timeout 0
111
112 pool_ttl 1201
113
114 pool_discard no
115
116 pool_cancel yes
117
118 pool_rollback yes
119# seconds
120 pool_client_idle_timeout 20
121# seconds
122 pool_idle_in_transaction_timeout 20
123
124 client_fwd_error yes
125 application_name_add_host yes
126 server_lifetime 1901
127 log_debug no
128
129 quantiles "0.99,0.95,0.5"
130 client_max 107
131 }
132}
133database "postgres" {
134 user "user_aq_internal_pooling" {
135 authentication "none"
136
137 storage "postgres_server"
138
139 pool "session"
140 storage_user "reshke"
141 storage_db "postgres"
142
143 log_debug yes
144 pool_discard yes
145 pool_routing "internal"
146
147 quantiles "0.99,0.95,0.5"
148 client_max 107
149 }
150
151 user "userstmt" {
152 authentication "none"
153
154 storage "postgres_server"
155 storage_password "1"
156 pool "statement"
157 pool_size 1
158
159 pool_timeout 0
160 pool_ttl 60
161 pool_discard no
162 pool_cancel yes
163 pool_rollback yes
164
165 client_fwd_error yes
166
167 application_name_add_host yes
168 reserve_session_server_connection no
169 server_lifetime 3600
170 log_debug no
171
172 quantiles "0.99,0.95,0.5"
173 client_max 107
174 }
175
176 user "useropt" {
177 authentication "none"
178
179 storage "postgres_server"
180 storage_password "1"
181 pool "statement"
182 pool_size 1
183
184 pool_timeout 0
185 pool_ttl 60
186 pool_discard no
187 pool_cancel yes
188 pool_rollback yes
189
190 client_fwd_error yes
191
192 application_name_add_host yes
193 reserve_session_server_connection no
194 server_lifetime 3600
195 log_debug no
196
197 quantiles "0.99,0.95,0.5"
198 client_max 107
199
200# this overrides anything passed by
201# PGOPTIONS from client
202 options {
203 "statement_timeout" "0"
204 }
205 }
206
207 user default {
208 authentication "none"
209 storage "postgres_server"
210 pool "session"
211 }
212}
213storage "local" {
214 type "local"
215}
216
217database "console" {
218 user default {
219 authentication "none"
220 role "admin"
221 pool "session"
222 storage "local"
223 }
224}
225
226
227locks_dir "/tmp/odyssey"
228
229graceful_die_on_errors yes
230enable_online_restart no
231bindwith_reuseport yes
值得一提的是:
1storage "postgres_server" {
2 type "remote"
3# host "[localhost]:5555,localhost"
4#updated. 要想支持本地连接,只用127.0.0.1
5 host "127.0.0.1"
6 port 5555
7 target_session_attrs "read-write"
8}
9
10#updated. 完整的数据库连接信息
11#因为auth是"none", 所以不用提供password这一部分信息
12database "mydb" {
13 user "mydb" {
14 authentication "none"
15 storage "postgres_server"
16
17 pool "session"
18 server_lifetime 3600
19
20 log_debug yes
21 pool_discard yes
22
23 quantiles "0.99,0.95,0.5"
24 client_max 1007
25 }
26}
storage "postgres_server"这一部分,
target_session_attrs 的值,针对单机单实例的PG而言,只能用:"read-write"。你如果填的是"read-only",它认为你需要访问的是standby结点。这就是两码事。
host "127.0.0.1", 访问本机,如果你用"localhost", 也会出错。这里统一采用"127.0.0.1"。到时候在pg_hba.conf里头对应起来就行。
database "mydb",是你真正要访问的后端数据库的相关信息。odyssey会根据你连接串中的信息遍历所有的database进行匹配,然后进去访问验证。相当于一个防火墙配置。
因为我本机启动的是5555端口,所以这里需要调整一下. 重启odyssey以后,看看log:
1[06:45:13-postgres@centos1:/usr/odyssey/conf]$ nohup odyssey odyssey-dev.conf &
2[1] 39856
看看相关日志:
1[06:00:22-postgres@centos1:/pgccc/soft/odyssey]$ tail -f nohup.out
222526 2023-05-09T22:00:20Z info [none none] (rules) storage postgres_server
322526 2023-05-09T22:00:20Z info [none none] (rules) type remote
422526 2023-05-09T22:00:20Z info [none none] (rules) host [localhost]:5555,centos1,[192.168.0.6]:5555
522526 2023-05-09T22:00:20Z info [none none] (rules) port 5555
622526 2023-05-09T22:00:20Z info [none none] (rules) log_debug no
722526 2023-05-09T22:00:20Z info [none none] (rules) log_query no
822526 2023-05-09T22:00:20Z info [none none] (rules) options: todo
922526 2023-05-09T22:00:20Z info [none none] (rules)
1022526 2023-05-09T22:00:20Z info [none none] (server) listening on 0.0.0.0:6432
1122526 2023-05-09T22:00:20Z info [none none] (server) listening on [::]:6432
我们在本地数据库里准备一下mydb:
1[06:47:32-postgres@centos1:/var/lib/pgsql/14/data]$ psql
2psql (14.7)
3Type "help" for help.
4
5postgres=# create database mydb;
6CREATE DATABASE
7postgres=# create user mydb with password 'test123';
8CREATE ROLE
9postgres=# grant all privileges on database mydb to mydb;
10GRANT
11
12postgres=# select usename, passwd from pg_shadow where usename = 'mydb';
13 usename | passwd
14
15-------------
16---------
17 mydb | SCRAM-SHA-256$4096:nrsSRLtpBRvwipSIDJ5ZQw==$hYAEW/Qn7Z8hxPt4lstHOPerzKFJQqoTTMViYnE4xMU=:MJsYAH3clQm8gIg4SAfiPRnhxC8KqsXCdF4K
18MtLhR2Q=
19(1 row)
20
21-- 顺便看一下防火墙配置,默认应该是scram-sha-256
22postgres=# \! cat 14/data/pg_hba.conf
23cat: 14/data/pg_hba.conf: No such file or directory
24postgres=# \! cat /var/lib/pgsql/14/data/pg_hba.conf
25local all all trust
26host all all 127.0.0.1/32 trust
27host all all samenet scram-sha-256
28host all all ::1/128 trust
29local replication all trust
30host replication all 127.0.0.1/32 trust
31host replication all samenet scram-sha-256
32host replication all ::1/128 trust
33
34--确认一下password_encryption
35postgres=# show password_encryption ;
36 password_encryption
37---------------------
38 scram-sha-256
39(1 row)
我们尝试着连一下:
1[06:44:44-postgres@centos1:/var/lib/pgsql/14/data]$ psql -h localhost -p 6432 -U mydb
2psql (14.7)
3Type "help" for help.
4
5mydb=> \d
6Did not find any relations.
7mydb=> create table t123(id int);
8CREATE TABLE
9mydb=> insert into t123 values(1);
10INSERT 0 1
11mydb=> select * from t123;
12 id
13----
14 1
15(1 row)
至此,这个本机机器上访问本机的连接池基本配置验证完毕。
后续,我们再进一步介绍,如何连接远程数据库,并带有相关验证方法,如: "md5", "scram-sha-256"等。同一个odyssey进程是可以配置访问很多不同的数据库的。甚至配备连接多个不同的主机。功能还是非常强大的。
甚至,如果在熟悉配置项如何配置的情况下,感觉它比pgbouncer还来得简单。
TODO: 介绍Odyssey连接池支持远程DB host, 支持scram-sha-256验证方式;同时支持读、读-写模式;基于Session和基于Transaction的区别。
[1] https://github.com/yandex/odyssey
[2] https://github.com/yandex/odyssey/blob/master/documentation/internals.md
原文作者:数据库杂记;如有侵权,请联系管理员删除。