Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/eigr/spawn-springboot-sdk
Spawn Springboot SDK
https://github.com/eigr/spawn-springboot-sdk
actor-model beam-vm concurrency erlang-distribution serverless-framework service-mesh sidecar spring-boot
Last synced: 2 months ago
JSON representation
Spawn Springboot SDK
- Host: GitHub
- URL: https://github.com/eigr/spawn-springboot-sdk
- Owner: eigr
- License: apache-2.0
- Created: 2022-06-13T21:24:09.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2023-08-27T14:42:27.000Z (over 1 year ago)
- Last Synced: 2023-08-27T16:26:20.029Z (over 1 year ago)
- Topics: actor-model, beam-vm, concurrency, erlang-distribution, serverless-framework, service-mesh, sidecar, spring-boot
- Language: Java
- Homepage: https://eigr.io/
- Size: 185 KB
- Stars: 7
- Watchers: 5
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Spawn Springboot SDK
Spawn Springboot SDK is the support library for the Spawn Actors system.
Spawn is based on the sidecar proxy pattern to provide the multi-language Actor Model framework.
Spawn's technology stack on top of BEAM VM (Erlang's virtual machine) provides support for different languages from its
native Actor model.For a broader understanding of Spawn please consult its official [repository](https://github.com/eigr-labs/spawn).
## Getting Started
The Spawn Springboot SDK allows you to configure a conventional Spring application to use the Spawn Actor Model.
In the sections below you will see how to do this.You'll need to make sure Spawn Proxy service is up and running.
With `docker-compose` you can define:> **_NOTE:_** _using docker is recommended for `dev purposes only`, see [spawn deploy](https://github.com/eigr/spawn#getting-started) for production examples._
```yml
version: "3.8"services:
spawn-proxy:
image: eigr/spawn-proxy:0.5.0
restart: always
environment:
PROXY_APP_NAME: spawn
PROXY_HTTP_PORT: 9001
PROXY_DATABASE_TYPE: mysql # postgres or others can be used
PROXY_DATABASE_NAME: eigr-functions-db
PROXY_DATABASE_USERNAME: admin
PROXY_DATABASE_SECRET: password
PROXY_DATABASE_HOST: localhost
PROXY_DATABASE_PORT: 5432
SPAWN_STATESTORE_KEY: 3Jnb0hZiHIzHTOih7t2cTEPEpY98Tu1wvQkPfq/XwqE=
USER_FUNCTION_HOST: 0.0.0.0 # Your Java Springboot application runtime host
USER_FUNCTION_PORT: 8090 # Your Java Springboot application runtime exposed port
ports:
- "9001:9001"
```### Maven Setup
First add the following dependency to your `pom.xml` file.```xml
com.github.eigr-labs.spawn-springboot-sdk
spawn-springboot-starter
v0.1.10```
You should also add jitpack.io repository.
```xml
jitpack.io
https://jitpack.io
```
You will also need to create a container image of your application.
You can do this using the protobuf-maven-plugin. Let's see how the pom.xml file would look with all this configured.```xml
jitpack.io
https://jitpack.io
spring-releases
Spring Releases
https://repo.spring.io/release
false
spring-releases
Spring Releases
https://repo.spring.io/release
false
com.github.eigr-labs.spawn-springboot-sdk
spawn-springboot-starter
v0.1.10
org.springframework.boot
spring-boot-starter-test
test
kr.motd.maven
os-maven-plugin
1.6.2
org.springframework.boot
spring-boot-maven-plugin
org.xolstice.maven.plugins
protobuf-maven-plugin
0.6.1
com.google.protobuf:protoc:3.19.2:exe:${os.detected.classifier}
grpc-java
io.grpc:protoc-gen-grpc-java:1.47.0:exe:${os.detected.classifier}
compile
compile-custom
org.apache.maven.plugins
maven-surefire-plugin
2.7
maven-dependency-plugin
2.5.1
getClasspathFilenames
properties
org.apache.maven.plugins
maven-compiler-plugin
1.8
1.8
```
### Defining your domain
Now that your setup is configured the second step is to add your protobuf files to the project.
For that create a folder called ***proto*** inside ***src/main***.Now just add your protobuf files that reflect your business domain inside **src/main/proto** folder. For example:
`example.proto`
```protobuf
syntax = "proto3";package io.eigr.spawn.example;
option java_multiple_files = true;
option java_package = "io.eigr.spawn.example";
option java_outer_classname = "ExampleProtos";message MyState {
int32 value = 1;
}message MyBusinessMessage {
int32 value = 1;
}
```It is important to try to separate the type of message that must be stored as the actors' state from the type of messages
that will be exchanged between their actors' operations calls. In other words, the Actor's internal state is also represented
as a protobuf type, and it is a good practice to separate these types of messages from the others in its business domain.In the above case `MyState` is the type protobuf that represents the state of the Actor that we will create later
while `MyBusiness` is the type of message that we will send and receive from this Actor.### Writing the Actor code
Now that the protobuf types have been created we can proceed with the code. Example definition of an Actor:
`Actor.java`:
```java
package io.eigr.spawn.example;import io.eigr.spawn.springboot.starter.ActorContext;
import io.eigr.spawn.springboot.starter.ActorKind;
import io.eigr.spawn.springboot.starter.Value;
import io.eigr.spawn.springboot.starter.annotations.Action;
import io.eigr.spawn.springboot.starter.annotations.Actor;
import lombok.extern.log4j.Log4j2;import java.util.Optional;
@Log4j2
@Actor(name = "joe", kind = ActorKind.SINGLETON, stateType = MyState.class, snapshotTimeout = 5000, deactivatedTimeout = 10000)
public class JoeActor {
@Action
public Value get(ActorContext context) {
log.info("Received invocation. Context: {}", context);
if (context.getState().isPresent()) {
MyState state = context.getState().get();
return Value.at().state(state)
.response(MyBusinessMessage.newBuilder()
.setValue(state.getValue())
.build())
.reply();
}
return Value.at().empty();
}@Action(name = "sum", inputType = MyBusinessMessage.class)
public Value sum(MyBusinessMessage msg, ActorContext context) {
log.info("Received invocation. Message: {}. Context: {}", msg, context);
int value = 1;
if (context.getState().isPresent()) {
log.info("State is present and value is {}", context.getState().get());
Optional oldState = context.getState();
value = oldState.get().getValue() + msg.getValue();
} else {
//log.info("State is NOT present. Msg getValue is {}", msg.getValue());
value = msg.getValue();
}log.info("New Value is {}", value);
MyBusinessMessage resultValue = MyBusinessMessage.newBuilder().setValue(value).build();return Value.at().response(resultValue).state(updateState(value)).reply();
}private MyState updateState(int value) {
return MyState.newBuilder().setValue(value).build();
}
}
```**Explaining the code:**
1. Every Actor must contain the `@Actor` annotation so that it can be registered as both a Spring Bean and a Spawn Actor.
2. Use the `@Action` annotation to tell Spawn which methods to expose as Actor actions.
Every command must have a name by which it can be invoked and may contain other input and output type annotations.3. An ActorContext object will always be passed to the action method via Spawn's sidecar proxy.
It is via ActorContext that it will be possible to access the current state of the Actor.
This will always be the second argument of the method unless your method is not taking any business type arguments,
in which case ActorContext will be passed as the first and only argument.
This is used when your method just needs to return some value.4. To return values and the updated state it will always be necessary to use the return type `Value`.
Return values can be one of three types:* **reply**: When the intention is to send some type of data to the caller.
* **noReply**: When you don't want to send any return to the caller and only the Actor's state can be updated.
* **empty**: Similar to noReply but no action will be taken regarding the actor state.### Setup Main application
`App.java`:
```java
package io.eigr.spawn.example;import io.eigr.spawn.springboot.starter.ActionRequest;
import io.eigr.spawn.springboot.starter.ActionResponse;
import io.eigr.spawn.springboot.starter.SpawnSystem;
import io.eigr.spawn.springboot.starter.autoconfigure.EnableSpawn;
// others imports omitted@Log4j2
@EnableSpawn // 1
@SpringBootApplication
@EntityScan("io.eigr.spawn.example") // 2
public class App {
public static void main(String[] args) {SpringApplication.run(App.class, args);}@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
SpawnSystem actorSystem = ctx.getBean(SpawnSystem.class); // 3
log.info("Let's invoke some Actor");
for (int i = 0; i < 10; i++) {
ActionRequest actionRequest = ActionRequest.of()
.actorName(actorName)
.action("sum")
.value(MyBusinessMessage.newBuilder().setValue(i).build())
.responseType(MyBusinessMessage.class)
.build(); // 4ActorResponse sumResult = actorSystem.invoke(actionRequest); // 5
log.info("Actor invoke Sum Actor Action value result: {}", sumResult.getValue().get());ActionRequest getRequest = ActionRequest.of(actorName, "get")
.responseType(MyState.class)
.build();ActionResponse getResult = actorSystem.invoke(getRequest);
log.info("Actor invoke Get Actor Action value result: {}", getResult.getValue());
}
};
}
}
```**Explaining the code:**
1. Enables the Spawn Actor system
2. Indicates in which package your actors will be searched. This is a Spring annotation and is for finding any beans your application declares.
3. All interaction with actors takes place through the `SpawnSystem` class. SpawnSystem in turn is a normal Spring Bean and can be injected into any Spring class normally, in the above case we prefer to retrieve it through Spring's own context with ***ctx.getBean(SpawnSystem.class)***.
4. To call your actors you will need to send the data type you defined as a protobuf via ActionRequest class.
5. Use the invoke method to perform the actions you defined on your actors.The complete example code can be found [here](https://github.com/eigr-labs/spawn-springboot-sdk/tree/main/spawn-springboot-examples).
## **Documentation**
- [Actor options](./docs/actor-options.md)
- [Singleton](./docs/actor-options.md#singleton-actor)
- [Abstract](./docs/actor-options.md#abstract-actor)
- [Pooled](./docs/actor-options.md#pooled-actor)
- [Default Actions](./docs/actor-options.md#default-actions)
- [Actor workflows](./docs/actor-workflows.md)## **Examples**
You can check [spawn-springboot-examples folder](./spawn-springboot-examples) to see some examples
## **Environment variables:**
- `PROXY_HTTP_PORT` This is the port of spawn proxy service
- `PROXY_HTTP_HOST` This is the host of spawn proxy service
- `USER_FUNCTION_PORT` This is the port that your service will expose to communicate with Spawn