Concurrencia y resistencia de estilo directo seguro para Scala en el JVM. Requiere JDK 21 y Scala 3. Las áreas que nos gustaría cubrir con OX son:
Todo lo anterior debe permitir la observabilidad de la lógica comercial orquestada. Nuestro objetivo es habilitar la escritura de código simple orientado a la expresión en estilo funcional. Nos gustaría mantener la sobrecarga de la sintaxis al mínimo, preservando trazas de pila amigables para los desarrolladores y sin comprometer el rendimiento.
Algunos de los anteriores ya están abordados en la API, algunos están surgiendo en el futuro. ¡Nos encantaría tu ayuda para dar forma al proyecto!
Para probar OX, use la siguiente dependencia, utilizando cualquier SBT:
" com.softwaremill.ox " %% " core " % " 0.5.3 "
O Scala-cli:
//> using dep " com.softwaremill.ox::core:0.5.3 "
La documentación está disponible en https://ox.softwaremill.com, los Scaladocs se pueden navegar en https://javadoc.io.
Ejecutar dos cálculos en paralelo:
def computation1 : Int = { sleep( 2 .seconds); 1 }
def computation2 : String = { sleep( 1 .second); " 2 " }
val result1 : ( Int , String ) = par(computation1, computation2)
// (1, "2")
Tiempo de espera un cálculo:
def computation3 : Int = { sleep( 2 .seconds); 1 }
val result2 : Either [ Throwable , Int ] = either.catching(timeout( 1 .second)(computation3))
// `timeout` only completes once the loosing branch is interrupted & done
Cálculos de la carrera dos:
def computation4 : Int = { sleep( 2 .seconds); 1 }
def computation5 : Int = { sleep( 1 .second); 2 }
val result3 : Int = raceSuccess(computation4, computation5)
// as before, the loosing branch is interrupted & awaited before returning a result
Concurrencia y supervisión estructurada:
// equivalent of par
supervised {
val f1 = fork { sleep( 2 .seconds); 1 }
val f2 = fork { sleep( 1 .second); 2 }
(f1.join(), f2.join())
}
Manejo de errores dentro de un alcance de concurrencia estructurado:
supervised {
forkUser :
sleep( 1 .second)
println( " Hello! " )
forkUser :
sleep( 500 .millis)
throw new RuntimeException ( " boom! " )
}
// on exception, all other forks are interrupted ("let it crash")
// the scope ends & re-throws only when all forks complete (no "leftovers")
Vuelva a intentar un cálculo:
def computationR : Int = ???
retry( RetryConfig .backoff( 3 , 100 .millis, 5 .minutes, Jitter . Equal ))(computationR)
Repita un cálculo:
def computationR : Int = ???
repeat( RepeatConfig .fixedRateForever( 100 .millis))(computationR)
Asignar un recurso en un alcance:
supervised {
val writer = useCloseableInScope( new java.io. PrintWriter ( " test.txt " ))
// ... use writer ...
} // writer is closed when the scope ends (successfully or with an error)
Cree una aplicación que se apaga limpiamente cuando se interrumpe con Sigint/Sigterm:
object MyApp extends OxApp :
def run ( args : Vector [ String ])( using Ox ) : ExitCode =
// ... your app's code ...
// might use fork {} to create top-level background threads
ExitCode . Success
Actores simples de tipo seguro:
class Stateful { def increment ( delta : Int ) : Int = ??? }
supervised :
val ref = Actor .create( new Stateful )
// ref can be shared across forks, but only within the concurrency scope
ref.ask(_.increment( 5 ))
Cree un flujo y transformación simple utilizando una API funcional:
Flow .iterate( 0 )(_ + 1 ) // natural numbers
.filter(_ % 2 == 0 )
.map(_ + 1 )
.intersperse( 5 )
// compute the running total
.mapStateful(() => 0 ) { (state, value) =>
val newState = state + value
(newState, newState)
}
.take( 10 )
.runForeach(n => println(n.toString))
Cree flujos que realicen E/S y administren la concurrencia:
def sendHttpRequest ( entry : String ) : Unit = ???
Flow
.fromInputStream( this .getClass().getResourceAsStream( " /list.txt " ))
.linesUtf8
.mapPar( 4 )(sendHttpRequest)
.runDrain()
Fusionar dos flujos, manejando adecuadamente la falla de cualquiera de las ramas:
val f1 = Flow .tick( 123 .millis, " left " )
val f2 = Flow .tick( 312 .millis, " right " )
f1.merge(f2).take( 100 ).runForeach(println)
Integre el flujo con otros componentes utilizando una API imperativa:
def readNextBatch () : List [ String ] = ???
Flow .usingEmit { emit =>
forever :
readNextBatch().foreach(emit.apply)
}
Utilice canales de alto rendimiento completables para la comunicación entre la comida dentro de los alcances de concurrencia:
val c = Channel .buffered[ String ]( 8 )
c.send( " Hello, " )
c.send( " World " )
c.done()
Seleccione de los canales de Go:
val c = Channel .rendezvous[ Int ]
val d = Channel .rendezvous[ Int ]
select(c.sendClause( 10 ), d.receiveClause)
Desenviar a Eithers y combinar errores en un tipo de sindicato:
val v1 : Either [ Int , String ] = ???
val v2 : Either [ Long , String ] = ???
val result : Either [ Int | Long , String ] = either :
v1.ok() ++ v2.ok()
Valores de tubería y toque a funciones para usar el dot-syntax:
def compute : Int = ???
def computeMore ( v : Int ) : Long = ???
compute
.pipe( 2 * _)
.tap(println)
.pipe(computeMore)
¡Más en los documentos!.
El objetivo más amplio de Scala de estilo directo es permitir a los equipos ofrecer software de trabajo rápidamente y con confianza. Nuestros otros proyectos, incluidos STTP Client y Tapir, también incluyen integraciones directamente adaptadas al estilo directo.
Además, consulte también The Gears Project, una biblioteca experimental de plataformas múltiples que también cubre Scala de estilo directo.
Todas las sugerencias bienvenidas :)
Para compilar y probar, ejecutar:
sbt compile
sbt test
¡Vea la lista de problemas y elija uno! O informa el suyo.
Si tiene dudas sobre el por qué o cómo funciona algo, no dude en hacer una pregunta sobre el discurso o a través de Github. Esto probablemente significa que la documentación, los Scaladocs o el código no están claros y se puede mejorar en beneficio de todos.
Para desarrollar la documentación, puede usar el script doc/watch.sh
, que ejecuta Sphinx usando Python. Use doc/requirements.txt
para configurar su entorno Python con pip
. Alternativamente, si es un usuario de NIX, ejecute nix develop
en doc/
para iniciar un shell con un entorno que permita ejecutar watch.sh
. Además, puede usar la tarea SBT compileDocumentation
para verificar, que todos los fragmentos de código se compilan correctamente.
Cuando tenga un PR listo, eche un vistazo a nuestra guía "Cómo preparar una buena PR". ¡Gracias! :)
Ofrecemos servicios de desarrollo comercial. ¡Contáctenos para obtener más información sobre nosotros!
Copyright (c) 2023-2024 Softwaremill https://softwaremill.com.