Scala微服务架构 一

Posted clockq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala微服务架构 一相关的知识,希望对你有一定的参考价值。

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微服务架构 一的主要内容,如果未能解决你的问题,请参考以下文章

微服务架构之「 下一代微服务 Service Mesh 」

下一代的微服务架构基础是ServiceMesh?

Scala微服务架构 二

云原生微服务架构的技术内涵

2018 年 Service Mesh 元年,被誉为是下一代微服务架构

干货2018 年 Service Mesh 元年,被誉为是下一代微服务架构