akka actors使用_如果要在C ++项目中使用Actors或CSP,只需看一下SObjectizer

akka actors使用

关于SObjectizer及其历史的几句话 (A few words about SObjectizer and its history)

SObjectizer is a rather small C++ framework that simplifies the development of multithreaded applications. SObjectizer allows a developer to use approaches from Actor, Publish-Subscribe and Communicating Sequential Processes (CSP) models. It's an OpenSource project that is distributed under BSD-3-CLAUSE license.

SObjectizer是一个相当小的C ++框架,它简化了多线程应用程序的开发。 SObjectizer允许开发人员使用Actor,Publish-Subscribe和Communicating Sequential Processes(CSP)模型中的方法。 这是一个开放源代码项目,已获得BSD-3-CLAUSE许可。

SObjectizer has a long history. SObjectizer itself was born in 2002 as SObjectizer-4 project. But it was based on ideas from previous SCADA Objectizer that was developed between 1995 and 2000. SObjectizer-4 was open-sourced in 2006, but its evolution was stopped soon after that. A new version of SObjectizer with the name SObjectizer-5 was started in 2010 and was open-sourced in 2013. The evolution of SObjectizer-5 is still in progress and SObjectizer-5 has incorporated many new features since 2013.

SObjectizer历史悠久。 SObjectizer本身诞生于2002年,当时是SObjectizer-4项目。 但这是基于1995年至2000年开发的先前SCADA Objectizer的思想。SObjectizer-4于2006年开源,但此后不久就停止了其发展。 新版本的SObjectizer(名称为SObjectizer-5)于2010年启动,并于2013年开源。SObjectizer-5的演进仍在进行中,SObjectizer-5自2013年以来已合并了许多新功能。

SObjectizer is more or less known in the Russian segment of the Internet, but almost unknown outside of the exUSSR. It's because the SObjectizer was mainly used for local projects in exUSSR-countries and many articles, presentations, and talks about SObjectizer are in Russian.

SObjectizer在Internet的俄罗斯部分或多或少是众所周知的,但是在exUSSR之外几乎是未知的。 这是因为SObjectizer主要用于exUSSR国家的本地项目,许多关于SObjectizer的文章,演示文稿和讨论都是俄语的。

SObjectizer和类似工具的利基市场 (A niche for SObjectizer and similar tools)

Multithreading is used in Parallel computing as well as in Concurrent computing. But there is a big difference between Parallel and Concurrent computing. And, as a consequence, there are tools targeted Parallel computing, and there are tools for Concurrent computing, and they are different.

多线程用于并行计算以及并行计算 。 但是并行计算和并行计算之间有很大的区别。 结果,存在针对并行计算的工具,以及针对并行计算的工具,它们是不同的。

Roughly speaking, Parallel computing is about using several cores to reduce calculation times. For example, transcoding a video file from one format to another can take one hour on one CPU core, but just 15 minutes on four CPU cores. Tools like OpenMP, Intel TBB, HPX or cpp-taskflow are designed to be used in Parallel computing. And those tools support appropriate for that area approaches, like task-based or data-flow programming.

粗略地说,并行计算就是要使用几个内核来减少计算时间。 例如,在一个CPU内核上将视频文件从一种格式转码为另一种格式可能需要一个小时,而在四个CPU内核上则只需15分钟。 诸如OpenMP,Intel TBB,HPX或cpp-taskflow之类的工具旨在用于并行计算。 这些工具支持适用于该领域方法的工具,例如基于任务的编程或数据流编程。

Concurrent computing is about dealing with many (probably different) tasks at the same time. Database server or MQ-broker can be good examples: a server has to accept a connection, read and parse data from accepted connections, handle received requests (performing several actions for every request), sending responses and so on. Strictly speaking, there is no need to use multithreading in concurrent computing: all those tasks can be performed on just one worker thread. But usage of multithreading and several CPU cores can make your application more performant, scalable and responsive.

并行计算是关于同时处理许多(可能不同)的任务。 数据库服务器或MQ-broker就是一个很好的例子:服务器必须接受连接,从接受的连接中读取和解析数据,处理接收到的请求(对每个请求执行多个操作),发送响应等等。 严格来说,在并发计算中不需要使用多线程:所有这些任务都可以在一个工作线程上执行。 但是使用多线程和几个CPU内核可以使您的应用程序更具性能,可伸缩性和响应能力。

Approaches like Actor Model or CSP are intended for dealing with Concurrent computing. Good examples of usage Actors in Concurrent computing area are InfineSQL project and Yandex Message-Queue. Both of those projects use actors inside.

Actor Model或CSP之类的方法旨在处理并发计算。 在并发计算领域中使用Actor的好例子是InfineSQL项目Yandex Message-Queue 。 这两个项目都在内部使用actor。

So the tools like SObjectizer, QP/C++ or CAF, those support Actor Model, are useful in solving tasks from Concurrent computing area. It means that usage of SObjectizer probably won't give you anything in tasks like conversion of video streams. But you can get a very different result implementing a message broker on top of SObjectizer.

因此,支持Actor模型的SObjectizer,QP / C ++或CAF之类的工具对于解决并行计算领域的任务很有用。 这意味着在诸如视频流转换之类的任务中,使用SObjectizer可能不会为您提供任何帮助。 但是您可以在SObjectizer之上实现消息代理得到一个截然不同的结果。

免责声明 (Disclaimer)

Usage of Actor- or CSP models can give you huge benefits in some tasks, but there are no guarantees that those models are appropriate for your particular problem. The talk about applicability of Actor- or CSP models is beyond the scope of that article. Let's assume that Actor or/and CSP model is applicable for your tasks and you know how to use them efficiently.

使用Actor或CSP模型可以在某些任务中为您带来巨大的好处,但不能保证这些模型适合您的特定问题。 关于Actor或CSP模型的适用性的讨论不在该文章的讨论范围之内。 假设Actor或/和CSP模型适用于您的任务,并且您知道如何有效地使用它们。

SObjectizer可以给用户什么? (What SObjectizer can give to a user?)

开箱即用的无共享原则 (Shared-nothing and fire-and-forget principles just out of box)

Usage of Actors assumes the absence of any shared data. Every actor owns its data and this data is not visible to anyone else. This is shared-nothing principle that is well known in distributed application development, for an example. In multithreaded application shared-nothing principle has an important benefit: it allows to avoid such dangerous problems for work with shared data as deadlocks and data-races.

Actor的使用假定不存在任何共享数据。 每个参与者都拥有其数据,其他任何人都看不到该数据。 例如,这是分布式应用程序开发中众所周知的无共享原则 。 在多线程应用程序中,无共享原则有一个重要的好处:它可以避免使用共享数据时发生死锁和数据争用之类的危险问题。

Interaction between actors (agents) in SObjectizer is performed only via asynchronous messages. One agent sends a message to another agent and this operation doesn't block the sender (in a common case).

SObjectizer中的参与者(代理)之间的交互仅通过异步消息执行。 一个代理向另一个代理发送消息,并且此操作不会阻止发件人(在常见情况下)。

Asynchronous interaction allows using another useful principle: fire-and-forget. When some agent needs some operation to be done, it sends (fires) a message and continues its work. In most cases, the message will be received and processed.

异步交互允许使用另一个有用的原理: 即发即弃 。 当某些代理需要完成某些操作时,它将发送(触发)消息并继续其工作。 在大多数情况下,将接收并处理该消息。

For example, there can be an agent that reads accepted connections and parses incoming data. If the whole PDU is read and parsed the agent just sends that PDU to another agent-processor and returns to reading/parsing new incoming data.

例如,可以有一个代理读取已接受的连接并解析传入的数据。 如果读取并解析了整个PDU,则代理程序仅将该PDU发送给另一个代理程序处理器,然后返回读取/解析新的传入数据。

调度员 (Dispatchers)

Dispatchers are one of the cornerstones of SObjectizer. Dispatchers provide a working context (aka worker thread) on that an agent will handle incoming messages. Instead of creating worker threads (or pools of threads) manually a user creates dispatchers and binds agents to them. A user can create as many dispatchers in an application as he/she wants.

调度程序是SObjectizer的基石之一。 调度程序提供了一个工作上下文(又称工作线程),代理可以处理传入的消息。 用户无需手动创建工作线程(或线程池),而是创建调度程序并将代理绑定到它们。 用户可以根据需要在应用程序中创建任意数量的调度程序。

The best thing with dispatchers and agents in SObjectizer is the separation of concepts: dispatchers are responsible for managing working context and own message queues, agents perform application logic and do not bother about worker context. It allows moving an agent from one dispatcher to another literally by a click. Yesterday an agent worked on one_thread dispatcher, today we can rebind it to active_obj dispatcher, and tomorrow we can rebind it to thread_pool dispatcher. Without changing a line in the agent's implementation.

在SObjectizer中使用调度程序和代理的最好之处是概念的分离:调度程序负责管理工作上下文和自己的消息队列,代理执行应用程序逻辑,而不必担心工作程序上下文。 它允许通过单击从字面上将代理从一个调度程序移动到另一个调度程序。 昨天,代理在one_thread调度程序上工作,今天我们可以将其重新绑定到active_obj调度程序,明天我们可以将其重新绑定到thread_pool调度程序。 无需在代理的实现中更改任何一行。

There are eight types of dispatchers in SObjectizer-5.6.0 (and another one can be found in so5extra companion project): starting from very simple ones (one_thread or thread_pool) to sophisticated ones (like adv_thread_pool or prio_dedicated_threads::one_per_prio). And a user can write its own dispatcher for specific conditions.

SObjectizer-5.6.0中八种调度程序 ( 另外一种可以在so5extra伴随项目中找到):从非常简单的调度程序(one_thread或thread_pool)到复杂的调度程序(如adv_thread_pool或prio_dedicated_threads :: one_per_prio)。 用户可以针对特定条件编写自己的调度程序。

分层状态机是内置功能 (Hierarchical state machines are built-in functionality)

Agents (actors) in SObjectizer are state machines: the reaction on an incoming message depends on the current state of the agent. SObjectizer supports most of the hierarchical state machines (HSM) features: nested states, deep- and shallow history for a state, on_enter/on_exit handlers, time limits for staying in a state. Only orthogonal states are not supported in SObjectizer now (we didn't see a necessity of that feature in our projects, and nobody asked us to add support for that feature).

SObjectizer中的代理(角色)是状态机:对传入消息的React取决于代理的当前状态。 SObjectizer支持大多数分层状态机(HSM)功能:嵌套状态,状态的深浅历史记录,on_enter / on_exit处理程序,保持状态的时间限制。 现在,SObjectizer中仅不支持正交状态(我们没有在项目中看到该功能的必要性,也没有人要求我们添加对该功能的支持)。

类似于CSP的渠道 (CSP-like channels just out of box)

There is no need to use SObjectizer's agents (aka actors). The whole application can be developed just using std::thread objects and SObjectizer's mchains (aka CSP-channels). In that case application development with SObjectizer will be somewhat similar to the development in Go language (including an analog of Go's select construct that allows to waits messages from several channels).

无需使用SObjectizer的代理(也称为actor)。 只需使用std::thread对象和SObjectizer的mchains(即CSP-channels)即可开发整个应用程序。 在这种情况下,使用SObjectizer进行应用程序开发将与使用Go语言进行开发类似(包括Go的select结构的类似物,该结构允许等待来自多个通道的消息)。

SObjectizer's mchains can have a very important feature: incorporated back-pressure mechanism. If a user creates a size-limited mchain and then tries to push a message into the full mchain the send operation can block sender for some time. It allows to solve a famous problem with a fast producer and a slow consumer.

SObjectizer的mchain可以具有非常重要的功能:合并了反压机制。 如果用户创建了大小受限制的mchain,然后尝试将消息推送到完整的mchain,则send操作可能会阻止发件人一段时间。 它可以解决快速生产者和缓慢消费者的著名问题。

SObjectizer's mchains have another interesting feature: a mchain can be used as very simple load distribution tool. Several threads can wait on receive from the same mchain at the same time. If a new message is sent to that mchain just one thread will read and handle that message.

SObjectizer的mchain具有另一个有趣的功能:mchain可以用作非常简单的负载分配工具。 多个线程可以同时等待来自同一mchain的接收 。 如果将新消息发送到该mchain,则只有一个线程将读取并处理该消息。

应用程序只有一部分可以使用SObjectizer (Only a part of an application can use SObjectizer)

There is no need to use SObjectizer in every part on an application. Just a part of an application can be developed by using SObjectizer. So if you already use Qt or wxWidgets, or Boost.Asio as the main framework for your application it is possible to use SObjectize in just one submodule of your app.

无需在应用程序的每个部分中使用SObjectizer。 使用SObjectizer可以开发应用程序的一部分。 因此,如果您已经使用Qt或wxWidgets或Boost.Asio作为应用程序的主要框架,则可以仅在应用程序的一个子模块中使用SObjectize。

We had experience on usage of SObjectizer for development of libraries which hide the usage of SObjectizer as an implementation detail. The public API of those libraries didn't expose the presence of SObjectizer at all. SObjectizer was entirely under the control of a library: the library started and stopped SObjectizer as it needed. Those libraries were used in applications that were completely unaware of the presence of SObjectizer.

我们有使用SObjectizer进行库开发的经验,这些库将SObjectizer的用法隐藏为实现细节。 这些库的公共API根本没有公开SObjectizer的存在。 SObjectizer完全在一个库的控制之下:该库根据需要启动和停止SObjectizer。 这些库用于完全不知道SObjectizer存在的应用程序中。

If SObjectizer is used only in a part of an application there is a task of communication between SObjectizer- and non-SObjectizer-parts of the application. This task is easily solved: messages from a non-SObjectizer-part to SObjectizer-part can be sent via ordinary message-delivery SObjectizer's mechanism. Messages in the opposite direction can be delivered via mchains.

如果仅在应用程序的一部分中使用SObjectizer,则需要在应用程序的SObjectizer部件和非SObjectizer部件之间进行通信。 这个任务很容易解决:从非SObjectizer部分到SObjectizer部分的消息可以通过普通的消息传递SObjectizer机制发送。 可以通过mchain传递相反方向的消息。

您可以同时运行多个SObjectizer实例 (You can run several instances of SObjectizer at the same time)

SObjectizer allows run of several instances of SObjectizer (called SObjectizer Environment) in one application at the same time. Every SObjectizer Environment will be independent of other such environments.

SObjectizer允许在一个应用程序中同时运行多个SObjectizer实例(称为SObjectizer Environment)。 每个SObjectizer环境都将独立于其他此类环境。

This feature is invaluable in situations where you have to build an application from several independent modules. Some modules can use SObjectizer, some don't. Those modules that require SObjectizer can run its copy of SObjectizer Environment and that won't have an influence on other modules in the application.

在必须从多个独立模块构建应用程序的情况下,此功能非常有用。 有些模块可以使用SObjectizer,有些则不能。 那些需要SObjectizer的模块可以运行其SObjectizer Environment的副本,并且不会对应用程序中的其他模块产生影响。

计时器是SObjectizer的一部分 (Timers are part of SObjectizer)

Support of timers in the form of delayed and periodic messages is another of the cornerstones of SObjectizer. SObjectizer has several implementations of timer mechanisms (timer_wheel, timer_heap, and timer_list) and can handle tens, hundreds and thousands of millions of timers in an application. A user can choose the most appropriate timer mechanism for an application. Moreover, a user can provide its own implementation of timer_thread/timer_manager if none of the standard ones is appropriate for the user's conditions.

支持延迟和定期消息形式的计时器是SObjectizer的另一个基础。 SObjectizer具有计时器机制的几种实现(timer_wheel,timer_heap和timer_list),并且可以处理应用程序中数以千万计的计时器。 用户可以为应用程序选择最合适的计时器机制。 此外,如果没有标准的标准程序适合用户的条件,则用户可以提供自己的timer_thread / timer_manager实现。

SObjectizer具有各种自定义点和调整选项 (SObjectizer has various customization points and tuning options)

SObjectizer allows customization of several important mechanisms. For example, a user can select one of the standard implementations of timer_thread (or timer_manager). Or can provide its own implementation. A user can select an implementation of lock objects used by message queues in SObjectizer's dispatchers. Or can provide its own implementation.

SObjectizer允许自定义几种重要的机制。 例如,用户可以选择timer_thread(或timer_manager)的标准实现之一。 或者可以提供自己的实现。 用户可以选择由SObjectizer的调度程序中的消息队列使用的锁定对象的实现。 或者可以提供自己的实现。

A user can implement its own dispatcher. A user can implement its own message box. A user can implement its own message envelope. A user can implement its own event_queue_hook. And so on.

用户可以实现自己的调度程序。 用户可以实现自己的消息框。 用户可以实现自己的消息信封。 用户可以实现自己的event_queue_hook。 等等。

在哪里可以使用SObjectizer? (Where SObjectizer can or can't be used?)

It's much easier to say where SObjectizer can't be used by objective reasons. So we start the discussion by enumerating such areas and then we'll give some examples of the usage of SObjectizer in the past (and not only in the past).

说出出于客观原因不能使用SObjectizer的地方要容易得多。 因此,我们通过列举这些领域来开始讨论,然后再给出一些过去(不仅是过去)使用SObjectizer的示例。

无法在哪里使用SObjectizer? (Where SObjectizer can't be used?)

As has been said above Actor- and CSP-models is not a good choice for high-performance computing and other areas of Parallel computing. So if you have to multiple matrixes or transcode video streams then tools like OpenMP, Intel TBB, cpp-taskflow, HPX or MPI will be more suitable.

如上所述,Actor模型和CSP模型对于高性能计算和并行计算的其他领域不是一个好的选择。 因此,如果您必须使用多个矩阵或对视频流进行转码,那么像OpenMP,Intel TBB,cpp-taskflow,HPX或MPI之类的工具将更适合。

硬实时系统 (Hard real-time systems)

Despite the fact that SObjectizer has its roots in SCADA-systems the current implementation of SObjectizer (aka SObjectizer-5) can't be used in hard real-time systems. It is mainly because of the usage of dynamic memory in SObjectizer implementation: messages are dynamically allocated objects (however, SObjectizer can use preallocated objects as messages), dispatchers use dynamic memory for message queues, even time-limits for agent's states use dynamically allocated objects to perform time-checking.

尽管SObjectizer根植于SCADA系统,但当前的SObjectizer实现(即SObjectizer-5)不能用于硬实时系统中。 这主要是由于SObjectizer实现中使用了动态内存:消息是动态分配的对象(但是,SObjectizer可以将预分配的对象用作消息),调度程序将动态内存用于消息队列,甚至代理状态的时限也使用动态分配的对象执行时间检查。

Unfortunately, the term "real-time" is heavily overused in the modern world. It is often said about real-time web services, like "real-time web application" or "real-time web analytics" and so on. The term "on-line" or "live" is more appropriate for such applications than the term "real-time", even in "soft real-time" form. Thus, if we speak about something like "real-time web application" then SObjectizer can easily be used in such "real-time" systems.

不幸的是,“实时”一词在现代世界中被大量使用。 人们经常谈论实时Web服务,例如“实时Web应用程序”或“实时Web分析”等。 术语“在线”或“实时”比术语“实时”更适用于此类应用程序,即使是“软实时”形式也是如此。 因此,如果我们谈论诸如“实时Web应用程序”之类的东西,那么SObjectizer可以轻松地用于此类“实时”系统中。

受限的嵌入式系统 (Constrained embedded systems)

SObjectizer relies on the С++ standard library: std::thread is used for thread management, std::atomic, std::mutex, std::condition_variable are used for data synchronization, RTTI and dynamic_cast are used insize SObjectizer (for example, std::type_index are used for message type identification), C++ exceptions are used for error reporting.

SObjectizer依赖于С++标准库: std::thread用于线程管理, std::atomicstd::mutexstd::condition_variable用于数据同步,RTTI和dynamic_cast用于SObjectizer的大小(例如, std::type_index用于消息类型识别),C ++异常用于错误报告。

It means that SObjectizer can't be used in environments where such facilities of the standard library are not available. For example, in the development of constrained embedded systems where only a part of C++ and C++ stdlib can be used.

这意味着SObjectizer不能在标准库的此类功能不可用的环境中使用。 例如,在受限嵌入式系统的开发中,只能使用C ++和C ++ stdlib的一部分。

过去在哪里使用过SObjectizer? (Where SObjectizer was used in the past?)

Now we try to speak briefly about some use-cases of SObjectizer's usage in the past (and not only in the past). Unfortunately, it isn't full information because there are some problems.

现在,我们尝试简要地谈论一下过去(不仅是过去)SObjectizer用法的一些用例。 不幸的是,由于存在一些问题,它不是完整的信息。

First of all, we don't know about all the usages of SObjectizer. SObjectizer is free software that can be used even in proprietary projects. So some people just get SObjectizer and use it without providing any feedback for us. Sometimes we acquire some information about SObjectizer's usage (but without any details), sometimes we know nothing.

首先,我们不了解SObjectizer的所有用法。 SObjectizer是免费软件,即使在专有项目中也可以使用。 因此,有些人只是获得SObjectizer并使用它而没有为我们提供任何反馈。 有时我们获得有关SObjectizer用法的一些信息(但没有任何细节),有时我们一无所知。

The second problem is permission to share information about the usage of SObjectizer in a particular project. We have received that permission very rarely, in most cases users of SObjectizer do not want to open implementation details of their projects (sometimes we understand the reasons, sometimes don't).

第二个问题是在特定项目中共享有关SObjectizer使用情况的信息的权限。 我们很少获得该许可,在大多数情况下,SObjectizer的用户都不希望打开其项目的实现详细信息(有时我们了解原因,有时却不了解)。

We apologize for the fact that the information provided looks so scarce and does not contain any details. Nevertheless, there are some examples of usage of SObjectizer:

对于所提供的信息如此稀缺且不包含任何详细信息,我们深表歉意。 尽管如此,仍有一些使用SObjectizer的示例:

  • SMS/USSD aggregation gateway that handles more than 500M of messages per month;

    SMS / USSD聚合网关,每月处理超过500M消息;
  • part of the system serving online payments via ATMs of one of the biggest Russian banks;

    通过最大的俄罗斯银行之一的ATM机进行在线支付的系统的一部分;
  • simulation modeling of economic processes (as part of Ph.D. research);

    经济过程的模拟建模(作为博士学位研究的一部分);
  • distributed data acquisition and analytic system. Data collected on points distributed worldwide by the commands from the central node. MQTT was used as a transport for control and acquired data distribution;

    分布式数据采集与分析系统。 通过中央节点的命令在全球分布的点上收集的数据。 MQTT用作控制和获取数据分发的传输;
  • testing environment for checking real-time control system for railway equipment;

    检验铁路设备实时控制系统的测试环境;
  • automatic control system for theatre scenery. More details can be found here;

    剧院风光自动控制系统。 更多细节可以在这里找到;

  • components of data management platform in an online advertising system.

    在线广告系统中数据管理平台的组件。

SObjectizer的味道 (A taste of SObjectizer)

Let's see several simple examples to take some taste of SObjectizer. Those are very simple examples which, we hope, do not require additional explanations excluding the comments in the code.

让我们看几个简单的例子,以品尝一下SObjectizer。 这些是非常简单的示例,我们希望它们不需要除代码中的注释之外的其他解释。

Actor Model风格的传统“ Hello,World”示例 (The traditional "Hello, World" example in Actor Model's style)

The simplest example with just one agent that reacts to hello message and finishes its work:

最简单的示例,其中只有一个代理对hello消息做出React并完成其工作:

#include <so_5/all.hpp>

// Message to be sent to an agent.
struct hello {
    std::string greeting_;
};

// Demo agent.
class demo final : public so_5::agent_t {
    void on_hello(mhood_t<hello> cmd) {
        std::cout << "Greeting received: " << cmd->greeting_ << std::endl;
        // Now agent can finish its work.
        so_deregister_agent_coop_normally();
    }

public:
    // There is no need is a separate constructor.
    using so_5::agent_t::agent_t;

    // Preparation of agent to work inside SObjectizer.
    void so_define_agent() override {
        // Subscription to 'hello' message.
        so_subscribe_self().event(&demo::on_hello);
    }
};

int main() {
    // Run SObjectizer instance.
    so_5::launch([](so_5::environment_t & env) {
        // Make and register an instance of demo agent.
        auto mbox = env.introduce_coop([](so_5::coop_t & coop) {
            auto * a = coop.make_agent<demo>();
            return a->so_direct_mbox();
        });

        // Send hello message to registered agent.
        so_5::send<hello>(mbox, "Hello, World!");
    });
}

带有代理和发布/订阅模型的“ Hello,World”的另一个版本 (Another version of "Hello, World" with agents and Publish/Subscribe model)

The simplest example with several agents, all of them react to the same instance of hello message:

具有几个代理的最简单的示例,它们全部对相同的hello消息实例做出React:

#include <so_5/all.hpp>

using namespace std::string_literals;

// Message to be sent to an agent.
struct hello {
    std::string greeting_;
};

// Demo agent.
class demo final : public so_5::agent_t {
    const std::string name_;

    void on_hello(mhood_t<hello> cmd) {
        std::cout << name_ << ": greeting received: "
                << cmd->greeting_ << std::endl;

        // Now agent can finish its work.
        so_deregister_agent_coop_normally();
    }

public:
    demo(context_t ctx, std::string name, so_5::mbox_t board)
        :   agent_t{std::move(ctx)}
        ,   name_{std::move(name)}
    {
        // Create a subscription for hello message from board.
        so_subscribe(board).event(&demo::on_hello);
    }
};

int main() {
    // Run SObjectizer instance.
    so_5::launch([](so_5::environment_t & env) {
        // Mbox to be used for speading hello message.
        auto board = env.create_mbox();

        // Create several agents in separate coops.
        for(const auto & n : {"Alice"s, "Bob"s, "Mike"s})
            env.register_agent_as_coop(env.make_agent<demo>(n, board));

        // Spread hello message to all subscribers.
        so_5::send<hello>(board, "Hello, World!");
    });
}

If we run that example we can receive something like that:

如果运行该示例,我们将收到类似的内容:

Alice: greeting received: Hello, World!
Bob: greeting received: Hello, World!
Mike: greeting received: Hello, World!

CSP风格的“ Hello,World”示例 ("Hello, World" example in CSP-style)

Let's look at an example of SObjectizer without any actors, just std::thread and CSP-like channels.

让我们看一个没有任何参与者的SObjectizer示例,只是std::thread和类似CSP的通道。

非常简单的版本 (Very simple version)

This is a very simple version that is not exception safe:

这是一个非常简单的版本,并非异常安全:

#include <so_5/all.hpp>

// Message to be sent to a channel.
struct hello {
    std::string greeting_;
};

void demo_thread_func(so_5::mchain_t ch) {
    // Wait while hello received.
    so_5::receive(so_5::from(ch).handle_n(1),
        [](so_5::mhood_t<hello> cmd) {
            std::cout << "Greeting received: " << cmd->greeting_ << std::endl;
        });
}

int main() {
    // Run SObjectizer in a separate thread.
    so_5::wrapped_env_t sobj;

    // Channel to be used.
    auto ch = so_5::create_mchain(sobj);

    std::thread demo_thread{demo_thread_func, ch};

    // Send a greeting.
    so_5::send<hello>(ch, "Hello, World!");

    // Wait for demo thread.
    demo_thread.join();
}

更强大,但仍然简单的版本 (More robust, but still simple version)

This is a modified version of the example shown above with the addition of exception safety:

这是上面显示的示例的修改版本,其中增加了异常安全性:

#include <so_5/all.hpp>

// Message to be sent to a channel.
struct hello {
    std::string greeting_;
};

void demo_thread_func(so_5::mchain_t ch) {
    // Wait while hello received.
    so_5::receive(so_5::from(ch).handle_n(1),
        [](so_5::mhood_t<hello> cmd) {
            std::cout << "Greeting received: " << cmd->greeting_ << std::endl;
        });
}

int main() {
    // Run SObjectizer in a separate thread.
    so_5::wrapped_env_t sobj;

    // Demo thread. We need object now, but thread will be started later.
    std::thread demo_thread;
    // Auto-joiner for the demo thread.
    auto demo_joiner = so_5::auto_join(demo_thread);

    // Channel to be used. This channel will be automatically closed
    // in the case of an exception.
    so_5::mchain_master_handle_t ch_handle{
        so_5::create_mchain(sobj),
        so_5::mchain_props::close_mode_t::retain_content
    };

    // Now we can run demo thread.
    demo_thread = std::thread{demo_thread_func, *ch_handle};

    // Send a greeting.
    so_5::send<hello>(*ch_handle, "Hello, World!");

    // There is no need to wait for something explicitly.
}

一个相当简单的HSM示例:blinking_led (A rather simple HSM example: blinking_led)

This is a standard example from SObjectizer's distribution. The main agent of this example is a HSM that can be described by the following statechart:

这是SObjectizer发行版中的一个标准示例。 此示例的主要代理是可以通过以下状态图描述的HSM:

blinking_led statechart

The source code of the example:

该示例的源代码:

#include <iostream>

#include <so_5/all.hpp>

class blinking_led final : public so_5::agent_t
{
    state_t off{ this }, blinking{ this },
        blink_on{ initial_substate_of{ blinking } },
        blink_off{ substate_of{ blinking } };

public :
    struct turn_on_off final : public so_5::signal_t {};

    blinking_led( context_t ctx ) : so_5::agent_t{ ctx }
    {
        this >>= off;

        off.just_switch_to< turn_on_off >( blinking );

        blinking.just_switch_to< turn_on_off >( off );

        blink_on
            .on_enter( []{ std::cout << "ON" << std::endl; } )
            .on_exit( []{ std::cout << "off" << std::endl; } )
            .time_limit( std::chrono::milliseconds{1500}, blink_off );

        blink_off
            .time_limit( std::chrono::milliseconds{750}, blink_on );
    }
};

int main()
{
    try
    {
        so_5::launch( []( so_5::environment_t & env ) {
            auto m = env.introduce_coop( []( so_5::coop_t & coop ) {
                    auto led = coop.make_agent< blinking_led >();
                    return led->so_direct_mbox();
                } );

            auto pause = []( unsigned int v ) {
                std::this_thread::sleep_for( std::chrono::seconds{v} );
            };

            std::cout << "Turn blinking on for 10s" << std::endl;
            so_5::send< blinking_led::turn_on_off >( m );
            pause( 10 );

            std::cout << "Turn blinking off for 5s" << std::endl;
            so_5::send< blinking_led::turn_on_off >( m );
            pause( 5 );

            std::cout << "Turn blinking on for 5s" << std::endl;
            so_5::send< blinking_led::turn_on_off >( m );
            pause( 5 );

            std::cout << "Stopping..." << std::endl;
            env.stop();
        } );
    }
    catch( const std::exception & ex )
    {
        std::cerr << "Error: " << ex.what() << std::endl;
    }

    return 0;
}

计时器,代理程序的重载控制和active_obj调度程序 (Timers, overload control for an agent and active_obj dispatcher)

Overload control is one of the main problems for actors: message-queues for actors are unlimited usually and this can lead to uncontrolled growth of queues if a fast message producer sends messages quicker then the receiver can handle them. The following example shows such SObjectizer's feature as message limits. It allows to limit count of messages in the agent's queue and defend the receiver from redundant messages.

过载控制是参与者的主要问题之一:参与者的消息队列通常是不受限制的,并且如果快速消息生成者更快地发送消息然后接收者可以处理它们,则这可能导致队列的不受控制的增长。 以下示例显示了SObjectizer的功能,例如消息限制 。 它允许限制代理队列中消息的数量,并保护接收者免受冗余消息的侵害。

This example also shows the usage of the timer in the form of a periodic message. The binding of agents to the active_obj dispatcher is also shown there. Binding to that dispatcher means that every agent of the coop will work on own worker thread (e.g. an agent becomes an active object).

此示例还以定期消息的形式显示了计时器的用法。 此处还显示了代理程序与active_obj调度程序的绑定。 绑定到该调度程序意味着该Coop的每个代理都将在自己的工作线程上工作(例如,代理成为活动对象)。

#include <so_5/all.hpp>

using namespace std::chrono_literals;

// Message to be sent to the consumer.
struct task {
    int task_id_;
};

// An agent for utilization of unhandled tasks.
class trash_can final : public so_5::agent_t {
public:
    // There is no need is a separate constructor.
    using so_5::agent_t::agent_t;

    // Preparation of agent to work inside SObjectizer.
    void so_define_agent() override {
        // Subscription to 'task' message.
        // Event-handler is specified in the form of a lambda-function.
        so_subscribe_self().event([](mhood_t<task> cmd) {
            std::cout << "unhandled task: " << cmd->task_id_ << std::endl;
        });
    }
};

// The consumer of 'task' messages.
class consumer final : public so_5::agent_t {
public:
    // We need the constructor.
    consumer(context_t ctx, so_5::mbox_t trash_mbox)
        :   so_5::agent_t{ctx +
                // Only three 'task' messages can wait in the queue.
                limit_then_redirect<task>(3,
                    // All other messages will go to that mbox.
                    [trash_mbox]{ return trash_mbox; })}
    {
        // Define a reaction to incoming 'task' message.
        so_subscribe_self().event([](mhood_t<task> cmd) {
            std::cout << "handling task: " << cmd->task_id_ << std::endl;
            std::this_thread::sleep_for(75ms);
        });
    }
};

// The producer of 'test' messages.
class producer final : public so_5::agent_t {
    const so_5::mbox_t dest_;
    so_5::timer_id_t task_timer_;
    int id_counter_{};

    // Type of periodic signal to produce new 'test' message.
    struct generate_next final : public so_5::signal_t {};

    void on_next(mhood_t<generate_next>) {
        // Produce a new 'task' message.
        so_5::send<task>(dest_, id_counter_);
        ++id_counter_;

        // Should the work be stopped?
        if(id_counter_ >= 10)
            so_deregister_agent_coop_normally();
    }

public:
    producer(context_t ctx, so_5::mbox_t dest)
        :   so_5::agent_t{std::move(ctx)}
        ,   dest_{std::move(dest)}
    {}

    void so_define_agent() override {
        so_subscribe_self().event(&producer::on_next);
    }

    // This method will be automatically called by SObjectizer
    // when agent starts its work inside SObjectizer Environment.
    void so_evt_start() override {
        // Initiate a periodic message with no initial delay
        // and repetition every 25ms.
        task_timer_ = so_5::send_periodic<generate_next>(*this, 0ms, 25ms);
    }
};

int main() {
    // Run SObjectizer instance.
    so_5::launch([](so_5::environment_t & env) {
        // Make and register coop with agents.
        // All agents will be bound to active_obj dispatcher and will
        // work on separate threads.
        env.introduce_coop(
                so_5::disp::active_obj::make_dispatcher(env).binder(),
                [](so_5::coop_t & coop) {
            auto * trash = coop.make_agent<trash_can>();
            auto * handler = coop.make_agent<consumer>(trash->so_direct_mbox());
            coop.make_agent<producer>(handler->so_direct_mbox());
        });
    });
}

If we run that example we can see the following output:

如果运行该示例,我们将看到以下输出:

handling task: 0
handling task: 1
unhandled task: 5
unhandled task: 6
handling task: 2
unhandled task: 8
unhandled task: 9
handling task: 3
handling task: 4
handling task: 7

This output shows that several messages that can't fit into the defined limit are rejected and redirected to another receiver.

此输出显示拒绝了几个无法满足定义的限制的消息,并将其重定向到另一个接收者。

更多例子 (More examples)

An example that more or less similar to the code from real-life applications can be found in our Shrimp demo project. Another set of interesting examples can be found in this mini-serie about classical "dining philosophers problem": part 1 and part 2. And, of course, there are a lot of examples in SObjectizer itself.

在我们的Shrimp演示项目中可以找到一个与真实应用程序的代码大致相似的示例 。 在这个有关经典“用餐哲学家问题”的微型著作中,可以找到另一组有趣的例子: 第1 部分第2部分 。 而且,当然, SObjectizer本身有很多示例

表现如何呢? (What about the performance?)

There is a very simple answer: it is more than good enough for us. SObjectizer can distribute millions of messages per second, and the actual speed depends on the types of used dispatchers, message kinds, load profile, hardware/OS/compiler used and so on. In a real application, we usually use just a fraction of SObjectizer speed.

有一个非常简单的答案:对我们而言,这已经足够了。 SObjectizer可以每秒分发数百万条消息,而实际速度取决于所使用的调度程序的类型,消息类型,负载配置文件,所使用的硬件/ OS /编译器等。 在实际的应用程序中,我们通常仅使用SObjectizer速度的一小部分。

The performance of SObjectizer for your particular task highly depends on your task, the particular solution of that task, on your hardware or virtual environment, on the version of your compiler and your OS. So the best way to find an answer to that question is to create own benchmark that will be specific to your task and experiment with it.

SObjectizer针对特定任务的性能在很大程度上取决于您的任务,该任务的特定解决方案,您的硬件或虚拟环境,您的编译器和OS的版本。 因此,找到该问题答案的最好方法是创建一个特定于您任务的基准并进行试验。

If you want numbers from some synthetic benchmarks then there are some programs in test/so_5/bench folder of SObjectizer distribution.

如果要从某些综合基准中获取数字,则SObjectizer发行版的test / so_5 / bench文件夹中有一些程序。

关于使用不同工具进行比较的注意事项 (A note about comparison with different tools)

We think that a benchmarking game comparing the speed of different tools is a marketing game. We did an attempt in the past but quickly realized that it is just a waste of our time. So we don't play that game now. We spend our time and our resources only on benchmarks which allow us to check the absence of performance degradation, to resolve some corner cases (like performance of MPMC mboxes with big amount of subscribers or performance of an agent with hundreds of thousands of subscriptions), to speed up some SObjectizer-specific operations (like registration/deregistration of a coop).

我们认为比较不同工具速度的基准测试游戏是营销游戏。 我们过去曾进行过尝试 ,但很快意识到这只是浪费时间。 因此,我们现在不玩该游戏。 我们仅将时间和资源用于基准测试,以检查是否存在性能下降的情况,以解决一些极端情况(例如具有大量订阅者的MPMC mbox的性能或具有数十万订阅的代理的性能),加快某些SObjectizer特定的操作(例如,合作组的注册/注销)。

So we leave the comparison of speed to those who like that game and have time to play it.

因此,我们将速度的比较留给喜欢该游戏并有时间玩的人。

为什么SObjectizer看起来完全一样? (Why SObjectizer does look exactly as it is?)

There are several "actor frameworks" for C++, and all of them look different. It seems that it has some objective reasons: every framework has its unique features and targets different goals. Moreover, actors in C++ can be implemented very differently. So the main question is not "why framework X doesn't look like framework Y?", but "why framework X does look as it is?"

C ++有几个“ actor框架”,而且它们看起来都不同。 似乎有一些客观原因:每个框架都有其独特的功能并针对不同的目标。 而且,C ++中的参与者可以以非常不同的方式实现。 因此,主要问题不是“为什么框架X看起来不像框架Y?”,而是“为什么框架X看起来像原来一样?”。

Now we'll try to describe some reasons behind the main SObjectizer's features briefly. We hope it allows a better understanding of SObjectizer's abilities. But before we start it is necessary to mention one very important thing: SObjectizer has never been an experiment. It was created for solving real-life working and it has been evolving based on the real-life experience.

现在,我们将尝试简要描述SObjectizer主要功能背后的一些原因。 我们希望它可以更好地理解SObjectizer的功能。 但是在我们开始之前,有必要提到一个非常重要的事情:SObjectizer从来没有做过实验。 它是为解决现实生活中的工作而创建的,并且根据现实生活中的经验不断发展。

代理是从agent_t派生的类的对象 (Agents are objects of classes derived from agent_t)

Agents (aka actors) in SObjectzer are objects of user-defined classes which must be derived from a special class agent_t. It may look redundant in tiny toy examples, but our experience shows that approach greatly simplifies the development of real software where agents usually have the size in several hundred of lines (you can see one of the examples here, but this blog-post is in Russian). Sometimes even in several thousands of lines.

SObjectzer中的代理(也称为actor)是用户定义类的对象,必须从特殊类agent_t 。 在很小的玩具示例中,它看起来似乎是多余的,但是我们的经验表明,这种方法极大地简化了实际软件的开发,在这种情况下,代理通常具有数百行的大小(您可以在此处看到一个示例,但该博客文章在俄语)。 有时甚至数千行。

The experience shows us that a simple agent with the first version in a hundred lines becomes much fatter and complex in several next years of evolution. So, after five years you can find a monster in a thousand lines with dozens of methods.

经验表明,在接下来的几年的发展中,具有一百个版本的第一个版本的简单代理会变得更加胖和复杂。 因此,五年后,您可以使用数十种方法在一千行中找到一个怪物。

Usage of classes allows us to manage the complexity of agents. We can use inheritance of classes. And we can use template classes too. These are very useful techniques that greatly simplify the development of families of agents with similar logic inside.

使用类可以使我们管理代理的复杂性。 我们可以使用类的继承。 我们也可以使用模板类。 这些非常有用的技术可以大大简化内部具有类似逻辑的代理族的开发。

消息作为用户结构/类的对象 (Messages as objects of user structs/classes)

Messages in SObjectizer are objects of user-defined structs or classes. There are at least two reasons for that:

SObjectizer中的消息是用户定义的结构或类的对象。 至少有两个原因:

  • the development of SObjectizer-5 started in 2010 when C++11 wasn't standardized yet. So in the beginning, we couldn't use such features of C++11 as variadic templates and std::tuple class. The only choice we had was the usage of an object of a class inherited from a special class message_t. Now there is no need to derive the type of message from message_t, but SObjectizer wraps a user object into message_t-derived object anyway under the hood;

    SObjectizer-5的开发始于2010年,当时C ++ 11还没有标准化。 因此,一开始,我们不能使用C ++ 11的这些功能(如可变参数模板和std::tuple类)。 我们唯一的选择是使用从特殊类message_t继承的类的对象。 现在,无需从message_t派生消息的类型,但是无论如何,SObjectizer都会将用户对象包装到message_t派生的对象中;

  • content of a message can easily be changed without modification of signatures of event handlers. And there is a control from a compiler: if you remove some field from a message or change its type then the compiler will tell you about wrong access to that field.

    无需修改事件处理程序的签名,即可轻松更改消息的内容。 并且有一个来自编译器的控件:如果您从消息中删除某些字段或更改其类型,则编译器会告诉您对该字段的错误访问。

Usage of messages as objects also allows to work with preallocated messages and to store a received message to some container and resend it later.

将消息用作对象还可以使用预分配的消息,并将接收到的消息存储到某个容器中,然后稍后重新发送。

代理商合作社 (Coops of agents)

A coop of agents is probably one of the unique SObjectizer's features. A coop is a group of agents which should be added to and removed from SObjectizer in a transaction manner. It means that if a coop contains three agents then all those agents should be added to SObjectizer successfully or no one of them should be added. Similarly, all three agents should be removed from SObjectizer or all three agents should continue their work.

代理合作社可能是SObjectizer的独特功能之一。 合作社是一组代理,应以事务方式将其添加到SObjectizer中或从中删除。 这意味着,如果一个协作组包含三个代理,则应将所有这些代理成功添加到SObjectizer或不添加任何一个。 同样,应从SObjectizer中删除所有三个代理,或者所有三个代理应继续其工作。

The need in coops was discovered soon after the start of SObjectizer life. It became obvious that agents would be created by groups, not by single instances. Coops were invented to simplify the life of a developer: there is no need to control the creation of the next agent and remove previously created agents if the creation of a new agent fails.

在SObjectizer生命周期开始后不久就发现了合作社的需求。 很明显,代理将由组而不是单个实例创建。 发明了Coops是为了简化开发人员的生活:如果新代理创建失败,则无需控制下一个代理的创建并删除先前创建的代理。

A coop can also be seen as a supervisor in all-for-one mode: if an agent from the coop fails then the whole coop will be removed from SObjectizer Environment and destroyed (a user can react to that and recreate the coop again).

合作社也可以看作是“一对一”模式的主管:如果合作社中的代理失败,则整个合作社将从SObjectizer Environment中删除并销毁(用户可以对此做出React并重新创建合作社)。

留言框 (Message boxes)

Message boxes are another unique SObjectizer's feature. Messages in SObjectizer are sent to a message box (mbox), not to an agent directly. There can be one receiver behind the mbox, or there can be a million subscribers, or there can be no one.

消息框是SObjectizer的另一个独特功能。 SObjectizer中的消息将发送到消息框(mbox),而不是直接发送到代理。 mbox后面可能有一个接收器,或者可能有一百万个订户,或者没有人。

Mboxes allows us to support the basic functionality of Publish-Subscribe model. A mbox can be seen as MQ-broker and the message type can be seen as a topic.

Mboxes使我们能够支持Publish-Subscribe模型的基本功能。 mbox可以看作是MQ-broker,消息类型可以看作是主题。

Mboxes allows us also implement various interesting forms of message delivery. For example, there is a round-robin mbox that spreads messages between subscribers in round-robin manner. There is also a retained mbox that holds the last sent message and resend it automatically for every new subscriber. There is also a simple wrapper around libmosquitto that allows to use MQTT as a transport for a distributed application.

Mboxes还使我们能够实现各种有趣的消息传递形式。 例如,有一个循环mbox ,它以循环方式在订户之间传播消息。 还有一个保留的mbox ,用于保存最后发送的消息,并自动为每个新订户重新发送。 在libmosquitto周围还有一个简单的包装器 ,可以将MQTT用作分布式应用程序的传输。

作为HSM的代理 (Agents as HSM)

Agents in SObjectizer are state machines. It was from the very beginning simply because SObjectizer has roots in SCADA field, where state machines are actively used. But it quickly became obvious that agents in the form of a state machine can be useful even in different niches (like telecom and finance applications).

SObjectizer中的代理是状态机。 从一开始就仅仅是因为SObjectizer根植于SCADA领域,而SCADA领域却活跃地使用了状态机。 但是很快就很明显,状态机形式的代理即使在不同的领域(例如电信和金融应用程序)也可以使用。

Support of hierarchical state machines (e.g. on_enter/on_exit handlers, nested states, time limits and so on) were added after some time of using SObjectizer in production. And this feature made SObjectizer yet more powerful and convenient tool.

在生产中使用SObjectizer一段时间后,添加了对分层状态机的支持(例如on_enter / on_exit处理程序,嵌套状态,时间限制等)。 而且,此功能使SObjectizer变得更强大,更方便。

C ++异常的用法 (Usage of C++ exceptions)

C++ exceptions are used in SObjectizer as the main error reporting mechanism. Despite the fact that the usage of C++ exception can sometimes be costly, we decided to use exceptions instead of error codes.

C ++异常在SObjectizer中用作主要的错误报告机制。 尽管事实上使用C ++异常有时可能会花费很大,但我们还是决定使用异常代替错误代码。

We had a negative experience with error codes in SObjectizer-4, where exceptions weren't used. This lead to ignorance of errors in application code and sometimes important actions weren't done because there was an error creating a new coop or sending a message. But this error was ignored and that fact was discovered much later.

我们对SObjectizer-4中的错误代码有负面的体验,其中没有使用异常。 这导致对应用程序代码中的错误的无知,并且有时由于创建新合作社或发送消息时出错而没有执行重要的操作。 但是这个错误被忽略了,后来发现了这个事实。

Usage of C++ exceptions in SObjectizer-5 allows writing more correct and robust code. In usual cases, exceptions are thrown by SObjectizer very rarely so the usage of exception has no negative impact on SObjectizer's performance or the performance of applications written on top of SObjectizer.

在SObjectizer-5中使用C ++异常可以编写更正确和更强大的代码。 在通常情况下,异常很少由SObjectizer引发,因此异常的使用不会对SObjectizer的性能或在SObjectizer之上编写的应用程序的性能产生负面影响。

不支持“开箱即用”的分布式应用程序 (No support for distributed applications "out of box")

SObjectzer-5 has no built-in support for distributed applications. It means that SObjectizer distributes messages just inside one process. If you need to organize inter-process or inter-note message distribution then you have to integrate some kind of IPC in your application.

SObjectzer-5不对分布式应用程序提供内置支持。 这意味着SObjectizer仅在一个进程内分发消息。 如果您需要组织进程间或注释间的消息分发,则必须在应用程序中集成某种IPC。

This is not because we can't implement some form of IPC in SObjectizer. We already had that in SObjectizer-4. And because we have such experience we decided not to do that in SObjectizer-5. We learned that there is no one type of IPC that perfectly fits for different conditions.

这不是因为我们无法在SObjectizer中实现某种形式的IPC。 我们已经在SObjectizer-4中有了它。 并且由于我们有这样的经验,我们决定在SObjectizer-5中不这样做。 我们了解到,没有一种IPC能够完美地适应不同的条件。

If you want to have good inter-node communication in your application you have to select appropriate underlying protocols. For example, if you have to spread millions of small packets with some short-living data (like distribution of measurement of the current weather conditions) then you have to use one IPC. But if you have to transfer huge BLOBs (like 4K/8K video streams or archives with financial data inside) then you have to use another IPC type.

如果要在应用程序中实现良好的节点间通信,则必须选择适当的基础协议。 例如,如果您必须散布数百万个带有一些短命数据的小数据包(例如当前天气状况的测量分布),则必须使用一个IPC。 但是,如果必须传输巨大的BLOB(例如4K / 8K视频流或内部包含财务数据的档案),则必须使用另一种IPC类型。

And we don't speak about introperability with software written in different languages...

而且我们不会谈论使用不同语言编写的软件的不可逆性...

You can believe that some universal "actor framework" can provide you an IPC that will be good for different conditions. But we know that it is just marketing bullshit. Our experience shows us that it is much simpler and much safer to add the IPC you need in your application then to rely on ideas, needs and knowledge of authors of a 3-rd party "actor framework".

您可以相信,某些通用的“参与者框架”可以为您提供适用于不同条件的IPC。 但是我们知道这只是营销废话。 我们的经验告诉我们,在您的应用程序中添加所需的IPC,然后依靠第三方“参与者框架”作者的想法,需求和知识,要简单,安全得多。

SObjectizer allows incorporating various types of IPC in the form of custom mboxes. So it allows hiding the fact of message distribution over a network from a SObjectizer's users.

SObjectizer允许以自定义mbox的形式合并各种类型的IPC。 因此,它允许向SObjectizer的用户隐藏通过网络分发消息的事实。

而不是结论 (Instead of the conclusion)

The SObjectizer framework is not a big one, but it isn't a small one. So it is impossible to give the reader a rather deep impression about SObjectizer in just one overview. Because of that, we invite you to take a look at the SObjectizer project.

SObjectizer框架不是一个大框架,但也不是一个小框架。 因此,不可能仅凭一个概述就使读者对SObjectizer产生深刻的印象。 因此,我们邀请您看一下SObjectizer项目。

SObjectizer itself lives on GitHub. There is the project's Wiki on GitHub and we recommend to start from SObjectizer 5.6 Basics and then go to articles from In-depth series. For those who want to go deeper, we can recommend Let's look under the SObjectizer's hood section.

SObjectizer本身位于GitHub上GitHub上有该项目的Wiki,我们建议从SObjectizer 5.6 Basics开始,然后转到In-depth系列文章。 对于那些想更深入的人,我们可以建议让我们看一下SObjectizer的内幕部分。

If you have any questions you can ask us in SObjectizer's group on Google's groups.

如果您有任何疑问,可以在Google 网上论坛的SObjectizer网上论坛中问我们。

翻译自: https://habr.com/en/post/458202/

akka actors使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值