Read this in other languages: English, 中文
Origin is a framework base on Vert.x, it used to simplify the process to create a web/standard application.
Select Vert.x due to its principle is reactive and container first.
It supports the following applications:
- Web application
- Standard application
Configure firstly, due to reactive methodology, the first thing is get configuration then initialize class, then the instance of class store in BeanFactory.
Use SPI to inject basic class.
- Web basic class,
com.origin.framework.spi.OriginRouter
- Standard basic class,
com.origin.framework.spi.OriginTask
Provide Single Patten and Cluster Patten.
- Single Patten, it's a separate instance.
- Cluster Patten, it depends on Zookeeper, and all instance on same znode share data and eventbus and so on.
Test case is get 100 data from database, and result row contains a long text, Test Code
Test Setting
- Database connection pool size is 50.
- Restrict memory is 512m for docker.
Conclusion
- Low concurrency, RT of Springboot will less than origin's RT.
- High concurrency, RT of origin will less than Springboot's RT.
- Docker, RT of origin will be less than Springboot's RT. In case of 128M memory, springboot application occurs OOM error sometimes.
Test Cases | Springboot Local | Origin Local | Springboot Docker | Origin Docker | ||||
Avg(ms) | Max(ms) | Avg(ms) | Max(ms) | Avg(ms) | Max(ms) | Avg(ms) | Max(ms) | |
50Threads*10Loops | 572 | 625 | 611 | 687 | 761 | 890 | 876 | 1226 |
100Threads*10Loops | 1083 | 2134 | 805 | 1117 | 1645 | 3211 | 1290 | 2460 |
500Threads*1Loops | 4652 | 6440 | 3051 | 3722 | 8023 | 10742 | 4944 | 6819 |
- origin-starter-pom pom project, declare dependencies and versions.
- origin-starter-web Use to create a web application.
- origin-starter-app Use to create a standard application.
due to Origin doesn't push to center maven repository, so you need to clone this project and compile it by yourself.
- clone the project.
- compile the project.
- add the dependency to your project.
- create a web project.
<dependency> <groupId>com.originframework</groupId> <artifactId>origin-starter-web</artifactId> <version>2.0</version> <scope>compile</scope> <type>jar</type> </dependency>
- create a standard project.
<dependency> <groupId>com.originframework</groupId> <artifactId>origin-starter-app</artifactId> <version>2.0-SNAPSHOT</version> <scope>compile</scope> <type>jar</type> </dependency>
- create a web project.
you can use origin-framework-cli to generate sample codes, please update cli.json according by your requirement.
- Project(string): project name.
- Port(int): specify port if it's a web application.
- Cluster(boolean): specify the project run as single node or cluster node.
- Group(string): group in
pom.xml
. - ArtifactId(string): artifcatId in
pom.xml
, and use group+artifactId as basic package. - Version(string): project version.
- OriginVersion(string): origin version that you use in the project, current it has 1.0, 2.0 and 2.1.
- App(boolean): if true,
conf.json
will contains default configuration of database. - ES(boolean): if true,
conf.json
will contains default configuration of elastic search. - Redis(boolean): if ture,
conf.json
will contains default configuration of redis.
-
create a spi file in
META-INF
, then add your service into the file.- Web application,add a spi file named
com.origin.framework.spi.OriginRouter
inMETA-INF
, then create a class implementcom.origin.framework.spi.OriginRouter
and add apis inroute()
method. Example:
public class BlogRouter implements OriginRouter { @Override public void router(OriginWebVertxContext originVertxContext, OriginConfig originConfig) { originVertxContext.getRouter().get("/blog") .handler(ctx -> { SqlClient sqlClient = OriginWebApplication.getBeanFactory().getSqlClient(); sqlClient.preparedQuery("select * from blog limit 10").execute() .onComplete(ar -> { if (ar.succeeded()) { RowSet<Row> rowSet = ar.result(); List<JsonObject> results = new ArrayList<>(rowSet.size()); rowSet.forEach(row -> results.add(row.toJson())); ctx.json(results); } else { ctx.fail(500, ar.cause()); } sqlClient.close(); }) .onFailure(err -> ctx.fail(500, err)); }); } }
- Standard application,add a spi file named
com.origin.framework.spi.OriginTask
inMETA-INF
, then create a class implementcom.origin.framework.spi.OriginTask
and add business logic inrun()
method. Example:
public class DataGenerator implements OriginTask { @Override public void run(OriginAppVertxContext originAppVertxContext, OriginAppConfig originAppConfig) { originAppVertxContext.getVertx().setPeriodic(5000, t -> { originAppConfig.getEventBus().publish("data", "demo"); }); } }
- Web application,add a spi file named
-
configuration first, create a configuration file named
conf/config.json
, and add child configuration in the file to initialize modules you required, such asserver
,db
,es
,redis
and so on. Completed configuration.
{
"server": {
"host": "127.0.0.1",
"port": 8080
},
"db": {
"port": 5432,
"host": "127.0.0.1",
"user": "postgres",
"password": "postgres",
"database": "origin",
"pool": {
"maxSize": 20
}
},
"redis": {
"endpoint": "redis://localhost:6379",
"role ": "MASTER",
"maxWaitingHandlers": 2048,
"netClientOptions": {
"TcpKeepAlive": true,
"TcpNoDelay": true
}
},
"es": {
"host": "localhost",
"port": 9200,
"schema": "http",
"data-type": "application/json"
}
}
- Start a single application.
- start a web application.
public class Main extends AbstractVerticle { public static void main(String[] args) { OriginWebApplication.runAsSingle(Main.class); } }
- Start a standard application.
public class Main extends AbstractVerticle { public static void main(String[] args) { OriginAppApplication.runAsSingle(Main.class); } }
- start a web application.
- Start an application that register into zookeeper node, it depends on zookeeper, all instance share data, eventbus if they are under a same zookeeper znode, you can add a
zookeeper.json
into your project to specify znode.- Start a web application
public class Main extends AbstractVerticle { public static void main(String[] args) { OriginWebApplication.runAsCluster(Main.class); } }
- Start a standard application.
public class Main extends AbstractVerticle { public static void main(String[] args) { OriginAppApplication.runAsCluster(Main.class); } }
- Start a web application
- package an executable jar, add
maven-shade-plugin
into pom.xml, specify main class, to avoid conflict of java version, should excludeSF
,RSA
,DSA
files.
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven-shade-plugin.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${main.verticle}</Main-Class>
<Main-Verticle>${main.verticle}</Main-Verticle>
</manifestEntries>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar
</outputFile>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
- create an executable file, add
native-image-maven-plugin
into pom.xml, it depends onnative-image
ofGraalVM
, so need addnative-image.properties
inMETA-INF\native-image
folder, the file specify how to package and how to lint static files, and you can use argument-agentlib:native-image-agent
to generate jni,proxy files, below is steps.- generate static files.
`java -agentlib:native-image-agent=config-output-dir=./src/main/resources/META-INF/native-image -cp .\target\config-1.0.0-SNAPSHOT-fat.jar com.kevin.sample.vertx.config.ConfigVerticle`
- create
native-image.properties
file.
Args =\ --report-unsupported-elements-at-runtime \ --initialize-at-run-time=io.netty.handler.ssl \ --initialize-at-build-time=org.slf4j.LoggerFactory,ch.qos.logback \ --trace-class-initialization=org.slf4j.MDC \ -H:ReflectionConfigurationResources=${.}/reflect-config.json \ -H:JNIConfigurationResources=${.}/jni-config.json \ -H:ResourceConfigurationResources=${.}/resource-config.json \ -H:Class=com.kevin.sample.vertx.config.ConfigVerticle \ -H:+PrintClassInitialization \ -H:+ReportExceptionStackTraces \ --no-fallback \ -H:Name=config \ --initialize-at-run-time=\ io.netty.handler.codec.compression.ZstdOptions
- add
native-image-maven-plugin
plugin, runmvn clean package
to generate an executable file.<plugin> <groupId>org.graalvm.nativeimage</groupId> <artifactId>native-image-maven-plugin</artifactId> <version>${graal.version}</version> <executions> <execution> <goals> <goal>native-image</goal> </goals> <phase>package</phase> </execution> </executions> </plugin>
- generate static files.
docker/Dokerfile
, build a docker image by source codes, depends on maven wrapper.docker/Dokerfile.fat-jar
, build a docker image by an executable jar file.docker/Dokerfile.legacy-jar
, build a docker image by an executable jar file, base image is openjdk.
Examplesinclude an example to start a web application and an example to create data flow cluster base on eventbus.