Scala微服务架构 一

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 因为公司的一个项目需求变动会非常频繁,同时改动在可控范围内,加上产品同学喜欢一些超前思维,我们的CTO决定提前开启八门,使用微服务架构。划重点微服务架构主要特点:==独立组件(自主开发升级)====开运一体(谁开发谁运维)====去中心化(语言,系统,数据,统统可以分开不一样)==一. 那么什么是微服务架构呢?引自 https://www.ibm.com/developerworks/community/blogs/“微服务”架构是近期软件应用领域非常热门的概念。

因为公司的一个项目需求变动会非常频繁,同时改动在可控范围内,加上产品同学喜欢一些超前思维,我们的CTO决定提前开启八门,使用微服务架构。

划重点

微服务架构主要特点:

  1. ==独立组件(自主开发升级)==
  2. ==开运一体(谁开发谁运维)==
  3. ==去中心化(语言,系统,数据,统统可以分开不一样)==

一. 那么什么是微服务架构呢?

引自 https://www.ibm.com/developerworks/community/blogs/

“微服务”架构是近期软件应用领域非常热门的概念。

微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。

特点

也就是说,微服务就是SOA的变种,在保证SOA业务分离能力的同时,减少每个业务粒度,同时去除了SOA中比较恼人的ESB.

用最精辟的话说就是==独立部署==,==快速迭代==.

通用特性

  1. 组件化,并可独立替换和升级
  2. 业务分离,每个服务团队运维一体化
  3. 服务产品化而非项目模式(其实和上面差不多,就是谁开发,谁运营)
  4. 智能端点与管道扁平化(Smartendpoints and dumb pipes):微服务架构主张将组件间通讯的相关业务逻辑/智能放在组件端点侧而非放在通讯组件中,通讯机制或组件应该尽量简单及松耦合。RESTful HTTP协议和仅提供消息路由功能的轻量级异步机制是微服务架构中最常用的通讯机制。
  5. “去中心化”治理(DecentralizedGovernance):整体式应用往往倾向于采用单一技术平台,微服务架构则鼓励使用合适的工具完成各自的任务,每个微服务可以考虑选用最佳工具完成(如不同的编程语言)。微服务的技术标准倾向于寻找其他开发者已成功验证解决类似问题的技术。
  6. “去中心化”数据管理(DecentralizedData Management):微服务架构倡导采用多样性持久化(PolyglotPersistence)的方法,让每个微服务管理其自有数据库,并允许不同微服务采用不同的数据持久化技术。
  7. 基础设施自动化(InfrastructureAutomation):云化及自动化部署等技术极大地降低了微服务构建、部署和运维的难度,通过应用持续集成和持续交付等方法有助于达到加速推出市场的目的。
  8. 故障处理设计(Designfor failure):微服务架构所带来的一个后果是必须考虑每个服务的失败容错机制。因此,微服务非常重视建立架构及业务相关指标的实时监控和日志机制。
  9. 演进式的设计(EvolutionaryDesign):如某应用是整体式应用,但逐渐朝微应用架构方向演进,整体式应用仍是核心,但新功能将使用应用所提供的API构建。再如在某微服务应用中,可替代性模块化设计的基本原则,在实施后发现某两个微服务经常必须同时更新,则这很可能意味着应将其合并为一个微服务。

自由组装

这个图片从资源利用率的角度,体现了微服务一个非常给力的优点
image

数据分离

同时,对应用组件封装的方式是整体架构与微服务架构的主要差异,微服务架构将相关联的业务逻辑及数据放在一起形成独立的边界,其目的是能在不影响其他应用组件(微服务)的情况下更快地交付并推出市场。
image

概念比较

在同一范畴内比较才有意义:

  • 微服务架构 vs. SOA – 两者都是架构风格范畴,但其关注领域与涉及范围不同。SOA更关注企业规模范围,微服务架构则更关注应用规模范围。
  • 微服务组件 vs. 服务组件 – 两者都是描述业务功能的具体实现,其区别在于粒度不同,此外还有在可管理性、灵活性上的差异。

概念混淆的不恰当比较

  • 微服务 vs. SOA – 不恰当的比较。微服务是组件范畴,而SOA是一种架构设计风格。因此应该比较的是微服务架构与SOA。
  • 微服务 vs. API – 不恰当的比较。 API是接口,是业务功能暴露的一种机制。微服务架构是用于实施业务功能的组件架构。因此直接比较它们是没有意义的。
  • 微服务 vs. 服务– 不恰当的比较。“服务”在不同的场景下有不同的含义,需要进一步澄清其描述的语境,是指服务实施、服务暴露、服务定义还是其他?微服务亦是如此,需要有特定语境才可判断比较是否有意义。

二. 前后对接协议 JsonApi

官网

※. 本期语法糖

※.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可以直接使用。

※.2 Scala的自定义注解(Annotation)

本段摘自

※.2.1 Annotation (注解)的介绍

Scala中的注解语法与Java中类似。
标准库定义的注解相关内容在包scala.annotation中。

注解的基本语法为:

@注解名称(注解参数...)
val a = ???

与Java注解的用法类似,注解参数不是必须的,一个元素允许拥有多个注解。

※.2.2 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

※.2.3 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

    // 解析注解语法树...
  }

}

※.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] {
    ...
}
目录
相关文章
|
3天前
|
敏捷开发 监控 数据管理
构建高效微服务架构的五大关键策略
【4月更文挑战第20天】在当今软件开发领域,微服务架构已经成为一种流行的设计模式,它允许开发团队以灵活、可扩展的方式构建应用程序。本文将探讨构建高效微服务架构的五大关键策略,包括服务划分、通信机制、数据管理、安全性考虑以及监控与日志。这些策略对于确保系统的可靠性、可维护性和性能至关重要。
|
15天前
|
API 数据库 开发者
构建高效可靠的微服务架构:后端开发的新范式
【4月更文挑战第8天】 随着现代软件开发的复杂性日益增加,传统的单体应用架构面临着可扩展性、维护性和敏捷性的挑战。为了解决这些问题,微服务架构应运而生,并迅速成为后端开发领域的一股清流。本文将深入探讨微服务架构的设计原则、实施策略及其带来的优势与挑战,为后端开发者提供一种全新视角,以实现更加灵活、高效和稳定的系统构建。
18 0
|
3天前
|
消息中间件 监控 持续交付
构建高效微服务架构:后端开发的进阶之路
【4月更文挑战第20天】 随着现代软件开发的复杂性日益增加,传统的单体应用已难以满足快速迭代和灵活部署的需求。微服务架构作为一种新兴的分布式系统设计方式,以其独立部署、易于扩展和维护的特点,成为解决这一问题的关键。本文将深入探讨微服务的核心概念、设计原则以及在后端开发实践中如何构建一个高效的微服务架构。我们将从服务划分、通信机制、数据一致性、服务发现与注册等方面入手,提供一系列实用的策略和建议,帮助开发者优化后端系统的性能和可维护性。
|
14天前
|
Kubernetes 安全 Java
构建高效微服务架构:从理论到实践
【4月更文挑战第9天】 在当今快速迭代与竞争激烈的软件市场中,微服务架构以其灵活性、可扩展性及容错性,成为众多企业转型的首选。本文将深入探讨如何从零开始构建一个高效的微服务系统,覆盖从概念理解、设计原则、技术选型到部署维护的各个阶段。通过实际案例分析与最佳实践分享,旨在为后端工程师提供一套全面的微服务构建指南,帮助读者在面对复杂系统设计时能够做出明智的决策,并提升系统的可靠性与维护效率。
|
5天前
|
机器学习/深度学习 运维 Prometheus
探索微服务架构下的系统监控策略
【4月更文挑战第18天】在当今快速迭代和持续部署盛行的软件工程实践中,微服务架构因其灵活性和可扩展性受到企业青睐。然而,随着服务的细粒度拆分和网络通信的增加,传统的监控手段已不再适用。本文将探讨在微服务环境中实施有效系统监控的策略,包括日志聚合、性能指标收集、分布式追踪以及异常检测等关键技术实践,旨在为读者提供构建稳定、可靠且易于维护的微服务系统的参考指南。
10 0
|
5天前
|
监控 持续交付 开发者
构建高效微服务架构:后端开发的新趋势
【4月更文挑战第18天】在数字化转型的浪潮中,微服务架构已成为企业提升系统灵活性、加速产品迭代的关键。此文深入探讨了构建高效微服务架构的实践方法,包括服务划分原则、容器化部署、持续集成/持续部署(CI/CD)流程以及监控与日志管理等关键技术点。通过分析具体案例,揭示了微服务在提高开发效率、降低维护成本及促进团队协作方面的显著优势。
|
6天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
8天前
|
监控 JavaScript 安全
构建微服务架构下的API网关
【4月更文挑战第15天】在微服务架构中,API网关扮演着至关重要的角色。它作为系统的唯一入口,不仅负责请求的路由、负载均衡和认证授权,还涉及到监控、日志记录和服务熔断等关键功能。本文将探讨如何构建一个高效且可靠的API网关,涵盖其设计原则、核心组件以及实现策略,旨在为后端开发人员提供一套实用的指导方案。
22 4
|
9天前
|
监控 负载均衡 API
构建高性能微服务架构:后端开发的最佳实践
【4月更文挑战第14天】 在当今快速发展的软件开发领域,微服务架构已成为构建可扩展、灵活且容错的系统的首选方法。本文深入探讨了后端开发人员在设计和维护高性能微服务时需要遵循的一系列最佳实践。我们将从服务划分原则、容器化部署、API网关使用、负载均衡、服务监控与故障恢复等方面展开讨论,并结合实际案例分析如何优化微服务性能及可靠性。通过本文的阅读,读者将获得实施高效微服务架构的实用知识与策略。
|
11天前
|
运维 监控 自动驾驶
构建可扩展的应用程序:Apollo与微服务架构的完美结合
构建可扩展的应用程序:Apollo与微服务架构的完美结合
32 10