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

海上钢琴师.jpg


前言

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

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

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

读者在阅读此文以后会有哪些收获呢?笔者但愿读者能有以下收获:java

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

背景与目标

背景

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

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

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

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

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

目标

​ 针对以上场景设计出一套 Gradle 工程结构在一样的代码库状况下来知足其需求,减小研发屡次开发成本。spring


工程结构设计

工程命名规范

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等。api

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/...),并切换分支为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

相关文章
相关标签/搜索