smart_message 0.0.10 → 0.0.12
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.
- checksums.yaml +4 -4
- data/.github/workflows/deploy-github-pages.yml +38 -0
- data/.gitignore +5 -0
- data/CHANGELOG.md +30 -0
- data/Gemfile.lock +35 -4
- data/README.md +169 -71
- data/Rakefile +29 -4
- data/docs/assets/images/ddq_architecture.svg +130 -0
- data/docs/assets/images/dlq_architecture.svg +115 -0
- data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
- data/docs/assets/images/enhanced-fluent-api.svg +149 -0
- data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
- data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
- data/docs/assets/images/fluent-api-demo.svg +59 -0
- data/docs/assets/images/performance-comparison.svg +161 -0
- data/docs/assets/images/redis-basic-architecture.svg +53 -0
- data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
- data/docs/assets/images/redis-queue-architecture.svg +101 -0
- data/docs/assets/images/smart_message.jpg +0 -0
- data/docs/assets/images/smart_message_walking.jpg +0 -0
- data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
- data/docs/assets/images/transport-comparison-matrix.svg +171 -0
- data/docs/assets/javascripts/mathjax.js +17 -0
- data/docs/assets/stylesheets/extra.css +51 -0
- data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
- data/docs/{architecture.md → core-concepts/architecture.md} +78 -138
- data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
- data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
- data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
- data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
- data/docs/{examples.md → getting-started/examples.md} +115 -89
- data/docs/{getting-started.md → getting-started/quick-start.md} +47 -18
- data/docs/guides/redis-queue-getting-started.md +697 -0
- data/docs/guides/redis-queue-patterns.md +889 -0
- data/docs/guides/redis-queue-production.md +1091 -0
- data/docs/index.md +64 -0
- data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
- data/docs/{logging.md → reference/logging.md} +1 -1
- data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
- data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
- data/docs/{serializers.md → reference/serializers.md} +3 -5
- data/docs/{transports.md → reference/transports.md} +133 -11
- data/docs/transports/memory-transport.md +374 -0
- data/docs/transports/redis-enhanced-transport.md +524 -0
- data/docs/transports/redis-queue-transport.md +1304 -0
- data/docs/transports/redis-transport-comparison.md +496 -0
- data/docs/transports/redis-transport.md +509 -0
- data/examples/README.md +98 -5
- data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
- data/examples/city_scenario/README.md +515 -0
- data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
- data/examples/city_scenario/citizen.rb +195 -0
- data/examples/city_scenario/city_diagram.svg +125 -0
- data/examples/city_scenario/common/health_monitor.rb +80 -0
- data/examples/city_scenario/common/logger.rb +30 -0
- data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
- data/examples/city_scenario/fire_department.rb +446 -0
- data/examples/city_scenario/fire_emergency_flow.svg +95 -0
- data/examples/city_scenario/health_department.rb +100 -0
- data/examples/city_scenario/health_monitoring_system.svg +130 -0
- data/examples/city_scenario/house.rb +244 -0
- data/examples/city_scenario/local_bank.rb +217 -0
- data/examples/city_scenario/messages/emergency_911_message.rb +81 -0
- data/examples/city_scenario/messages/emergency_resolved_message.rb +43 -0
- data/examples/city_scenario/messages/fire_dispatch_message.rb +43 -0
- data/examples/city_scenario/messages/fire_emergency_message.rb +45 -0
- data/examples/city_scenario/messages/health_check_message.rb +22 -0
- data/examples/city_scenario/messages/health_status_message.rb +35 -0
- data/examples/city_scenario/messages/police_dispatch_message.rb +46 -0
- data/examples/city_scenario/messages/silent_alarm_message.rb +38 -0
- data/examples/city_scenario/police_department.rb +316 -0
- data/examples/city_scenario/redis_monitor.rb +129 -0
- data/examples/city_scenario/redis_stats.rb +743 -0
- data/examples/city_scenario/room_for_improvement.md +240 -0
- data/examples/city_scenario/security_emergency_flow.svg +95 -0
- data/examples/city_scenario/service_internal_architecture.svg +154 -0
- data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
- data/examples/city_scenario/start_demo.sh +236 -0
- data/examples/city_scenario/stop_demo.sh +106 -0
- data/examples/city_scenario/visitor.rb +631 -0
- data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -1
- data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +13 -40
- data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -1
- data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +2 -2
- data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +4 -4
- data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
- data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +2 -2
- data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +17 -14
- data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +4 -4
- data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +8 -8
- data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +6 -6
- data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +2 -2
- data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +6 -6
- data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +19 -8
- data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -1
- data/examples/memory/README.md +163 -0
- data/examples/memory/memory_transport_architecture.svg +90 -0
- data/examples/memory/point_to_point_pattern.svg +94 -0
- data/examples/memory/publish_subscribe_pattern.svg +125 -0
- data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +5 -5
- data/examples/redis/README.md +230 -0
- data/examples/redis/alert_system_flow.svg +127 -0
- data/examples/redis/dashboard_status_flow.svg +107 -0
- data/examples/redis/device_command_flow.svg +113 -0
- data/examples/redis/redis_transport_architecture.svg +115 -0
- data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
- data/examples/redis/smart_home_system_architecture.svg +133 -0
- data/examples/redis_enhanced/README.md +319 -0
- data/examples/redis_enhanced/enhanced_01_basic_patterns.rb +233 -0
- data/examples/redis_enhanced/enhanced_02_fluent_api.rb +331 -0
- data/examples/redis_enhanced/enhanced_03_dual_publishing.rb +281 -0
- data/examples/redis_enhanced/enhanced_04_advanced_routing.rb +419 -0
- data/examples/redis_queue/01_basic_messaging.rb +221 -0
- data/examples/redis_queue/01_comprehensive_examples.rb +508 -0
- data/examples/redis_queue/02_pattern_routing.rb +405 -0
- data/examples/redis_queue/03_fluent_api.rb +422 -0
- data/examples/redis_queue/04_load_balancing.rb +486 -0
- data/examples/redis_queue/05_microservices.rb +735 -0
- data/examples/redis_queue/06_emergency_alerts.rb +777 -0
- data/examples/redis_queue/07_queue_management.rb +587 -0
- data/examples/redis_queue/README.md +366 -0
- data/examples/redis_queue/enhanced_01_basic_patterns.rb +233 -0
- data/examples/redis_queue/enhanced_02_fluent_api.rb +331 -0
- data/examples/redis_queue/enhanced_03_dual_publishing.rb +281 -0
- data/examples/redis_queue/enhanced_04_advanced_routing.rb +419 -0
- data/examples/redis_queue/redis_queue_architecture.svg +148 -0
- data/ideas/README.md +41 -0
- data/ideas/agents.md +1001 -0
- data/ideas/database_transport.md +980 -0
- data/ideas/improvement.md +359 -0
- data/ideas/meshage.md +1788 -0
- data/ideas/message_discovery.md +178 -0
- data/ideas/message_schema.md +1381 -0
- data/lib/smart_message/.idea/.gitignore +8 -0
- data/lib/smart_message/.idea/markdown.xml +6 -0
- data/lib/smart_message/.idea/misc.xml +4 -0
- data/lib/smart_message/.idea/modules.xml +8 -0
- data/lib/smart_message/.idea/smart_message.iml +16 -0
- data/lib/smart_message/.idea/vcs.xml +6 -0
- data/lib/smart_message/addressing.rb +15 -0
- data/lib/smart_message/base.rb +0 -2
- data/lib/smart_message/configuration.rb +1 -1
- data/lib/smart_message/logger.rb +15 -4
- data/lib/smart_message/plugins.rb +5 -2
- data/lib/smart_message/serializer.rb +14 -0
- data/lib/smart_message/transport/redis_enhanced_transport.rb +399 -0
- data/lib/smart_message/transport/redis_queue_transport.rb +555 -0
- data/lib/smart_message/transport/registry.rb +1 -0
- data/lib/smart_message/transport.rb +34 -1
- data/lib/smart_message/version.rb +1 -1
- data/lib/smart_message.rb +5 -52
- data/mkdocs.yml +184 -0
- data/p2p_plan.md +326 -0
- data/p2p_roadmap.md +287 -0
- data/smart_message.gemspec +2 -0
- data/smart_message.svg +51 -0
- metadata +170 -44
- data/docs/README.md +0 -57
- data/examples/dead_letters.jsonl +0 -12
- data/examples/temp.txt +0 -94
- data/examples/tmux_chat/README.md +0 -283
- data/examples/tmux_chat/bot_agent.rb +0 -278
- data/examples/tmux_chat/human_agent.rb +0 -199
- data/examples/tmux_chat/room_monitor.rb +0 -160
- data/examples/tmux_chat/shared_chat_system.rb +0 -328
- data/examples/tmux_chat/start_chat_demo.sh +0 -190
- data/examples/tmux_chat/stop_chat_demo.sh +0 -22
- /data/docs/{properties.md → core-concepts/properties.md} +0 -0
- /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -0,0 +1,1304 @@
|
|
1
|
+
# Redis Queue Transport
|
2
|
+
|
3
|
+
The **Redis Queue Transport** is SmartMessage's most advanced transport implementation, providing RabbitMQ-style queue/exchange routing using Redis Lists as persistent FIFO queues. Built on Ruby's Async framework for modern fiber-based concurrency, it combines Redis's exceptional performance with intelligent message routing capabilities.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
Redis Queue Transport is perfect for:
|
8
|
+
- **High-throughput applications** - 100K+ messages/second with minimal latency
|
9
|
+
- **Complex routing scenarios** - RabbitMQ-style pattern matching with Redis performance
|
10
|
+
- **Load-balanced processing** - Consumer groups for horizontal scaling
|
11
|
+
- **Persistent message queues** - Messages survive service restarts
|
12
|
+
- **Modern async applications** - Built on Ruby's Async framework
|
13
|
+
|
14
|
+
## Key Features
|
15
|
+
|
16
|
+
- 🚀 **RabbitMQ-Style Routing** - Pattern-based subscriptions with familiar syntax
|
17
|
+
- ⚡ **Ultra-High Performance** - ~0.5ms latency, 100K+ messages/second
|
18
|
+
- 🧬 **Async/Fiber Architecture** - Thousands of concurrent subscriptions
|
19
|
+
- 🔄 **Persistent Queues** - Redis Lists (LPUSH/BRPOP) for message durability
|
20
|
+
- ⚖️ **Load Balancing** - Consumer groups for horizontal scaling
|
21
|
+
- 🎯 **Enhanced Routing** - Multi-segment routing keys with from/to addressing
|
22
|
+
- 📊 **Queue Monitoring** - Comprehensive statistics and health monitoring
|
23
|
+
- 🛠️ **Fluent API** - Chainable subscription building for complex routing
|
24
|
+
|
25
|
+
## Architecture
|
26
|
+
|
27
|
+

|
28
|
+
|
29
|
+
The Redis Queue Transport combines the routing intelligence of message brokers with Redis's performance characteristics. Built on Ruby's Async framework, it uses cooperative fiber-based concurrency for massive scalability with minimal resource overhead.
|
30
|
+
|
31
|
+
### Performance Comparison
|
32
|
+
|
33
|
+
| Feature | RabbitMQ | Redis Queue (Async) | Performance Advantage |
|
34
|
+
|---------|----------|---------------------|----------------------|
|
35
|
+
| **Latency** | ~5ms | ~0.5ms | Redis **10x faster** |
|
36
|
+
| **Throughput** | 20K-30K msg/sec | 100K+ msg/sec | Redis **3-5x faster** |
|
37
|
+
| **Concurrent Subscribers** | ~100s | ~1000s | Async **10x more** |
|
38
|
+
| **Memory per Connection** | ~2MB | ~2KB | Async **1000x less** |
|
39
|
+
| **Pattern Matching** | Server-side | Client-side | Same syntax |
|
40
|
+
| **Load Balancing** | Queue groups | Consumer groups | Same behavior |
|
41
|
+
| **Setup Complexity** | High | Low | Redis **simpler** |
|
42
|
+
|
43
|
+
## Configuration
|
44
|
+
|
45
|
+
### Basic Configuration
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
SmartMessage.configure do |config|
|
49
|
+
config.transport = :redis_queue
|
50
|
+
config.transport_options = {
|
51
|
+
url: 'redis://localhost:6379',
|
52
|
+
db: 0,
|
53
|
+
queue_prefix: 'smart_message.queue',
|
54
|
+
consumer_group: 'default_workers',
|
55
|
+
block_time: 1000, # 1 second blocking read
|
56
|
+
max_queue_length: 10000
|
57
|
+
}
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
### Message Class Configuration
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
class OrderMessage < SmartMessage::Base
|
65
|
+
transport :redis_queue, {
|
66
|
+
url: 'redis://redis-server:6379',
|
67
|
+
consumer_group: 'order_processors',
|
68
|
+
queue_prefix: 'orders'
|
69
|
+
}
|
70
|
+
|
71
|
+
property :order_id, required: true
|
72
|
+
property :amount, required: true
|
73
|
+
|
74
|
+
def process
|
75
|
+
# Process order logic
|
76
|
+
puts "Processing order #{order_id} for $#{amount}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
### Advanced Configuration Options
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new({
|
85
|
+
# Connection settings
|
86
|
+
url: 'redis://username:password@redis.example.com:6379',
|
87
|
+
db: 2,
|
88
|
+
|
89
|
+
# Queue settings
|
90
|
+
queue_prefix: 'myapp.queues',
|
91
|
+
consumer_group: 'myapp_workers',
|
92
|
+
consumer_id: "#{Socket.gethostname}_#{Process.pid}",
|
93
|
+
|
94
|
+
# Performance tuning
|
95
|
+
block_time: 5000, # 5 second blocking timeout
|
96
|
+
max_queue_length: 50000, # Trim queues at 50k messages
|
97
|
+
batch_size: 10, # Process messages in batches
|
98
|
+
|
99
|
+
# Reliability settings
|
100
|
+
max_retries: 3,
|
101
|
+
retry_delay: 5, # 5 seconds between retries
|
102
|
+
dead_letter_queue: true,
|
103
|
+
|
104
|
+
# Connection pooling
|
105
|
+
pool_size: 5,
|
106
|
+
pool_timeout: 1
|
107
|
+
})
|
108
|
+
```
|
109
|
+
|
110
|
+
## Async Architecture
|
111
|
+
|
112
|
+
The Redis Queue Transport is built on Ruby's **Async framework**, providing modern fiber-based concurrency that offers significant advantages over traditional thread-based approaches:
|
113
|
+
|
114
|
+
### Key Benefits
|
115
|
+
|
116
|
+
- **Massive Scalability**: Support for thousands of concurrent subscribers with minimal resource overhead
|
117
|
+
- **Cooperative Concurrency**: Fibers yield control voluntarily, eliminating race conditions and reducing context switching overhead
|
118
|
+
- **Memory Efficiency**: Each fiber uses ~2KB vs ~2MB per thread (1000x less memory)
|
119
|
+
- **Test-Friendly**: Clean async lifecycle management with proper shutdown and cleanup
|
120
|
+
- **Non-Blocking I/O**: All Redis operations are fully asynchronous and non-blocking
|
121
|
+
|
122
|
+
### Usage with Async
|
123
|
+
|
124
|
+
All transport operations should be wrapped in `Async` blocks for optimal performance:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
# Basic async usage
|
128
|
+
Async do
|
129
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new(
|
130
|
+
url: 'redis://localhost:6379',
|
131
|
+
db: 0,
|
132
|
+
test_mode: false # Enable consumer tasks
|
133
|
+
)
|
134
|
+
|
135
|
+
# Subscribe to patterns
|
136
|
+
transport.subscribe_pattern("#.*.my_service") do |message_class, data|
|
137
|
+
# Process messages in async fiber
|
138
|
+
puts "Processing #{message_class} in fiber #{Async::Task.current.object_id}"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Publish messages
|
142
|
+
MyMessage.new(data: "test").publish
|
143
|
+
|
144
|
+
# Keep async context alive
|
145
|
+
sleep
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
### Async Best Practices
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
# Multiple concurrent subscriptions
|
153
|
+
Async do |task|
|
154
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new(...)
|
155
|
+
|
156
|
+
# Each subscription runs in its own fiber
|
157
|
+
task.async do
|
158
|
+
transport.subscribe_pattern("#.*.service1") { |msg, data| ... }
|
159
|
+
end
|
160
|
+
|
161
|
+
task.async do
|
162
|
+
transport.subscribe_pattern("#.*.service2") { |msg, data| ... }
|
163
|
+
end
|
164
|
+
|
165
|
+
task.async do
|
166
|
+
transport.subscribe_pattern("#.*.service3") { |msg, data| ... }
|
167
|
+
end
|
168
|
+
|
169
|
+
# All subscriptions run concurrently with minimal overhead
|
170
|
+
sleep
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
## Routing Keys and Patterns
|
175
|
+
|
176
|
+
### Routing Key Format
|
177
|
+
|
178
|
+
Messages are published with enhanced routing keys in the format:
|
179
|
+
```
|
180
|
+
namespace.message_type.from_uuid.to_uuid
|
181
|
+
```
|
182
|
+
|
183
|
+
Examples:
|
184
|
+
- `order.ordermessage.user123.service456`
|
185
|
+
- `emergency.alertmessage.security.broadcast`
|
186
|
+
- `system.healthcheck.monitor.all_services`
|
187
|
+
|
188
|
+
### Wildcard Patterns (RabbitMQ Compatible)
|
189
|
+
|
190
|
+
The Redis Queue Transport uses identical wildcard syntax to RabbitMQ:
|
191
|
+
|
192
|
+
- **`#`** = Zero or more words (matches `a`, `a.b`, `a.b.c`)
|
193
|
+
- **`*`** = Exactly one word (matches `a` but not `a.b`)
|
194
|
+
- **`.`** = Word separator (literal dot)
|
195
|
+
|
196
|
+
### Pattern Examples
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
# Recipient-based routing
|
200
|
+
transport.subscribe_pattern("#.*.payment_service") # All TO payment_service
|
201
|
+
transport.subscribe_pattern("#.*.user_#{user_id}") # Personal messages
|
202
|
+
|
203
|
+
# Sender-based routing
|
204
|
+
transport.subscribe_pattern("#.admin_service.*") # All FROM admin_service
|
205
|
+
transport.subscribe_pattern("#.#{service_name}.*") # All from your service
|
206
|
+
|
207
|
+
# Message type routing
|
208
|
+
transport.subscribe_pattern("order.#.*.*") # All order messages
|
209
|
+
transport.subscribe_pattern("alert.#.*.*") # All alerts
|
210
|
+
transport.subscribe_pattern("#{namespace}.#.*.*") # All in namespace
|
211
|
+
|
212
|
+
# Complex routing
|
213
|
+
transport.subscribe_pattern("emergency.#.security.*") # Security emergencies
|
214
|
+
transport.subscribe_pattern("#.*.#{env}_services") # Environment-specific
|
215
|
+
```
|
216
|
+
|
217
|
+
## Pattern Subscriptions
|
218
|
+
|
219
|
+
### Direct Pattern Subscriptions
|
220
|
+
|
221
|
+
```ruby
|
222
|
+
# Create transport instance
|
223
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new(options)
|
224
|
+
|
225
|
+
# Subscribe to messages TO specific services
|
226
|
+
transport.subscribe_pattern("#.*.payment_service") do |message_class, message_data|
|
227
|
+
puts "Payment service received: #{message_class}"
|
228
|
+
# Process payment message
|
229
|
+
end
|
230
|
+
|
231
|
+
# Subscribe to messages FROM specific services
|
232
|
+
transport.subscribe_pattern("#.admin_service.*") do |message_class, message_data|
|
233
|
+
puts "Message from admin: #{message_class}"
|
234
|
+
# Process admin message
|
235
|
+
end
|
236
|
+
|
237
|
+
# Subscribe to specific message types
|
238
|
+
transport.subscribe_pattern("order.#.*.*") do |message_class, message_data|
|
239
|
+
puts "Order message received: #{message_class}"
|
240
|
+
# Process any order message
|
241
|
+
end
|
242
|
+
```
|
243
|
+
|
244
|
+
### Message Class Subscriptions
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
# Standard SmartMessage subscription (auto-generates patterns)
|
248
|
+
class OrderMessage < SmartMessage::Base
|
249
|
+
transport :redis_queue
|
250
|
+
|
251
|
+
# This automatically subscribes to pattern: "order.ordermessage.*.*"
|
252
|
+
end
|
253
|
+
|
254
|
+
OrderMessage.subscribe # Subscribes to all OrderMessage instances
|
255
|
+
|
256
|
+
# Custom pattern subscription for message class
|
257
|
+
OrderMessage.subscribe_pattern("#.*.order_service") do |message|
|
258
|
+
# Only process orders directed to order_service
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
262
|
+
## Convenience Methods
|
263
|
+
|
264
|
+
The transport provides convenient methods for common routing patterns:
|
265
|
+
|
266
|
+
```ruby
|
267
|
+
# Simple recipient/sender filtering
|
268
|
+
transport.subscribe_to_recipient('my_service') # Messages TO me
|
269
|
+
transport.subscribe_from_sender('payment_service') # Messages FROM payment
|
270
|
+
transport.subscribe_to_type('OrderMessage') # All OrderMessage types
|
271
|
+
|
272
|
+
# Broadcast and alert patterns
|
273
|
+
transport.subscribe_to_broadcasts # All broadcast messages
|
274
|
+
transport.subscribe_to_alerts # Emergency/alert messages
|
275
|
+
|
276
|
+
# Environment-based subscriptions
|
277
|
+
transport.subscribe_to_environment('production') # Only prod messages
|
278
|
+
transport.subscribe_to_tenant('tenant_123') # Tenant-specific messages
|
279
|
+
```
|
280
|
+
|
281
|
+
## Fluent API
|
282
|
+
|
283
|
+
The fluent API provides an expressive way to build complex subscriptions:
|
284
|
+
|
285
|
+
### Basic Fluent Usage
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
# Simple FROM subscription
|
289
|
+
transport.where
|
290
|
+
.from('api_service')
|
291
|
+
.subscribe do |message_class, message_data|
|
292
|
+
puts "Message from API service: #{message_class}"
|
293
|
+
end
|
294
|
+
|
295
|
+
# Simple TO subscription
|
296
|
+
transport.where
|
297
|
+
.to('task_processor')
|
298
|
+
.subscribe do |message_class, message_data|
|
299
|
+
puts "Task for processor: #{message_class}"
|
300
|
+
end
|
301
|
+
|
302
|
+
# Type-specific subscription
|
303
|
+
transport.where
|
304
|
+
.type('TaskMessage')
|
305
|
+
.subscribe do |message_class, message_data|
|
306
|
+
puts "Task message received: #{message_class}"
|
307
|
+
end
|
308
|
+
```
|
309
|
+
|
310
|
+
### Advanced Fluent Combinations
|
311
|
+
|
312
|
+
```ruby
|
313
|
+
# Combined FROM and TO
|
314
|
+
transport.where
|
315
|
+
.from('web_app')
|
316
|
+
.to('event_processor')
|
317
|
+
.subscribe do |message_class, message_data|
|
318
|
+
puts "Web app event: #{message_class}"
|
319
|
+
end
|
320
|
+
|
321
|
+
# Consumer group with load balancing
|
322
|
+
transport.where
|
323
|
+
.type('TaskMessage')
|
324
|
+
.consumer_group('task_workers')
|
325
|
+
.subscribe do |message_class, message_data|
|
326
|
+
fiber_id = Async::Task.current.object_id.to_s[-4..-1]
|
327
|
+
puts "Fiber-#{fiber_id} processing: #{message_class}"
|
328
|
+
end
|
329
|
+
|
330
|
+
# Complex multi-criteria subscription
|
331
|
+
transport.where
|
332
|
+
.from(/^admin_.*/) # Regex pattern for admin services
|
333
|
+
.to('monitoring_service')
|
334
|
+
.consumer_group('monitoring_workers')
|
335
|
+
.subscribe do |message_class, message_data|
|
336
|
+
puts "Admin monitoring: #{message_class}"
|
337
|
+
end
|
338
|
+
|
339
|
+
# Type and destination combination
|
340
|
+
transport.where
|
341
|
+
.type('EventMessage')
|
342
|
+
.to(/.*analytics.*/) # Any service with 'analytics' in name
|
343
|
+
.subscribe do |message_class, message_data|
|
344
|
+
puts "Analytics event: #{message_class}"
|
345
|
+
end
|
346
|
+
```
|
347
|
+
|
348
|
+
### Dynamic Subscription Building
|
349
|
+
|
350
|
+
```ruby
|
351
|
+
# Build and modify subscriptions dynamically
|
352
|
+
base_subscription = transport.where.type('TaskMessage')
|
353
|
+
|
354
|
+
# Add criteria dynamically
|
355
|
+
urgent_tasks = base_subscription.from('urgent_processor')
|
356
|
+
pattern1 = urgent_tasks.build
|
357
|
+
puts "Urgent tasks pattern: #{pattern1}"
|
358
|
+
|
359
|
+
# Build different variations
|
360
|
+
analytics_tasks = base_subscription.to(/.*analytics.*/)
|
361
|
+
pattern2 = analytics_tasks.build
|
362
|
+
puts "Analytics tasks pattern: #{pattern2}"
|
363
|
+
|
364
|
+
# Subscribe with complex pattern
|
365
|
+
transport.where
|
366
|
+
.type('EventMessage')
|
367
|
+
.from(/^(web|mobile)_app$/) # From web or mobile app
|
368
|
+
.to(/.*analytics.*/) # To any analytics service
|
369
|
+
.consumer_group('analytics_processors')
|
370
|
+
.subscribe do |message_class, message_data|
|
371
|
+
puts "Complex pattern match: #{message_class}"
|
372
|
+
end
|
373
|
+
```
|
374
|
+
|
375
|
+
## Load Balancing
|
376
|
+
|
377
|
+
### Consumer Groups
|
378
|
+
|
379
|
+
Multiple consumers can share the same queue for automatic load distribution:
|
380
|
+
|
381
|
+
```ruby
|
382
|
+
# Create multiple workers in the same consumer group
|
383
|
+
worker1_transport = SmartMessage::Transport::RedisQueueTransport.new({
|
384
|
+
consumer_group: 'order_workers',
|
385
|
+
consumer_id: 'worker_1'
|
386
|
+
})
|
387
|
+
|
388
|
+
worker2_transport = SmartMessage::Transport::RedisQueueTransport.new({
|
389
|
+
consumer_group: 'order_workers',
|
390
|
+
consumer_id: 'worker_2'
|
391
|
+
})
|
392
|
+
|
393
|
+
worker3_transport = SmartMessage::Transport::RedisQueueTransport.new({
|
394
|
+
consumer_group: 'order_workers',
|
395
|
+
consumer_id: 'worker_3'
|
396
|
+
})
|
397
|
+
|
398
|
+
# All workers subscribe to same pattern
|
399
|
+
pattern = "#.*.order_service"
|
400
|
+
|
401
|
+
worker1_transport.subscribe_pattern(pattern) { |msg| puts "Worker 1: #{msg}" }
|
402
|
+
worker2_transport.subscribe_pattern(pattern) { |msg| puts "Worker 2: #{msg}" }
|
403
|
+
worker3_transport.subscribe_pattern(pattern) { |msg| puts "Worker 3: #{msg}" }
|
404
|
+
|
405
|
+
# Messages published to order_service will be distributed among workers
|
406
|
+
```
|
407
|
+
|
408
|
+
### Load Balancing with Fluent API
|
409
|
+
|
410
|
+
```ruby
|
411
|
+
# Multiple workers using fluent API
|
412
|
+
3.times do |i|
|
413
|
+
worker_transport = SmartMessage::Transport::RedisQueueTransport.new({
|
414
|
+
consumer_group: 'balanced_workers',
|
415
|
+
consumer_id: "worker_#{i + 1}"
|
416
|
+
})
|
417
|
+
|
418
|
+
worker_transport.where
|
419
|
+
.to('shared_service')
|
420
|
+
.consumer_group('balanced_workers')
|
421
|
+
.subscribe do |message_class, message_data|
|
422
|
+
puts "Worker #{i + 1} processing: #{message_class}"
|
423
|
+
end
|
424
|
+
end
|
425
|
+
```
|
426
|
+
|
427
|
+
### Priority-Based Load Balancing
|
428
|
+
|
429
|
+
```ruby
|
430
|
+
# High-priority worker pool
|
431
|
+
high_priority_transport = SmartMessage::Transport::RedisQueueTransport.new({
|
432
|
+
consumer_group: 'high_priority_workers'
|
433
|
+
})
|
434
|
+
|
435
|
+
high_priority_transport.where
|
436
|
+
.to('priority_queue')
|
437
|
+
.consumer_group('high_priority_workers')
|
438
|
+
.subscribe do |message_class, message_data|
|
439
|
+
puts "HIGH PRIORITY: #{message_class}"
|
440
|
+
end
|
441
|
+
|
442
|
+
# Normal priority worker pool
|
443
|
+
normal_priority_transport = SmartMessage::Transport::RedisQueueTransport.new({
|
444
|
+
consumer_group: 'normal_workers'
|
445
|
+
})
|
446
|
+
|
447
|
+
normal_priority_transport.where
|
448
|
+
.to('normal_queue')
|
449
|
+
.consumer_group('normal_workers')
|
450
|
+
.subscribe do |message_class, message_data|
|
451
|
+
puts "Normal priority: #{message_class}"
|
452
|
+
end
|
453
|
+
```
|
454
|
+
|
455
|
+
## Message Publishing
|
456
|
+
|
457
|
+
### Basic Publishing
|
458
|
+
|
459
|
+
```ruby
|
460
|
+
class OrderMessage < SmartMessage::Base
|
461
|
+
transport :redis_queue
|
462
|
+
|
463
|
+
property :order_id, required: true
|
464
|
+
property :customer_id, required: true
|
465
|
+
property :amount, required: true
|
466
|
+
end
|
467
|
+
|
468
|
+
# Publish with automatic routing
|
469
|
+
message = OrderMessage.new(
|
470
|
+
order_id: 'ORD-001',
|
471
|
+
customer_id: 'CUST-123',
|
472
|
+
amount: 99.99
|
473
|
+
)
|
474
|
+
|
475
|
+
# Set routing header for enhanced routing
|
476
|
+
message._sm_header.from = 'order_service'
|
477
|
+
message._sm_header.to = 'payment_service'
|
478
|
+
|
479
|
+
message.publish # Routes to: order.ordermessage.order_service.payment_service
|
480
|
+
```
|
481
|
+
|
482
|
+
### Publishing with Enhanced Headers
|
483
|
+
|
484
|
+
```ruby
|
485
|
+
# Explicit header setting
|
486
|
+
OrderMessage.new(
|
487
|
+
order_id: 'ORD-002',
|
488
|
+
customer_id: 'CUST-456',
|
489
|
+
amount: 149.99,
|
490
|
+
_sm_header: {
|
491
|
+
from: 'api_gateway',
|
492
|
+
to: 'order_service',
|
493
|
+
correlation_id: SecureRandom.uuid,
|
494
|
+
tenant_id: 'tenant_123'
|
495
|
+
}
|
496
|
+
).publish
|
497
|
+
|
498
|
+
# Broadcast publishing
|
499
|
+
AlertMessage.new(
|
500
|
+
alert_type: 'maintenance',
|
501
|
+
message: 'System maintenance starting in 10 minutes',
|
502
|
+
_sm_header: {
|
503
|
+
from: 'admin_service',
|
504
|
+
to: 'broadcast' # Routes to all subscribers of broadcasts
|
505
|
+
}
|
506
|
+
).publish
|
507
|
+
|
508
|
+
# Environment-specific publishing
|
509
|
+
DeploymentMessage.new(
|
510
|
+
version: '2.1.0',
|
511
|
+
changelog: 'Bug fixes and performance improvements',
|
512
|
+
_sm_header: {
|
513
|
+
from: 'ci_cd_service',
|
514
|
+
to: 'prod_services' # Only production services
|
515
|
+
}
|
516
|
+
).publish
|
517
|
+
```
|
518
|
+
|
519
|
+
### Batch Publishing
|
520
|
+
|
521
|
+
```ruby
|
522
|
+
# Publish multiple messages efficiently
|
523
|
+
messages = []
|
524
|
+
|
525
|
+
100.times do |i|
|
526
|
+
messages << TaskMessage.new(
|
527
|
+
task_id: "BATCH-#{i}",
|
528
|
+
priority: 'normal',
|
529
|
+
_sm_header: {
|
530
|
+
from: 'batch_processor',
|
531
|
+
to: 'worker_pool'
|
532
|
+
}
|
533
|
+
)
|
534
|
+
end
|
535
|
+
|
536
|
+
# Batch publish for better performance
|
537
|
+
messages.each(&:publish) # Redis pipelining automatically optimizes this
|
538
|
+
```
|
539
|
+
|
540
|
+
## Queue Management
|
541
|
+
|
542
|
+
### Queue Statistics
|
543
|
+
|
544
|
+
```ruby
|
545
|
+
# Get comprehensive queue statistics
|
546
|
+
stats = transport.queue_stats
|
547
|
+
|
548
|
+
# Example output:
|
549
|
+
# {
|
550
|
+
# "smart_message.queue.#_*_payment_service" => {
|
551
|
+
# length: 42,
|
552
|
+
# pattern: "#.*.payment_service",
|
553
|
+
# consumers: 2,
|
554
|
+
# consumer_group: "payment_workers",
|
555
|
+
# last_activity: Time.now - 30
|
556
|
+
# },
|
557
|
+
# "smart_message.queue.order_#_*_*" => {
|
558
|
+
# length: 0,
|
559
|
+
# pattern: "order.#.*.*",
|
560
|
+
# consumers: 3,
|
561
|
+
# consumer_group: "order_workers",
|
562
|
+
# last_activity: Time.now - 5
|
563
|
+
# }
|
564
|
+
# }
|
565
|
+
|
566
|
+
# Process statistics
|
567
|
+
stats.each do |queue_name, info|
|
568
|
+
puts "Queue: #{queue_name}"
|
569
|
+
puts " Messages: #{info[:length]}"
|
570
|
+
puts " Consumers: #{info[:consumers]}"
|
571
|
+
puts " Pattern: #{info[:pattern]}"
|
572
|
+
puts " Last Activity: #{info[:last_activity]}"
|
573
|
+
puts ""
|
574
|
+
end
|
575
|
+
```
|
576
|
+
|
577
|
+
### Routing Table Inspection
|
578
|
+
|
579
|
+
```ruby
|
580
|
+
# View active routing patterns and their mappings
|
581
|
+
routing_table = transport.routing_table
|
582
|
+
|
583
|
+
# Example output:
|
584
|
+
# {
|
585
|
+
# "#.*.payment_service" => ["smart_message.queue.#_*_payment_service"],
|
586
|
+
# "order.#.*.*" => ["smart_message.queue.order_#_*_*"],
|
587
|
+
# "#.admin_service.*" => ["smart_message.queue.#_admin_service_*"]
|
588
|
+
# }
|
589
|
+
|
590
|
+
# Analyze routing patterns
|
591
|
+
routing_table.each do |pattern, queues|
|
592
|
+
puts "Pattern '#{pattern}':"
|
593
|
+
puts " Routes to #{queues.size} queue(s):"
|
594
|
+
queues.each { |queue| puts " - #{queue}" }
|
595
|
+
puts ""
|
596
|
+
end
|
597
|
+
```
|
598
|
+
|
599
|
+
### Queue Health Monitoring
|
600
|
+
|
601
|
+
```ruby
|
602
|
+
# Monitor queue health and performance
|
603
|
+
def monitor_queue_health(transport)
|
604
|
+
stats = transport.queue_stats
|
605
|
+
health_issues = []
|
606
|
+
|
607
|
+
stats.each do |queue_name, info|
|
608
|
+
# Check for potential issues
|
609
|
+
if info[:length] > 1000
|
610
|
+
health_issues << "WARNING: Queue #{queue_name} has #{info[:length]} messages (high load)"
|
611
|
+
elsif info[:length] > 0 && info[:consumers] == 0
|
612
|
+
health_issues << "ERROR: Queue #{queue_name} has messages but no consumers"
|
613
|
+
elsif info[:consumers] > 10
|
614
|
+
health_issues << "INFO: Queue #{queue_name} has #{info[:consumers]} consumers (possible over-provisioning)"
|
615
|
+
end
|
616
|
+
|
617
|
+
# Check for stale queues
|
618
|
+
if info[:last_activity] && info[:last_activity] < Time.now - 300 # 5 minutes
|
619
|
+
health_issues << "WARNING: Queue #{queue_name} inactive for #{Time.now - info[:last_activity]} seconds"
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
if health_issues.any?
|
624
|
+
puts "Queue Health Issues:"
|
625
|
+
health_issues.each { |issue| puts " #{issue}" }
|
626
|
+
else
|
627
|
+
puts "All queues healthy ✅"
|
628
|
+
end
|
629
|
+
|
630
|
+
health_issues
|
631
|
+
end
|
632
|
+
|
633
|
+
# Run health check
|
634
|
+
health_issues = monitor_queue_health(transport)
|
635
|
+
```
|
636
|
+
|
637
|
+
### Administrative Operations
|
638
|
+
|
639
|
+
```ruby
|
640
|
+
# Clear specific queue
|
641
|
+
transport.clear_queue("smart_message.queue.#_*_test_service")
|
642
|
+
|
643
|
+
# Clear all queues for consumer group
|
644
|
+
transport.clear_consumer_group_queues("test_workers")
|
645
|
+
|
646
|
+
# Get queue length
|
647
|
+
length = transport.queue_length("smart_message.queue.order_#_*_*")
|
648
|
+
puts "Order queue length: #{length}"
|
649
|
+
|
650
|
+
# Pause/resume consumer groups
|
651
|
+
transport.pause_consumer_group("maintenance_workers")
|
652
|
+
transport.resume_consumer_group("maintenance_workers")
|
653
|
+
|
654
|
+
# Get consumer group information
|
655
|
+
consumer_info = transport.consumer_group_info("order_workers")
|
656
|
+
puts "Consumer group info: #{consumer_info}"
|
657
|
+
```
|
658
|
+
|
659
|
+
## Error Handling and Reliability
|
660
|
+
|
661
|
+
### Retry Mechanisms
|
662
|
+
|
663
|
+
```ruby
|
664
|
+
class ReliableMessage < SmartMessage::Base
|
665
|
+
transport :redis_queue, {
|
666
|
+
max_retries: 3,
|
667
|
+
retry_delay: 5, # 5 seconds between retries
|
668
|
+
exponential_backoff: true # 5s, 25s, 125s delays
|
669
|
+
}
|
670
|
+
|
671
|
+
property :data, required: true
|
672
|
+
|
673
|
+
def process
|
674
|
+
begin
|
675
|
+
# Potentially failing operation
|
676
|
+
external_service.process(data)
|
677
|
+
rescue ExternalService::TemporaryError => e
|
678
|
+
# Retryable error - will be retried automatically
|
679
|
+
raise SmartMessage::Errors::RetryableError, e.message
|
680
|
+
rescue ExternalService::ValidationError => e
|
681
|
+
# Permanent error - goes to dead letter queue
|
682
|
+
raise SmartMessage::Errors::PermanentError, e.message
|
683
|
+
end
|
684
|
+
end
|
685
|
+
end
|
686
|
+
```
|
687
|
+
|
688
|
+
### Dead Letter Queue
|
689
|
+
|
690
|
+
```ruby
|
691
|
+
# Configure dead letter queue
|
692
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new({
|
693
|
+
dead_letter_queue: true,
|
694
|
+
dead_letter_prefix: 'dlq',
|
695
|
+
max_retries: 3
|
696
|
+
})
|
697
|
+
|
698
|
+
# Monitor dead letter queue
|
699
|
+
dlq_stats = transport.dead_letter_stats
|
700
|
+
puts "Dead letter messages: #{dlq_stats[:count]}"
|
701
|
+
puts "Recent errors: #{dlq_stats[:recent_errors]}"
|
702
|
+
|
703
|
+
# Process dead letter messages
|
704
|
+
transport.process_dead_letters do |message_data, error_info|
|
705
|
+
puts "Dead letter: #{message_data}"
|
706
|
+
puts "Error: #{error_info[:error_message]}"
|
707
|
+
puts "Retry count: #{error_info[:retry_count]}"
|
708
|
+
|
709
|
+
# Decide whether to retry, route elsewhere, or discard
|
710
|
+
if error_info[:retry_count] < 5 && fixable_error?(error_info[:error_message])
|
711
|
+
:retry # Retry the message
|
712
|
+
else
|
713
|
+
:discard # Discard the message
|
714
|
+
end
|
715
|
+
end
|
716
|
+
```
|
717
|
+
|
718
|
+
### Circuit Breaker Pattern
|
719
|
+
|
720
|
+
```ruby
|
721
|
+
class CircuitBreakerMessage < SmartMessage::Base
|
722
|
+
transport :redis_queue, {
|
723
|
+
circuit_breaker: true,
|
724
|
+
failure_threshold: 5, # Open circuit after 5 failures
|
725
|
+
recovery_timeout: 60, # Try again after 60 seconds
|
726
|
+
half_open_max_calls: 3 # Allow 3 calls when half-open
|
727
|
+
}
|
728
|
+
|
729
|
+
def process
|
730
|
+
# External service call
|
731
|
+
external_service.call(data)
|
732
|
+
end
|
733
|
+
end
|
734
|
+
```
|
735
|
+
|
736
|
+
## Use Cases and Examples
|
737
|
+
|
738
|
+
### Microservices Communication
|
739
|
+
|
740
|
+
```ruby
|
741
|
+
# API Gateway routing requests to services
|
742
|
+
class ServiceRequest < SmartMessage::Base
|
743
|
+
transport :redis_queue
|
744
|
+
|
745
|
+
property :service_name, required: true
|
746
|
+
property :operation, required: true
|
747
|
+
property :payload, default: {}
|
748
|
+
end
|
749
|
+
|
750
|
+
# Route based on service name
|
751
|
+
api_gateway_transport.where
|
752
|
+
.type('ServiceRequest')
|
753
|
+
.subscribe do |message_class, message_data|
|
754
|
+
request = JSON.parse(message_data)
|
755
|
+
|
756
|
+
# Forward to appropriate service
|
757
|
+
ServiceRequest.new(request.merge(
|
758
|
+
'_sm_header' => {
|
759
|
+
'from' => 'api_gateway',
|
760
|
+
'to' => request['service_name']
|
761
|
+
}
|
762
|
+
)).publish
|
763
|
+
end
|
764
|
+
|
765
|
+
# Services subscribe to their own requests
|
766
|
+
user_service_transport.where
|
767
|
+
.to('user_service')
|
768
|
+
.consumer_group('user_workers')
|
769
|
+
.subscribe do |message_class, message_data|
|
770
|
+
# Process user service request
|
771
|
+
end
|
772
|
+
|
773
|
+
payment_service_transport.where
|
774
|
+
.to('payment_service')
|
775
|
+
.consumer_group('payment_workers')
|
776
|
+
.subscribe do |message_class, message_data|
|
777
|
+
# Process payment service request
|
778
|
+
end
|
779
|
+
```
|
780
|
+
|
781
|
+
### Event-Driven Architecture
|
782
|
+
|
783
|
+
```ruby
|
784
|
+
# Order processing workflow
|
785
|
+
class OrderPlaced < SmartMessage::Base
|
786
|
+
transport :redis_queue
|
787
|
+
|
788
|
+
property :order_id, required: true
|
789
|
+
property :customer_id, required: true
|
790
|
+
property :items, required: true
|
791
|
+
end
|
792
|
+
|
793
|
+
# Multiple services process order events
|
794
|
+
inventory_service.where
|
795
|
+
.type('OrderPlaced')
|
796
|
+
.consumer_group('inventory_workers')
|
797
|
+
.subscribe do |message_class, message_data|
|
798
|
+
# Reserve inventory
|
799
|
+
end
|
800
|
+
|
801
|
+
payment_service.where
|
802
|
+
.type('OrderPlaced')
|
803
|
+
.consumer_group('payment_workers')
|
804
|
+
.subscribe do |message_class, message_data|
|
805
|
+
# Process payment
|
806
|
+
end
|
807
|
+
|
808
|
+
shipping_service.where
|
809
|
+
.type('OrderPlaced')
|
810
|
+
.consumer_group('shipping_workers')
|
811
|
+
.subscribe do |message_class, message_data|
|
812
|
+
# Arrange shipping
|
813
|
+
end
|
814
|
+
|
815
|
+
analytics_service.where
|
816
|
+
.type('OrderPlaced')
|
817
|
+
.consumer_group('analytics_workers')
|
818
|
+
.subscribe do |message_class, message_data|
|
819
|
+
# Track metrics
|
820
|
+
end
|
821
|
+
```
|
822
|
+
|
823
|
+
### Multi-Tenant Systems
|
824
|
+
|
825
|
+
```ruby
|
826
|
+
# Tenant isolation via routing
|
827
|
+
class TenantMessage < SmartMessage::Base
|
828
|
+
transport :redis_queue
|
829
|
+
|
830
|
+
property :tenant_id, required: true
|
831
|
+
property :data, required: true
|
832
|
+
end
|
833
|
+
|
834
|
+
# Each tenant gets isolated message processing
|
835
|
+
tenant_transport.where
|
836
|
+
.from(/^tenant_#{tenant_id}_.*/)
|
837
|
+
.to(/^tenant_#{tenant_id}_.*/)
|
838
|
+
.consumer_group("tenant_#{tenant_id}_workers")
|
839
|
+
.subscribe do |message_class, message_data|
|
840
|
+
# Process tenant-specific message
|
841
|
+
end
|
842
|
+
|
843
|
+
# Admin messages broadcast to all tenants
|
844
|
+
admin_transport.where
|
845
|
+
.from('admin_service')
|
846
|
+
.to('broadcast')
|
847
|
+
.subscribe do |message_class, message_data|
|
848
|
+
# Process admin broadcast
|
849
|
+
end
|
850
|
+
```
|
851
|
+
|
852
|
+
### Real-Time Monitoring and Alerting
|
853
|
+
|
854
|
+
```ruby
|
855
|
+
# Monitoring system with alert routing
|
856
|
+
class MetricMessage < SmartMessage::Base
|
857
|
+
transport :redis_queue
|
858
|
+
|
859
|
+
property :metric_name, required: true
|
860
|
+
property :value, required: true
|
861
|
+
property :threshold, required: true
|
862
|
+
property :severity, default: 'info'
|
863
|
+
end
|
864
|
+
|
865
|
+
# Route based on severity
|
866
|
+
monitoring_transport.where
|
867
|
+
.type('MetricMessage')
|
868
|
+
.subscribe do |message_class, message_data|
|
869
|
+
metric = JSON.parse(message_data)
|
870
|
+
|
871
|
+
if metric['value'] > metric['threshold']
|
872
|
+
severity = if metric['value'] > metric['threshold'] * 2
|
873
|
+
'critical'
|
874
|
+
elsif metric['value'] > metric['threshold'] * 1.5
|
875
|
+
'warning'
|
876
|
+
else
|
877
|
+
'info'
|
878
|
+
end
|
879
|
+
|
880
|
+
AlertMessage.new(
|
881
|
+
alert_type: metric['metric_name'],
|
882
|
+
message: "#{metric['metric_name']} is #{metric['value']} (threshold: #{metric['threshold']})",
|
883
|
+
severity: severity,
|
884
|
+
_sm_header: {
|
885
|
+
from: 'monitoring_service',
|
886
|
+
to: severity == 'critical' ? 'pager_duty' : 'alert_dashboard'
|
887
|
+
}
|
888
|
+
).publish
|
889
|
+
end
|
890
|
+
end
|
891
|
+
```
|
892
|
+
|
893
|
+
## Best Practices
|
894
|
+
|
895
|
+
### Naming Conventions
|
896
|
+
|
897
|
+
```ruby
|
898
|
+
# Use descriptive queue prefixes
|
899
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new({
|
900
|
+
queue_prefix: 'myapp.production.queues' # Environment-specific
|
901
|
+
})
|
902
|
+
|
903
|
+
# Use meaningful consumer group names
|
904
|
+
transport.where
|
905
|
+
.to('order_service')
|
906
|
+
.consumer_group('order_processors') # Descriptive group name
|
907
|
+
.subscribe
|
908
|
+
```
|
909
|
+
|
910
|
+
### Resource Management
|
911
|
+
|
912
|
+
```ruby
|
913
|
+
# Proper cleanup on shutdown
|
914
|
+
at_exit do
|
915
|
+
transport.disconnect
|
916
|
+
transport.cleanup_consumer_groups
|
917
|
+
end
|
918
|
+
|
919
|
+
# Connection pooling for high-throughput applications
|
920
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new({
|
921
|
+
pool_size: 10,
|
922
|
+
pool_timeout: 5
|
923
|
+
})
|
924
|
+
```
|
925
|
+
|
926
|
+
### Monitoring and Observability
|
927
|
+
|
928
|
+
```ruby
|
929
|
+
# Regular health monitoring with Async
|
930
|
+
Async do
|
931
|
+
loop do
|
932
|
+
stats = transport.queue_stats
|
933
|
+
|
934
|
+
# Log queue lengths
|
935
|
+
stats.each do |queue, info|
|
936
|
+
if info[:length] > 100
|
937
|
+
Rails.logger.warn "Queue #{queue} backing up: #{info[:length]} messages"
|
938
|
+
end
|
939
|
+
end
|
940
|
+
|
941
|
+
# Check consumer health
|
942
|
+
dead_consumers = stats.select { |_, info| info[:consumers] == 0 && info[:length] > 0 }
|
943
|
+
unless dead_consumers.empty?
|
944
|
+
Rails.logger.error "Queues with no consumers: #{dead_consumers.keys}"
|
945
|
+
end
|
946
|
+
|
947
|
+
sleep 30
|
948
|
+
end
|
949
|
+
end
|
950
|
+
```
|
951
|
+
|
952
|
+
### Performance Optimization
|
953
|
+
|
954
|
+
```ruby
|
955
|
+
# Batch processing for high throughput
|
956
|
+
transport.where
|
957
|
+
.to('batch_processor')
|
958
|
+
.subscribe do |message_class, message_data|
|
959
|
+
# Process in batches
|
960
|
+
batch = []
|
961
|
+
|
962
|
+
batch << JSON.parse(message_data)
|
963
|
+
|
964
|
+
if batch.size >= 10 # Process every 10 messages
|
965
|
+
process_batch(batch)
|
966
|
+
batch.clear
|
967
|
+
end
|
968
|
+
end
|
969
|
+
|
970
|
+
# Use appropriate block times
|
971
|
+
fast_transport = SmartMessage::Transport::RedisQueueTransport.new({
|
972
|
+
block_time: 100 # Low latency, higher CPU usage
|
973
|
+
})
|
974
|
+
|
975
|
+
efficient_transport = SmartMessage::Transport::RedisQueueTransport.new({
|
976
|
+
block_time: 5000 # Higher latency, lower CPU usage
|
977
|
+
})
|
978
|
+
```
|
979
|
+
|
980
|
+
## Migration from Other Transports
|
981
|
+
|
982
|
+
### From Redis Pub/Sub
|
983
|
+
|
984
|
+
```ruby
|
985
|
+
# Old Redis Pub/Sub approach
|
986
|
+
class OrderMessage < SmartMessage::Base
|
987
|
+
transport :redis # Simple pub/sub, everyone gets message
|
988
|
+
end
|
989
|
+
|
990
|
+
OrderMessage.subscribe # All subscribers get ALL messages
|
991
|
+
|
992
|
+
# New Redis Queue approach
|
993
|
+
class OrderMessage < SmartMessage::Base
|
994
|
+
transport :redis_queue # Queue-based with routing
|
995
|
+
end
|
996
|
+
|
997
|
+
# Targeted subscriptions
|
998
|
+
OrderMessage.where.to('order_processor').subscribe # Only my orders
|
999
|
+
OrderMessage.where.from('api_gateway').subscribe # Only from gateway
|
1000
|
+
OrderMessage.subscribe_pattern("#.*.order_service") # RabbitMQ-style patterns
|
1001
|
+
```
|
1002
|
+
|
1003
|
+
### From RabbitMQ
|
1004
|
+
|
1005
|
+
```ruby
|
1006
|
+
# RabbitMQ configuration
|
1007
|
+
class OrderMessage < SmartMessage::Base
|
1008
|
+
transport :rabbitmq, {
|
1009
|
+
exchange: 'orders',
|
1010
|
+
queue: 'order_processing'
|
1011
|
+
}
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
# Equivalent Redis Queue configuration
|
1015
|
+
class OrderMessage < SmartMessage::Base
|
1016
|
+
transport :redis_queue, {
|
1017
|
+
queue_prefix: 'orders',
|
1018
|
+
consumer_group: 'order_processors'
|
1019
|
+
}
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
# Pattern subscriptions work identically
|
1023
|
+
transport.subscribe_pattern("#.*.order_service") # Same syntax!
|
1024
|
+
```
|
1025
|
+
|
1026
|
+
## Troubleshooting
|
1027
|
+
|
1028
|
+
### Common Issues
|
1029
|
+
|
1030
|
+
**Queue Not Processing Messages**
|
1031
|
+
```ruby
|
1032
|
+
# Check queue stats
|
1033
|
+
stats = transport.queue_stats
|
1034
|
+
puts "Queue stats: #{stats}"
|
1035
|
+
|
1036
|
+
# Verify pattern matching
|
1037
|
+
pattern = "#.*.my_service"
|
1038
|
+
test_key = "order.ordermessage.api_gateway.my_service"
|
1039
|
+
matches = transport.send(:routing_key_matches_pattern?, test_key, pattern)
|
1040
|
+
puts "Pattern '#{pattern}' matches '#{test_key}': #{matches}"
|
1041
|
+
```
|
1042
|
+
|
1043
|
+
**High Memory Usage**
|
1044
|
+
```ruby
|
1045
|
+
# Monitor queue lengths
|
1046
|
+
stats = transport.queue_stats
|
1047
|
+
long_queues = stats.select { |_, info| info[:length] > 1000 }
|
1048
|
+
puts "Long queues: #{long_queues}"
|
1049
|
+
|
1050
|
+
# Configure queue limits
|
1051
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new({
|
1052
|
+
max_queue_length: 5000 # Trim at 5k messages
|
1053
|
+
})
|
1054
|
+
```
|
1055
|
+
|
1056
|
+
**Consumer Not Receiving Messages**
|
1057
|
+
```ruby
|
1058
|
+
# Check consumer group membership
|
1059
|
+
consumer_info = transport.consumer_group_info("my_workers")
|
1060
|
+
puts "Consumers: #{consumer_info}"
|
1061
|
+
|
1062
|
+
# Verify routing table
|
1063
|
+
routing_table = transport.routing_table
|
1064
|
+
puts "Routing table: #{routing_table}"
|
1065
|
+
```
|
1066
|
+
|
1067
|
+
### Debug Mode
|
1068
|
+
|
1069
|
+
```ruby
|
1070
|
+
# Enable comprehensive debugging
|
1071
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new({
|
1072
|
+
debug: true,
|
1073
|
+
log_level: :debug
|
1074
|
+
})
|
1075
|
+
|
1076
|
+
# Monitor Redis commands
|
1077
|
+
# In separate terminal: redis-cli MONITOR
|
1078
|
+
```
|
1079
|
+
|
1080
|
+
### Performance Debugging
|
1081
|
+
|
1082
|
+
```ruby
|
1083
|
+
# Measure publishing performance
|
1084
|
+
start_time = Time.now
|
1085
|
+
1000.times { |i| TestMessage.new(id: i).publish }
|
1086
|
+
duration = Time.now - start_time
|
1087
|
+
puts "Published 1000 messages in #{duration}s (#{1000/duration} msg/s)"
|
1088
|
+
|
1089
|
+
# Measure processing performance
|
1090
|
+
processed = 0
|
1091
|
+
start_time = Time.now
|
1092
|
+
|
1093
|
+
transport.subscribe_pattern("#.*.*") do |message_class, message_data|
|
1094
|
+
processed += 1
|
1095
|
+
if processed % 100 == 0
|
1096
|
+
rate = processed / (Time.now - start_time)
|
1097
|
+
puts "Processed #{processed} messages (#{rate} msg/s)"
|
1098
|
+
end
|
1099
|
+
end
|
1100
|
+
```
|
1101
|
+
|
1102
|
+
## API Reference
|
1103
|
+
|
1104
|
+
### Transport Class
|
1105
|
+
|
1106
|
+
```ruby
|
1107
|
+
class SmartMessage::Transport::RedisQueueTransport < Base
|
1108
|
+
# Configuration
|
1109
|
+
def initialize(options = {})
|
1110
|
+
def configure
|
1111
|
+
def connected?
|
1112
|
+
def connect
|
1113
|
+
def disconnect
|
1114
|
+
|
1115
|
+
# Publishing
|
1116
|
+
def do_publish(message_class, serialized_message)
|
1117
|
+
|
1118
|
+
# Subscriptions
|
1119
|
+
def subscribe(message_class, process_method, filter_options = {})
|
1120
|
+
def subscribe_pattern(pattern, &block)
|
1121
|
+
def unsubscribe(message_class, process_method)
|
1122
|
+
def unsubscribe!(message_class)
|
1123
|
+
|
1124
|
+
# Convenience methods
|
1125
|
+
def subscribe_to_recipient(recipient_id)
|
1126
|
+
def subscribe_from_sender(sender_id)
|
1127
|
+
def subscribe_to_type(message_type)
|
1128
|
+
def subscribe_to_broadcasts
|
1129
|
+
def subscribe_to_alerts
|
1130
|
+
|
1131
|
+
# Fluent API
|
1132
|
+
def where
|
1133
|
+
|
1134
|
+
# Management
|
1135
|
+
def queue_stats
|
1136
|
+
def routing_table
|
1137
|
+
def queue_length(queue_name)
|
1138
|
+
def clear_queue(queue_name)
|
1139
|
+
def clear_all_queues
|
1140
|
+
def consumer_group_info(group_name)
|
1141
|
+
|
1142
|
+
# Health and monitoring
|
1143
|
+
def health_check
|
1144
|
+
def performance_metrics
|
1145
|
+
def dead_letter_stats
|
1146
|
+
end
|
1147
|
+
```
|
1148
|
+
|
1149
|
+
### Fluent API Builder
|
1150
|
+
|
1151
|
+
```ruby
|
1152
|
+
class SmartMessage::Transport::RedisQueueSubscriptionBuilder
|
1153
|
+
def from(sender_id)
|
1154
|
+
def to(recipient_id)
|
1155
|
+
def type(message_type)
|
1156
|
+
def consumer_group(group_name)
|
1157
|
+
def build # Returns pattern string
|
1158
|
+
def subscribe(&block) # Subscribes with built pattern
|
1159
|
+
end
|
1160
|
+
```
|
1161
|
+
|
1162
|
+
## Configuration Reference
|
1163
|
+
|
1164
|
+
### Transport Options
|
1165
|
+
|
1166
|
+
| Option | Type | Default | Description |
|
1167
|
+
|--------|------|---------|-------------|
|
1168
|
+
| `url` | String | `redis://localhost:6379` | Redis connection URL |
|
1169
|
+
| `db` | Integer | `0` | Redis database number |
|
1170
|
+
| `queue_prefix` | String | `smart_message.queue` | Prefix for queue names |
|
1171
|
+
| `consumer_group` | String | `default_workers` | Consumer group name |
|
1172
|
+
| `consumer_id` | String | `#{hostname}_#{pid}` | Unique consumer identifier |
|
1173
|
+
| `block_time` | Integer | `1000` | Blocking timeout in milliseconds |
|
1174
|
+
| `max_queue_length` | Integer | `10000` | Maximum messages per queue |
|
1175
|
+
| `batch_size` | Integer | `1` | Messages to process per batch |
|
1176
|
+
| `max_retries` | Integer | `3` | Maximum retry attempts |
|
1177
|
+
| `retry_delay` | Integer | `5` | Seconds between retries |
|
1178
|
+
| `exponential_backoff` | Boolean | `false` | Use exponential backoff for retries |
|
1179
|
+
| `dead_letter_queue` | Boolean | `false` | Enable dead letter queue |
|
1180
|
+
| `dead_letter_prefix` | String | `dlq` | Prefix for dead letter queues |
|
1181
|
+
| `circuit_breaker` | Boolean | `false` | Enable circuit breaker |
|
1182
|
+
| `failure_threshold` | Integer | `5` | Circuit breaker failure threshold |
|
1183
|
+
| `recovery_timeout` | Integer | `60` | Circuit breaker recovery timeout |
|
1184
|
+
| `pool_size` | Integer | `1` | Connection pool size |
|
1185
|
+
| `pool_timeout` | Integer | `1` | Connection pool timeout |
|
1186
|
+
| `debug` | Boolean | `false` | Enable debug logging |
|
1187
|
+
|
1188
|
+
## Examples
|
1189
|
+
|
1190
|
+
The `examples/redis_queue/` directory contains comprehensive, production-ready examples demonstrating Redis Queue Transport capabilities:
|
1191
|
+
|
1192
|
+
### Core Queue Messaging
|
1193
|
+
- **[01_basic_messaging.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/01_basic_messaging.rb)** - Foundation queue messaging patterns
|
1194
|
+
- Basic message publishing and consumption
|
1195
|
+
- Consumer group configuration
|
1196
|
+
- Queue-based message processing
|
1197
|
+
|
1198
|
+
- **[01_comprehensive_examples.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/01_comprehensive_examples.rb)** - Complete feature demonstration
|
1199
|
+
- All Redis Queue Transport capabilities in one example
|
1200
|
+
- Pattern routing, load balancing, error handling
|
1201
|
+
- Production patterns and best practices
|
1202
|
+
|
1203
|
+
### Advanced Routing & Patterns
|
1204
|
+
- **[02_pattern_routing.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/02_pattern_routing.rb)** - RabbitMQ-style pattern routing
|
1205
|
+
- Topic-based routing with patterns
|
1206
|
+
- Wildcard subscriptions (`#`, `*`)
|
1207
|
+
- Complex routing scenarios
|
1208
|
+
|
1209
|
+
- **[03_fluent_api.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/03_fluent_api.rb)** - Fluent API usage examples
|
1210
|
+
- Chain-based subscription configuration
|
1211
|
+
- Dynamic routing with builder pattern
|
1212
|
+
- Readable subscription management
|
1213
|
+
|
1214
|
+
### Production Features
|
1215
|
+
- **[04_load_balancing.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/04_load_balancing.rb)** - Load balancing across workers
|
1216
|
+
- Consumer group scaling
|
1217
|
+
- Work distribution patterns
|
1218
|
+
- High-availability setups
|
1219
|
+
|
1220
|
+
- **[05_microservices.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/05_microservices.rb)** - Microservices communication
|
1221
|
+
- Service-to-service messaging
|
1222
|
+
- Distributed system coordination
|
1223
|
+
- Cross-service event handling
|
1224
|
+
|
1225
|
+
- **[06_emergency_alerts.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/06_emergency_alerts.rb)** - Emergency alert system
|
1226
|
+
- Priority message handling
|
1227
|
+
- Alert escalation workflows
|
1228
|
+
- Critical message processing
|
1229
|
+
|
1230
|
+
- **[07_queue_management.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/07_queue_management.rb)** - Queue management utilities
|
1231
|
+
- Queue monitoring and statistics
|
1232
|
+
- Dead letter queue handling
|
1233
|
+
- Performance optimization techniques
|
1234
|
+
|
1235
|
+
### Enhanced Transport Integration
|
1236
|
+
- **[enhanced_01_basic_patterns.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/enhanced_01_basic_patterns.rb)** - Enhanced transport patterns
|
1237
|
+
- **[enhanced_02_fluent_api.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/enhanced_02_fluent_api.rb)** - Enhanced fluent API
|
1238
|
+
- **[enhanced_03_dual_publishing.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/enhanced_03_dual_publishing.rb)** - Dual publishing strategies
|
1239
|
+
- **[enhanced_04_advanced_routing.rb](https://github.com/MadBomber/smart_message/blob/main/examples/redis_queue/enhanced_04_advanced_routing.rb)** - Advanced routing scenarios
|
1240
|
+
|
1241
|
+
### Key Features Demonstrated
|
1242
|
+
|
1243
|
+
The Redis Queue examples showcase enterprise-grade messaging capabilities:
|
1244
|
+
- **Persistent Queues** - Guaranteed message delivery with Redis Streams
|
1245
|
+
- **Load Balancing** - Work distribution across consumer groups
|
1246
|
+
- **Pattern Routing** - RabbitMQ-style topic routing (`user.orders.#`)
|
1247
|
+
- **Dead Letter Queues** - Failed message handling and replay
|
1248
|
+
- **Circuit Breakers** - Resilient error handling and recovery
|
1249
|
+
- **Performance Monitoring** - Queue statistics and health monitoring
|
1250
|
+
- **Fluent API** - Builder pattern for complex subscriptions
|
1251
|
+
|
1252
|
+
### Running Examples
|
1253
|
+
|
1254
|
+
```bash
|
1255
|
+
# Prerequisites: Start Redis server (version 5.0+)
|
1256
|
+
redis-server
|
1257
|
+
|
1258
|
+
# Navigate to the SmartMessage directory
|
1259
|
+
cd smart_message
|
1260
|
+
|
1261
|
+
# Run queue transport examples
|
1262
|
+
ruby examples/redis_queue/01_basic_messaging.rb
|
1263
|
+
ruby examples/redis_queue/02_pattern_routing.rb
|
1264
|
+
ruby examples/redis_queue/04_load_balancing.rb
|
1265
|
+
|
1266
|
+
# Monitor queue activity
|
1267
|
+
redis-cli XINFO GROUPS smart_message.queue.TestMessage
|
1268
|
+
redis-cli XLEN smart_message.queue.TestMessage
|
1269
|
+
```
|
1270
|
+
|
1271
|
+
### Example Patterns
|
1272
|
+
|
1273
|
+
The queue examples demonstrate these routing patterns:
|
1274
|
+
- **Service patterns**: `user.orders.#`, `payment.*.processed`
|
1275
|
+
- **Priority routing**: `urgent.*`, `normal.*`, `bulk.*`
|
1276
|
+
- **Geographic routing**: `*.us-east.#`, `*.eu-west.#`
|
1277
|
+
- **System patterns**: `system.alerts.#`, `monitoring.*.stats`
|
1278
|
+
|
1279
|
+
### Production Features
|
1280
|
+
|
1281
|
+
The Redis Queue examples demonstrate enterprise requirements:
|
1282
|
+
- **High Availability** - Consumer group failover and recovery
|
1283
|
+
- **Scalability** - Horizontal scaling with multiple workers
|
1284
|
+
- **Durability** - Message persistence with Redis Streams
|
1285
|
+
- **Observability** - Comprehensive monitoring and metrics
|
1286
|
+
- **Error Handling** - Dead letter queues and retry mechanisms
|
1287
|
+
|
1288
|
+
Each example includes detailed logging, error handling, and demonstrates production-ready patterns for mission-critical messaging systems.
|
1289
|
+
|
1290
|
+
### Additional Resources
|
1291
|
+
|
1292
|
+
For comprehensive Redis Queue documentation:
|
1293
|
+
- **[Redis Queue Getting Started Guide](../guides/redis-queue-getting-started.md)** - Step-by-step setup
|
1294
|
+
- **[Redis Queue Production Guide](../guides/redis-queue-production.md)** - Production deployment
|
1295
|
+
- **[Redis Queue Patterns Guide](../guides/redis-queue-patterns.md)** - Advanced messaging patterns
|
1296
|
+
|
1297
|
+
## Related Documentation
|
1298
|
+
|
1299
|
+
- [Redis Transport](redis-transport.md) - Basic Redis pub/sub transport
|
1300
|
+
- [Redis Enhanced Transport](redis-enhanced-transport.md) - Enhanced routing capabilities
|
1301
|
+
- [Transport Comparison](redis-transport-comparison.md) - Compare all Redis transports
|
1302
|
+
- [Transport Overview](../reference/transports.md) - All available transports
|
1303
|
+
|
1304
|
+
The Redis Queue Transport represents the pinnacle of SmartMessage's transport evolution, combining the simplicity of Redis with the sophistication of enterprise message brokers. Its unique architecture delivers both the performance characteristics developers need and the routing intelligence that complex applications demand.
|