Laravel 如何设计微服务架构,及如何进行微服务间沟通?
如题,我目前有需要用 Laravel 设计微服务架构的需求,但能找到的相关资料不多
目前已有的一个思考方向是使用 K8S 统合各个独立的 Laravel 小服务,再开放统一对外的 API Gateway
但碰到一个问题是各个服务间要如何在不发生耦合的状况下沟通
举例来说:订单发生后需要减少库存,若库存成功扣除后要再告诉订单更新状态
我目前想到三种解决方式:
- 在 API Gateway 处理流程问题,等订单服务回传成功后,再呼叫库存服务。
好处:流程简单直觉、使用者当下就可知道订单结果
坏处:API Gateway 会发生强耦合、API Gateway 会出现状态,违反微服务的 Stateless 原则 - 在订单服务的建立方法中直接呼叫库存服务
好处与坏处都跟方法一差不多,但更多的坏处是微服务本身会变复杂、与其他服务发生强耦合,且违反微服务间不能直接沟通的原则 - 借由某种广播机制,在订单建立完成后,由订单服务发出「订单建立成功」的广播,告诉其他服务有订单建立。库存服务接收到广播后就会扣除库存,并再次发出「库存扣除成功」的广播,由订单服务再次接收。而其他不相干的服务就会忽略广播。
好处:各服务完全解耦,彼此只借由单次事件进行沟通,不会在双方服务留下状态,且后续有逻辑改动只需要调整事件即可,不用动服务主逻辑
坏处:无法及时告诉使用者订单产生结果,会造成较差的使用者体验
我目前想采用的是第三种模式,因为我司目前还处于新创阶段,功能仍在快速调整,因此开发上最好能像电源一样快速插拔,若服务出现强耦合就会降低产品调整的速度。相较之下,第三种方法造成的使用者体验降低都还可以接受。
我搜寻过后似乎有「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 机制,可以如下设计
- 以 AWS 来说,各个微服务都会有自己对应的 SQS,事件产生时,将事件发送到 SNS,SNS 就会广播到所有 SQS,并由各服务各自处理队列
- 若非使用 AWS,而是自架 Message Queue,就是自己架个 Message Queue Service,里面的 Redis 为集群,收到广播事件后就一次写入集群所有 Redis,再由各服务执行
思路上大概如上,但未经过实作,待实作过后会再上来补充说明
关于 LearnKu
高认可度评论:
创业阶段的东西不建议用微服务,创业阶段讲求的是开发效率,而不是架构能撑多少用户。微服务不光会降低开发效率,而且出现问题排查起来也会变得及其麻烦。另外如果服务拆分不合理,微服务将会变成一个技术债务。
再来说说微服务之间的通信,既然已经解耦,那么最好的通信方式就是消息队列。
微服务的话,
lumen感觉更适合. 我们的微服务全是对外开放的, 然后将API认证跟服务间的请求封装到Packagist,每个服务都composer这个包就好@OneStep 谢谢回复
我司目前有 2 个专案,一个是旧的 Laravel 单体架构,我打算先维持 Laravel 的架构,后续慢慢拆解成 Lumen 微服务;一个正在开发,也是使用 Lumen
有几个问题请教您:
Packagist这段可以说明详细点吗,这个包里做了什么,每个服务 require 包之后又要做什么?packagist里面封装了各个服务间公用的配置, 还有token验证(其实是由USER的服务提供), 还有各个服务间的通信.require之后只需要调用就行, 比如服务间的通信 使用每个服务只提供接口, 前端 APP之类的完成业务逻辑,按需调用各个服务
各个服务单独部署的,所以load balance 也没问题的
这个作法真棒,谢谢分享
针对
每个服务只提供接口, 前端 APP之类的完成业务逻辑,按需调用各个服务想再请教您我目前想到这样做会有以下几种状况,不知道您会怎么解决
创业阶段的东西不建议用微服务,创业阶段讲求的是开发效率,而不是架构能撑多少用户。微服务不光会降低开发效率,而且出现问题排查起来也会变得及其麻烦。另外如果服务拆分不合理,微服务将会变成一个技术债务。
再来说说微服务之间的通信,既然已经解耦,那么最好的通信方式就是消息队列。
@TimJuly 谢谢建议
我们目前的状况是已经创业 1 年多,之前也是讲求开发效率而采用单体架构
但因后来 pivot 为工具型平台,我们必须快速调整功能,且不希望功能间互相影响
在这样的考量下我才决定把公司的架构慢慢转向微服务
目前规划的作法是慢慢剥离主架构,所有的新功能都必须采用新架构,借以降低初期的阵痛期
待之后公司发展较稳健,再 Hire 专业的架构师来协助重新调整
@leochien 安全性的话,目前主要靠基于 OAuth 2.0 颁发的token来验证各个接口的访问权限,应该问题不大
前段构成业务逻辑,的确会造成前段请求各个服务的请求比较多,不过感觉影响不大,我们前段都是异步请求的
还有一个是订单创建成功,但是因为网络原因造成前段没有请求库存API, 这个估计就得看没有请求API的原因了,我感觉有2种:
已经请求库存API, 但是因为网络问题请求失败 ,这种情况,我们前段一般会根据请求的失败原因(token过期我们一般抛 401, 参数错误 422, 没权限 403 之类的)重构请求, 比如token 过期, API返回:
// 然后前段判断网络状态, 如果401 会调用刷新token的接口更新token,然后重新请求
@OneStep 謝謝回覆,確實使用 Token 能確保不會有權限問題
在服務之前通常會有 LoadBalancer,請問您會選擇每個服務都有各自的 LoadBalancer 嗎?
另外想請教您的 Service Discovery 是如何設計的呢?
@OneStep 可否分享一下你们是怎么用 lumen 来构建微服务的?Thanks~
@leochien 每个服务都可以
LoadBalancer, 不过一般按需来用, 毕竟每个服务的使用频率不一样Service Discovery现在尝试使用docker来实现@LDL1023 如果是构建微服务, 其实跟框架就没多大关系了, 如果你喜欢java, 你也可以用java来开发
@OneStep 自己實現
Service Descovery不會有點難以維護嗎?我們當初是評估各種微服務解決方案後,選擇最完整的 Kubernetes,既可繼續使用 Docker,又可享受 K8S 的各種好處再請教您,請問您傾向如何設計微服務間溝通?是讓各微服務互相溝通,還是使用 Message Queue 這類的機制呢?又是如何實作該機制?
恩, 刚去了解了下
Kubernetes, 谢谢哦~服务间的沟通, 基于
redis的Message Queue, 和直接请求都有用, 基于业务场景,请求分为同步响应和异步响应, 异步响应的都是使用的 Message Queue, 同步响应直接就返回响应的数据了,就不做阐述了. 提交异步请求, 需要带一个异步接收地址的参数, 被请求方在队列里处理完成后,通知异步接收地址,然后根据响应内容完成响应的逻辑@OneStep 我比较喜欢 php,但是现在网上的方案基本都是 java。想知道一下 lumen 是怎么做的
@OneStep 那若是異步請求需要同時呼叫多個服務,各請求又沒有相依關係的話,在這種使用接收地址的方法中,是不是相對會耗用較多請求資源,比起廣播來說
@LDL1023 我認為微服務的重點在於架構設計是否合理,彼此的溝通方式是否定義明確,若設計良好的微服務應可由數種不同語言來實現各服務
@leochien 我感觉广播的话也有局限性,因为是单向的数据流,跟踪处理结果会比较麻烦,例如 网络异常, 参数错误....等导致请求失败
我们同步请求后,被请求方会立即返回数据或者请求结果(处理成功或者失败), 异步的场景主要是被请求方处理周期比较长,或者频率很高,需要使用队列等情况, 但是处理成功后也会返回处理结果或者数据, 这样请求方可以根据处理结果或者数据继续处理自己的逻辑
@LDL1023 微服务只是种架构思想,把一个服务拆分为很多小的服务(例如商城拆分为: 用户,订单,商品,支付,物流等), 各个服务间相对独立,这样维护扩展起来比较方便, 比如 用户 模块, 你用
lumen需要完成 用户的增删改查等功能, 然后提供统一标准的API .这样你应该比较好理解
想请教一下API Gateway是什么?
@PENGPN 我也是很久很久之前听到这个名词,没人给我解释,我片面理解
大佬,您的实践怎么样?