1. 程式人生 > >Scala微服務架構 一

Scala微服務架構 一

AC import print 列表 erl 可用 override 自動生成 中繼

Scala微服務架構 一

因為公司的一個項目需求變動會非常頻繁,同時改動在可控範圍內,加上產品同學喜歡一些超前思維,我們的CTO決定提前開啟八門,使用微服務架構。

那麽什麽是微服務架構呢?

1.1 微服務架構

“微服務”架構是近期軟件應用領域非常熱門的概念。

1.1.1 什麽是微服務架構

微服務是一種架構風格,一個大型復雜軟件應用由一個或多個微服務組成。系統中的各個微服務可被獨立部署,各個微服務之間是松耦合的。每個微服務僅關註於完成一件任務並很好地完成該任務。在所有情況下,每個任務代表著一個小的業務能力。

特點

也就是說,微服務就是SOA的變種,在保證SOA業務分離能力的同時,減少每個業務粒度,同時去除了SOA中比較惱人的ESB.

自由組裝

這個圖片從資源利用率的角度,體現了微服務一個非常給力的優點
技術分享圖片

數據分離

同時,對應用組件封裝的方式是整體架構與微服務架構的主要差異,微服務架構將相關聯的業務邏輯及數據放在一起形成獨立的邊界,其目的是能在不影響其他應用組件(微服務)的情況下更快地交付並推出市場。
技術分享圖片

1.2 前後對接協議 JsonApi

官網

1.3 本期語法糖

1.3.1 Scala的Package Object

官網講解https://www.scala-lang.org/docu/files/packageobjects/packageobjects.html
為啥使用Package Object而不是Object呢?
原因很簡單,答案是==代碼更幹凈==。如何做到的呢?

我copy了官網的例子,諸位請看:

// in file gardening/fruits/Fruit.scala
package gardening.fruits
case class Fruit(name: String, color: String)
object apple extends Fruit("Apple", "green")
object plum extends Fruit("Plum", "blue")
object banana extends Fruit("Banana", "yellow") 

上面我在/gardening/fruits/包下定義了幾個Object

如果想使用直接使用上面定義的幾個Object,可以這樣

package com

object main {
    import gardening.fruits._
    val planted = List(apple, plum, banana)    
    def showFruit(fruit: Fruit) {
        println(fruit.name +"s are "+ fruit.color)
    }
}

但是,其實我們還有更簡潔的調用方式,各位也已經猜到了,就是Package Object
只要在調用包的上一級/gardening/包下定義Package Object fruits,這樣的寫法就和class和objcet的伴生關系一樣,我們可以直接訪問伴生包中的變量,方法和隱式。

// in file gardening/fruits/package.scala
package gardening
package object fruits {
    val planted = List(apple, plum, banana)               
    def showFruit(fruit: Fruit) {
        println(fruit.name +"s are "+ fruit.color)
    }
}

這裏要註意,思想不要局限,反過來也是可以的,比如在Package Object fruits中定義了一系列的object,在/gardening/包下的object可以直接使用。

1.3.2 Scala的自定義註解(Annotation)

本段摘自

Annotation (註解)的介紹

Scala中的註解語法與Java中類似。
標準庫定義的註解相關內容在包scala.annotation中。

註解的基本語法為:

@註解名稱(註解參數...)
val a = ???

與Java註解的用法類似,註解參數不是必須的,一個元素允許擁有多個註解。

Annotation的定義

==Scala中的自定義註解不是接口/特質,而是類.==
自定義註解需要從註解特質中繼承,Scala中提供了兩類註解特質:

  • scala.annotation.ClassfileAnnotation 由Java編譯器生成註解
  • scala.annotation.StaticAnnotation 由Scala編譯器生成註解

兩類註解特質都繼承自基類scala.annotation.Annotation.兩類註解特質的基類相同,因此自定義註解類時允許同時混入兩類註解特質。

  • 繼承自ClassfileAnnotation基類的註解在使用時參數應以具名參數(named arguments)形式傳入。
  • 繼承自StaticAnnotation基類的註解無此限制。

定義註解類語法與普通類相同:

// 標記註解
class 註解名稱 extends StaticAnnotation/ClassfileAnnotation

// 有參註解
class 註解名稱(參數表...) extends StaticAnnotation/ClassfileAnnotation

Annotation的解析

通過反射機制獲取註解信息,相關API位於scala.reflect.runtime.universe包路徑下。

獲取註解

  • 獲取類的註解:
    1. 使用typeOf()方法,獲取Type類型的類信息。typeOf[Test]: Type
    2. 通過Type.typeSymbol獲取Symbol。type.typeSymbol: Symbol
    3. 通過Symbol.annotations獲取List[Annotation](註解列表)。symbol.annotations.head: Annotation
    4. 根據註解列表的head獲得語法樹annotation.tree: Tree
  • 獲取方法/成員字段的註解:
    1. 使用typeOf()方法,獲取Type類型的類信息。typeOf[Test]: Type
    2. 通過Type.decls/decl()方法篩選出目標成員的Symbol。type.decl(TermName("ff ")): Symbol
    3. 通過Symbol.annotations獲取List[Annotation](註解列表)。symbol.annotations.head: Annotation
    4. 根據註解列表的head獲得語法樹annotation.tree: Tree
  • 獲取方法參數的註解:
    1. 使用typeOf()方法,獲取Type類型的類信息。typeOf[Test]: Type
    2. 通過Type.decls/decl()方法篩選出目標方法的MethodSymbol。type.decl(TermName("ff ")): MethodSymbol
      通過MethodSymbol.paramLists方法獲取目標方法的參數表(List[List[Symbol]]類型,方法柯裏化可能會有多個參數表)。
    3. 通過Symbol.annotations獲取List[Annotation](註解列表)。methodSymbol.annotations.head: Annotation

解析註解Dome
應使用Annotation.tree方法獲取註解語法樹,類型為scala.reflect.runtime.universe.Tree。

import scala.annotation.StaticAnnotation
import scala.reflect.runtime.universe._

class CustomAnnotation(name: String, num: Int) extends StaticAnnotation {
  override def toString = s"Annotation args: name -> $name, num -> $num"
}

@CustomAnnotation("Annotation for Class", 2333)
class Test {
  @CustomAnnotation("Annotation for Class", 6666)
  val ff = ""
}

object Main extends App {

  {
    // 獲取類型註解
    val tpe: Type = typeOf[Test]
    val symbol: Symbol = tpe.typeSymbol //獲取類型符號信息
    val annotation: Annotation = symbol.annotations.head
    val tree: Tree = annotation.tree //獲取語法樹

    // 解析註解語法樹...
  }

  {
    // 獲取成員字段註解
    val tpe: Type = typeOf[Test]
    val symbol: Symbol = tpe.decl(TermName("ff ")) //獲取字段符號信息
    val annotation: Annotation = symbol.annotations.head
    val tree: Tree = annotation.tree

    // 解析註解語法樹...
  }

}

1.3.3 Scala的implicit用法

平時我們比較常見的應該是使用implicit修飾val或者def,如果在函數的定義中,有如下函數簽名

def divide(x: Int, y: Int)(implicit i2d: Int => Double): Double

則調用上述的divide方法時必須顯示或隱式的傳入一個類型為Int=>Double的函數映射.如:

divide(1, 2)(i => i.toDouble)

如果在當前名稱空間中,正好有了這個Int => Double的函數映射,並且是隱式的,如:

implicit val test: Int => Double = i => i.toDouble

則函數調用的時候,則可以省略後面的隱式傳入,編譯器會自動使用當前名稱空間的可用隱式,調用就變為:

divide(1, 2)

Implicit class

那麽問題來了,如果implicit修飾的是class呢?
其實,我們可以認為帶有這個關鍵字的類的主構造函數可用於隱式轉換。
舉個例子, 創建隱式類時,只需要在對應的類前加上implicit關鍵字。比如:

// 定義
object Helpers {
  implicit class IntWithTimes(x: Int) {
    def times[A](f: => A): Unit = {
      def loop(current: Int): Unit =
        if(current > 0) {
          f
          loop(current - 1)
        }
      loop(x)
    }
  }
}

// 調用
object main {
    import Helpers._
    5 times println("HI")
}

可以發現, 5是個Int型, 沒有times方法,會自動查找名稱空間,找到可以轉換為IntWithTimes類型,並且帶有times方法. 所以上面函數可以調用成功.

限制條件

  1. 使用隱式類時,類名必須在當前作用域內可見且無歧義,這一要求與隱式值等其他隱式類型轉換方式類似。
  2. 只能在別的trait/類/對象內部定義。
  3. 構造函數只能攜帶一個非隱式參數。
  4. 在同一作用域內,不能有任何方法、成員或對象與隱式類同名。(也就說明case class 不可被implicit修飾,因為自動生成伴生類)

Implicit object

那麽implicit修飾object又是什麽鬼呢?

下面是個非常常見的例子:

object Math {
  trait NumberLike[T] {
    def plus(x: T, y: T): T
    def divide(x: T, y: Int): T
    def minus(x: T, y: T): T
  }
  object NumberLike {
    implicit object NumberLikeDouble extends NumberLike[Double] {
      def plus(x: Double, y: Double): Double = x + y
      def divide(x: Double, y: Int): Double = x / y
      def minus(x: Double, y: Double): Double = x - y
    }
    implicit object NumberLikeInt extends NumberLike[Int] {
      def plus(x: Int, y: Int): Int = x + y
      def divide(x: Int, y: Int): Int = x / y
      def minus(x: Int, y: Int): Int = x - y
    }
  }
}

實際上,看到的隱式object我們可以認為它就是

implicit val NumberLikeDouble: NumberLike[Double] = new NumberLike[Double] {
    ...
}

Scala微服務架構 一