利用 ActiveMessaging 连接消息队列及嵌入 C 和 C++ 代码
1. 利用 ActiveMessaging 连接消息队列
1.1 问题描述
公司的大部分基础设施基于异步消息传递,其中一个关键组件是中央订单处理程序。需要构建一个 Rails 应用程序,通过向中央订单处理程序发送消息来下订单,订单将存储在本地数据库中,并且应用程序要监听订单处理程序发出的订单状态消息,以提供良好的用户体验并跟踪订单的当前状态。
1.2 准备工作
- 执行创建消息基础设施的所有安装步骤。
- 从 Rails 应用程序的根目录安装 ActiveMessaging 插件:
mschmidt> script/plugin install \
> http://activemessaging.googlecode.com/svn/trunk/plugins/\
> activemessaging
1.3 解决方案
1.3.1 构建订单模型
在数据库中创建订单模型:
create_table :orders do |t|
t.column :customer, :string
t.column :product, :string
t.column :quantity, :int
t.column :status, :string, :default => 'OPEN'
t.timestamps
end
1.3.2 配置文件编辑
- broker.yml :定义消息代理的所有连接参数,使用 ActiveMQ 和 STOMP 协议:
development:
adapter: stomp
login: ""
passcode: ""
host: localhost
port: 61613
reliable: true
reconnectDelay: 5
- messaging.rb :定义要使用的所有消息队列的符号名称:
ActiveMessaging::Gateway.define do |s|
s.destination :order, '/queue/orders.input'
s.destination :order_status, '/queue/orders.status'
end
1.3.3 订单控制器实现
require 'activemessaging/processor'
class OrderController < ApplicationController
include ActiveMessaging::MessageSender
publishes_to :order
def add
order = Order.new(params[:order])
if request.post? and order.save
flash.now[:notice] = 'Order has been submitted.'
publish :order, order.to_xml
redirect_to :action => 'show_status', :id => order.id
end
end
def show_status
@order = Order.find(params[:id])
end
end
1.3.4 添加处理器
运行以下命令创建一个处理器:
mschmidt> ruby script/generate processor OrderStatus
处理器代码如下:
require 'rexml/document'
class OrderStatusProcessor < ApplicationProcessor
subscribes_to :order_status
def on_message(message)
doc = REXML::Document.new(message)
order_id = doc.root.attributes['id']
order_status = doc.root.text
order = Order.find(order_id)
order.status = order_status
order.save
logger.debug "Status of order #{order_id} is #{order_status}."
end
end
1.3.5 启动轮询器
mschmidt> ruby script/poller run
1.3.6 订单处理程序模拟器
require 'stomp'
require 'rexml/document'
class OrderHandler
attr_accessor :user, :password, :host, :port
def initialize
@user, @password = '', ''
@host, @port = 'localhost', 61613
end
def handle_orders(in_queue, out_queue)
connection = Stomp::Connection.open @user, @password, @host, @port
connection.subscribe in_queue, { :ack => 'client' }
puts "Waiting for messages in #{in_queue}."
while true
message = connection.receive
body = message.body
message_id = message.headers['message-id']
puts "Got a message: #{body} (#{message_id})"
order_status = get_order_status(body)
options = { 'persistent' => 'false' }
connection.send out_queue, order_status, options
connection.ack message_id
end
connection.disconnect
end
private
def get_order_status(body)
doc = REXML::Document.new(body)
order_id = doc.root.attributes['id']
"<order-status id='#{order_id}'>SHIPPED</order-status>"
end
end
order_handler = OrderHandler.new
order_handler.handle_orders(
'/queue/orders.input',
'/queue/orders.status'
)
1.4 系统架构
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(Rails App):::process -->|orders.input| B(Message Broker):::process
B -->|orders.input| C(Order Handler):::process
C -->|orders.status| B
B -->|orders.status| D(AM Poller):::process
D -->|invoke action| A
2. 嵌入 C 和 C++ 代码
2.1 问题描述
公司存在大量遗留的 C 或 C++ 代码,并且担心 Ruby 有时无法满足所有性能需求,需要将 C/C++ 代码直接嵌入到 Ruby 程序中。
2.2 准备工作
安装 ruby-inline gem:
$ gem install RubyInline
注意:ruby-inline 在 Microsoft Windows 系统上运行效果不佳。
2.3 解决方案
2.3.1 阶乘示例
- 纯 Ruby 实现 :
class FactorialTest
def factorial(n)
result = 1
n.downto(2) { |x| result *= x }
result
end
end
- C 实现 :
require 'inline'
class FactorialTest
inline(:C) do |builder|
builder.c <<-CSOURCE
long factorial_c(const int n) {
long result = 1, i = n;
while (i >= 2)
result *= i--;
return result;
}
CSOURCE
end
end
2.3.2 性能测试
require 'benchmark'
label_width = 6
test = FactorialTest.new
Benchmark.bm(label_width) do |x|
x.report('ruby: ') { 1.upto(10_000_000) { test.factorial(12) } }
x.report('C: ') { 1.upto(10_000_000) { test.factorial_c(12) } }
end
测试结果如下:
| 方法 | 用户时间 | 系统时间 | 总时间 | 实际时间 |
| ---- | ---- | ---- | ---- | ---- |
| ruby: | 44.490000 | 0.150000 | 44.640000 | 44.665101 |
| C: | 2.570000 | 0.010000 | 2.580000 | 2.577188 |
2.3.3 多字节整数解码示例
- Ruby 实现 :
class MultiByteIntegerReader
def self.get_multibyte_integer(raw_data)
multi_int = 0
while raw_data[0][7] == 1
multi_int = (multi_int << 7) | (raw_data.shift & 0x7f)
end
(multi_int << 7) | raw_data.shift
end
end
- C 实现 :
require 'inline'
class MultiByteIntegerReader
inline do |builder|
builder.c_singleton <<-CSOURCE
VALUE get_multibyte_integer_c(VALUE raw_data) {
unsigned int multi_int = 0;
unsigned int current_value = FIX2INT(rb_ary_shift(raw_data));
while ((current_value & 0x80)) {
multi_int = (multi_int << 7) | (current_value & 0x7f);
current_value = FIX2INT(rb_ary_shift(raw_data));
}
return INT2FIX((multi_int << 7) | current_value);
}
CSOURCE
end
end
2.3.4 性能测试
require 'benchmark'
label_width = 6
runs = 10_000_000
Benchmark.bm(label_width) do |x|
x.report('ruby: ') do
1.upto(runs) do
MultiByteIntegerReader.get_multibyte_integer([0x81, 0x06])
end
end
x.report('C: ') do
1.upto(runs) do
MultiByteIntegerReader.get_multibyte_integer_c([0x81, 0x06])
end
end
end
测试结果如下:
| 方法 | 用户时间 | 系统时间 | 总时间 | 实际时间 |
| ---- | ---- | ---- | ---- | ---- |
| ruby: | 27.990000 | 0.060000 | 28.050000 | 28.059626 |
| C: | 6.490000 | 0.000000 | 6.490000 | 6.494582 |
2.3.5 嵌入 C++ 代码示例
require 'inline'
class MessagePrinter
inline(:C) do |builder|
builder.include '<iostream>'
builder.include '<string>'
builder.add_compile_flags '-x c++', '-lstdc++'
builder.c <<-CSOURCE
void shout_message(int i, char* message) {
std::string upperCase = std::string(message);
std::transform(
upperCase.begin(), upperCase.end(),
upperCase.begin(), ::toupper
);
while (i-- > 0)
std::cout << upperCase << std::endl;
}
CSOURCE
end
end
MessagePrinter.new.shout_message 3, 'Hello, world!'
2.4 嵌入 C 和 C++ 代码的注意事项
- 数据类型转换 :Ruby Inline 会自动进行一些基本数据类型的转换,例如将整数转换为 Fixnum 实例,将 String 对象转换为 char*。但对于复杂的数据类型,如 C++ 的 std::string,没有特定的类型转换。
- 性能问题 :虽然 C 和 C++ 代码通常比 Ruby 代码快,但在实际应用中,需要考虑编译和数据转换的开销。例如,在阶乘示例中,C 函数比 Ruby 函数快约二十倍;在多字节整数解码示例中,C 版本比 Ruby 版本快约四倍。
- 错误处理 :C 和 C++ 代码可能会出现溢出等错误,而 Ruby 会自动处理一些错误情况。例如,在阶乘计算中,C 版本可能会因为整数溢出而返回错误结果,而 Ruby 会自动将 Fixnum 对象转换为 Bignum 对象。
2.5 更紧密的集成
如果需要更紧密地集成 Ruby 和 C++,可以考虑使用 Rice。Rice 是一个围绕 Ruby 的 C API 的 C++ 包装器,它可以让你更轻松地将 C++ 代码与 Ruby 的内部结构结合起来。
3. 总结
3.1 技术要点回顾
- ActiveMessaging 连接消息队列 :通过 ActiveMessaging 插件,可以方便地在 Rails 应用程序中连接消息队列,实现异步消息传递。主要步骤包括安装插件、配置消息代理和队列、创建订单模型和控制器、添加处理器以及启动轮询器。
- 嵌入 C 和 C++ 代码 :使用 ruby-inline gem 可以将 C 和 C++ 代码嵌入到 Ruby 程序中,提高性能。但需要注意数据类型转换、性能开销和错误处理等问题。
3.2 应用场景
- 消息队列 :适用于需要异步处理任务的场景,如订单处理、数据同步等。通过消息队列,可以提高系统的性能和可扩展性。
- 嵌入 C 和 C++ 代码 :适用于对性能要求较高的场景,如计算密集型任务、处理大量数据等。通过嵌入 C 和 C++ 代码,可以充分利用其高性能的特点。
3.3 未来展望
随着技术的不断发展,Ruby 和相关技术也在不断进步。例如,Ruby 1.9 版本通过将程序编译为字节码来提高性能。未来,可能会有更多的工具和方法来简化消息队列的使用和 C/C++ 代码的嵌入,进一步提高开发效率和系统性能。
3.4 操作步骤总结
3.4.1 ActiveMessaging 连接消息队列操作步骤
- 安装 ActiveMessaging 插件:
mschmidt> script/plugin install \
> http://activemessaging.googlecode.com/svn/trunk/plugins/\
> activemessaging
- 配置消息代理(broker.yml):
development:
adapter: stomp
login: ""
passcode: ""
host: localhost
port: 61613
reliable: true
reconnectDelay: 5
- 配置消息队列(messaging.rb):
ActiveMessaging::Gateway.define do |s|
s.destination :order, '/queue/orders.input'
s.destination :order_status, '/queue/orders.status'
end
- 创建订单模型:
create_table :orders do |t|
t.column :customer, :string
t.column :product, :string
t.column :quantity, :int
t.column :status, :string, :default => 'OPEN'
t.timestamps
end
- 创建订单控制器:
require 'activemessaging/processor'
class OrderController < ApplicationController
include ActiveMessaging::MessageSender
publishes_to :order
def add
order = Order.new(params[:order])
if request.post? and order.save
flash.now[:notice] = 'Order has been submitted.'
publish :order, order.to_xml
redirect_to :action => 'show_status', :id => order.id
end
end
def show_status
@order = Order.find(params[:id])
end
end
- 添加处理器:
mschmidt> ruby script/generate processor OrderStatus
处理器代码:
require 'rexml/document'
class OrderStatusProcessor < ApplicationProcessor
subscribes_to :order_status
def on_message(message)
doc = REXML::Document.new(message)
order_id = doc.root.attributes['id']
order_status = doc.root.text
order = Order.find(order_id)
order.status = order_status
order.save
logger.debug "Status of order #{order_id} is #{order_status}."
end
end
- 启动轮询器:
mschmidt> ruby script/poller run
- 启动订单处理程序模拟器:
require 'stomp'
require 'rexml/document'
class OrderHandler
attr_accessor :user, :password, :host, :port
def initialize
@user, @password = '', ''
@host, @port = 'localhost', 61613
end
def handle_orders(in_queue, out_queue)
connection = Stomp::Connection.open @user, @password, @host, @port
connection.subscribe in_queue, { :ack => 'client' }
puts "Waiting for messages in #{in_queue}."
while true
message = connection.receive
body = message.body
message_id = message.headers['message-id']
puts "Got a message: #{body} (#{message_id})"
order_status = get_order_status(body)
options = { 'persistent' => 'false' }
connection.send out_queue, order_status, options
connection.ack message_id
end
connection.disconnect
end
private
def get_order_status(body)
doc = REXML::Document.new(body)
order_id = doc.root.attributes['id']
"<order-status id='#{order_id}'>SHIPPED</order-status>"
end
end
order_handler = OrderHandler.new
order_handler.handle_orders(
'/queue/orders.input',
'/queue/orders.status'
)
3.4.2 嵌入 C 和 C++ 代码操作步骤
- 安装 ruby-inline gem:
$ gem install RubyInline
- 嵌入 C 代码示例(阶乘计算):
require 'inline'
class FactorialTest
inline(:C) do |builder|
builder.c <<-CSOURCE
long factorial_c(const int n) {
long result = 1, i = n;
while (i >= 2)
result *= i--;
return result;
}
CSOURCE
end
end
- 嵌入 C++ 代码示例:
require 'inline'
class MessagePrinter
inline(:C) do |builder|
builder.include '<iostream>'
builder.include '<string>'
builder.add_compile_flags '-x c++', '-lstdc++'
builder.c <<-CSOURCE
void shout_message(int i, char* message) {
std::string upperCase = std::string(message);
std::transform(
upperCase.begin(), upperCase.end(),
upperCase.begin(), ::toupper
);
while (i-- > 0)
std::cout << upperCase << std::endl;
}
CSOURCE
end
end
MessagePrinter.new.shout_message 3, 'Hello, world!'
3.5 流程图总结
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(开始):::process --> B{选择技术}:::process
B -->|ActiveMessaging| C(安装插件):::process
B -->|嵌入 C/C++| D(安装 ruby-inline):::process
C --> E(配置消息代理和队列):::process
E --> F(创建订单模型和控制器):::process
F --> G(添加处理器):::process
G --> H(启动轮询器):::process
H --> I(启动订单处理程序模拟器):::process
D --> J(嵌入 C/C++ 代码):::process
J --> K(测试性能):::process
I --> L(结束):::process
K --> L
通过以上的总结和操作步骤,我们可以更好地理解和应用 ActiveMessaging 连接消息队列和嵌入 C 和 C++ 代码的技术,在实际项目中提高系统的性能和可扩展性。
ActiveMessaging与C/C++集成实践
超级会员免费看
10

被折叠的 条评论
为什么被折叠?



