jetstream-api 0.4.3__tar.gz → 0.4.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/PKG-INFO +134 -8
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/README.md +133 -7
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/__init__.py +1 -1
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/admin.py +116 -19
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/common.py +13 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/msg.py +7 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/properties.py +114 -21
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/qmgr.py +272 -21
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/jetstream_api.egg-info/PKG-INFO +134 -8
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/pyproject.toml +1 -1
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/LICENSE.txt +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/admintopic.py +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/connectivity.py +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/exception.py +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/pattern.py +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/producer.py +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/py.typed +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/topic.py +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/ilink/util.py +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/jetstream_api.egg-info/SOURCES.txt +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/jetstream_api.egg-info/dependency_links.txt +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/jetstream_api.egg-info/requires.txt +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/jetstream_api.egg-info/top_level.txt +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/setup.cfg +0 -0
- {jetstream_api-0.4.3 → jetstream_api-0.4.5}/tests/test_example.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jetstream-api
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.5
|
|
4
4
|
Summary: Python client API for iLink M.O.M. (Message Oriented Middleware)
|
|
5
5
|
Author: snowjeans
|
|
6
6
|
License: Boost Software License - Version 1.0 - August 17th, 2003
|
|
@@ -137,15 +137,141 @@ print(adm.getTopicList())
|
|
|
137
137
|
클러스터 큐 관리자에 접속하면 후보 주소를 캐싱해 두었다가, 리더가 바뀌어도 따라갑니다.
|
|
138
138
|
|
|
139
139
|
```python
|
|
140
|
-
qmgr.
|
|
141
|
-
print(qmgr.get_cluster_addresses())
|
|
142
|
-
qmgr.reconnect()
|
|
140
|
+
qmgr.connect("10.0.0.1", 5000, "APP", True) # 주소 하나면 됩니다
|
|
141
|
+
print(qmgr.get_cluster_addresses()) # ['10.0.0.1:5000', '10.0.0.2:5000']
|
|
142
|
+
qmgr.reconnect() # 새 리더를 찾아 재접속
|
|
143
|
+
|
|
144
|
+
# 첫 접속 시점의 장애까지 대비하려면 목록으로 (포트 인자 없음)
|
|
145
|
+
qmgr.connect("10.0.0.1:5000,10.0.0.2:5000", "APP", True)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
접속에 성공하면 핸드셰이크 직후 나머지 노드 주소를 서버에서 받아 캐싱하므로
|
|
149
|
+
**주소는 하나만 주면 됩니다.** 목록은 첫 접속 시점의 장애까지 대비할 때 씁니다.
|
|
150
|
+
|
|
151
|
+
### 0.4.4 변경 - 자동 재접속은 옵션이 아니라 기본 동작
|
|
152
|
+
|
|
153
|
+
캐싱된 endpoint 목록이 있으면(= 클러스터) 통신 장애로 실패한 put/get 을 페일오버
|
|
154
|
+
재접속 후 **한 번 자동 재시도합니다.** `set_auto_reconnect()` 는 더 이상 필요 없으며,
|
|
155
|
+
불러도 무시됩니다(기존 코드 호환용으로만 남겨 두었습니다).
|
|
156
|
+
|
|
157
|
+
| 상황 | 동작 |
|
|
158
|
+
|---|---|
|
|
159
|
+
| 미커밋 트랜잭션 **있음** | 예외를 올립니다. 앱이 `reconnect()` 후 트랜잭션을 처음부터 다시 수행 |
|
|
160
|
+
| 미커밋 트랜잭션 **없음** (auto-commit 포함) | 내부에서 재접속 후 1회 재시도 |
|
|
161
|
+
| endpoint 목록 없음 (비클러스터/구엔진) | 예외를 올립니다 - 갈 곳이 없습니다 |
|
|
162
|
+
|
|
163
|
+
> 재시도는 **at-least-once** 입니다. 서버가 처리한 뒤 응답이 유실된 시점에 재시도하면
|
|
164
|
+
> **중복 put 이 생길 수 있습니다.** 재시도는 같은 msgId 로 나가므로 수신측에서 메시지 ID
|
|
165
|
+
> 로 걸러낼 수 있습니다. 미커밋 트랜잭션이 없을 때만 재시도하므로 트랜잭션 유실은 없습니다.
|
|
166
|
+
|
|
167
|
+
### 장애 전환(failover) 시 앱이 해야 할 일
|
|
168
|
+
|
|
169
|
+
리더가 죽으면 새 리더가 뽑힐 때까지 **아무도 접속을 받지 않습니다.** 실측(2노드 클러스터,
|
|
170
|
+
엔진 7.0.1.3341) **약 15~20초**가 걸립니다. 그 사이의 재접속 시도는 정상적으로 실패하므로,
|
|
171
|
+
**한 번 실패했다고 끝내지 말고 재시도**해야 합니다.
|
|
172
|
+
|
|
173
|
+
세션 종류에 따라 앱이 할 일이 갈립니다.
|
|
174
|
+
|
|
175
|
+
| 세션 | 실패 시점에 미커밋 | 앱이 할 일 |
|
|
176
|
+
|---|---|---|
|
|
177
|
+
| auto-commit | 없음 | **아무것도 안 해도 됩니다.** 그 연산을 다시 부르기만 하면 라이브러리가 재접속·재시도합니다 |
|
|
178
|
+
| transacted | 없음 | 위와 같습니다 |
|
|
179
|
+
| transacted | 있음 | `reconnect()` 로 자리를 옮기고 **트랜잭션을 처음부터 다시** 수행해야 합니다 |
|
|
180
|
+
|
|
181
|
+
`access_queue()` 는 **자동 재접속 대상이 아닙니다.** 끊긴 세션에서 부르면 그대로 실패하니,
|
|
182
|
+
재접속에 성공한 **뒤에** 핸들을 다시 얻으십시오. 기존 핸들을 계속 쓰는 쪽은 스스로 복구됩니다.
|
|
183
|
+
|
|
184
|
+
#### (1) 트랜잭션 재수행이 필요 없는 경우 — auto-commit
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
import time
|
|
188
|
+
from ilink.qmgr import ILQmgr
|
|
189
|
+
from ilink.exception import ILException, ILSessionException, ILOperationException
|
|
190
|
+
|
|
191
|
+
q = ILQmgr()
|
|
192
|
+
q.set_endpoint_cache_file("/var/run/myapp/ilink-endpoints.txt") # 재기동 대비(선택)
|
|
193
|
+
q.connect("10.0.0.1", 5000, "collector", True) # auto-commit
|
|
194
|
+
queue = q.access_queue("APP.EVENTS") # 핸들은 한 번만 얻는다
|
|
195
|
+
|
|
196
|
+
def publish(payload):
|
|
197
|
+
"""페일오버가 나도 이 함수는 그대로 둔다 - 라이브러리가 복구한다."""
|
|
198
|
+
for attempt in range(1, 11):
|
|
199
|
+
try:
|
|
200
|
+
return queue.put(payload) # 실패하면 내부에서 재접속 + 1회 재시도
|
|
201
|
+
except ILOperationException:
|
|
202
|
+
raise # 서버가 거절 - 재시도해도 같다
|
|
203
|
+
except (ILException, ILSessionException):
|
|
204
|
+
if attempt == 10:
|
|
205
|
+
raise # 승격이 20초 넘게 안 끝났다
|
|
206
|
+
time.sleep(3) # 리더 승격을 기다렸다가 다시
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
앱 코드에 `reconnect()` 가 없다는 점이 요지입니다. 같은 큐 핸들로 `put` 을 다시 부르기만
|
|
210
|
+
하면, 승격이 끝난 시점의 호출이 새 리더에 붙어 성공합니다(실측 20.6초에 복구).
|
|
211
|
+
|
|
212
|
+
#### (2) 트랜잭션 재수행이 필요한 경우 — transacted
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
import time
|
|
216
|
+
from ilink.qmgr import ILQmgr
|
|
217
|
+
from ilink.exception import ILException, ILSessionException, ILOperationException
|
|
218
|
+
|
|
219
|
+
MAX_RETRY = 10
|
|
220
|
+
|
|
221
|
+
def process_batch(records):
|
|
222
|
+
"""한 번의 트랜잭션 = 전부 커밋되거나 전부 없던 일이 된다."""
|
|
223
|
+
q = ILQmgr()
|
|
224
|
+
q.set_endpoint_cache_file("/var/run/myapp/ilink-endpoints.txt")
|
|
225
|
+
q.connect("10.0.0.1", 5000, "billing-svc", False) # transacted
|
|
226
|
+
try:
|
|
227
|
+
for attempt in range(1, MAX_RETRY + 1):
|
|
228
|
+
try:
|
|
229
|
+
queue = q.access_queue("APP.ORDERS") # 재접속 뒤 핸들을 다시 얻는다
|
|
230
|
+
for rec in records:
|
|
231
|
+
queue.put(rec)
|
|
232
|
+
q.commit() # 여기까지 와야 확정된다
|
|
233
|
+
return
|
|
234
|
+
except ILOperationException:
|
|
235
|
+
q.rollback() # 서버 거절 - 재시도 무의미
|
|
236
|
+
raise
|
|
237
|
+
except (ILException, ILSessionException):
|
|
238
|
+
if attempt == MAX_RETRY:
|
|
239
|
+
raise
|
|
240
|
+
try:
|
|
241
|
+
q.reconnect() # 승격 전이면 여기서 또 실패한다
|
|
242
|
+
except Exception:
|
|
243
|
+
time.sleep(3) # 기다렸다가 다음 회차에 다시
|
|
244
|
+
# 루프 처음으로 -> 배치 전체를 다시 넣는다
|
|
245
|
+
finally:
|
|
246
|
+
q.disconnect()
|
|
143
247
|
```
|
|
144
248
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
249
|
+
읽어야 할 포인트 셋입니다.
|
|
250
|
+
|
|
251
|
+
1. **재시도 단위는 트랜잭션 전체**입니다. 실패 지점부터 이어붙이면 안 됩니다 — 앞서 넣은
|
|
252
|
+
것들도 커밋되지 않았으므로 함께 사라집니다(실측: 미커밋 1건 유실, 재수행 후 depth 2 정상).
|
|
253
|
+
2. **`reconnect()` 자체가 실패할 수 있습니다.** 승격 전에 부르면 실패하는 게 정상이라,
|
|
254
|
+
루프 안에서 간격을 두고 다시 불러야 합니다(실측 2번째 시도, 16.5초에 성공).
|
|
255
|
+
3. **`ILOperationException` 과 통신 예외를 갈라 잡습니다.** 전자는 서버가 판단해 거절한
|
|
256
|
+
것이라 재시도가 무의미하고, 후자만 재접속 대상입니다. 파이썬의 예외 클래스는 평면
|
|
257
|
+
구조라 `ILSessionException` 은 `ILException` 의 하위 타입이 **아닙니다** — 통신 장애를
|
|
258
|
+
잡으려면 **둘 다** 적어야 합니다(자바는 모두 `ILException` 으로 감싸므로 하나면 됩니다).
|
|
259
|
+
|
|
260
|
+
#### 앱이 재기동되는 경우
|
|
261
|
+
|
|
262
|
+
캐싱은 메모리에만 있으므로 프로세스가 죽으면 사라집니다. 설정에 남은 옛 리더 주소로 다시
|
|
263
|
+
붙을 때 이렇게 갈립니다.
|
|
264
|
+
|
|
265
|
+
| 옛 리더의 상태 | 결과 |
|
|
266
|
+
|---|---|
|
|
267
|
+
| 살아 있고 **팔로워로 강등** | 그 노드가 리더 힌트를 주므로 **자동으로 새 리더에 접속** |
|
|
268
|
+
| **죽어 있음** | 갈 곳이 없어 실패 — `set_endpoint_cache_file()` 을 쓰거나 주소를 목록으로 주십시오 |
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
q = ILQmgr()
|
|
272
|
+
q.set_endpoint_cache_file("/var/run/myapp/ilink-endpoints.txt")
|
|
273
|
+
q.connect("10.0.0.1", 5000, "APP", True) # 이 주소가 죽어 있어도 파일 후보로 붙는다
|
|
274
|
+
```
|
|
149
275
|
|
|
150
276
|
## 0.4.0 신규 - 관리 연결 하나로 pub/sub
|
|
151
277
|
|
|
@@ -91,15 +91,141 @@ print(adm.getTopicList())
|
|
|
91
91
|
클러스터 큐 관리자에 접속하면 후보 주소를 캐싱해 두었다가, 리더가 바뀌어도 따라갑니다.
|
|
92
92
|
|
|
93
93
|
```python
|
|
94
|
-
qmgr.
|
|
95
|
-
print(qmgr.get_cluster_addresses())
|
|
96
|
-
qmgr.reconnect()
|
|
94
|
+
qmgr.connect("10.0.0.1", 5000, "APP", True) # 주소 하나면 됩니다
|
|
95
|
+
print(qmgr.get_cluster_addresses()) # ['10.0.0.1:5000', '10.0.0.2:5000']
|
|
96
|
+
qmgr.reconnect() # 새 리더를 찾아 재접속
|
|
97
|
+
|
|
98
|
+
# 첫 접속 시점의 장애까지 대비하려면 목록으로 (포트 인자 없음)
|
|
99
|
+
qmgr.connect("10.0.0.1:5000,10.0.0.2:5000", "APP", True)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
접속에 성공하면 핸드셰이크 직후 나머지 노드 주소를 서버에서 받아 캐싱하므로
|
|
103
|
+
**주소는 하나만 주면 됩니다.** 목록은 첫 접속 시점의 장애까지 대비할 때 씁니다.
|
|
104
|
+
|
|
105
|
+
### 0.4.4 변경 - 자동 재접속은 옵션이 아니라 기본 동작
|
|
106
|
+
|
|
107
|
+
캐싱된 endpoint 목록이 있으면(= 클러스터) 통신 장애로 실패한 put/get 을 페일오버
|
|
108
|
+
재접속 후 **한 번 자동 재시도합니다.** `set_auto_reconnect()` 는 더 이상 필요 없으며,
|
|
109
|
+
불러도 무시됩니다(기존 코드 호환용으로만 남겨 두었습니다).
|
|
110
|
+
|
|
111
|
+
| 상황 | 동작 |
|
|
112
|
+
|---|---|
|
|
113
|
+
| 미커밋 트랜잭션 **있음** | 예외를 올립니다. 앱이 `reconnect()` 후 트랜잭션을 처음부터 다시 수행 |
|
|
114
|
+
| 미커밋 트랜잭션 **없음** (auto-commit 포함) | 내부에서 재접속 후 1회 재시도 |
|
|
115
|
+
| endpoint 목록 없음 (비클러스터/구엔진) | 예외를 올립니다 - 갈 곳이 없습니다 |
|
|
116
|
+
|
|
117
|
+
> 재시도는 **at-least-once** 입니다. 서버가 처리한 뒤 응답이 유실된 시점에 재시도하면
|
|
118
|
+
> **중복 put 이 생길 수 있습니다.** 재시도는 같은 msgId 로 나가므로 수신측에서 메시지 ID
|
|
119
|
+
> 로 걸러낼 수 있습니다. 미커밋 트랜잭션이 없을 때만 재시도하므로 트랜잭션 유실은 없습니다.
|
|
120
|
+
|
|
121
|
+
### 장애 전환(failover) 시 앱이 해야 할 일
|
|
122
|
+
|
|
123
|
+
리더가 죽으면 새 리더가 뽑힐 때까지 **아무도 접속을 받지 않습니다.** 실측(2노드 클러스터,
|
|
124
|
+
엔진 7.0.1.3341) **약 15~20초**가 걸립니다. 그 사이의 재접속 시도는 정상적으로 실패하므로,
|
|
125
|
+
**한 번 실패했다고 끝내지 말고 재시도**해야 합니다.
|
|
126
|
+
|
|
127
|
+
세션 종류에 따라 앱이 할 일이 갈립니다.
|
|
128
|
+
|
|
129
|
+
| 세션 | 실패 시점에 미커밋 | 앱이 할 일 |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| auto-commit | 없음 | **아무것도 안 해도 됩니다.** 그 연산을 다시 부르기만 하면 라이브러리가 재접속·재시도합니다 |
|
|
132
|
+
| transacted | 없음 | 위와 같습니다 |
|
|
133
|
+
| transacted | 있음 | `reconnect()` 로 자리를 옮기고 **트랜잭션을 처음부터 다시** 수행해야 합니다 |
|
|
134
|
+
|
|
135
|
+
`access_queue()` 는 **자동 재접속 대상이 아닙니다.** 끊긴 세션에서 부르면 그대로 실패하니,
|
|
136
|
+
재접속에 성공한 **뒤에** 핸들을 다시 얻으십시오. 기존 핸들을 계속 쓰는 쪽은 스스로 복구됩니다.
|
|
137
|
+
|
|
138
|
+
#### (1) 트랜잭션 재수행이 필요 없는 경우 — auto-commit
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
import time
|
|
142
|
+
from ilink.qmgr import ILQmgr
|
|
143
|
+
from ilink.exception import ILException, ILSessionException, ILOperationException
|
|
144
|
+
|
|
145
|
+
q = ILQmgr()
|
|
146
|
+
q.set_endpoint_cache_file("/var/run/myapp/ilink-endpoints.txt") # 재기동 대비(선택)
|
|
147
|
+
q.connect("10.0.0.1", 5000, "collector", True) # auto-commit
|
|
148
|
+
queue = q.access_queue("APP.EVENTS") # 핸들은 한 번만 얻는다
|
|
149
|
+
|
|
150
|
+
def publish(payload):
|
|
151
|
+
"""페일오버가 나도 이 함수는 그대로 둔다 - 라이브러리가 복구한다."""
|
|
152
|
+
for attempt in range(1, 11):
|
|
153
|
+
try:
|
|
154
|
+
return queue.put(payload) # 실패하면 내부에서 재접속 + 1회 재시도
|
|
155
|
+
except ILOperationException:
|
|
156
|
+
raise # 서버가 거절 - 재시도해도 같다
|
|
157
|
+
except (ILException, ILSessionException):
|
|
158
|
+
if attempt == 10:
|
|
159
|
+
raise # 승격이 20초 넘게 안 끝났다
|
|
160
|
+
time.sleep(3) # 리더 승격을 기다렸다가 다시
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
앱 코드에 `reconnect()` 가 없다는 점이 요지입니다. 같은 큐 핸들로 `put` 을 다시 부르기만
|
|
164
|
+
하면, 승격이 끝난 시점의 호출이 새 리더에 붙어 성공합니다(실측 20.6초에 복구).
|
|
165
|
+
|
|
166
|
+
#### (2) 트랜잭션 재수행이 필요한 경우 — transacted
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
import time
|
|
170
|
+
from ilink.qmgr import ILQmgr
|
|
171
|
+
from ilink.exception import ILException, ILSessionException, ILOperationException
|
|
172
|
+
|
|
173
|
+
MAX_RETRY = 10
|
|
174
|
+
|
|
175
|
+
def process_batch(records):
|
|
176
|
+
"""한 번의 트랜잭션 = 전부 커밋되거나 전부 없던 일이 된다."""
|
|
177
|
+
q = ILQmgr()
|
|
178
|
+
q.set_endpoint_cache_file("/var/run/myapp/ilink-endpoints.txt")
|
|
179
|
+
q.connect("10.0.0.1", 5000, "billing-svc", False) # transacted
|
|
180
|
+
try:
|
|
181
|
+
for attempt in range(1, MAX_RETRY + 1):
|
|
182
|
+
try:
|
|
183
|
+
queue = q.access_queue("APP.ORDERS") # 재접속 뒤 핸들을 다시 얻는다
|
|
184
|
+
for rec in records:
|
|
185
|
+
queue.put(rec)
|
|
186
|
+
q.commit() # 여기까지 와야 확정된다
|
|
187
|
+
return
|
|
188
|
+
except ILOperationException:
|
|
189
|
+
q.rollback() # 서버 거절 - 재시도 무의미
|
|
190
|
+
raise
|
|
191
|
+
except (ILException, ILSessionException):
|
|
192
|
+
if attempt == MAX_RETRY:
|
|
193
|
+
raise
|
|
194
|
+
try:
|
|
195
|
+
q.reconnect() # 승격 전이면 여기서 또 실패한다
|
|
196
|
+
except Exception:
|
|
197
|
+
time.sleep(3) # 기다렸다가 다음 회차에 다시
|
|
198
|
+
# 루프 처음으로 -> 배치 전체를 다시 넣는다
|
|
199
|
+
finally:
|
|
200
|
+
q.disconnect()
|
|
97
201
|
```
|
|
98
202
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
203
|
+
읽어야 할 포인트 셋입니다.
|
|
204
|
+
|
|
205
|
+
1. **재시도 단위는 트랜잭션 전체**입니다. 실패 지점부터 이어붙이면 안 됩니다 — 앞서 넣은
|
|
206
|
+
것들도 커밋되지 않았으므로 함께 사라집니다(실측: 미커밋 1건 유실, 재수행 후 depth 2 정상).
|
|
207
|
+
2. **`reconnect()` 자체가 실패할 수 있습니다.** 승격 전에 부르면 실패하는 게 정상이라,
|
|
208
|
+
루프 안에서 간격을 두고 다시 불러야 합니다(실측 2번째 시도, 16.5초에 성공).
|
|
209
|
+
3. **`ILOperationException` 과 통신 예외를 갈라 잡습니다.** 전자는 서버가 판단해 거절한
|
|
210
|
+
것이라 재시도가 무의미하고, 후자만 재접속 대상입니다. 파이썬의 예외 클래스는 평면
|
|
211
|
+
구조라 `ILSessionException` 은 `ILException` 의 하위 타입이 **아닙니다** — 통신 장애를
|
|
212
|
+
잡으려면 **둘 다** 적어야 합니다(자바는 모두 `ILException` 으로 감싸므로 하나면 됩니다).
|
|
213
|
+
|
|
214
|
+
#### 앱이 재기동되는 경우
|
|
215
|
+
|
|
216
|
+
캐싱은 메모리에만 있으므로 프로세스가 죽으면 사라집니다. 설정에 남은 옛 리더 주소로 다시
|
|
217
|
+
붙을 때 이렇게 갈립니다.
|
|
218
|
+
|
|
219
|
+
| 옛 리더의 상태 | 결과 |
|
|
220
|
+
|---|---|
|
|
221
|
+
| 살아 있고 **팔로워로 강등** | 그 노드가 리더 힌트를 주므로 **자동으로 새 리더에 접속** |
|
|
222
|
+
| **죽어 있음** | 갈 곳이 없어 실패 — `set_endpoint_cache_file()` 을 쓰거나 주소를 목록으로 주십시오 |
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
q = ILQmgr()
|
|
226
|
+
q.set_endpoint_cache_file("/var/run/myapp/ilink-endpoints.txt")
|
|
227
|
+
q.connect("10.0.0.1", 5000, "APP", True) # 이 주소가 죽어 있어도 파일 후보로 붙는다
|
|
228
|
+
```
|
|
103
229
|
|
|
104
230
|
## 0.4.0 신규 - 관리 연결 하나로 pub/sub
|
|
105
231
|
|
|
@@ -2989,12 +2989,20 @@ class ILAdminQmgr:
|
|
|
2989
2989
|
qmgr.startChannel("TO.QM2")
|
|
2990
2990
|
|
|
2991
2991
|
qmgr2.createQueue(ILQueueProperty("APP.ORDERS")) # 받는 쪽
|
|
2992
|
+
|
|
2993
|
+
.. note:: 구엔진(6.3.3 미만) 호환
|
|
2994
|
+
|
|
2995
|
+
큐 속성은 엔진 6.3.3 에서 전송 레이아웃이 한 번 바뀐 **유일한**
|
|
2996
|
+
속성이다(4096 -> 2200). 붙어 있는 엔진의 리비전을 보고 알아서 옛
|
|
2997
|
+
레이아웃으로 보내므로 호출부는 달라질 게 없다. 다만 구엔진에는
|
|
2998
|
+
만료(``expiry``)/영속성(``deliveryMode``)/파티션 설정이 아예 없어
|
|
2999
|
+
**그 값들은 무시된다** - 설정해도 반영되지 않는다.
|
|
2992
3000
|
"""
|
|
2993
3001
|
try:
|
|
2994
3002
|
self.service.apply(
|
|
2995
3003
|
self.qmgrName,
|
|
2996
3004
|
ILC.MIMQ_CREATE_QUEUE_REQUEST,
|
|
2997
|
-
|
|
3005
|
+
self._queueBlob(prop),
|
|
2998
3006
|
ILC.MIMQ_SUCCESSFUL_QUEUE_CREATION,
|
|
2999
3007
|
)
|
|
3000
3008
|
except _PASSTHROUGH_EXCEPTIONS:
|
|
@@ -3097,6 +3105,41 @@ class ILAdminQmgr:
|
|
|
3097
3105
|
except Exception as exc:
|
|
3098
3106
|
raise ILException(exc) from exc
|
|
3099
3107
|
|
|
3108
|
+
def _queueBlob(self, prop: ILQueueProperty) -> bytes:
|
|
3109
|
+
"""큐 속성을 **붙어 있는 엔진이 이해하는 레이아웃**으로 직렬화한다.
|
|
3110
|
+
|
|
3111
|
+
Args:
|
|
3112
|
+
prop (ILQueueProperty): 보낼 큐 속성.
|
|
3113
|
+
|
|
3114
|
+
Returns:
|
|
3115
|
+
bytes: rev 3155 미만이면 구버전(4096), 그 외에는 현행(2200) 블롭.
|
|
3116
|
+
|
|
3117
|
+
Note:
|
|
3118
|
+
판정을 ``0 < revision`` 으로 한정한 이유는 다른 버전 게이트와 같다.
|
|
3119
|
+
버전 문자열을 파싱하지 못하면 revision 이 0 으로 남는데, 그때 구버전
|
|
3120
|
+
취급을 하면 최신 엔진에 4096 을 보내 조용히 깨진다.
|
|
3121
|
+
"""
|
|
3122
|
+
return prop.get_serialized(
|
|
3123
|
+
legacy=0 < self.revision < ILC.QUEUE_LAYOUT_MIN_REVISION
|
|
3124
|
+
)
|
|
3125
|
+
|
|
3126
|
+
def _requireQueueFlowSupport(self) -> None:
|
|
3127
|
+
"""유량 조회를 지원하지 않는 엔진이면 그 자리에서 실패시킨다.
|
|
3128
|
+
|
|
3129
|
+
Raises:
|
|
3130
|
+
ILOperationException: 엔진이 6.3.4(rev 3299) 미만일 때.
|
|
3131
|
+
|
|
3132
|
+
Note:
|
|
3133
|
+
유량 통계는 6.3.4 에서 들어왔다. 구엔진은 요청 코드 자체를 모르므로
|
|
3134
|
+
응답이 없거나 엉뚱한 리포트가 온다. 무엇이 잘못됐는지 알 수 없는
|
|
3135
|
+
타임아웃 대신 **왜 안 되는지 적힌 예외**로 끊는다.
|
|
3136
|
+
"""
|
|
3137
|
+
if 0 < self.revision < ILC.QUEUE_FLOW_MIN_REVISION:
|
|
3138
|
+
raise ILOperationException(
|
|
3139
|
+
"MIMQE_NOT_SUPPORTED (queue flow statistics require engine 6.3.4, "
|
|
3140
|
+
f"rev {ILC.QUEUE_FLOW_MIN_REVISION}+, current: {self.revision})"
|
|
3141
|
+
)
|
|
3142
|
+
|
|
3100
3143
|
def getQueueProperty(self, queueName: str) -> ILQueueProperty:
|
|
3101
3144
|
"""큐 하나의 전체 속성을 조회한다(종류 무관).
|
|
3102
3145
|
|
|
@@ -3258,12 +3301,20 @@ class ILAdminQmgr:
|
|
|
3258
3301
|
prop = qmgr.getQueueProperty("APP.ORDERS")
|
|
3259
3302
|
prop.setMaxDepth(50000)
|
|
3260
3303
|
qmgr.setQueueProperty(prop)
|
|
3304
|
+
|
|
3305
|
+
.. note:: 구엔진(6.3.3 미만) 호환
|
|
3306
|
+
|
|
3307
|
+
큐 속성은 엔진 6.3.3 에서 전송 레이아웃이 한 번 바뀐 **유일한**
|
|
3308
|
+
속성이다(4096 -> 2200). 붙어 있는 엔진의 리비전을 보고 알아서 옛
|
|
3309
|
+
레이아웃으로 보내므로 호출부는 달라질 게 없다. 다만 구엔진에는
|
|
3310
|
+
만료(``expiry``)/영속성(``deliveryMode``)/파티션 설정이 아예 없어
|
|
3311
|
+
**그 값들은 무시된다** - 설정해도 반영되지 않는다.
|
|
3261
3312
|
"""
|
|
3262
3313
|
try:
|
|
3263
3314
|
self.service.apply(
|
|
3264
3315
|
self.qmgrName,
|
|
3265
3316
|
ILC.MIMQ_SET_QUEUE_PROPERTY,
|
|
3266
|
-
|
|
3317
|
+
self._queueBlob(prop),
|
|
3267
3318
|
ILC.MIMQ_SUCCESSFUL_SET_QUEUE_PROPERTY,
|
|
3268
3319
|
)
|
|
3269
3320
|
except _PASSTHROUGH_EXCEPTIONS:
|
|
@@ -3442,23 +3493,39 @@ class ILAdminQmgr:
|
|
|
3442
3493
|
``getIncomingDataSize()`` / ``getOutgoingDataSize()`` / ``getDepth()``).
|
|
3443
3494
|
|
|
3444
3495
|
Raises:
|
|
3445
|
-
ILOperationException: 그런 큐가 없을 때.
|
|
3496
|
+
ILOperationException: 그런 큐가 없을 때. 또는 엔진이 6.3.4(rev 3299)
|
|
3497
|
+
미만이라 유량 통계 자체를 지원하지 않을 때.
|
|
3446
3498
|
ILException: 통신 실패.
|
|
3447
3499
|
|
|
3448
3500
|
Note:
|
|
3449
3501
|
**한 시점의 스냅샷**이다. 시간에 따른 추이를 보려면
|
|
3450
3502
|
:meth:`getQueueFlowInfoArray` 를 쓴다.
|
|
3451
3503
|
|
|
3452
|
-
..
|
|
3504
|
+
.. note:: 값은 **1초 주기**로 채워진다
|
|
3505
|
+
|
|
3506
|
+
유량 스냅샷은 서버가 1초마다 적재한다. 그래서 갓 만든 큐도 **조회
|
|
3507
|
+
자체는 즉시 되지만**(엔진 r3336+) 유입/유출 값은 첫 적재가 돌 때까지
|
|
3508
|
+
0 이다 - 실측: put 직후 0초 시점 0, 1.2초 시점 5건/2695B (r3336).
|
|
3509
|
+
즉시 깊이가 필요하면 :meth:`getQueueRealTimeInfo` 를 쓰라. 그쪽은
|
|
3510
|
+
적재 주기와 무관하게 현재 값을 준다.
|
|
3453
3511
|
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3512
|
+
**엔진 r3336 미만에서는 갓 만든 큐가 최대 1초간 조회되지 않는다** -
|
|
3513
|
+
첫 적재 전까지 ``MIMQE_OBJECT_NOT_FOUND`` 가 난다(r3335 실측
|
|
3514
|
+
0.1~1.1초, 단건·배열 50회). put/get 을 해도 앞당겨지지 않는다.
|
|
3515
|
+
|
|
3516
|
+
.. versionchanged:: 0.1.6
|
|
3517
|
+
이전 문서는 "유량 테이블은 주기적(약 1분) 갱신"이라고 적었으나
|
|
3518
|
+
**사실이 아니다**. 실서버에서 관측된 35초 지연은 적재 주기가
|
|
3519
|
+
아니라 **적재 스레드가 분 단위 통계 리포트에 붙들려 있던 것**으로,
|
|
3520
|
+
엔진 r3331 통계 컴팩션으로 해소됐다.
|
|
3521
|
+
|
|
3522
|
+
.. versionchanged:: 0.4.4
|
|
3523
|
+
엔진 r3336 이 큐 생성 시점에 유량 항목을 미리 등재하도록 바뀌어
|
|
3524
|
+
**생성 직후 조회 실패가 사라졌다**(24/24 첫 시도 성공, 값은 0).
|
|
3525
|
+
재시도 코드는 구엔진 대비로만 남기면 된다.
|
|
3460
3526
|
|
|
3461
3527
|
"""
|
|
3528
|
+
self._requireQueueFlowSupport()
|
|
3462
3529
|
try:
|
|
3463
3530
|
report_msg = self.service.request(
|
|
3464
3531
|
self.qmgrName, ILC.MIMQ_GET_QUEUE_ACCESS_INFO, queueName
|
|
@@ -3495,23 +3562,39 @@ class ILAdminQmgr:
|
|
|
3495
3562
|
실제 기록이 그보다 적으면 0 으로 채운 항목이 들어간다.
|
|
3496
3563
|
|
|
3497
3564
|
Raises:
|
|
3498
|
-
ILOperationException: 그런 큐가 없을 때.
|
|
3565
|
+
ILOperationException: 그런 큐가 없을 때. 또는 엔진이 6.3.4(rev 3299)
|
|
3566
|
+
미만이라 유량 통계 자체를 지원하지 않을 때.
|
|
3499
3567
|
ILException: 통신 실패.
|
|
3500
3568
|
|
|
3501
3569
|
Note:
|
|
3502
3570
|
0 으로 채워진 항목과 실제로 유량이 0 이었던 구간은 값만으로 구분되지
|
|
3503
3571
|
않는다. 적체 추세만 보는 용도로 쓰라.
|
|
3504
3572
|
|
|
3505
|
-
..
|
|
3573
|
+
.. note:: 값은 **1초 주기**로 채워진다
|
|
3574
|
+
|
|
3575
|
+
유량 스냅샷은 서버가 1초마다 적재한다. 그래서 갓 만든 큐도 **조회
|
|
3576
|
+
자체는 즉시 되지만**(엔진 r3336+) 유입/유출 값은 첫 적재가 돌 때까지
|
|
3577
|
+
0 이다 - 실측: put 직후 0초 시점 0, 1.2초 시점 5건/2695B (r3336).
|
|
3578
|
+
즉시 깊이가 필요하면 :meth:`getQueueRealTimeInfo` 를 쓰라. 그쪽은
|
|
3579
|
+
적재 주기와 무관하게 현재 값을 준다.
|
|
3506
3580
|
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3581
|
+
**엔진 r3336 미만에서는 갓 만든 큐가 최대 1초간 조회되지 않는다** -
|
|
3582
|
+
첫 적재 전까지 ``MIMQE_OBJECT_NOT_FOUND`` 가 난다(r3335 실측
|
|
3583
|
+
0.1~1.1초, 단건·배열 50회). put/get 을 해도 앞당겨지지 않는다.
|
|
3584
|
+
|
|
3585
|
+
.. versionchanged:: 0.1.6
|
|
3586
|
+
이전 문서는 "유량 테이블은 주기적(약 1분) 갱신"이라고 적었으나
|
|
3587
|
+
**사실이 아니다**. 실서버에서 관측된 35초 지연은 적재 주기가
|
|
3588
|
+
아니라 **적재 스레드가 분 단위 통계 리포트에 붙들려 있던 것**으로,
|
|
3589
|
+
엔진 r3331 통계 컴팩션으로 해소됐다.
|
|
3590
|
+
|
|
3591
|
+
.. versionchanged:: 0.4.4
|
|
3592
|
+
엔진 r3336 이 큐 생성 시점에 유량 항목을 미리 등재하도록 바뀌어
|
|
3593
|
+
**생성 직후 조회 실패가 사라졌다**(24/24 첫 시도 성공, 값은 0).
|
|
3594
|
+
재시도 코드는 구엔진 대비로만 남기면 된다.
|
|
3513
3595
|
|
|
3514
3596
|
"""
|
|
3597
|
+
self._requireQueueFlowSupport()
|
|
3515
3598
|
try:
|
|
3516
3599
|
res_msg = self.service.inquire(
|
|
3517
3600
|
self.qmgrName, ILC.MIMQ_GET_QUEUE_ACCESS_INFO_ARRAY, queueName
|
|
@@ -4885,6 +4968,15 @@ class ILAdminQmgr:
|
|
|
4885
4968
|
Note:
|
|
4886
4969
|
**누가 언제 마지막으로 이 큐를 건드렸는지** 알 수 있어 소비자가
|
|
4887
4970
|
멈춘 상황을 진단할 때 유용하다.
|
|
4971
|
+
|
|
4972
|
+
.. note:: 구엔진(6.3.3 미만)에 붙었을 때
|
|
4973
|
+
|
|
4974
|
+
그 시절 엔진은 이 요청에 **큐 속성 블롭을 통째로** 답한다. 사용 현황
|
|
4975
|
+
통계가 속성 안에 들어 있었기 때문이다. 클라이언트가 알아서 판별해
|
|
4976
|
+
같은 필드를 채우므로 호출부는 달라질 게 없다. 다만
|
|
4977
|
+
:attr:`~ilink.properties.ILQueueRealTimeInfo.partitions` 는 **빈
|
|
4978
|
+
목록**이다 - 파티셔닝은 6.3.3 에서 들어온 기능이라 구엔진 큐에는
|
|
4979
|
+
파티션이라는 개념 자체가 없다.
|
|
4888
4980
|
"""
|
|
4889
4981
|
try:
|
|
4890
4982
|
res_msg = self.service.inquire(
|
|
@@ -4892,7 +4984,12 @@ class ILAdminQmgr:
|
|
|
4892
4984
|
)
|
|
4893
4985
|
if res_msg.getType() == ILC.MIMQ_REPORT_MESSAGE:
|
|
4894
4986
|
raise ILOperationException(res_msg.getReportMsg().getArg2())
|
|
4895
|
-
|
|
4987
|
+
data = res_msg.getDataMsg().getData() or b""
|
|
4988
|
+
# 구엔진(rev 3155 미만)은 이 요청에 **큐 속성 블롭 4096** 을 그대로
|
|
4989
|
+
# 답한다. 그 시절에는 사용 현황 통계가 속성 안에 들어 있었기 때문이다.
|
|
4990
|
+
if len(data) >= ILC.MIMQ_VO_QUEUE_LEN_LEGACY:
|
|
4991
|
+
return ILQueueRealTimeInfo.from_legacy_queue_blob(data)
|
|
4992
|
+
return ILQueueRealTimeInfo(data)
|
|
4896
4993
|
except _PASSTHROUGH_EXCEPTIONS:
|
|
4897
4994
|
raise
|
|
4898
4995
|
except Exception as exc:
|
|
@@ -719,6 +719,19 @@ class ILC:
|
|
|
719
719
|
MIMQ_VO_SERVER_LEN = 428
|
|
720
720
|
MIMQ_VO_QMGR_LEN = 666
|
|
721
721
|
MIMQ_VO_QUEUE_LEN = 2200
|
|
722
|
+
#: 큐 속성 블롭의 **구버전(4096) 길이**. 엔진 6.3.3 미만이 쓰던 레이아웃이다.
|
|
723
|
+
#: 0~1772 와 2142~2162 는 현행과 같고, 그 사이 370 바이트만 다르다 -
|
|
724
|
+
#: 구버전은 사용 현황 통계, 현행은 만료/영속성/파티셔닝 설정이 들어간다.
|
|
725
|
+
MIMQ_VO_QUEUE_LEN_LEGACY = 4096
|
|
726
|
+
#: 큐 속성이 2200 레이아웃으로 바뀐 **첫 엔진 리비전**(6.3.3.3155).
|
|
727
|
+
#: 이 값보다 낮으면 4096 레이아웃으로 주고받아야 한다.
|
|
728
|
+
QUEUE_LAYOUT_MIN_REVISION = 3155
|
|
729
|
+
#: 구버전(4096) 블롭에서 공통 접두가 끝나고 레이아웃이 갈리는 지점.
|
|
730
|
+
#: 큐 **유량 통계** 조회가 들어온 첫 엔진 리비전(6.3.4.3299).
|
|
731
|
+
QUEUE_FLOW_MIN_REVISION = 3299
|
|
732
|
+
QUEUE_LEGACY_DIVERGE_OFFSET = 1772
|
|
733
|
+
#: 구버전(4096) 블롭에서 incomingDelayDepth 가 있는 위치. 현행과 같다.
|
|
734
|
+
QUEUE_LEGACY_TAIL_OFFSET = 2142
|
|
722
735
|
MIMQ_VO_CHANNEL_LEN = 2048
|
|
723
736
|
MIMQ_VO_DEST_ITEM_LEN = 1024
|
|
724
737
|
MIMQ_VO_QUEUE_INFO_LEN = 512
|
|
@@ -2524,7 +2524,14 @@ class ILMsgUtil:
|
|
|
2524
2524
|
elif cls is ILQmgrProperty:
|
|
2525
2525
|
serialized_length = ILC.MIMQ_VO_QMGR_LEN
|
|
2526
2526
|
elif cls is ILQueueProperty:
|
|
2527
|
+
# 큐 속성만 컨테이너 길이가 바뀐 적이 있다(엔진 6.3.3 에서 4096 -> 2200).
|
|
2528
|
+
# 버전을 따지는 대신 **응답 길이에서 보폭을 역산**한다. 구엔진에 붙어도
|
|
2529
|
+
# 목록이 그대로 읽히고, 새 엔진에서는 계산 결과가 2200 이라 무해하다.
|
|
2527
2530
|
serialized_length = ILC.MIMQ_VO_QUEUE_LEN
|
|
2531
|
+
if count > 0:
|
|
2532
|
+
stride = (len(ba) - count_length) // count
|
|
2533
|
+
if stride == ILC.MIMQ_VO_QUEUE_LEN_LEGACY:
|
|
2534
|
+
serialized_length = ILC.MIMQ_VO_QUEUE_LEN_LEGACY
|
|
2528
2535
|
elif cls is ILChannelProperty:
|
|
2529
2536
|
serialized_length = ILC.MIMQ_VO_CHANNEL_LEN
|
|
2530
2537
|
elif cls is ILSpokeProperty:
|