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.jsonwill contains default configuration of database. - ES(boolean): if true,
conf.jsonwill contains default configuration of elastic search. - Redis(boolean): if ture,
conf.jsonwill 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.OriginRouterinMETA-INF, then create a class implementcom.origin.framework.spi.OriginRouterand 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.OriginTaskinMETA-INF, then create a class implementcom.origin.framework.spi.OriginTaskand 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,redisand 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.jsoninto 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-plugininto pom.xml, specify main class, to avoid conflict of java version, should excludeSF,RSA,DSAfiles.
<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-plugininto pom.xml, it depends onnative-imageofGraalVM, so need addnative-image.propertiesinMETA-INF\native-imagefolder, the file specify how to package and how to lint static files, and you can use argument-agentlib:native-image-agentto 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.propertiesfile.
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-pluginplugin, runmvn clean packageto 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.