我开发了一个应用程序,允许我们的客户创建自己的会员保护网站。然后,我的应用程序连接到外部API服务(客户特定的api_key / api_url),以同步/更新/添加数据到此其他服务。好吧,我已经为这个其他服务编写了一个API包装器。但是,我现在看到连接为零的随机丢弃。以下是我目前正在使用连接的方式:
我有一个xml / rpc连接类
class ApiConnection
attr_accessor :api_url, :api_key, :retry_count
def initialize(url, key)
@api_url = url
@api_key = key
@retry_count = 1
end
def api_perform(class_type, method, *args)
server = XMLRPC::Client.new3({'host' => @api_url, 'path' => "/api/xmlrpc", 'port' => 443, 'use_ssl' => true})
result = server.call("#{class_type}.#{method}", @api_key, *args)
return result
end
end
我还有一个模块可以包含在我的模型中以访问和调用api方法
module ApiService
# Set account specific ApiConnection obj
def self.set_account_api_conn(url, key)
if ac = Thread.current[:api_conn]
ac.api_url, ac.api_key = url, key
else
Thread.current[:api_conn] = ApiConnection.new(url, key)
end
end
########################
### Email Service ###
########################
def api_email_optin(email, reason)
# Enables you to opt contacts in
Thread.current[:api_conn].api_perform('APIEmailService', 'optIn', email, reason)
end
### more methods here ###
end
然后在应用程序控制器中,我使用设置Thread.current [:api_conn]的before过滤器在每个请求上创建一个新的ApIConnection对象。这是因为我有数百个客户,每个客户都有自己的api_key和api_url,同时使用该应用程序。
# In before_filter of application controller
def set_api_connection
Thread.current[:api_conn] = ApiService.set_account_api_conn(url, key)
end
我的问题是,我已经读到使用Thread.current
并不是处理此问题的最理想方式,我想知道这是否是ApiConnection在随机请求中为零的原因。所以我想知道如何更好地设置这个包装器。
答案 0 :(得分:6)
回答1
我希望问题是连接完成之前的下一个请求,然后before_filter会覆盖正在进行的连接的连接。我试图远离线程。 fork_off更容易,但也有一些警告,特别是关于性能。
我尝试将这样的逻辑转移到某种后台作业。一个常见的解决方案是延迟作业https://github.com/collectiveidea/delayed_job,这样你就不必乱用线程,它更健壮,更容易调试。然后,只要有人登录,您就可以启动后台作业以异步同步服务。
@account.delay.optin_via_email(email,user)
这将序列化帐户,将其保存到作业队列,在那里它将被延迟的作业取消序列化,并且将调用延迟后的方法。您可以拥有任意数量的后台作业,甚至可以使用一些专门用于某些类型操作的作业队列(通过使用作业优先级 - 假设两个bj用于高prio作业,一个用于低prio作业)
回答2
只需将其作为对象
def before_filter
@api_connection = ApiConnection.new(url, key)
end
然后您可以在控制器方法中使用该连接
def show
#just use it straight off
@api_connection.api_perform('APIEmailService', 'optIn', email, reason)
# or send the connection as a parameter to some other class
ApiService.do_stuff(@api_connection)
end
回答3
最简单的解决方案可能就是在您需要时创建api连接
class User < ActiveRecord::Base
def api_connection
# added caching of the the connection in object
# doing this makes taking a block a little pointless but making methods take blocks
# makes the scope of incoming variables more explicit and looks better imho
# might be just as good to not keep @conn as an instance variable
@conn = ApiConnection.new(url, key) unless @conn
if block_given?
yield(@conn)
else
@conn
end
end
end
通过这种方式,您可以轻松地忘记连接的创建,并拥有一个新的方便。这可能会有性能损失,但我怀疑它们是微不足道的,除非有额外的登录请求
@user.api_connection do { |conn| conn.optin_via_email(email,user) }