Spring Cloud 体系微服务设计(一):兼容单体、分布式、微服务三种模式下的项目工程化设计方案...

海上钢琴师.jpg


前言

​    本文主要是以实战方式来介绍微服务下多团队多服务多功能模块下的项目工程结构设计,希望读者通过参考此文章的设计方案后可以自己设计一套满足自己企业的可扩展灵活性较高的项目工程层次结构。

读者在阅读此文之前应该具备哪些前提知识呢?笔者简要的列了一下如下内容:

  • 了解 Gradle 基本知识、 Gradle 父子工程管理、Gradle复合构建等相关知识。
  • 了解 Spring Boot自动配置、Spring IOC 自动注入特性、Spring Cloud 工程项目和框架特性。
  • 了解 Spring Cloud 微服务下 Feign Client 进行服务之间通信的使用和原理,特别是@ Feign Client 注解的 name 和 url 属性特性。

读者在阅读此文之后会有哪些收获呢?笔者希望读者能有如下收获:

  • 了解在微服务体系下工程如何命名、工程统一依赖版本如何控制、Git仓库怎么设计来满足多团队多服务的需要和扩展。
  • 了解具体服务功能模块的代码工程如何拆分设计来实现用同一套代码库设计出既支持构建成微服务体系,又支持构建成分布式,还可以构建成单体系统,做到工程间的灵活组配。
  • 读者阅读后可以自己搭建一套可扩展的项目工程结构。

背景与目标

背景

​    本文以项目demo来演示工程设计,这样会便于读者理解,这里假设定出如下项目场景:

爪哇留声机公司(以下简称爪哇公司)是一家专门为第三方企业做业务系统的,对于不同第三方企业的规模大小,爪哇公司在开发部署系统的时候可能会有所变化,其中爪哇公司的业务系统包含的如下三个功能:

  • 订单功能,其中订单含有国内订单和国外订单两大业务模块(公司小王团队负责)。
  • 支付功能(公司小李团队负责)。
  • 物流功能(公司小刘团队负责)。

背景一:第三方企业A是一家规模非常大的企业,用户也很庞大,而且也不差钱,那么爪哇公司在给A企业做产品时可能为了性能扩展等需要,会将现有产品化系统微服务化成三个服务(订单服务、支付服务、物流服务)分别部署来满足A企业需求,其中订单服务后续可能会因国内外订单量太大而进一步拆分成国内订单服务和国外订单服务。

背景二:第三方企业B是一家规模小的企业,用户量并不大,那么爪哇公司在给B企业做产品时只部署一个服务(此服务同时包含订单、支付、物流三项功能)即可满足B企业需求,大大减少企业运维等成本。

目标

​    针对以上场景设计出一套 Gradle 工程结构在同样的代码库情况下来满足其需求,减少研发多次开发成本。


工程结构设计

工程命名规范

project 命名规范

​    项目-服务-功能-模块,如:javalsj-order-foreign-api、javalsj-order-foreign-impl。

project package 命名规范

​    项目.服务.功能.模块.领域,如:javalsj.order.foreign.api、javalsj.order.foreign.api.vo、javalsj.order.foreign.api.dto等。

project 工程结构

工程结构层次图

项目工程结构设计图 .jpg


工程结构图层次文字描述

javalsj 
    整个爪哇项目根目录。

javalsj-frontend 
    爪哇前端根目录。

javalsj-frontend-vue 
    爪哇vue前端项目工程。

javalsj-backend
    爪哇后端根目录。

javalsj-commom
    后端服务公用模块目录。

javalsj-common-base 
    后端服务无容器概念公用模块工程,如:工具常量类、响应实体等。

javalsj-common-web
    后端servlet容器公用模块工程,依赖javalsj-common-base,如:每个servlet服务需要的跨域过滤器配置、web异常拦截器等。

javalsj-common-webflux 
    后端webflux容器公用模块工程,依赖javalsj-common-base,如:每个webflux服务(网关)需要的webflux异常拦截器等。

javalsj-gateway 
    后端网关服务目录。

javalsj-gateway-app
    后端网关应用工程。

javalsj-auth 
    后端授权认证服务目录。

javalsj-auth-app
    后端授权认证独立启动工程,分布式部署。

javalsj-auth-app-microservice
    后端授权认证微服务启动工程,微服务部署。

javalsj-auth-api
    api工程,后端支付服务api接口工程,外部工程调用时注入统一依赖api工程。

javalsj-auth-impl
    impl工程,后端支付服务api的接口实现工程,依赖javalsj-auth-api工程实现controller的具体逻辑。注:api和impl组合可以用来构建单体服务架构下的程序。

javalsj-auth-client
    client工程,后端支付服务提供给外部微服务调用的客户端工程,依赖javalsj-auth-api工程实现 Feign Client 服务调用客户端,其他微服务工程依赖该client工程实现微服务调用。

javalsj-order
    后端订单服务目录。

javalsj-order-foreign-api
    api工程,后端国外订单服务api接口工程,外部工程调用时注入统一依赖api工程。

javalsj-order-foreign-impl
    impl工程,后端国外订单服务api的接口实现工程,依赖javalsj-order-foreign-api工程实现controller的具体逻辑。注:api和impl组合可以用来构建单体服务架构下的程序。

javalsj-order-foreign-client
    client工程,后端国外订单服务提供给外部微服务调用的客户端工程,依赖javalsj-order-foreign-api工程实现 Feign Client 服务调用客户端,其他微服务工程依赖该client工程实现微服务调用。

javalsj-order-foregin-app-microservice
    app-microservice工程,后端国外订单服务的微服务启动工程,依赖javalsj-order-foreign-impl,若需要调用其他微服务,则依赖其他微服务的client工程代码。

javalsj-order-foregin-app
    app工程,后端国外订单服务的独立启动工程,可用于分布式调用。

javalsj-order-internal-api
    api工程,后端国内订单服务api接口工程,外部工程调用时注入统一依赖api工程。

javalsj-order-internal-impl
    impl工程,后端国内订单服务api的接口实现工程,依赖javalsj-order-internal-api工程实现controller的具体逻辑。注:api和impl组合可以用来构建单体服务架构下的程序。

javalsj-order-internal-client
    client工程,后端国内订单服务提供给外部微服务调用的客户端工程,依赖javalsj-order-internal-api工程实现 Feign Client 服务调用客户端,其他微服务工程依赖该client工程实现微服务调用。

javalsj-order-internal-app-microservice
    app工程,后端国内订单服务的启动工程,依赖javalsj-order-internal-impl,若需要调用其他微服务,则依赖其他微服务的client工程代码。

javalsj-order-internal-app
    app工程,后端国内订单服务的独立启动工程,可用于分布式部署。

javalsj-pay 
    后端支付服务目录。

javalsj-pay-api
    api工程,后端支付服务api接口工程,外部工程调用时注入统一依赖api工程,包结构:javalsj.pay.api、javalsj.pay.api.vo、javalsj.pay.api.dto。

javalsj-pay-impl
    impl工程,后端支付服务api的接口实现工程,依赖javalsj-pay-api工程实现controller的具体逻辑。注:api和impl组合可以用来构建单体服务架构下的程序,包结构:javalsj.pay.impl.controller、javalsj.pay.impl.do、javalsj.pay.impl.service、javalsj.pay.impl.service.impl、javalsj.pay.impl.dao、javalsj.pay.impl.dao.impl。

javalsj-pay-client
    client工程,后端支付服务提供给外部微服务调用的客户端工程,依赖javalsj-pay-api工程实现 Feign Client 服务调用客户端,其他微服务工程依赖该client工程实现微服务调用,工程包结构:javalsj.pay.client、javalsj.pay.client.fallbackfactory。

javalsj-pay-app-microservice
    app-microservice工程,后端支付服务的启动工程,依赖javalsj-pay-impl,若需要调用其他微服务,则依赖其他微服务的client工程代码。

javalsj-order-pay-app
    app工程,后端支付服务的独立启动工程,可用于分布式部署。

javalsj-logistics
    后端物流服务目录。

javalsj-logistics-api
    api工程,后端物流服务api接口工程,外部工程调用时注入统一依赖api工程。

javalsj-logistics-impl
    impl工程,后端物流服务api的接口实现工程,依赖javalsj-logistics-api工程实现controller的具体逻辑。注:api和impl组合可以用来构建单体服务架构下的程序。

javalsj-logistics-client
    client工程,后端物流服务提供给外部微服务调用的客户端工程,依赖javalsj-logistics-api工程实现 Feign Client 服务调用客户端,其他微服务工程依赖该client工程实现微服务调用。

javalsj-logistics-app-microservice
    app-microservice工程,后端物流服务的启动工程,依赖javalsj-logistics-impl,若需要调用其他微服务,则依赖其他微服务的client工程代码。

javalsj-logistics-app
    app工程,后端物流服务的独立启动工程,可用于分布式部署。

javalsj-app
    后端构建单体启动服务工程。


工程结构图层次设计说明

​    为了便于理解设计,现在进行工程结构层次设计说明,通过上面的一段描述,读者可以看到每个服务工程都被拆分成了app、app-microservice、api、impl、client 5个 project,读者可以试先按文字描述来预想以下几个问题:

  1. 拆分的这5个工程分别是个什么东东,它们是分别负责干什么的?
  2. 为什么要这样拆分,这样拆分的好处在哪里呢?
  3. 怎样去使用这5个工程进行组配来满足单体、分布式、微服务的工程构建呢?

1. 拆分的这几个工程分别是负责干什么的?

app
    独立服务部署应用启动工程。通过 app 的 build.gradle 来构建当前独立服务需要的工程依赖,依赖主要为 api、impl 工程。若该服务需要调用其他服务则依赖再加其他服务的client工程。通过 app 的 application.yml 配置文件设置 Feign Client 设置 name 、url 直连属性。

app-microservice
    微服务部署启动工程。通过 app-microservice 的 build.gradle 来构建当前独立服务需要的工程依赖,依赖主要为 api、impl 工程。若该服务需要调用其他服务则依赖再加其他服务的client工程。通过 app 的 application.yml 配置文件设置 Feign Client 只设置 name 属性。

api
    服务 api 接口工程,工程模块互相依赖时统一依赖其他服务模块的api接口工程,然后再通过构建依赖 impl 或者 client 工程,利用 Spring  IOC 自动注入机制来决定该接口最终是调用服务内部的 controller,还是调用其他服务的client客户端。

impl
    服务 api 的接口实现工程,依赖api工程。该工程只用于服务内部构建,不允许其他服务做依赖,工程内容主要是包含 controller、service、dao等业务逻辑模块。

client
    服务提供给外部服务调用的 client 客户端工程,依赖 api 工程。该工程只用于其他服务构建依赖,不允许服务内部做依赖,工程内容主要是包含 Feign Client 的通信模块,单独拎出来是为了减少其他服务调用该服务时都写一份client的冗余操作。

2. 为什么要这样拆分,这样拆分的好处在哪里呢?

​    读者可以通过上面工程结构发现,这样拆分工程的是会增加大量的工程数量,对工程规范性要求也变得较高这是其中的一个缺点。但是在多团队工程规模较大的情况下,这种做法又带来了很大的优点和灵活性,具体如下:

工程随意组合来适应项目需求
    在实际工作中,拆分微服务的业务边界其实是一个比较费劲的工作量,而且随着项目的不断扩大,本身拆分的边界已经不满足性能需求了,此时可能会做出如下两种场景改造。
    1.对已拆分的服务再做二次拆分。
    场景:一开始只是把订单业务拆分成独立的服务,但是后续发现订单业务服务扛不住了此时可能就需要再拆分成国内和国外订单两个服务)。
    2.对已拆分的服务做合并。
    场景:一开始拆分了国内和国外订单两个服务,但是后续发现订单业务量不大,此时为了降低运维成本可能就会把这两个服务合并成一个订单业务)。以上两个场景均可通过该工程方案解决。

接口工程多实现可以横向扩展
    现有api接口工程提供 impl 和 client 两种实现,其中 client 为 feign client 组件实现,如果后需有 Dubbo 或者 Webservice 等实现,也可以 扩展添加client-webservice或者client-dubbo工程,具体使用哪个工程,只需要在app启动工程做依赖即可。

3. 怎样去使用这5个工程进行组配来满足单体、分布式、微服务的工程构建呢?

​    读者可以参考下列不同服务进行 Gradle 组合构建来理解组配方案。

单体服务模式

​    启动工程:javalsj-app
    Gradle 依赖:javalsj-common-base、javalsj-common-web、javalsj-auth-api、javalsj-auth-impl、javalsj-order-foreign-api、javalsj-order-foreign-impl、javalsj-order-internal-api、javalsj-order-internal-impl、javalsj-pay-api、javalsj-pay-impl、javalsj-logistics-api、javalsj-logistics-impl

分布式服务模式

​    授权认证服务

​        启动工程:javalsj-auth-app
        Gradle 依赖:javalsj-common-base、javalsj-common-web、javalsj-auth-api、javalsj-auth-impl

​    订单服务

​        启动工程:javalsj-order-app
        Gradle 依赖:javalsj-common-base、javalsj-common-web、javalsj-order-foreign-api、javalsj-order-foreign-impl、javalsj-order-internal-api、javalsj-order-internal-impl、javalsj-pay-api、javalsj-pay-client

​    支付服务

​        启动工程:javalsj-pay-app
        Gradle 依赖:javalsj-common-base、javalsj-common-web、javalsj-pay-api、javalsj-pay-impl、javalsj-logistics-api、javalsj-logistics-client

​    物流服务

​        启动工程:javalsj-logistics-app
        Gradle 依赖:javalsj-common-base、javalsj-common-web、javalsj-logistics-api、javalsj-logistics-impl

​    备注:分布式构建app依赖时是不需要依赖服务注册发现和配置中心组件。

微服务模式

​    授权认证微服务

​        启动工程:javalsj-auth-app-microservice
        Gradle 依赖:javalsj-common-base、javalsj-common-web、javalsj-auth-api、javalsj-auth-impl

​    国内订单微服务

​        启动工程:javalsj-order-internal-app-microservice
        Gradle 依赖:javalsj-common-base、javalsj-common-web、javalsj-order-internal-api、javalsj-order-internal-impl、javalsj-pay-api、javalsj-pay-client

​    国外订单微服务

​        启动工程:javalsj-order-foreign-app-microservice
        Gradle 依赖:javalsj-common-base、javalsj-common-web、javalsj-order-foreign-api、javalsj-order-foreign-impl、javalsj-pay-api、javalsj-pay-client

​    支付微服务

​        启动工程:javalsj-pay-app-microservice
        Gradle 依赖:javalsj-common-base、javalsj-common-web、javalsj-pay-api、javalsj-pay-impl、javalsj-logistics-api、javalsj-logistics-client

​    物流微服务

​        启动工程:javalsj-logistics-app-microservice
        Gradle 依赖:javalsj-common-base、javalsj-common-web、javalsj-logistics-api、javalsj-logistics-impl

​    备注:微服务构建app依赖时是需要依赖服务注册发现和配置中心组件。 
    
附加信息
    上面分布式和微服务的依赖工程一样,又是如何做到划分的呢?这是利用了 Feign Client 的组件特性。我们知道 @Feign Client 有两个属性: name 和 url,其中name属性是必要的,url 属性非必须,在处理时有下列特性。若 url 属性存在,feign会直连url地址调用,此时不会走服务发现,相当于分布式调用,生产时可以把url配置集群的nginx节点地址来达到分布式的负载均衡策略。 若 url 属性是空的时候,feign会按name从服务发现找对应服务名的服务集群,并按负载均衡策略选择其中的一个节点进行调用。两者在工程级别上只是在 app 和 app-microservice 的 application.yml 做配置url参数及是否依赖服务注册与发现等组件的区别。


工程Git仓库设计

​    针对上述工程结构层次来设计Git仓库,现列出的解决方案有两种,分别是整个项目只使用一个Git仓库和每个团队服务拥有各自的Git仓库,那么两者有什么区别呢?简单列出如下区别供参考:

仓库数    
    一个Git仓库,1个。
    多个Git仓库,N个。

依赖版本管理
    一个Git仓库,则 Gradle 版本控制也是统一的,不用研发人员特别关注。
    多个Git仓库,则 Gradle 版本控制需要研发人员特别关注,为了版本一致,实际使用时会把依赖包版本放在统一的一个公共目录下使用,每个Git仓库在做依赖时统一引用公共版本模块来达到一致性。

使用灵活性
    一个Git仓库,则直接Clone仓库到本地即可,不用关注工程之间依赖的层次文件目录位置放的对不对。
    多个Git仓库,则需要Clone多个仓库到本地,且多个仓库之间有引用的话,还需要特别注意仓库目录位置是否放的对不对,若不对的话在build时就会报未找到依赖包的错误。

团队灵活性
    一个Git仓库,每次都会拉取整个服务代码,若多团队的情况下则拉取的代码库可能会比较大,即时当前团队可能不会关注的其他团队代码也会被拉取下来,在提交代码需要Merge的概率也会大大提高。
    多个Git仓库,每个团队只拉取自己团队的代码,拉取的代码相对较小,便于整个多团队的Git权限管理等。

友情提示:针对以上区别,笔者在这里推荐第二种每个团队服务拥有各自的 Git 仓库的方式,这也是我们实际生产中使用的方式,其实两者差异不是太大,只用做好仓库的管理和规范化即可。笔者为了方便简化的说明工程代码内部的核心内容,此处采取整个项目使用一个 Git 仓库,希望不会影响读者的思路。


Git仓库结构层次图

仓库目录简介

Git仓库目录演示.gif

javalsj-app

​    是整个单体服务的app服务目录。

javalsj-common

​    是整个多团队多服务公用的依赖工程,比如所有微服务都公用的包工具,当前其存放的包主要有以下:

  • javalsj-common-base:无服务容器概念的公用工程,主要存放工具类、统一响应体对象等。
  • javalsj-common-web :Servlet 容器概念的公用工程,主要存放跨域过滤器、web统一上下文拦截过滤器、异常拦截器等,如:各Web容器微服务依赖该工程。
  • javalsj-common-webflux:WebFlux 容器概念的公用工程,主要存放 WebFlux公用的类等,如:Spring Cloud Gateway网关依赖该工程。
      此目录可扩展加入其它公用工程,我们知道 Servlet 和 WebFlux 两种容器在 spring boot 体系下是无法共存的,这也是拆分成两个公用工程的原因之一。

javalsj-gradle

是整个多团队多服务公用的Gradle构建依赖目录。其下存放 Gradle 统一版本控制 version.gradle 文件、发布程序到maven仓库的 push2maven.gradle 文件,方便对整个项目的依赖版本做控制。

javalsj-order

是订单业务团队工程存放的目录。

javalsj-pay

是支付业务团队工程存放的目录。

javalsj-logistics

是物流业务团队工程存放的目录。


工程实例讲解

Gradle 安装

​    从 Gradle 官网 https://gradle.org/ 下载新版 Gradle 版本并解压,并设置环境变量,安装后在D:/SoftFile/gradle-5.6.2路径下新建文件夹 userhome,用于存放 Gradle 下载的依赖文件,如图。

Gradle安装和环境配置.gif

Git仓库代码拉取

​    通过 Git Clone 实例项目(地址:https://gitee.com/wangjihong/javalsj_backend.git),并切换分支为develop, 如图。

Git实例项目拉取.gif


各服务模式实例演示

演示场景

业务演示场景.jpg

​    如图所示,我们本次实例主要业务是让用户访问获取订单 rest api 请求来获取对应的订单、支付、物流信息。订单模块提供订单的id、code信息,支付模块提供payId、payCode信息,物流模块提供logisticsId、logisticsCode信息。

演示服务设计

单体服务
服务设计

单体服务-调用图.jpg

​    如图所示,单体服务由于只集成了个模块impl工程,所以订单内部注入到PayApi和LogisticsApi的实现类即为PayController和LogisticsController,调用时也是代码类本身方法的调用,不依赖于Feign组件。

启动

​    IDEA导入单体启动 app 工程: W:/Workspace/git_workspace/javalsj/javalsj_backend/javalsj-app,然后启动单体服务如图:

单体服务-IDEA导入.gif

​    使用Postman工具模拟访问单体服务订单的rest接口地址:http://localhost:8001/api/order/v1/internal ,结果如下:

单体服务-Postman访问效果.jpg

​    由上可见,单体服务集成订单、支付、物流模块后均正常访问。

原理说明

​    在单体启动工程 javalsj-app 我们可以看到因为单体不涉及服务之间的调用所以 build.gradle 构建脚本只依赖了各模块的 impl 工程:

  • 订单业务模块库 javalsj-order-foreign-impl、javalsj-order-internal-impl 工程。

  • 支付业务模块库 javalsj-pay-impl 工程。

  • 物流业务模块库 javalsj-logistics-impl 工程。

Gradle 构建脚本

buildscript {
    ext {
        springBootGradlePluginVersion = '2.2.0.RELEASE'
        dependencyManagementPluginVersion = '1.0.8.RELEASE'
        junitPlatformGradlePluginVersion = '1.2.0'
        buildGradleVersion = '3.1.0'
    }
    repositories {
        mavenLocal()
        maven { url "http://maven.aliyun.com/nexus/content/groups/public" }
        mavenCentral()
        maven { url 'https://maven.aliyun.com/repository/public/' }
        maven { url 'https://maven.aliyun.com/repository/spring/' }
    }
    dependencies {
        classpath "org.junit.platform:junit-platform-gradle-plugin:${junitPlatformGradlePluginVersion}"
        classpath "io.spring.gradle:dependency-management-plugin:${dependencyManagementPluginVersion}"
        classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootGradlePluginVersion}"
        classpath "com.android.tools.build:gradle:${buildGradleVersion}"
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'eclipse'
apply plugin: 'java-library'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'org.junit.platform.gradle.plugin'

apply from: "../javalsj-gradle/push2maven.gradle"
apply from: "../javalsj-gradle/version.gradle"

dependencies {
    // 订单模块库
    implementation commonLib.javalsj_common_web
    implementation orderLib.javalsj_order_foreign_impl
    implementation orderLib.javalsj_order_internal_impl
    // 支付模块库
    implementation payLib.javalsj_pay_impl
    // 物流模块库
    implementation logisticsLib.javalsj_logistics_impl

}

​    application.yml 配置没什么特别的

server:
  port: 8001
  servlet:
    context-path: /
    session:
      timeout: 10800

分布式服务
服务设计

分布式服务-调用图.jpg

​    如图所示,分布式服务由于集成了模块impl工程和支付物流的client工程,所以订单内部注入到PayApi和LogisticsApi的实现类即为Pay Feign Client 和Logistics Feign Client ,加上启动配置文件中设置了 Feign Client 的url属性,所以调用时是采用的直连url的方式,达到分布式调用的效果。

启动
  • IDEA导入分布式下订单服务 app 工程: W:/Workspace/git_workspace/javalsj/javalsj_backend/javalsj-order/javalsj-order-app并启动。

  • IDEA导入分布式下支付服务 app 工程: W:/Workspace/git_workspace/javalsj/javalsj_backend/javalsj-pay/javalsj-pay-app并启动。

  • IDEA导入分布式下物流服务 app 工程: W:/Workspace/git_workspace/javalsj/javalsj_backend/javalsj-logistics/javalsj-logistics-app并启动。

如图。

分布式服务-IDEA导入.gif

原理说明

​    在分布式订单启动工程 javalsj-order-app 我们可以看到因为单体涉及服务之间的调用所以 build.gradle 构建脚本依赖了自身模块的 impl 工程以及支付、物流模块的 client 工程。client工程供订单服务使用Feign Client 对支付、物流服务接口 url 进行调用,:

  • 分布式订单服务,依赖模块库 javalsj-order-foreign-impl、javalsj-order-internal-impl、javalsj-pay-client、javalsj-logistics-client工程。

  • 分布式支付服务,依赖模块库 javalsj-pay-impl 工程。

  • 分布式物流服务,依赖模块库 javalsj-logistics-impl 工程。

分布式与单体区别如下,由于支付、物流服务没什么特殊,所以只拿订单服务的脚本和配置看下。

Gradle 构建脚本区别

app启动工程增加其他模块的client工程依赖。

buildscript {       ext {           springBootGradlePluginVersion = '2.2.0.RELEASE'           dependencyManagementPluginVersion = '1.0.8.RELEASE'           junitPlatformGradlePluginVersion = '1.2.0'           buildGradleVersion = '3.1.0'       }       repositories {           mavenLocal()           maven { url "http://maven.aliyun.com/nexus/content/groups/public" }           mavenCentral()           maven { url 'https://maven.aliyun.com/repository/public/' }           maven { url 'https://maven.aliyun.com/repository/spring/' }       }       dependencies {           classpath "org.junit.platform:junit-platform-gradle-plugin:${junitPlatformGradlePluginVersion}"           classpath "io.spring.gradle:dependency-management-plugin:${dependencyManagementPluginVersion}"           classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootGradlePluginVersion}"           classpath "com.android.tools.build:gradle:${buildGradleVersion}"       }   }      apply plugin: 'java'   apply plugin: 'idea'   apply plugin: 'eclipse'   apply plugin: 'java-library'   apply plugin: 'io.spring.dependency-management'   apply plugin: 'org.junit.platform.gradle.plugin'      apply from: "../../javalsj-gradle/push2maven.gradle"   apply from: "../../javalsj-gradle/version.gradle"      dependencies {       implementation orderLib.javalsj_order_foreign_impl       implementation orderLib.javalsj_order_internal_impl       // 支付接口客户端       implementation payLib.javalsj_pay_client       // 物流接口客户端       implementation logisticsLib.javalsj_logistics_client   }

application.yml 配置区别

​    增加了@ Feign Client 的url属性值配置(上文也提到了分布式部署方式利用了 Feign Client  url 属性值若存在,则 Feign 会直连url 进行地址调用,此时不会走服务发现,相当于分布式调用的组件特性。):

custom:     service:       name: order-service     service-name:       pay: pay-service       logistics: logistics-service     service-url:         // 增加支付URL直连地址       pay: http://localhost:9003       // 增加物流URL直连地址       logistics: http://localhost:9004      management:     endpoints:       web:         exposure:           include: '*'   server:     port: 9002     servlet:       context-path: /       session:         timeout: 10800   feign:     httpclient:       enabled: false     okhttp:       enabled: true

微服务
准备

​    微服务情况下,我们首先需要搭建服务注册与发现,此处使用 Nacos 开源组件,下载安装启动过程如图。

nacos安装启动.gif

服务设计

微服务-调用图.jpg

​    如图所示,微服务由于集成了模块impl工程和支付物流的client工程,所以订单内部注入到PayApi和LogisticsApi的实现类即为Pay Feign Client 和Logistics Feign Client ,启动类加了服务发现注解@EnableDiscoveryClient,启动配置文件中没有设置 Feign Client 的url属性,只设置了name属性,所以调用时会走 Nacos 服务发现找到对应name的服务进行负载均衡调用,达到微服务调用的效果。

启动
  • IDEA导入国内订单微服务 app 工程: W:/Workspace/git_workspace/javalsj/javalsj_backend/javalsj-order/javalsj-order-internal-app-microservice并启动。

  • IDEA导入支付微服务 app 工程: W:/Workspace/git_workspace/javalsj/javalsj_backend/javalsj-pay/javalsj-pay-app-microservice并启动。

  • IDEA导入物流微服务 app 工程: W:/Workspace/git_workspace/javalsj/javalsj_backend/javalsj-logistics/javalsj-logistics-app-microservice并启动。

如图。

微服务-IDEA导入app.gif

原理说明

​    在微服务启动工程 javalsj-order-app 我们可以看到因为单体涉及服务之间的调用所以 build.gradle 构建脚本依赖了自身模块的 impl 工程以及支付、物流模块的 client 工程。client工程供订单服务使用Feign Client 对支付、物流服务接口 url 进行调用,:

  • 订单微服务,依赖模块库 javalsj-order-foreign-impl、javalsj-order-internal-impl、javalsj-pay-client、javalsj-logistics-client、spring-cloud-starter-alibaba-nacos-discovery工程。

  • 支付微服务,依赖模块库 javalsj-pay-impl 、spring-cloud-starter-alibaba-nacos-discovery工程。

  • 物流微服务,依赖模块库 javalsj-logistics-impl、spring-cloud-starter-alibaba-nacos-discovery 工程。

微服务与分布式的区别如下:

Gradle 构建脚本:

app-microservice启动工程增加了 Nacos 服务注册与发现依赖。

buildscript {
    ext {
        springBootGradlePluginVersion = '2.2.0.RELEASE'
        dependencyManagementPluginVersion = '1.0.8.RELEASE'
        junitPlatformGradlePluginVersion = '1.2.0'
        buildGradleVersion = '3.1.0'
    }
    repositories {
        mavenLocal()
        maven { url "http://maven.aliyun.com/nexus/content/groups/public" }
        mavenCentral()
        maven { url 'https://maven.aliyun.com/repository/public/' }
        maven { url 'https://maven.aliyun.com/repository/spring/' }
    }
    dependencies {
        classpath "org.junit.platform:junit-platform-gradle-plugin:${junitPlatformGradlePluginVersion}"
        classpath "io.spring.gradle:dependency-management-plugin:${dependencyManagementPluginVersion}"
        classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootGradlePluginVersion}"
        classpath "com.android.tools.build:gradle:${buildGradleVersion}"
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'eclipse'
apply plugin: 'java-library'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'org.junit.platform.gradle.plugin'

apply from: "../../javalsj-gradle/push2maven.gradle"
apply from: "../../javalsj-gradle/version.gradle"

dependencies {
    implementation orderLib.javalsj_order_internal_impl
    implementation payLib.javalsj_pay_client
    implementation logisticsLib.javalsj_logistics_client
    // 服务注册与发现
    implementation pluginLib.spring_cloud_starter_alibaba_nacos_discovery
}

application.yml 配置区别

​    去掉了@ Feign Client 的url属性值配置(上文也提到了微服务部署方式利用了 Feign Client  url 属性值若不存在,则 Feign 会通过name属性进行服务发现负载均衡到目标服务,此时会调用微服务模式的组件特性。):

custom:
  service:
    name: order-internal-service
  service-name:
    pay: pay-service
    logistics: logistics-service
spring:
  application:
    name: ${custom.service.name}
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
management:
  endpoints:
    web:
      exposure:
        include: '*'
server:
  port: 9002
  servlet:
    context-path: /
    session:
      timeout: 10800
feign:
  httpclient:
    enabled: false
  okhttp:
    enabled: true

启动Application类区别

增加了服务发现注解@EnableDiscoveryClient。

package com.javalsj.order.internal.app.microservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.Enable Feign Client s;
// 增加服务发现注解
@EnableDiscoveryClient
@SpringBootApplication
public class JavalsjOrderInternalAppMicroserviceApplication {

    public static void main(String[] args) {
        SpringApplication.run(JavalsjOrderInternalAppMicroserviceApplication.class, args);
    }

}


总结

​    本文主要介绍了同一套代码库工程结构设计以及在适配单体服务、分布式、微服务各种模式下的工程设计思路,笔者为了让读者脱离纯理论来便于理解文章,通过写的一个Demo工程实例来演示各服务模式下的配置区别,读者可以通过实例代码自行本地运行测试以便更易理解。(PS:如有设计疑问,可以在文章的评论区发表,笔者看到后会及时回复,互相学习,谢谢)。

彩蛋:
前后端微服务技术体系架构图简版.jpg

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王继红(男)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值