|
1 | 1 | # Postgresql Storage |
2 | 2 |
|
| 3 | +# 一、这是什么 |
| 4 | +以PostgreSQL为存储引擎的[Storage](https://github.com/storage-lock/go-storage)实现,当前仓库为比较底层的存储层实现,你可以与[storage-lock](https://github.com/storage-lock/go-storage-lock)结合使用,或者这个项目[PostgreSQL-locks](https://github.com/storage-lock/go-postgresql-storage)里专门封装提供了一些PostgreSQL锁相关的更易用友好的API。 |
| 5 | + |
| 6 | + |
| 7 | +# 二、安装依赖 |
3 | 8 |
|
4 | 9 | ```bash |
5 | 10 | go get -u github.com/storage-lock/go-postgresql-storage |
6 | 11 | ``` |
7 | 12 |
|
| 13 | +# 三、API Examples |
8 | 14 |
|
| 15 | +## 3.1 从DSN创建PostgreSQLStorage |
9 | 16 |
|
10 | | -## 3.2 Postgresql |
11 | | - |
12 | | -### 3.2.1 快速开始 |
| 17 | +在Golang的世界中连接数据库最常见的就是DSN,下面的例子演示了如何从一个DSN创建PostgreSQLStorage: |
13 | 18 |
|
14 | 19 | ```go |
15 | 20 | package main |
16 | 21 |
|
17 | 22 | import ( |
18 | 23 | "context" |
19 | 24 | "fmt" |
20 | | - storage_lock "github.com/storage-lock/go-storage-lock" |
21 | | - "strings" |
22 | | - "sync" |
23 | | - "time" |
| 25 | + |
| 26 | + postgresql_storage "github.com/storage-lock/go-postgresql-storage" |
24 | 27 | ) |
25 | 28 |
|
26 | 29 | func main() { |
27 | 30 |
|
28 | | - // Docker启动Postgresql: |
29 | | - // docker run -d --name storage-lock-postgres -p 5432:5432 -e POSTGRES_PASSWORD=UeGqAm8CxYGldMDLoNNt postgres:14 |
| 31 | + // 使用一个DSN形式的数据库连接字符串创建ConnectionManager |
| 32 | + testDsn := "host=127.0.0.1 user=postgres password=UeGqAm8CxYGldMDLoNNt port=5432 dbname=postgres sslmode=disable" |
| 33 | + connectionManager := postgresql_storage.NewPostgreSQLConnectionGetterFromDSN(testDsn) |
30 | 34 |
|
31 | | - // DSN的写法参考驱动的支持:https://github.com/lib/pq |
32 | | - dsn := "host=192.168.128.206 user=postgres password=UeGqAm8CxYGldMDLoNNt port=5432 dbname=postgres sslmode=disable" |
33 | | - |
34 | | - // 这个是最为重要的,通常是要锁住的资源的名称 |
35 | | - lockId := "must-serial-operation-resource-foo" |
36 | | - |
37 | | - // 第一步创建一把分布式锁 |
38 | | - lock, err := storage_lock.NewPostgreSQLStorageLock(context.Background(), lockId, dsn) |
| 35 | + // 然后从这个ConnectionManager创建PostgreSQL Storage |
| 36 | + options := postgresql_storage.NewPostgreSQLStorageOptions().SetConnectionManager(connectionManager) |
| 37 | + storage, err := postgresql_storage.NewPostgreSQLStorage(context.Background(), options) |
39 | 38 | if err != nil { |
40 | | - fmt.Printf("[ %s ] Create Lock Failed: %v\n", time.Now().Format("2006-01-02 15:04:05"), err) |
41 | | - return |
| 39 | + panic(err) |
42 | 40 | } |
| 41 | + fmt.Println(storage.GetName()) |
| 42 | + |
| 43 | +} |
| 44 | + |
| 45 | +``` |
| 46 | + |
| 47 | + |
| 48 | +## 3.2 从连接属性(ip、端口、用户名、密码等等)中创建PostgreSQLStorage |
| 49 | + |
| 50 | +或者你的配置文件中存放的并不是DSN,而是零散的几个连接属性,下面是一个创建PostgreSQLStorage的例子: |
| 51 | + |
| 52 | +```go |
| 53 | +package main |
43 | 54 |
|
44 | | - // 第二步使用这把锁,这里就模拟多个节点竞争执行的情况,他们会线程安全的往resource里写数据 |
45 | | - resource := strings.Builder{} |
46 | | - var wg sync.WaitGroup |
47 | | - for i := 0; i < 10; i++ { |
48 | | - workerId := fmt.Sprintf("worker-%d", i) |
49 | | - wg.Add(1) |
50 | | - go func() { |
51 | | - defer wg.Done() |
52 | | - |
53 | | - // 获取锁 |
54 | | - err := lock.Lock(context.Background(), workerId) |
55 | | - if err != nil { |
56 | | - fmt.Printf("[ %s ] workerId = %s, lock failed: %v \n", time.Now().Format("2006-01-02 15:04:05"), workerId, err) |
57 | | - return |
58 | | - } |
59 | | - // 退出的时候释放锁 |
60 | | - defer func() { |
61 | | - err := lock.UnLock(context.Background(), workerId) |
62 | | - if err != nil { |
63 | | - fmt.Printf("[ %s ] workerId = %s, unlock failed: %v \n", time.Now().Format("2006-01-02 15:04:05"), workerId, err) |
64 | | - return |
65 | | - } |
66 | | - }() |
67 | | - |
68 | | - // 假装有耗时的操作 |
69 | | - fmt.Printf("[ %s ] workerId = %s, begin write resource \n", time.Now().Format("2006-01-02 15:04:05"), workerId) |
70 | | - time.Sleep(time.Second * 3) |
71 | | - // 接下来是操作竞态资源 |
72 | | - resource.WriteString(workerId) |
73 | | - fmt.Printf("[ %s ] workerId = %s, write resource done \n", time.Now().Format("2006-01-02 15:04:05"), workerId) |
74 | | - resource.WriteString("\n") |
75 | | - |
76 | | - }() |
| 55 | +import ( |
| 56 | + "context" |
| 57 | + "fmt" |
| 58 | + |
| 59 | + postgresql_storage "github.com/storage-lock/go-postgresql-storage" |
| 60 | +) |
| 61 | + |
| 62 | +func main() { |
| 63 | + |
| 64 | + // 使用一个DSN形式的数据库连接字符串创建ConnectionManager |
| 65 | + host := "127.0.0.1" |
| 66 | + port := uint(5432) |
| 67 | + username := "postgres" |
| 68 | + passwd := "UeGqAm8CxYGldMDLoNNt" |
| 69 | + database := "postgres" |
| 70 | + connectionManager := postgresql_storage.NewPostgreSQLConnectionGetter(host, port, username, passwd, database) |
| 71 | + |
| 72 | + // 然后从这个ConnectionManager创建PostgreSQL Storage |
| 73 | + options := postgresql_storage.NewPostgreSQLStorageOptions().SetConnectionManager(connectionManager) |
| 74 | + storage, err := postgresql_storage.NewPostgreSQLStorage(context.Background(), options) |
| 75 | + if err != nil { |
| 76 | + panic(err) |
77 | 77 | } |
78 | | - wg.Wait() |
79 | | - |
80 | | - // 观察最终的输出是否和日志一致 |
81 | | - fmt.Printf("[ %s ] Resource: \n", time.Now().Format("2006-01-02 15:04:05")) |
82 | | - fmt.Println(resource.String()) |
83 | | - |
84 | | - // Output: |
85 | | - // [ 2023-03-13 00:29:37 ] workerId = worker-3, begin write resource |
86 | | - // [ 2023-03-13 00:29:40 ] workerId = worker-3, write resource done |
87 | | - // [ 2023-03-13 00:29:40 ] workerId = worker-5, begin write resource |
88 | | - // [ 2023-03-13 00:29:43 ] workerId = worker-5, write resource done |
89 | | - // [ 2023-03-13 00:29:43 ] workerId = worker-8, begin write resource |
90 | | - // [ 2023-03-13 00:29:46 ] workerId = worker-8, write resource done |
91 | | - // [ 2023-03-13 00:29:46 ] workerId = worker-6, begin write resource |
92 | | - // [ 2023-03-13 00:29:49 ] workerId = worker-6, write resource done |
93 | | - // [ 2023-03-13 00:29:50 ] workerId = worker-2, begin write resource |
94 | | - // [ 2023-03-13 00:29:53 ] workerId = worker-2, write resource done |
95 | | - // [ 2023-03-13 00:29:56 ] workerId = worker-0, begin write resource |
96 | | - // [ 2023-03-13 00:29:59 ] workerId = worker-0, write resource done |
97 | | - // [ 2023-03-13 00:30:00 ] workerId = worker-1, begin write resource |
98 | | - // [ 2023-03-13 00:30:03 ] workerId = worker-1, write resource done |
99 | | - // [ 2023-03-13 00:30:04 ] workerId = worker-4, begin write resource |
100 | | - // [ 2023-03-13 00:30:07 ] workerId = worker-4, write resource done |
101 | | - // [ 2023-03-13 00:30:08 ] workerId = worker-9, begin write resource |
102 | | - // [ 2023-03-13 00:30:11 ] workerId = worker-9, write resource done |
103 | | - // [ 2023-03-13 00:30:14 ] workerId = worker-7, begin write resource |
104 | | - // [ 2023-03-13 00:30:18 ] workerId = worker-7, write resource done |
105 | | - // [ 2023-03-13 00:30:18 ] Resource: |
106 | | - // worker-3 |
107 | | - // worker-5 |
108 | | - // worker-8 |
109 | | - // worker-6 |
110 | | - // worker-2 |
111 | | - // worker-0 |
112 | | - // worker-1 |
113 | | - // worker-4 |
114 | | - // worker-9 |
115 | | - // worker-7 |
| 78 | + fmt.Println(storage.GetName()) |
116 | 79 |
|
117 | 80 | } |
118 | 81 |
|
119 | 82 | ``` |
120 | 83 |
|
121 | | -### 3.2.2 详细配置 |
| 84 | + |
| 85 | +## 3.3 从sql.DB创建PostgreSQLStorage |
| 86 | + |
| 87 | +或者现在你已经有从其它渠道创建的能够连接到PostgreSQL的sql.DB,则也可以从这个*sql.DB创建PostgreSQLStorage |
122 | 88 |
|
123 | 89 | ```go |
124 | 90 | package main |
125 | 91 |
|
126 | 92 | import ( |
127 | 93 | "context" |
| 94 | + "database/sql" |
128 | 95 | "fmt" |
129 | | - storage_lock "github.com/storage-lock/go-storage-lock" |
130 | | - "strings" |
131 | | - "sync" |
132 | | - "time" |
| 96 | + |
| 97 | + "github.com/storage-lock/go-storage" |
| 98 | + |
| 99 | + postgresql_storage "github.com/storage-lock/go-postgresql-storage" |
133 | 100 | ) |
134 | 101 |
|
135 | 102 | func main() { |
136 | 103 |
|
137 | | - // Docker启动Postgresql: |
138 | | - // docker run -d --name storage-lock-postgres -p 5432:5432 -e POSTGRES_PASSWORD=UeGqAm8CxYGldMDLoNNt postgres:14 |
139 | | - |
140 | | - // DSN的写法参考驱动的支持:https://github.com/lib/pq |
141 | | - dsn := "host=192.168.128.206 user=postgres password=UeGqAm8CxYGldMDLoNNt port=5432 dbname=postgres sslmode=disable" |
142 | | - |
143 | | - // 第一步先配置存储介质相关的参数,包括如何连接到这个数据库,连接上去之后锁的信息存储到哪里等等 |
144 | | - // 配置如何连接到数据库 |
145 | | - connectionGetter := storage_lock.NewPostgreSQLStorageConnectionGetterFromDSN(dsn) |
146 | | - storageOptions := &storage_lock.PostgreSQLStorageOptions{ |
147 | | - // 数据库连接获取方式,可以使用内置的从DSN获取连接,也可以自己实现接口决定如何连接 |
148 | | - ConnectionGetter: connectionGetter, |
149 | | - // 选择锁信息存放在哪个schema下,默认为public |
150 | | - Schema: "public", |
151 | | - // 锁的信息是存储在哪张表中的,不设置的话默认为storage_lock |
152 | | - TableName: "storage_lock_table", |
153 | | - } |
154 | | - storage, err := storage_lock.NewPostgreSQLStorage(context.Background(), storageOptions) |
| 104 | + // 使用一个DSN形式的数据库连接字符串创建ConnectionManager |
| 105 | + testDsn := "host=127.0.0.1 user=postgres password=UeGqAm8CxYGldMDLoNNt port=5432 dbname=postgres sslmode=disable" |
| 106 | + db, err := sql.Open("postgres", testDsn) |
155 | 107 | if err != nil { |
156 | | - fmt.Println("Create Storage Failed: " + err.Error()) |
157 | | - return |
| 108 | + panic(err) |
158 | 109 | } |
| 110 | + connectionManager := storage.NewFixedSqlDBConnectionManager(db) |
159 | 111 |
|
160 | | - // 第二步配置锁的参数,在上面创建的Storage的上创建一把锁 |
161 | | - lockOptions := &storage_lock.StorageLockOptions{ |
162 | | - // 这个是最为重要的,通常是要锁住的资源的名称 |
163 | | - LockId: "must-serial-operation-resource-foo", |
164 | | - LeaseExpireAfter: time.Second * 30, |
165 | | - LeaseRefreshInterval: time.Second * 5, |
166 | | - VersionMissRetryTimes: 3, |
167 | | - } |
168 | | - lock := storage_lock.NewStorageLock(storage, lockOptions) |
169 | | - |
170 | | - // 第三步开始使用锁,模拟多个节点竞争同一个锁使用的情况 |
171 | | - resource := strings.Builder{} |
172 | | - var wg sync.WaitGroup |
173 | | - for i := 0; i < 10; i++ { |
174 | | - workerId := fmt.Sprintf("worker-%d", i) |
175 | | - wg.Add(1) |
176 | | - go func() { |
177 | | - defer wg.Done() |
178 | | - |
179 | | - // 获取锁 |
180 | | - err := lock.Lock(context.Background(), workerId) |
181 | | - if err != nil { |
182 | | - fmt.Printf("[ %s ] workerId = %s, lock failed: %v \n", time.Now().Format("2006-01-02 15:04:05"), workerId, err) |
183 | | - return |
184 | | - } |
185 | | - // 退出的时候释放锁 |
186 | | - defer func() { |
187 | | - err := lock.UnLock(context.Background(), workerId) |
188 | | - if err != nil { |
189 | | - fmt.Printf("[ %s ] workerId = %s, unlock failed: %v \n", time.Now().Format("2006-01-02 15:04:05"), workerId, err) |
190 | | - return |
191 | | - } |
192 | | - }() |
193 | | - |
194 | | - // 假装有耗时的操作 |
195 | | - fmt.Printf("[ %s ] workerId = %s, begin write resource \n", time.Now().Format("2006-01-02 15:04:05"), workerId) |
196 | | - time.Sleep(time.Second * 3) |
197 | | - // 接下来是操作竞态资源 |
198 | | - resource.WriteString(workerId) |
199 | | - fmt.Printf("[ %s ] workerId = %s, write resource done \n", time.Now().Format("2006-01-02 15:04:05"), workerId) |
200 | | - resource.WriteString("\n") |
201 | | - |
202 | | - }() |
| 112 | + // 然后从这个ConnectionManager创建PostgreSQL Storage |
| 113 | + options := postgresql_storage.NewPostgreSQLStorageOptions().SetConnectionManager(connectionManager) |
| 114 | + storage, err := postgresql_storage.NewPostgreSQLStorage(context.Background(), options) |
| 115 | + if err != nil { |
| 116 | + panic(err) |
203 | 117 | } |
204 | | - wg.Wait() |
205 | | - |
206 | | - // 观察最终的输出是否和日志一致 |
207 | | - fmt.Printf("[ %s ] Resource: \n", time.Now().Format("2006-01-02 15:04:05")) |
208 | | - fmt.Println(resource.String()) |
209 | | - |
210 | | - // Output: |
211 | | - // [ 2023-03-13 00:33:38 ] workerId = worker-0, begin write resource |
212 | | - // [ 2023-03-13 00:33:41 ] workerId = worker-0, write resource done |
213 | | - // [ 2023-03-13 00:33:42 ] workerId = worker-3, begin write resource |
214 | | - // [ 2023-03-13 00:33:45 ] workerId = worker-3, write resource done |
215 | | - // [ 2023-03-13 00:33:45 ] workerId = worker-6, begin write resource |
216 | | - // [ 2023-03-13 00:33:48 ] workerId = worker-6, write resource done |
217 | | - // [ 2023-03-13 00:33:49 ] workerId = worker-5, begin write resource |
218 | | - // [ 2023-03-13 00:33:52 ] workerId = worker-5, write resource done |
219 | | - // [ 2023-03-13 00:33:53 ] workerId = worker-2, begin write resource |
220 | | - // [ 2023-03-13 00:33:56 ] workerId = worker-2, write resource done |
221 | | - // [ 2023-03-13 00:33:57 ] workerId = worker-8, begin write resource |
222 | | - // [ 2023-03-13 00:34:00 ] workerId = worker-8, write resource done |
223 | | - // [ 2023-03-13 00:34:01 ] workerId = worker-4, begin write resource |
224 | | - // [ 2023-03-13 00:34:04 ] workerId = worker-4, write resource done |
225 | | - // [ 2023-03-13 00:34:04 ] workerId = worker-1, begin write resource |
226 | | - // [ 2023-03-13 00:34:07 ] workerId = worker-1, write resource done |
227 | | - // [ 2023-03-13 00:34:08 ] workerId = worker-9, begin write resource |
228 | | - // [ 2023-03-13 00:34:11 ] workerId = worker-9, write resource done |
229 | | - // [ 2023-03-13 00:34:11 ] workerId = worker-7, begin write resource |
230 | | - // [ 2023-03-13 00:34:14 ] workerId = worker-7, write resource done |
231 | | - // [ 2023-03-13 00:34:14 ] Resource: |
232 | | - // worker-0 |
233 | | - // worker-3 |
234 | | - // worker-6 |
235 | | - // worker-5 |
236 | | - // worker-2 |
237 | | - // worker-8 |
238 | | - // worker-4 |
239 | | - // worker-1 |
240 | | - // worker-9 |
241 | | - // worker-7 |
| 118 | + fmt.Println(storage.GetName()) |
242 | 119 |
|
243 | 120 | } |
244 | 121 |
|
|
0 commit comments