1. 程式人生 > >基於AKKA HTTP構建查詢HBase的RESTful API完整過程

基於AKKA HTTP構建查詢HBase的RESTful API完整過程

如果還不清楚akka http的使用,可參看 使用scala基於AKKA HTTP開發REST介面的簡單例項,工程目錄結構如下:

整個處理過程:客戶端傳送get或者post請求->服務端處理->結果返回客戶端(以json字串方式返回),這裡的客戶端測試工具採用的是IDEA自帶的rest測試工具,可通過Tools->Test RESTful Web Service調出。

看服務端的一個get請求處理程式碼:

val route = get {
  path("row") {
    parameter("rowkey") { rowkey =>
      val result: Future[Option[String]] = selectByRowkey(rowkey)
      onSuccess(result) {
        case Some(item) if (!"".equals(item)) => complete(item)
        case Some(item) if ("".equals(item)) => complete("nothing was found with the rowkey:" + rowkey)
        case None => complete(StatusCodes.NotFound)
      }
    }
  }
} 

程式碼的邏輯是根據rowkey查hbase對應的記錄,比較簡單。查詢的時候使用Future非同步處理查詢請求,查詢完成時將結果返回客戶端,啟動服務,在rest客戶端測試:

配置好後,點選左上角的綠色三角箭頭進行測試,如果是POST請求(服務端接受post引數可參看官網文件),測試配置如下:

還可以通過瀏覽器測試get請求,如在瀏覽器輸入:http://localhost:8080/row?rowkey=1001進行測試。

業務邏輯編寫測試完成後緊接著就是將程式部署到伺服器上去,在前一篇介紹akka http的例項時,將依賴的jar包整個打進專案jar包中 ,配置檔案也沒有分離,本次對這兩項做了優化,先看專案在伺服器端的部署結構:

其中:bin-存放程式啟停shell指令碼(java -jar方式執行程式)

           conf-存放的配置檔案

           lib-第三方依賴包

           logs-程式日誌目錄

一.專案打包不包含依賴包

這個給出完整的pom檔案,供參考

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.vip.bigdata</groupId>
  <artifactId>api</artifactId>
  <version>1.0-SNAPSHOT</version>
  <inceptionYear>2008</inceptionYear>
  <properties>
    <scala.version>2.12.6</scala.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>${scala.version}</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.4</version>
    </dependency>
    <dependency>
      <groupId>com.typesafe.akka</groupId>
      <artifactId>akka-http_2.12</artifactId>
      <version>10.1.3</version>
    </dependency>
    <dependency>
      <groupId>com.typesafe.akka</groupId>
      <artifactId>akka-stream_2.12</artifactId>
      <version>2.5.12</version>
    </dependency>
    <dependency>
      <groupId>com.typesafe.akka</groupId>
      <artifactId>akka-http-spray-json_2.12</artifactId>
      <version>10.1.3</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.16</version>
    </dependency>
    <dependency>
      <groupId>org.apache.hbase</groupId>
      <artifactId>hbase-it</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.hbase</groupId>
      <artifactId>hbase-common</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>com.aliyun.phoenix</groupId>
      <artifactId>ali-phoenix-core</artifactId>
      <version>4.11.0-AliHBase-1.1-0.3</version>
    </dependency>
    <!--<dependency>-->
      <!--<groupId>jdk.tools</groupId>-->
      <!--<artifactId>jdk.tools</artifactId>-->
      <!--<version>1.8</version>-->
      <!--<scope>system</scope>-->
      <!--<systemPath>C:/Program Files/Java/jdk1.8.0_171/lib/tools.jar</systemPath>-->
    <!--</dependency>-->
  </dependencies>

  <build>
    <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
      <excludes>
        <exclude>conf/*</exclude>
      </excludes>
    </resource>
    </resources>
    <sourceDirectory>src/main/scala</sourceDirectory>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.6</version>
        <configuration>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
              <classpathPrefix>lib/</classpathPrefix>
              <mainClass>com.vip.bigdata.server.HbaseServer</mainClass>
            </manifest>
            <addMavenDescriptor>false</addMavenDescriptor>
          </archive>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.10</version>
        <executions>
          <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}/lib</outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.scala-tools</groupId>
        <artifactId>maven-scala-plugin</artifactId>
        <version>2.15.2</version>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <scalaVersion>${scala.version}</scalaVersion>
          <args>
            <arg>-target:jvm-1.8</arg>
          </args>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

最主要是配置這兩塊的內容:

maven-jar-plugin外掛裡的:
<manifest>
  <addClasspath>true</addClasspath>
  <classpathPrefix>lib/</classpathPrefix>
  <mainClass>com.vip.bigdata.server.HbaseServer</mainClass>
</manifest>
maven-dependency-plugin外掛裡的:
<execution>
  <id>copy-dependencies</id>
  <phase>package</phase>
  <goals>
    <goal>copy-dependencies</goal>
  </goals>
  <configuration>
    <outputDirectory>${project.build.directory}/lib</outputDirectory>
  </configuration>
</execution>

上面的配置是把依賴的jar包拷貝打包後的lib資料夾下,如下所示:

把lib資料夾的包傳到伺服器上的lib資料夾下。 

二.打包不包含配置檔案

在有一篇博文中有提到:maven打包不包含配置檔案

在上面給出的配置中:

<resource>
  <directory>src/main/resources</directory>
  <filtering>true</filtering>
  <excludes>
    <exclude>conf/*</exclude>
  </excludes>
</resource>

這個地方就是配置打包時不包含conf下所有的配置檔案,將conf裡的配置檔案上傳到伺服器中的conf目錄下,需要注意的是讀取配置檔案的類也要響應的調整路徑,下面給出完成的程式碼:

package com.vip.bigdata.util

import java.io.{BufferedInputStream, File, FileInputStream}
import java.util.Properties

import scala.util.Try

object PropertyConfig extends  Logging {
  val directory = new File("..")
  val filePath = directory.getAbsolutePath
  val appConfName = "application.properties"
  val appConfPath = filePath + "/conf/"+appConfName
  val prop = new Properties() {
    new Properties() {
      if (sys.env.get("os").mkString.toLowerCase.contains("windows")) {
        logInfo("load resource from peoperties file inside on windows")
        if (Try(new BufferedInputStream(new FileInputStream(appConfPath))).isFailure)
          logWarn("Cannot load resource from properties file inside on windows, maybe properties file doesn't exists")
      } else {
        if (new File(appConfPath).exists()) {
          logInfo("Load resource from properties file outside")
          load(new FileInputStream(appConfPath))
        } else {
          logInfo("Load resource from properties file inside")
          if (Try(load(this.getClass.getClassLoader.getResourceAsStream(appConfPath))).isFailure)
            logWarn("Cannot load resource from properties file inside, maybe properties file doesn't exists")
        }
      }
    }.getProperty(appConfName, appConfName)
      .split(",")
      .foreach(line => {
        val f = filePath+"/conf/"+line
        if (sys.env.get("os").mkString.toLowerCase.contains("windows")) {
          logInfo("load resource from peoperties file inside on windows")
          if (Try(load(new BufferedInputStream(new FileInputStream(f)))).isFailure)
            logWarn("Cannot load resource from properties file inside on windows, maybe properties file doesn't exists")
        } else {
          if (new File(f).exists()) {
            logInfo(s"Load resource $f from properties file outside")
            load(new FileInputStream(f))
          } else {
            logInfo(s"Load resource $f from properties file inside")
            if (Try(new BufferedInputStream(new FileInputStream(f))).isFailure)
              logWarn(s"Cannot load resource $f from properties file inside, maybe properties file $f doesn't exists")
          }
        }
      })
  }

  def getProperty(key: String): String = {
    logInfo(s"the $key of property value is:"+this.prop.getProperty(key))
    return this.prop.getProperty(key);
  }

}

上面的程式碼是先載入application.properties配置檔案,這個配置檔案裡放的是要載入的所有的配置檔案,格式如下:

配置檔案間以逗號隔開,這樣所有的配置檔案都會一次性載入進去,上面的類繼承了自定義的Logging日誌類,可以忽略。

還需要注意一點,因為把log4j的配置檔案放到了conf目錄下,程式碼要稍作改動,不然無法讀到log4j的配置檔案,日誌功能也就不起作用了,具體如下,因為我有一個Logging日誌介面,所以在裡面新增下面的程式碼即可,

PropertyConfigurator.configure(filePath+"/conf/log4j.properties")//log4j的完整日誌路徑

至此整個工程部署完成就可以進行測試了。