灵药中同时执行和后台任务?

时间:2018-10-07 15:24:10

标签: concurrency elixir phoenix-framework ecto

我在控制器中有一个发送大量文本消息的功能。(1000条消息) 控制器功能如下所示。

def send_sms(conn, _params) do
  // code is abbreviated
  # Spawn new process for sending message and saving to database
  spawn(fn -> send_message_and_save(user, recipients, phone_numbers) end)
  // code is abbreviated
end

和此功能

def send_message_and_save(user, recipients, phone_numbers) do
    // code is abbreviated 
    results = Sms.send_sms_with_messaging_service_async(phone_numbers, recipients.message, msg_sid, status_callback, account, token)

    case Sales.confirm_order(recipients, attrs) do
      {:ok, %{id: order_id, user_id: user_id}} ->
        Messenger.create_message_status(results, order_id, user_id)
        # Update Bitly status as Saved
        if is_nil(recipients.bitly_id) do
        else
          bitly = Texting.Bitly.get_bitly_by_id(recipients.bitly_id)
          Bitly.confirm_changeset(bitly) |> Bitly.update()
        end
        {:ok, "Message sent successfully. Your analytics data will be updated shortly."}
      {:error, _changeset} ->
        {:error, "Can't send message!"}
    end
end

send_message_and_save函数中的作业过程是这样的 1.使用外部api请求发送1000条消息。 -我是使用Task.Supervisor完成的。async_stream/6 2. confrim_order(将订单模式标记为“已确认”状态)并进行更新(更新) 3. create_message_status(创建操作) 4. get_bitly_by_id(获取操作) 5. Bitly.confirm_changeset和Bitly.update(更新操作)

在此过程中,总共将发生5000个数据库操作。 向外部api请求发出请求后,对于每个外部api请求,将有三个状态回调请求发送到我的网络服务器。 这意味着,发送1000条消息将对message_status模式(接受,已交付或未交付)进行3000万次更新操作。

因此,发送1000条消息将导致8000个数据库操作,而我尝试这样做,由于超时,我的网站变得缓慢且缺少一些状态回调请求(外部api说“连接超时可能有很多原因发生;常见原因是长时间运行的数据库查询或外部进程以及对外部系统的调用需要很长时间才能返回”)

那么我该如何改善这种情况?我该如何正确设计? 请帮助:(

1 个答案:

答案 0 :(得分:0)

我将首先使用ETS as a cache-您知道您需要从数据库中预先提取所有信息,因此请继续将其加载到内存中。然后,您可以启动一个负责ETS表的进程,并且可以对内存表中的该表进行所有以后的查询,这比为每个要向其发送消息的用户进行多个DB调用要快得多。请注意,ETS表只能在本地访问**,但是如果将其包装在一个进程中,则该进程将像其他任何OTP进程一样在全球范围内可用。

许多应用程序的数据库都有瓶颈,因此(可以在许多系统上使用)数据库工作量越少越好。

**本地是指节点本地。如果仅在一台服务器上运行应用程序,那么所有内容都是本地的,您无需考虑太多。如果您打算对应用程序进行集群,则需要使用API​​将ETS表包装在一个进程中。进程在所有群集的BEAM节点上都有唯一的地址,因此您可以使用该API来访问内存表。在大多数情况下,您实际上可以将ETS视为更快的Redis。从高层次来看,它们是同一回事,但是ETS使用erlang / elixir术语,因此它没有redis的字符串序列化开销,从而使其更快。