1678963980
ZIO — это передовая платформа для создания облачных JVM-приложений. ZIO позволяет разработчикам создавать передовые приложения, которые являются чрезвычайно масштабируемыми, протестированными, надежными, отказоустойчивыми, ресурсобезопасными, эффективными и наблюдаемыми благодаря своему удобному, но мощному функциональному ядру.
Akka и ZIO — это библиотеки в Scala для создания параллельных, масштабируемых и отказоустойчивых приложений.
Akka — это набор инструментов и среда выполнения для создания высокопараллельных, распределенных и отказоустойчивых систем. Он предоставляет акторов, которые представляют собой легкие единицы вычислений, которые взаимодействуют друг с другом путем обмена сообщениями. Akka также включает в себя инструменты для кластеризации, маршрутизации и сохраняемости, что делает его подходящим для создания реактивных приложений.
С другой стороны, ZIO (ZIO означает «ZIO Is Our») — чисто функциональная библиотека, обеспечивающая типобезопасный и компонуемый способ написания параллельного и асинхронного кода. Он предоставляет набор абстракций, таких как волокна, представляющие собой легкие потоки, которые можно комбинировать для создания сложных приложений, и эффекты, являющиеся неизменяемыми и составными описаниями вычислений с побочными эффектами. ZIO также включает мощную модель параллелизма и поддержку асинхронных операций ввода-вывода.
ZIO и Akka — мощные платформы для создания параллельных и распределенных приложений на Scala. Однако есть несколько причин, по которым мы можем выбрать ZIO, а не Akka:
В ZIO акторы реализованы как тип волокна, который представляет собой облегченный поток, который можно запускать одновременно с другими волокнами. Актер — это, по сути, волокно, которое может получать сообщения и реагировать на них.
Одним из способов создания актора в ZIO является использование метода actor.make, который принимает функцию, определяющую поведение актора. Функция принимает два параметра: первый — это начальное состояние актора, а второй — сообщение, которое получает актор.
Чтобы создать некоторую систему акторов в ZIO, нам нужно добавить некоторые зависимости, связанные с ZIO, в build.sbt. Проверьте ниже зависимость, которую мы использовали:
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % zioVersion,
"dev.zio" %% "zio-streams" % zioVersion,
"dev.zio" %% "zio-kafka" % "2.0.7",
"dev.zio" %% "zio-json" % "0.4.2",
"dev.zio" %% "zio-dynamodb" % "0.2.6",
"dev.zio" %% "zio-test" % zioVersion,
"dev.zio" %% "zio-actors" % "0.1.0",
"dev.zio" %% "zio-http" % "0.0.4",
"dev.zio" %% "zio-http-testkit" % "0.0.3",
"io.d11" %% "zhttp" % "2.0.0-RC11"
)
Затем, переходя к кодовой базе, мы представили актера ZIO таким образом, что есть интеграция потребителя кафки и производителя кафки, который работает параллельно со всеми актерами.
Давайте определим главного актера для TicketBookingSystem.
object TicketBookingSystem extends ZIOAppDefault {
val actorSystem = ActorSystem("ticketBookingSystem")
def run = {
println("starting actor system ")
for {
ticketInfoConsumerProducer <- KafkaConsumer.consumerRun.fork
_ <- ticketInfoConsumerProducer.join
} yield ()
}
}
Здесь мы инициализируем Kafka Consumer.
def consumerRun: ZIO[Any, Throwable, Unit] = {
println("starting KafkaConsumer ")
val finalInfoStream =
Consumer
//create a kafka consumer here with respect to a particular topic
for {
theatreActor <- actorSystem.flatMap(x =>
x.make("ticketBookingflowActor", zio.actors.Supervisor.none, (),
theatreActor))
theatreActorData <- theatreActor ! ticketBooking
} yield theatreActorData
}
.map(_.offset)
.aggregateAsync(Consumer.offsetBatches)
.mapZIO(_.commit)
.drain
finalInfoStream.runDrain.provide(KafkaProdConsLayer.consumerLayer ++
KafkaProdConsLayer.producer)
}
В Kafka Consumer мы передаем информацию о бронировании актеру театра с помощью метода tell (!). Этот актер обработает данные и получит детали платежа, подтвердит билет и передаст его следующему актеру.
Реализация TheatreActor для TicketBookingSystem –
Это один из способов, которым мы можем создать систему акторов и различных других акторов и связать их с помощью метода ask или tell. В нашем коде мы добавили нашего первого актора в самого потребителя kafka, который срабатывает. И оттуда мы можем получить доступ к нашим следующим актерам.
object ThreatreActor {
val theatreActor: Stateful[Any, Unit, ZioMessage] = new Stateful[Any, Unit,
ZioMessage] {
override def receive[A](state: Unit, msg: ZioMessage[A], context:
Context): Task[(Unit, A)] =
msg match {
case BookingMessage(value) => {
println("ThreatreActor ................" + value)
val ticketConfirm= Booking(value.uuid, value.bookingDate,
value.theatreName, value.theatreLocation, value.seatNumbers,
value.cardNumber, value.pin,
value.cvv, value.otp, Some("Success"), Some("Confirmed"))
for{
paymentActor <- actorSystem.flatMap(x =>
x.make("paymentGatewayflowActor", zio.actors.Supervisor.none, (),
paymentGatewayflowActor))
paymentDetails <- paymentActor ? BookingMessage(value)
bookingSyncActor <- actorSystem.flatMap(x =>
x.make("bookingSyncActor", zio.actors.Supervisor.none, (), bookingSyncActor))
_ <- bookingSyncActor ! BookingMessage(ticketConfirm)
}yield {
println("Completed Theatre Actor")
((),())}
}
case _ => throw new Exception("Wrong value Input")
}
}
}
Этот код приведет нас к paymentActor, который написан следующим образом:
Реализация PaymentActor для TicketBookingSystem –
object PaymentGatewayActor {
val paymentGatewayflowActor: Stateful[Any, Unit, ZioMessage] = new
Stateful[Any, Unit, ZioMessage] {
override def receive[A](state: Unit, msg: ZioMessage[A], context:
Context): Task[(Unit, A)] =
msg match {
case BookingMessage(value) =>
println("paymentInfo ................" + value)
val booking = Booking(value.uuid, value.bookingDate,
value.theatreName, value.theatreLocation, value.seatNumbers,
value.cardNumber, value.pin,
value.cvv, value.otp, Some("Success"), Some(""))
for {
bookingSyncActor <- actorSystem.flatMap(x =>
x.make("bookingSyncActor", zio.actors.Supervisor.none, (), bookingSyncActor))
//ZIO.succeed(booking)
}yield{
println("paymentInfo return................" + booking)
( BookingMessage(booking), ())
}
case _ => throw new Exception("Wrong value Input")
}
}
}
Тот же theatreActor приведет нас к bookingSyncActor, который написан так:
Реализация bookingSyncActor для TicketBookingSystem –
val bookingSyncActor: Stateful[Any, Unit, ZioMessage] = new Stateful[Any,
Unit, ZioMessage] {
override def receive[A](state: Unit, msg: ZioMessage[A], context:
Context): Task[(Unit, A)] =
msg match {
case BookingMessage(value) =>
println("bookingSyncActor ................" + value)
for {
_ <- KafkaProducer.producerRun(value)
_ <- f1(value).provide(
netty.NettyHttpClient.default,
config.AwsConfig.default,
dynamodb.DynamoDb.live,
DynamoDBExecutor.live
)
}yield((),())
}
} // plus some other computations.
Эти разные акторы будут собирать некоторую информацию из класса базового случая и давать некоторую релевантную информацию об отдельных актерах.
Отправка ответа обратно клиенту –
Для ответного сообщения производитель создает данные по другой теме (ответное сообщение).
for {
_ <- KafkaProducer.producerRun(value)
//logic for db
} yield((),())
Конечно, давайте проверим, как тестировать наших актеров с помощью модульного теста.
Для приведенного выше кода theatreActor мы создали theatreActorSpec, который можно записать в этом простом формате. Мы можем проверить правильность введенных данных в актере или нет.
object ThreatreActorSpec extends ZIOAppDefault{
val data: Booking = booking.handler.actor.JsonSampleData.booking
override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] =
for {
system <- ActorSystem("ticketBookingSystem")
actor <- system.make("ticketBookingflowActor", Supervisor.none, (),
theatreActor)
result <- actor ! BookingMessage(data)
}
yield result
}
Чтобы запустить эту службу -
В заключение, ZIO Actors — это мощная и эффективная библиотека для создания параллельных и распределенных систем на Scala. Он обеспечивает упрощенный и типобезопасный подход к параллелизму, позволяя разработчикам легко моделировать свои потребности в параллелизме для предметной области без сложностей традиционных систем акторов. Благодаря расширенным функциям, таким как прозрачность местоположения, перехват сообщений и контроль, ZIO Actors упрощает разработку и развертывание масштабируемых и отказоустойчивых распределенных приложений.
Здесь мы создали различных актеров, таких как актер театра, актер платежа и актер синхронизации бронирования, которые работают в соответствии с их логикой. Кроме того, мы попытались реализовать акторы ZIO с помощью ZIO kafka, и это очень хороший пример такой простой интеграции.
Кроме того, его интеграция с экосистемой ZIO обеспечивает бесшовную композицию с другими функциональными библиотеками, предоставляя разработчикам мощный и целостный набор инструментов для создания надежного и удобного в сопровождении программного обеспечения. Дополнительные блоги см. здесь . Дополнительные блоги ZIO см. здесь
Оригинальный источник статьи: https://blog.knoldus.com/
1678959749
ZIO 是用于创建云原生 JVM 应用程序的尖端框架。ZIO 使开发人员能够构建最佳实践应用程序,这些应用程序具有极高的可扩展性、经过测试、健壮、有弹性、资源安全、高效且可观察,这要归功于其用户友好但功能强大的核心。
Akka 和 ZIO 都是 Scala 中用于构建并发、可扩展和容错应用程序的库。
Akka 是用于构建高度并发、分布式和容错系统的工具包和运行时。它提供了参与者,这是轻量级的计算单元,通过交换消息相互通信。Akka 还包括用于集群、路由和持久化的工具,使其非常适合构建反应式应用程序。
另一方面,ZIO(ZIO 代表“ZIO Is Our”)是一个纯函数库,它提供了一种类型安全且可组合的方式来编写并发和异步代码。它提供了一组抽象,例如纤程,它们是轻量级的线程,可以组合起来创建复杂的应用程序,以及效果,它们是对副作用计算的不可变和可组合的描述。ZIO 还包括强大的并发模型和对异步 IO 操作的支持。
ZIO 和 Akka 都是在 Scala 中构建并发和分布式应用程序的强大框架。但是,有一些我们可能会选择 ZIO 而不是 Akka 的原因:
在 ZIO 中,actor 被实现为一种 fiber,它是一种轻量级线程,可以与其他 fiber 并发运行。Actor 本质上是一根可以接收消息并对其做出反应的纤维。
要在 ZIO 中创建一个 actor,一种方法是我们可以使用 方法actor.make,它采用一个定义 actor 行为的函数。该函数有两个参数:第一个是actor的初始状态,第二个是actor接收到的消息。
要在 ZIO 中创建一些 actor 系统,我们需要在 build.sbt 中添加一些 ZIO 相关的依赖项。在下面检查我们使用的依赖项-
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % zioVersion,
"dev.zio" %% "zio-streams" % zioVersion,
"dev.zio" %% "zio-kafka" % "2.0.7",
"dev.zio" %% "zio-json" % "0.4.2",
"dev.zio" %% "zio-dynamodb" % "0.2.6",
"dev.zio" %% "zio-test" % zioVersion,
"dev.zio" %% "zio-actors" % "0.1.0",
"dev.zio" %% "zio-http" % "0.0.4",
"dev.zio" %% "zio-http-testkit" % "0.0.3",
"io.d11" %% "zhttp" % "2.0.0-RC11"
)
然后转向代码库,我们以这样一种方式引入了 ZIO actor,即集成了 kafka consumer,kafka producer,它与所有 actors 并行工作。
让我们为 TicketBookingSystem 定义一个主要参与者。
object TicketBookingSystem extends ZIOAppDefault {
val actorSystem = ActorSystem("ticketBookingSystem")
def run = {
println("starting actor system ")
for {
ticketInfoConsumerProducer <- KafkaConsumer.consumerRun.fork
_ <- ticketInfoConsumerProducer.join
} yield ()
}
}
在这里,我们正在初始化 Kafka Consumer。
def consumerRun: ZIO[Any, Throwable, Unit] = {
println("starting KafkaConsumer ")
val finalInfoStream =
Consumer
//create a kafka consumer here with respect to a particular topic
for {
theatreActor <- actorSystem.flatMap(x =>
x.make("ticketBookingflowActor", zio.actors.Supervisor.none, (),
theatreActor))
theatreActorData <- theatreActor ! ticketBooking
} yield theatreActorData
}
.map(_.offset)
.aggregateAsync(Consumer.offsetBatches)
.mapZIO(_.commit)
.drain
finalInfoStream.runDrain.provide(KafkaProdConsLayer.consumerLayer ++
KafkaProdConsLayer.producer)
}
在 Kafka Consumer 中,我们使用 tell 方法 (!) 将预订信息传递给剧院演员。该演员将处理数据并获取付款详细信息并确认票证并传递给下一个演员。
TicketBookingSystem 的 TheatreActor 实现 –
这是我们可以创建演员系统和各种其他演员并可以使用询问或告诉方法链接它们的方式之一。在我们的代码中,我们在 kafka 消费者本身中添加了我们的第一个演员,它正在被触发。从那里,我们可以访问我们的下一个演员。
object ThreatreActor {
val theatreActor: Stateful[Any, Unit, ZioMessage] = new Stateful[Any, Unit,
ZioMessage] {
override def receive[A](state: Unit, msg: ZioMessage[A], context:
Context): Task[(Unit, A)] =
msg match {
case BookingMessage(value) => {
println("ThreatreActor ................" + value)
val ticketConfirm= Booking(value.uuid, value.bookingDate,
value.theatreName, value.theatreLocation, value.seatNumbers,
value.cardNumber, value.pin,
value.cvv, value.otp, Some("Success"), Some("Confirmed"))
for{
paymentActor <- actorSystem.flatMap(x =>
x.make("paymentGatewayflowActor", zio.actors.Supervisor.none, (),
paymentGatewayflowActor))
paymentDetails <- paymentActor ? BookingMessage(value)
bookingSyncActor <- actorSystem.flatMap(x =>
x.make("bookingSyncActor", zio.actors.Supervisor.none, (), bookingSyncActor))
_ <- bookingSyncActor ! BookingMessage(ticketConfirm)
}yield {
println("Completed Theatre Actor")
((),())}
}
case _ => throw new Exception("Wrong value Input")
}
}
}
此代码将带我们到 paymentActor,其编写如下 -
TicketBookingSystem 的 PaymentActor 实现 –
object PaymentGatewayActor {
val paymentGatewayflowActor: Stateful[Any, Unit, ZioMessage] = new
Stateful[Any, Unit, ZioMessage] {
override def receive[A](state: Unit, msg: ZioMessage[A], context:
Context): Task[(Unit, A)] =
msg match {
case BookingMessage(value) =>
println("paymentInfo ................" + value)
val booking = Booking(value.uuid, value.bookingDate,
value.theatreName, value.theatreLocation, value.seatNumbers,
value.cardNumber, value.pin,
value.cvv, value.otp, Some("Success"), Some(""))
for {
bookingSyncActor <- actorSystem.flatMap(x =>
x.make("bookingSyncActor", zio.actors.Supervisor.none, (), bookingSyncActor))
//ZIO.succeed(booking)
}yield{
println("paymentInfo return................" + booking)
( BookingMessage(booking), ())
}
case _ => throw new Exception("Wrong value Input")
}
}
}
相同的 theatreActor 将带我们到 bookingSyncActor,如下所示 -
TicketBookingSystem 的 bookingSyncActor 实现 –
val bookingSyncActor: Stateful[Any, Unit, ZioMessage] = new Stateful[Any,
Unit, ZioMessage] {
override def receive[A](state: Unit, msg: ZioMessage[A], context:
Context): Task[(Unit, A)] =
msg match {
case BookingMessage(value) =>
println("bookingSyncActor ................" + value)
for {
_ <- KafkaProducer.producerRun(value)
_ <- f1(value).provide(
netty.NettyHttpClient.default,
config.AwsConfig.default,
dynamodb.DynamoDb.live,
DynamoDBExecutor.live
)
}yield((),())
}
} // plus some other computations.
这些不同的参与者将从基本案例类中收集一些信息,并将提供一些关于各个参与者的相关信息。
将响应发送回客户端 –
对于回复消息,生产者正在生成关于不同主题的数据(回复消息)
for {
_ <- KafkaProducer.producerRun(value)
//logic for db
} yield((),())
当然,让我们检查一下如何使用单元测试来测试我们的演员。
对于上面的 theatreActor 代码,我们创建了可以用这种简单格式编写的 theatreActorSpec。我们可以检查输入的数据在演员中是否正确。
object ThreatreActorSpec extends ZIOAppDefault{
val data: Booking = booking.handler.actor.JsonSampleData.booking
override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] =
for {
system <- ActorSystem("ticketBookingSystem")
actor <- system.make("ticketBookingflowActor", Supervisor.none, (),
theatreActor)
result <- actor ! BookingMessage(data)
}
yield result
}
要运行此服务 –
总之,ZIO Actors 是一个强大而高效的库,用于在 Scala 中构建并发和分布式系统。它提供了一种轻量级和类型安全的并发方法,允许开发人员轻松地模拟他们特定领域的并发需求,而无需传统参与者系统的复杂性。ZIO Actors 凭借其位置透明、消息拦截和监督等高级功能,简化了高度可扩展和容错分布式应用程序的开发和部署。
在这里,我们创建了各种 actor,如剧院 actor、支付 actor 和预订同步 actor,它们按照它们的逻辑工作。此外,我们尝试使用 ZIO kafka 实现 ZIO actor,这是这种简单集成的一个很好的例子。
此外,它与 ZIO 生态系统的集成允许与其他功能库无缝组合,为开发人员提供强大且内聚的工具包,用于构建健壮且可维护的软件。更多博客请参考这里。更多 ZIO 博客,请参考这里
文章原文出处:https: //blog.knoldus.com/
1678955951
ZIO is a cutting-edge framework for creating cloud-native JVM applications. ZIO enables developers to construct best-practice applications that are extremely scalable, tested, robust, resilient, resource-safe, efficient, and observable thanks to its user-friendly yet strong functional core.
Akka and ZIO are both the libraries in Scala for building concurrent, scalable, and fault-tolerant applications.
Akka is a toolkit and runtime for building highly concurrent, distributed, and fault-tolerant systems. It provides actors, which are lightweight units of computation that communicate with each other by exchanging messages. Akka also includes tools for clustering, routing, and persistence, making it well-suited for building reactive applications.
On the other hand, ZIO (ZIO stands for “ZIO Is Our”) is a purely functional library that provides a type-safe and composable way to write concurrent and asynchronous code. It provides a set of abstractions such as fibers, which are lightweight threads that can be composed to create complex applications, and effects, which are immutable and composable descriptions of side-effecting computations. ZIO also includes a powerful concurrency model and support for asynchronous IO operations.
ZIO and Akka are both powerful frameworks for building concurrent and distributed applications in Scala. However, there are some reasons why we might choose ZIO over Akka:
In ZIO, actors are implemented as a type of fiber, which is a lightweight thread that can be run concurrently with other fibers. An actor is essentially a fiber that can receive messages and react to them.
To create an actor in ZIO, one of the way is we can use the actor.make
method, which takes a function that defines the behavior of the actor. The function takes two parameters: the first one is the initial state of the actor, and the second one is the message that the actor receives.
To create some actor system in ZIO, we need to add some ZIO related dependencies in build.sbt. Check below for the dependency which we have used-
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % zioVersion,
"dev.zio" %% "zio-streams" % zioVersion,
"dev.zio" %% "zio-kafka" % "2.0.7",
"dev.zio" %% "zio-json" % "0.4.2",
"dev.zio" %% "zio-dynamodb" % "0.2.6",
"dev.zio" %% "zio-test" % zioVersion,
"dev.zio" %% "zio-actors" % "0.1.0",
"dev.zio" %% "zio-http" % "0.0.4",
"dev.zio" %% "zio-http-testkit" % "0.0.3",
"io.d11" %% "zhttp" % "2.0.0-RC11"
)
Then moving forward to codebase, we have introduced ZIO actor in such a way that there is an integration of kafka consumer, kafka producer which is is working parallely with all the actors.
Lets define a main actor for the TicketBookingSystem.
object TicketBookingSystem extends ZIOAppDefault {
val actorSystem = ActorSystem("ticketBookingSystem")
def run = {
println("starting actor system ")
for {
ticketInfoConsumerProducer <- KafkaConsumer.consumerRun.fork
_ <- ticketInfoConsumerProducer.join
} yield ()
}
}
Here, we are initializing Kafka Consumer.
def consumerRun: ZIO[Any, Throwable, Unit] = {
println("starting KafkaConsumer ")
val finalInfoStream =
Consumer
//create a kafka consumer here with respect to a particular topic
for {
theatreActor <- actorSystem.flatMap(x =>
x.make("ticketBookingflowActor", zio.actors.Supervisor.none, (),
theatreActor))
theatreActorData <- theatreActor ! ticketBooking
} yield theatreActorData
}
.map(_.offset)
.aggregateAsync(Consumer.offsetBatches)
.mapZIO(_.commit)
.drain
finalInfoStream.runDrain.provide(KafkaProdConsLayer.consumerLayer ++
KafkaProdConsLayer.producer)
}
In Kafka Consumer, we are passing booking information to the theatre actor by using tell method (!). This actor will process the data and fetch the payment details and confirm ticket and pass on to next actor.
TheatreActor implementation for TicketBookingSystem –
This is one of the way we can create actor system and various other actors and can link them using ask or tell method.In our code, we have added our first actor in kafka consumer itself, which is getting triggered. And from there, we can get access to our next actors.
object ThreatreActor {
val theatreActor: Stateful[Any, Unit, ZioMessage] = new Stateful[Any, Unit,
ZioMessage] {
override def receive[A](state: Unit, msg: ZioMessage[A], context:
Context): Task[(Unit, A)] =
msg match {
case BookingMessage(value) => {
println("ThreatreActor ................" + value)
val ticketConfirm= Booking(value.uuid, value.bookingDate,
value.theatreName, value.theatreLocation, value.seatNumbers,
value.cardNumber, value.pin,
value.cvv, value.otp, Some("Success"), Some("Confirmed"))
for{
paymentActor <- actorSystem.flatMap(x =>
x.make("paymentGatewayflowActor", zio.actors.Supervisor.none, (),
paymentGatewayflowActor))
paymentDetails <- paymentActor ? BookingMessage(value)
bookingSyncActor <- actorSystem.flatMap(x =>
x.make("bookingSyncActor", zio.actors.Supervisor.none, (), bookingSyncActor))
_ <- bookingSyncActor ! BookingMessage(ticketConfirm)
}yield {
println("Completed Theatre Actor")
((),())}
}
case _ => throw new Exception("Wrong value Input")
}
}
}
This code will take us to paymentActor which is written as below –
PaymentActor implementation for TicketBookingSystem –
object PaymentGatewayActor {
val paymentGatewayflowActor: Stateful[Any, Unit, ZioMessage] = new
Stateful[Any, Unit, ZioMessage] {
override def receive[A](state: Unit, msg: ZioMessage[A], context:
Context): Task[(Unit, A)] =
msg match {
case BookingMessage(value) =>
println("paymentInfo ................" + value)
val booking = Booking(value.uuid, value.bookingDate,
value.theatreName, value.theatreLocation, value.seatNumbers,
value.cardNumber, value.pin,
value.cvv, value.otp, Some("Success"), Some(""))
for {
bookingSyncActor <- actorSystem.flatMap(x =>
x.make("bookingSyncActor", zio.actors.Supervisor.none, (), bookingSyncActor))
//ZIO.succeed(booking)
}yield{
println("paymentInfo return................" + booking)
( BookingMessage(booking), ())
}
case _ => throw new Exception("Wrong value Input")
}
}
}
The same theatreActor will take us to bookingSyncActor which is written as below –
bookingSyncActor implementation for TicketBookingSystem –
val bookingSyncActor: Stateful[Any, Unit, ZioMessage] = new Stateful[Any,
Unit, ZioMessage] {
override def receive[A](state: Unit, msg: ZioMessage[A], context:
Context): Task[(Unit, A)] =
msg match {
case BookingMessage(value) =>
println("bookingSyncActor ................" + value)
for {
_ <- KafkaProducer.producerRun(value)
_ <- f1(value).provide(
netty.NettyHttpClient.default,
config.AwsConfig.default,
dynamodb.DynamoDb.live,
DynamoDBExecutor.live
)
}yield((),())
}
} // plus some other computations.
These different actors will collect some info from the base case class and will give some relevant info regarding the individual actors.
Sending a response back to client –
For reply message, the producer is producing the data on a different topic (reply message)
for {
_ <- KafkaProducer.producerRun(value)
//logic for db
} yield((),())
Sure, Lets check how to test our actors using unit test.
For the above theatreActor code, we have created theatreActorSpec which can be written in this simple format. We can check if the data entered is getting correct in the actor or not.
object ThreatreActorSpec extends ZIOAppDefault{
val data: Booking = booking.handler.actor.JsonSampleData.booking
override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] =
for {
system <- ActorSystem("ticketBookingSystem")
actor <- system.make("ticketBookingflowActor", Supervisor.none, (),
theatreActor)
result <- actor ! BookingMessage(data)
}
yield result
}
To run this service –
In conclusion, ZIO Actors is a powerful and efficient library for building concurrent and distributed systems in Scala. It provides a lightweight and type-safe approach to concurrency, allowing developers to easily model their domain-specific concurrency needs without the complexities of traditional actor systems. With its advanced features such as location transparency, message interception, and supervision, ZIO Actors simplifies the development and deployment of highly scalable and fault-tolerant distributed applications.
Here, we created various actors such as theatre actor, payment actor and booking sync actor which are working as per their logic. Also, we tried to implement ZIO actors with ZIO kafka and this is a very good example of this simple integration.
Additionally, its integration with the ZIO ecosystem allows for seamless composition with other functional libraries, providing developers with a powerful and cohesive toolkit for building robust and maintainable software. For more blogs, please refer here . For more ZIO blogs, refer here
Original article source at: https://blog.knoldus.com/
1677907920
Concurrent computing in Julia with actors.
Actors
implements the Actor Model of computation:
An actor ... in response to a message it receives, can concurrently:
- send a finite number of messages to other actors;
- create a finite number of new actors;
- designate the behavior to be used for the next message it receives.
Actors
make(s) concurrency easy to understand and reason about and integrate(s) well with Julia's multi-threading and distributed computing. It provides an API for writing reactive applications, that are:
The following example defines two behavior functions: greet
and hello
and spawns two actors with them. sayhello
will forward a message to greeter
, get a greeting string back and deliver it as a result:
julia> using Actors
julia> import Actors: spawn
julia> greet(greeting, msg) = greeting*", "*msg*"!" # a greetings server
greet (generic function with 1 method)
julia> hello(greeter, to) = request(greeter, to) # a greetings client
hello (generic function with 1 method)
julia> greeter = spawn(greet, "Hello") # start the server with a greet string
Link{Channel{Any}}(Channel{Any}(sz_max:32,sz_curr:0), 1, :default)
julia> sayhello = spawn(hello, greeter) # start the client with a link to the server
Link{Channel{Any}}(Channel{Any}(sz_max:32,sz_curr:0), 1, :default)
julia> request(sayhello, "World") # request the client
"Hello, World!"
julia> request(sayhello, "Kermit")
"Hello, Kermit!"
Please look into the manual for more information and more serious examples.
Actors
is part of the Julia GitHub group JuliaActors. Please join!
Author: JuliaActors
Source Code: https://github.com/JuliaActors/Actors.jl
License: MIT license
1647191460
In this video we will do a brief introduction of Actors in Swift. Recently introduced alongside async await patterns, actors help simplify concurrency in your iOS projects. They help to synchronize calls, enforce threads/queues, and a whole lot more. We will work in Xcode 13 with the latest version of Swift.
💻 Source Code: https://patreon.com/iOSAcademy
🎥 Subscribe for more: https://www.youtube.com/iOSAcademy?sub_confirmation=1
#swift #actors #asyncawait #ios #xcode
1614834120
This tutorial will walk you through each step of writing a blazingly fast WebSocket client in Actix Web, in-depth, and with a working repository as reference.
We’ll be building a simple chatroom that echos messages to everyone in a room, and include private messages. I’ll also explain every step, so you can extend this example and write your own WebSocket server in Actix Web.
Repo for the completed project: https://github.com/antholeole/actix-sockets
Everything else will be talked about in this tutorial.
In the Actix architecture, there are two primary components: Actors and Messages. Think of each actor as its own object in memory, with a mailbox. Actors can read their mailbox and respond to their mail accordingly, whether it be by sending mail to another actor, changing its state, or maybe by doing nothing at all. That’s it! That’s all that actors are — simple little things that read and respond to mail.
Actors are so ungodly fast because they work entirely independent of each other. One actor can be on its own thread, or on a different machine entirely. As long as the actor can read its mail, it works perfectly.
It’s important to note that the actor just exists in memory, with its address passed around like Addr<Actor>
. The Actor itself can mutate its properties (maybe you have a “messages_received” property, and you need to increment it on every message) but you can’t do that anywhere else. Instead, with the Addr<Actor>
element, you can do .send(some_message)
to put a message in that Actor’s mailbox.
In Actix web, each socket connection is its own Actor, and the “Lobby” (we’ll get to that) is its own actor also.
#rust #actors #actix-web
1594475820
Java comes with a built-in multi-threading model based on shared data and locks. To use this model, you decide what data will be shared by multiple threads and mark as “synchronized” sections of the code that access the shared data.
It also provides a locking mechanism to ensure that only one thread can access the shared data at a time. Lock operations remove possibilities for race conditions but simultaneously add possibilities for deadlocks. In Scala, you can still use Java threads, but the “Actor model” is the preferred approach for concurrency.
Actors provide a concurrency model that is easier to work with and can, therefore, help you avoid many of the difficulties of using Java’s native concurrency model.
Let’s see an example,
case class Add(num1: Int, num2: Int)
case class Substract(num1: Int, num2: Int)
case class Divide(num1: Int, num2: Int)
class Calculator extends Actor {
def receive = {
case Add(num1, num2) => context.actorOf(Props[Addition]) ! Add(num1, num2)
case Substract(num1, num2) => context.actorOf(Props[Substraction]) ! Substract(num1, num2)
case Divide(num1, num2) => context.actorOf(Props[Division]) ! Divide(num1, num2)
}
}
class Addition extends Actor {
def receive = {
case Add(num1, num2) => println(num1 + num2)
}
}
class Substraction extends Actor {
def receive = {
case Substract(num1, num2) => println(num1 - num2)
}
}
class Division extends Actor {
def receive = {
case Divide(num1, num2) => println(num1 % num2)
}
}
The output of this is as follows,
Started Calculating.....
Addition
Substraction
Divide
5
1
2
If you observe the output, main actor sends all three messages to the actor Calculator (parent actor) at the same time and all three operations performed asynchronously.
#scala #actors #akka #concurrency #scala