Laravel 如何设计微服务架构,及如何进行微服务间沟通?

如题,我目前有需要用 Laravel 设计微服务架构的需求,但能找到的相关资料不多
目前已有的一个思考方向是使用 K8S 统合各个独立的 Laravel 小服务,再开放统一对外的 API Gateway
但碰到一个问题是各个服务间要如何在不发生耦合的状况下沟通

举例来说:订单发生后需要减少库存,若库存成功扣除后要再告诉订单更新状态
我目前想到三种解决方式:

  1. 在 API Gateway 处理流程问题,等订单服务回传成功后,再呼叫库存服务。
    好处:流程简单直觉、使用者当下就可知道订单结果
    坏处:API Gateway 会发生强耦合、API Gateway 会出现状态,违反微服务的 Stateless 原则
  2. 在订单服务的建立方法中直接呼叫库存服务
    好处与坏处都跟方法一差不多,但更多的坏处是微服务本身会变复杂、与其他服务发生强耦合,且违反微服务间不能直接沟通的原则
  3. 借由某种广播机制,在订单建立完成后,由订单服务发出「订单建立成功」的广播,告诉其他服务有订单建立。库存服务接收到广播后就会扣除库存,并再次发出「库存扣除成功」的广播,由订单服务再次接收。而其他不相干的服务就会忽略广播。
    好处:各服务完全解耦,彼此只借由单次事件进行沟通,不会在双方服务留下状态,且后续有逻辑改动只需要调整事件即可,不用动服务主逻辑
    坏处:无法及时告诉使用者订单产生结果,会造成较差的使用者体验

我目前想采用的是第三种模式,因为我司目前还处于新创阶段,功能仍在快速调整,因此开发上最好能像电源一样快速插拔,若服务出现强耦合就会降低产品调整的速度。相较之下,第三种方法造成的使用者体验降低都还可以接受。
我搜寻过后似乎有「Message Queue」这种解法,但有点不明白它如何用 Laravel 实作,且有一部分文章来源表示 Message Queue 已经是过时的技术
想请问有没有人设计过微服务架构,或曾经碰过类似的状况能给我一些建议,谢谢

---------------------- 4/3 补充说明

我发现我之前对 Message Queue 的理解有错,因此上来补充说明
我之前对 Queue 的机制,是认为他注册的事件是一段程式码,等待 worker 执行
但 Laravel 的事件机制,实际上是由 Event 注册事件名称及相关资料,等 worker 空闲后,呼叫 Listener 接收事件
Event 注册的资料如下所示

array (
  'displayName' => 'App\\Mail\\SaveAsDraft',
  'job' => 'Illuminate\\Queue\\CallQueuedHandler@call',
  'maxTries' => NULL,
  'timeout' => NULL,
  'timeoutAt' => NULL,
  'data' => 
  array (
    'commandName' => 'Illuminate\\Mail\\SendQueuedMailable',
    'command' => 'O:34:"Illuminate\\Mail\\SendQueuedMailable":3:{s:8:"mailable";O:20:"App\\Mail\\SaveAsDraft":20:{s:29:"' . "\0" . 'App\\Mail\\SaveAsDraft' . "\0" . 'service";O:45:"Illuminate\\Contracts\\Database\\ModelIdentifier":3:{s:5:"class";s:18:"App\\ServicePackage";s:2:"id";i:522;s:10:"connection";s:5:"mysql";}s:4:"from";a:0:{}s:2:"to";a:1:{i:0;a:2:{s:4:"name";N;s:7:"address";s:21:"mengluyange@gmail.com";}}s:2:"cc";a:0:{}s:3:"bcc";a:0:{}s:7:"replyTo";a:0:{}s:7:"subject";N;s:11:"' . "\0" . '*' . "\0" . 'markdown";N;s:4:"view";N;s:8:"textView";N;s:8:"viewData";a:0:{}s:11:"attachments";a:0:{}s:14:"rawAttachments";a:0:{}s:9:"callbacks";a:0:{}s:10:"connection";N;s:5:"queue";N;s:15:"chainConnection";N;s:10:"chainQueue";N;s:5:"delay";N;s:7:"chained";a:0:{}}s:5:"tries";N;s:7:"timeout";N;}',
  ),
  'id' => '1',
  'attempts' => 0,
  'type' => 'mail',
  'tags' => 
  array (
    0 => 'App\\ServicePackage:522',
  ),
  'pushedAt' => 1514296314,
)

里面并无存放任何程式码,仅告诉 Listener 要做什么,用什么去做,剩下的逻辑都是在 Listener 里面

所以针对我上面提到的 Message Queue 机制,可以如下设计

  1. 以 AWS 来说,各个微服务都会有自己对应的 SQS,事件产生时,将事件发送到 SNS,SNS 就会广播到所有 SQS,并由各服务各自处理队列
  2. 若非使用 AWS,而是自架 Message Queue,就是自己架个 Message Queue Service,里面的 Redis 为集群,收到广播事件后就一次写入集群所有 Redis,再由各服务执行

思路上大概如上,但未经过实作,待实作过后会再上来补充说明

簡永哲 Leo Chien IT Director | 大師鏈 MasterChain E: s950329@hotmail.com
本帖已被设为精华帖!
本帖由系统于 3年前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 20
TimJuly

创业阶段的东西不建议用微服务,创业阶段讲求的是开发效率,而不是架构能撑多少用户。微服务不光会降低开发效率,而且出现问题排查起来也会变得及其麻烦。另外如果服务拆分不合理,微服务将会变成一个技术债务。

再来说说微服务之间的通信,既然已经解耦,那么最好的通信方式就是消息队列。

6年前 评论

微服务的话,lumen 感觉更适合. 我们的微服务全是对外开放的, 然后将API认证跟服务间的请求封装到Packagist,每个服务都composer这个包就好

6年前 评论

@OneStep 谢谢回复
我司目前有 2 个专案,一个是旧的 Laravel 单体架构,我打算先维持 Laravel 的架构,后续慢慢拆解成 Lumen 微服务;一个正在开发,也是使用 Lumen
有几个问题请教您:

  1. API 认证跟服务间的请求封装到 Packagist 这段可以说明详细点吗,这个包里做了什么,每个服务 require 包之后又要做什么?
  2. 微服务全部对外开放的话,不会造成用户端的效能下降吗?且这样如何做 Load Balance 呢?
  3. 请问您会如何设计服务间的沟通,如我在文中所述的情景,您会如何在 Lumen 中处理?
6年前 评论

packagist 里面封装了各个服务间公用的配置, 还有token验证(其实是由USER的服务提供), 还有各个服务间的通信. require 之后只需要调用就行, 比如服务间的通信 使用

// 获取当前登录用户的ID
app('user')->get('id');
// 向order服务的url发送post请求
app('order')->post('url', data);

每个服务只提供接口, 前端 APP之类的完成业务逻辑,按需调用各个服务
各个服务单独部署的,所以load balance 也没问题的

6年前 评论

packagist 里面封装了各个服务间公用的配置, 还有token验证(其实是由USER的服务提供), 还有各个服务间的通信. require 之后只需要调用就行

这个作法真棒,谢谢分享

针对 每个服务只提供接口, 前端 APP之类的完成业务逻辑,按需调用各个服务 想再请教您
我目前想到这样做会有以下几种状况,不知道您会怎么解决

  1. 订单成立后因网路问题造成 APP 端没有调用 库存 API
  2. 暴露库存 API,会有安全性问题
  3. APP 端需呼叫大量 API,降低使用者体验
6年前 评论
TimJuly

创业阶段的东西不建议用微服务,创业阶段讲求的是开发效率,而不是架构能撑多少用户。微服务不光会降低开发效率,而且出现问题排查起来也会变得及其麻烦。另外如果服务拆分不合理,微服务将会变成一个技术债务。

再来说说微服务之间的通信,既然已经解耦,那么最好的通信方式就是消息队列。

6年前 评论

@TimJuly 谢谢建议
我们目前的状况是已经创业 1 年多,之前也是讲求开发效率而采用单体架构
但因后来 pivot 为工具型平台,我们必须快速调整功能,且不希望功能间互相影响
在这样的考量下我才决定把公司的架构慢慢转向微服务
目前规划的作法是慢慢剥离主架构,所有的新功能都必须采用新架构,借以降低初期的阵痛期
待之后公司发展较稳健,再 Hire 专业的架构师来协助重新调整

6年前 评论

@leochien 安全性的话,目前主要靠基于 OAuth 2.0 颁发的token来验证各个接口的访问权限,应该问题不大
前段构成业务逻辑,的确会造成前段请求各个服务的请求比较多,不过感觉影响不大,我们前段都是异步请求的

还有一个是订单创建成功,但是因为网络原因造成前段没有请求库存API, 这个估计就得看没有请求API的原因了,我感觉有2种:

  • 已经请求库存API, 但是因为网络问题请求失败 ,这种情况,我们前段一般会根据请求的失败原因(token过期我们一般抛 401, 参数错误 422, 没权限 403 之类的)重构请求, 比如token 过期, API返回:

    {
    success: false,
    data: [],
    errors: {
    code: 'UNAUTHORIZED',
     message: '未授权的请求'
    }
    }

    // 然后前段判断网络状态, 如果401 会调用刷新token的接口更新token,然后重新请求

    post(url, data, res) {
    url: url,
    method: 'POST',
    header: token,
    success: response => {
    if (!response.data.success && statusCode === 401) {
         this.reToken().then(res => {
             post(url, data, res => {
                  res(response)
             })
        })
    }
    }
    }
  • 还有一种就是没有发送请求了, 这个一般都是业务逻辑的问题了
6年前 评论

@OneStep 謝謝回覆,確實使用 Token 能確保不會有權限問題
在服務之前通常會有 LoadBalancer,請問您會選擇每個服務都有各自的 LoadBalancer 嗎?
另外想請教您的 Service Discovery 是如何設計的呢?

6年前 评论
LDL1023

@OneStep 可否分享一下你们是怎么用 lumen 来构建微服务的?Thanks~

6年前 评论

@leochien 每个服务都可以 LoadBalancer , 不过一般按需来用, 毕竟每个服务的使用频率不一样
Service Discovery 现在尝试使用docker来实现

6年前 评论

@LDL1023 如果是构建微服务, 其实跟框架就没多大关系了, 如果你喜欢java, 你也可以用java来开发

6年前 评论

@OneStep 自己實現 Service Descovery 不會有點難以維護嗎?我們當初是評估各種微服務解決方案後,選擇最完整的 Kubernetes,既可繼續使用 Docker,又可享受 K8S 的各種好處
再請教您,請問您傾向如何設計微服務間溝通?是讓各微服務互相溝通,還是使用 Message Queue 這類的機制呢?又是如何實作該機制?

6年前 评论

恩, 刚去了解了下Kubernetes, 谢谢哦~
服务间的沟通, 基于redis 的Message Queue, 和直接请求都有用, 基于业务场景,请求分为同步响应和异步响应, 异步响应的都是使用的 Message Queue, 同步响应直接就返回响应的数据了,就不做阐述了. 提交异步请求, 需要带一个异步接收地址的参数, 被请求方在队列里处理完成后,通知异步接收地址,然后根据响应内容完成响应的逻辑

6年前 评论
LDL1023

@OneStep 我比较喜欢 php,但是现在网上的方案基本都是 java。想知道一下 lumen 是怎么做的

6年前 评论

@OneStep 那若是異步請求需要同時呼叫多個服務,各請求又沒有相依關係的話,在這種使用接收地址的方法中,是不是相對會耗用較多請求資源,比起廣播來說
@LDL1023 我認為微服務的重點在於架構設計是否合理,彼此的溝通方式是否定義明確,若設計良好的微服務應可由數種不同語言來實現各服務

6年前 评论

@leochien 我感觉广播的话也有局限性,因为是单向的数据流,跟踪处理结果会比较麻烦,例如 网络异常, 参数错误....等导致请求失败
我们同步请求后,被请求方会立即返回数据或者请求结果(处理成功或者失败), 异步的场景主要是被请求方处理周期比较长,或者频率很高,需要使用队列等情况, 但是处理成功后也会返回处理结果或者数据, 这样请求方可以根据处理结果或者数据继续处理自己的逻辑

6年前 评论

@LDL1023 微服务只是种架构思想,把一个服务拆分为很多小的服务(例如商城拆分为: 用户,订单,商品,支付,物流等), 各个服务间相对独立,这样维护扩展起来比较方便, 比如 用户 模块, 你用lumen 需要完成 用户的增删改查等功能, 然后提供统一标准的API .
这样你应该比较好理解

6年前 评论

想请教一下API Gateway是什么?

5年前 评论

@PENGPN 我也是很久很久之前听到这个名词,没人给我解释,我片面理解

上面讲的微服务架构,这些服务对外开放的接口,我理解为网关。

5年前 评论

大佬,您的实践怎么样?

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!