基於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的完整日誌路徑
至此整個工程部署完成就可以進行測試了。