Java/Scala杂记之二


Overview

最近的Java/Scala项目又积累了一些工具,研究不深,权当记录。至于spray系列,等把akka研究明白再写吧(巨型坑)。

typesafe-config

最早接触这个配置库是在spray的时候,当时觉得语法还不错,而且这个库是纯Java写的,没有什么第三方依赖,这是最爽的,省得解决依赖蛋疼。

配置:

//application.conf
SqlExecutorJDBC {
  zkQuorum = "datanode0,datanode1,datanode2"
  zkPort = 2181
  zkRoot = "/hbase-unsecure"
}

先来个Java的例子:

Config conf = ConfigFactory.load();
String zkQuorum = "datanode0,datanode1,datanode2";
if (conf.hasPath("SqlExecutorJDBC.zkQuorum")) {
	zkQuorum = conf.getString("SqlExecutorJDBC.zkQuorum");
	logger.info("zkQuorum[{}]", zkQuorum);
}
int zkPort = 2181;
if (conf.hasPath("SqlExecutorJDBC.zkPort")) {
	zkPort = conf.getInt("SqlExecutorJDBC.zkPort");
	logger.info("zkPort[{}]", zkPort);
}
String zkRoot = "/hbase-unsecure";
if (conf.hasPath("SqlExecutorJDBC.zkRoot")) {
	zkRoot = conf.getString("SqlExecutorJDBC.zkRoot");
	logger.info("zkRoot[{}]", zkRoot);
}

再来个Scala的例子:

val config = ConfigFactory.load()
lazy val zkQuorum = Try(config.getString("SqlExecutorJDBC.zkQuorum")).getOrElse("datanode0,datanode1,datanode2")
lazy val zkPort = Try(conf.getInt(SqlExecutorJDBC.zkPort")).getOrElse(2181)
lazy val zkRoot = Try(conf.getString("SqlExecutorJDBC.zkRoot")).getOrElse("/hbase-unsecure")

用起来挺简单的,而且支持更多层级的配置,支持的类型也更多,比原始的.properties更强大,可读性更好,也比xml写起来更方便,所以以后有配置就用这个了。不过话说来,JVM的配置化简直是扯淡,所有程序都打在jar包里,改一个配置还得先解包,改完再重新打包,这和写程序里好像并没有什么卵区别啊?

HikariCP

说来也巧,最早知道HikariCP也是在研究spray的时候,话说现在HikariCP现在号称JDBC界性能第一连接池。当初搞JavaEE的时候,用iBatis比较多,对连接池基本采用配置封装,没有实际用过,那时候还是c3p0的天下,看来c3p0躺着睡觉的时候,HikariCP已经成为了老大。一开始还以为这个库是个日本人写的,不过在github上看了一下作者,是个在日本生活的欧美人。废话不多说,直接上代码:

class SqlExecutorJDBC(zkQuorum: String, zkPort: Int, zkRoot: String) {
  val logger = LoggerFactory.getLogger(getClass)
  val config = new HikariConfig()
  config.setJdbcUrl(s"jdbc:phoenix:$zkQuorum:$zkPort:$zkRoot")
  config.addDataSourceProperty("cachePrepStmts", "true")
  config.addDataSourceProperty("prepStmtCacheSize", "250")
  config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
  config.setAutoCommit(true)
  config.setMaximumPoolSize(64)
  config.setMinimumIdle(16)
  val ds = new HikariDataSource(config)
  def getTaskResult(key: String): (String, String) = {
    val sql = s"SELECT DATA_VALUE,DATA_STATUS FROM DATA_STATUS WHERE DATA_KEY='$key'"
    Try(ds.getConnection) match {
      case Success(conn) =>
        Try(conn.createStatement().executeQuery(removeTailing(sql))) match {
          case Success(rs) =>
            Try(rs.next())
            val value = Try(rs.getString(1)).getOrElse("")
            val status = Try(rs.getString(2)).getOrElse("-1")
            Try(conn.close())
            (value, status)
          case Failure(e) =>
            logger.warn(s"execute sql failed, ${e.toString}")
            Try(conn.close())
            ("", "-1")
        }
      case Failure(e) =>
        logger.warn(s"get connection from cp failed, ${e.toString}")
        ("", "-1")
    }
  }
  def stop() = ds.close()
  def removeTailing(query: String): String = {
    if (query != null && query.length > 0 && query.endsWith(";")) {
      return query.substring(0, query.length - 1)
    }
    query
  }
}

HikariCP初始化配置的方式很多,因为我项目里已经选用了typesafe-config做配置管理,就不采用配置文件的形式初始化了,直接用JDBC url,这里是一个apache phoenix的例子。之前没怎么用过JDBC,ResultSet在读取值之前先next一下真是蛋疼,这里卡了我好久……这里有一处比较蛋疼的,因为我要主动归还连接给连接池,所以要调用Connection.close()方法,但是本来可以只写一个Try的,但是为保留一个conn的引用,不得不多写了一些。

Apache Phoenix

apache的大坑之一,简单的说是一个构建在HBase上的SQL引擎,将普通SQL语句翻译成HBase的基本api,兼容JDBC,对SQL的支持并不是什么都可以,有它自己的语法。这个项目坑的地方在于,文档一点不实用,只说了它是干嘛,能用SQL语法怎么怎么样,但是一个example都没有,可能觉得JDBC大家应该都会吧……坑的第二个地方在于引入一个phoenix-core的包导致maven编译出现各种依赖冲突的问题,它自己依赖的下面都是各种冲突,真是搞不清楚maven的机制。

这个东西是之前焦老师引入用来做DMP数据存储的,之前用了一个开源的PhoenixRESTServer,但是这货有一个问题,就是经常整个服务阻塞住了,不清楚是因为发生异常导致服务状态无法恢复,还是它的连接池的连接并不释放(没主动调用close)的缘故,反正就是很坑,我一怒之下就给改成自己通过JDBC连接了。不过这东西Google都搜不到,貌似是Hortonworks的一个员工自己做的。

具体怎么用在上面HikariCP的例子已经有说明了,所以这里也不多说。总之东西倒是不错的东西,就是文档太简略了。而且最好自己通过JDBC连接,千万别用那个坑爹的PhoenixRESTServer。

spray-json

话说spray让我接触了好多库,spray-json是独立发布的一个json的序列化反序列化工具,通过引入一个叫做json protocal的东西,定义如果把json序列化成一个scala对象,这点比jackson这种序列化成map的要好一些,毕竟强类型支持。这个库的文档还是比较细的(相对于其他spray的文档),所以先不上例子了。强类型不方便的地方在于写转换方法比较麻烦,这点和go的变准json库差太多了,go的那个是目前我认为用起来最方便的。spray-json还有一个比较坑的地方在于,它写反序列化方法时,模式匹配的竟然是一个序列,特么的序列是什么鬼,如果有字段忽略的情况,那顺序就变了啊,好歹给个map啊……

Conclusion

已经到了第二期,JVM写一个服务真是蛋疼菊紧,1/3时间写代码,1/3时间等编译和JVM启动,1/3时间解决引入库之间的依赖版本冲突,还是Go又快又好啊。希望到了下一期,就可以写spray/slick/akka了……

Reference

  1. Configuration library for JVM languages
  2. 光 HikariCP・A solid high-performance JDBC connection pool at last
  3. High performance relational database layer over HBase for low latency applications
  4. spray-json is a lightweight, clean and efficient JSON implementation in Scala

文章作者: Odin
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Odin !
  目录