Java
Vert.x
spring-boot

SpringBoot + Vert.x(3.0.0) + SqlTemplate を試す

More than 3 years have passed since last update.

はじめに

Vert.xのv3がリリースされたので、SpringBootの中からVert.xを使って、Bootiful SQL Template
を利用してみた。ソースはこちら

コンセプト

  • SpringBootを使ってJavaアプリケーションを作成する。
  • Vert.xはSpringBootアプリケーションの中でembeddedの形で使う。

プロジェクト構成

├── pom.xml
└── src
    └── main
        ├── java
        │   └── muraken
        │       └── example
        │           ├── Application.java
        │           ├── HttpServerVerticle.java
        │           ├── SqlTemplateService.java
        │           ├── SqlTemplateVerticle.java
        │           └── entity
        │               ├── Dept.java
        │               └── Emp.java
        └── resources
            ├── application.properties
            ├── data.sql
            ├── schema.sql
            └── sql
                ├── selectAll.sql
                ├── selectByArgs.sql
                ├── selectByEmpno.sql
                └── selectByParam.sql

pom.xml

pom.xmlにはVert.x、SpringBoot、SqlTemplate、Jacksonを追加。
長いので、リンク先参照。

Application.java

Vert.xのVerticleを@Beanで登録してみる。SqlTemplateはJDBCアクセスするblocking codeなので、SqlTemplateをラップするSqlTemplateVerticleはWorkerVerticleとしてdeployする。

Application.java
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {

  @Bean
  SqlTemplate sqlTemplate(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
    return new SqlTemplate(jdbcTemplate, namedParameterJdbcTemplate);
  }

  @Bean
  ObjectMapper jsonMapper() {
    return new ObjectMapper();
  }

  @Bean
  SqlTemplateVerticle sqlTemplateVerticle() {
    return new SqlTemplateVerticle();
  }

  @Bean
  HttpServerVerticle httpServerVerticle() {
    return new HttpServerVerticle();
  }

  public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

    final Vertx vertx = Vertx.vertx();

    vertx.deployVerticle(context.getBean(SqlTemplateVerticle.class), new DeploymentOptions().setWorker(true));
    vertx.deployVerticle(context.getBean(HttpServerVerticle.class));

  }
}

HttpServerVerticle.java

HttpServerを立ち上げて、リクエストが来たらEventBusを使って、SqlTemplateVerticleにメッセージを送信し、レスポンスをそのまま画面に返す。

HttpServerVerticle.java
@Component
public class HttpServerVerticle extends AbstractVerticle {

  public void start() {
    console("start.");

    vertx.createHttpServer().requestHandler(req -> {
      console("Received a http request");

      vertx.eventBus().send("eb.sqltemplate", "findAll req", ar -> {
        if (ar.succeeded()) {
          console("Received reply: " + ar.result().body());
          req.response().end((String) ar.result().body());
        }
      });
    }).listen(8080);
  }

  private void console(String message) {
    System.out.println("[" + Thread.currentThread().getName() + " ] " + getClass().getName() + " : " + message);
  }
}

SqlTemplateVerticle.java

SqlTemplateを利用するSqlTemplateServiceをインジェクションしてもらう。JacksonのObjectMapperもそうしたけど、これはNGかも。
EventBusからメッセージが来たら、SqlTemplateServiceを呼び出して、結果をJSONに変換してリプライを返すようにしている。
Vert.xのJsonObjectは取り回しがやりづらく、結局JSONで扱うなら、Stringでもよいのではないかと。

SqlTemplateVerticle.java
@Component
public class SqlTemplateVerticle extends AbstractVerticle {

  @Autowired
  private SqlTemplateService sqlTemplateService;

  @Autowired
  private ObjectMapper jsonMapper;

  public void start() throws JsonProcessingException {
    console("start.");

    vertx.eventBus().consumer("eb.sqltemplate", message -> {
      console("Received a message: " + message.body());

      List<Emp> emps = this.sqlTemplateService.findAll();

      try {
        String json =this.jsonMapper.writeValueAsString(emps);
        message.reply(json);
      } catch (JsonProcessingException e) {
        e.printStackTrace();
      }
    });
  }

  private void console(String message) {
    System.out.println("[" + Thread.currentThread().getName() + " ] " + getClass().getName() + " : " + message);
  }
}

SqlTemplateService.java

こちらはBootiful SQL Templateから抜粋で拝借。 2Way SQLですよ。

SqlTemplateService.java
@Component
public class SqlTemplateService {
  @Autowired
  SqlTemplate template;

  public List<Emp> findAll() {
    return template.forList("sql/selectAll.sql", Emp.class);
  }
}

実行

通常のJavaアプリケーションとして実行できる。Java8が必要。
実行したらlocalhosto:8080にアクセスする。

mvn package

java -jar target/springboot-vertx-example-1.0.0.jar

起動すると SqlTemplateVerticleがWorkerThreadで、HttpServerVerticleがEventLoopThreadで動作しているのが分かる。

2015-06-25 00:27:00.858  INFO 73715 --- [           main] muraken.example.Application              : Started Application in 4.783 seconds (JVM running for 6.034)
[vert.x-worker-thread-0 ] muraken.example.SqlTemplateVerticle : start.
[vert.x-eventloop-thread-4 ] muraken.example.HttpServerVerticle : start.

続けてlocalhost:8080にアクセスすると次のようになる。

[vert.x-eventloop-thread-4 ] muraken.example.HttpServerVerticle : Received a http request
[vert.x-worker-thread-1 ] muraken.example.SqlTemplateVerticle : Received a message: findAll req
2015-06-25 00:28:38.357 DEBUG 73715 --- [worker-thread-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2015-06-25 00:28:38.358 DEBUG 73715 --- [worker-thread-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [select
    *
from
    emp]
[vert.x-eventloop-thread-4 ] muraken.example.HttpServerVerticle : Received reply: [{"empno":7369,"ename":"SMITH","job":"CLERK","mgr":7902,"hiredate":345826800000,"sal":800.0,"comm":null},{"empno":7499,"ename":"ALLEN","job":"SALESMAN","mgr":7698,"hiredate":351442800000,"sal":1600.0,"comm":300.0},{"empno":7521,"ename":"WARD","job":"SALESMAN","mgr":7698,"hiredate":351615600000,"sal":1250.0,"comm":500.0},{"empno":7566,"ename":"JONES","job":"MANAGER","mgr":7839,"hiredate":354985200000,"sal":2975.0,"comm":null},{"empno":7654,"ename":"MARTIN","job":"SALESMAN","mgr":7698,"hiredate":370450800000,"sal":1250.0,"comm":1400.0},{"empno":7698,"ename":"BLAKE","job":"MANAGER","mgr":7839,"hiredate":352220400000,"sal":2850.0,"comm":null},{"empno":7782,"ename":"CLARK","job":"MANAGER","mgr":7839,"hiredate":347814000000,"sal":2450.0,"comm":null},{"empno":7788,"ename":"SCOTT","job":"ANALYST","mgr":7566,"hiredate":408207600000,"sal":3000.0,"comm":null},{"empno":7839,"ename":"KING","job":"PRESIDENT","mgr":null,"hiredate":374770800000,"sal":5000.0,"comm":null},{"empno":7844,"ename":"TURNER","job":"SALESMAN","mgr":7698,"hiredate":368722800000,"sal":1500.0,"comm":0.0},{"empno":7876,"ename":"ADAMS","job":"CLERK","mgr":7788,"hiredate":411145200000,"sal":1100.0,"comm":null},{"empno":7900,"ename":"JAMES","job":"CLERK","mgr":7698,"hiredate":376153200000,"sal":950.0,"comm":null},{"empno":7902,"ename":"FORD","job":"ANALYST","mgr":7566,"hiredate":376153200000,"sal":3000.0,"comm":null},{"empno":7934,"ename":"MILLER","job":"CLERK","mgr":7782,"hiredate":380559600000,"sal":1300.0,"comm":null}]

何回かブラウザでアクセスすると、HttpServerVerticleは常にvert.x-eventloop-thread-4で動作するのに対して、SqlTemplateVerticleはvert.x-worker-thread-1の1の部分が、2, 3, 4...となり、スレッドが変わることが分かる。

感想

  • Vert.x v3のリリースおめでとうございます!
  • SpringBoot、data.sqlとschema.sqlが超便利!(そこ?)
  • EventBusを使ってnon blockingな非同期メッセージング処理が簡単に書ける
  • 今回やってないけど、cluster組むのもEventBusがあるから簡単。
  • Bootiful SQL Template2Way SQLで 堅くて良いよね。