本文将以技术调研模式编写,非技术同学可跳过。
基于组件(插件)模式设计构建的入口服务实现中,使用 Go 原生包 plugin 的时候,会存在功能缺陷问题,不足以支撑预期能力;在使用 go-plugin 开源包的时候,虽然功能缺陷得到了弥补,但丧失了部分的性能(组件与主程序通信成本)。
详细见上文 千万级入口服务[Gateway]框架设计(一)
详细见上上文 千万级入口服务[Gateway]框架设计(一)
本文将介绍另一种基于 “分层” 、“管道” 的架构模式。
分层的思路,从你开始学习计算机就见到了。计算机结构 ”冯诺伊曼“ 模型、数据库/Linux 内核结构,等等。可以说 “分层” 是对复杂性问题拆解,化繁为简的主要且有效的思路。
将业务逻辑进行上层抽象,分别划分为几层,层级之间上下级依赖。但需要注意的是,对于业务抽象控制粒度越细,实现成本也就会越大;并且如果业务的调用关系是动态的,抽象层多会导致整体复杂度上升,量级变重;后续业务通信无充分了解抽象分层结构的情况下,功能实现的质量存在不菲的成本,对后续服务的维护、扩展都增加了一层迷雾。
动态的调用关系:出现在复杂业务场景中,即下游的服务存在多个,实际的调用服务由上游服务的响应字段决定,整个路由呈现一种动态关系拓扑。
维护、扩展的迷雾:指由于未充分了解框架设计就进行迭代开发,无法保证交付的功能充分利用框架特征,使得产出质量存在不同程度值得揣测的地方。
下面从不同的划分粒度区分为三种方式。
在现行的基础上进行粒度放大,故
在 Web 模式上,把现有流程切分为 router + execute 两部分。
在 handle 的基础上,增加 hook 约束,故
在 Web 模式上,把现有流程切分为 router + afterhook\execute\beforehook\assert 的逻辑。
router
afterhook\execute\beforehook\assert
需要对 当前业务梳理,重新划分子服务调用次序,分层并发调用,故
使用前提是,存在静态子服务拓扑图,将服务调用关系进行动态染色划分为不同的层级,依据层级服务进行并发调用。
管道的思想,如果是传统语言见的可能较少,在 Goland、Ruby 等新式,尤其是语言层面支持并发、面向云原生的设计中比较常见,主打的是对性能的极致追求、高性能下的大数据、大模型 等对数据的流式处理。
注:云原生了解可前往:云原生应用架构的迁移 一 :增量迁移范式
不过不要紧,可以简单的先把中间件的概念移植到管道中来,进而再深入学习。这里基于 Goland 中的 Channel 进行设计。
注:Goland 了解可前往:Go 语言的设计反思
上述的几个方案和实现中,都是在请求内部的串改并,甚至是抛离了语言层面「在现有的PHP基础上操作即可」。
这里谈论一种基于管道的模式设计,实现服务层级的并行,特别适用于特大并发场景、符合 Go 语言特征。
主协程将负责各个自服务的监听、并发调用,业务协程只作为服务的发起和响应的接收。将不同的请求以子服务维度进行汇聚,并行调用。
这里做方案简单的概述:
但需要注意的是,
package main
import (
"fmt"
"sync"
)
type FInput struct{
callback chan FInput
param string
res string
}
func main () {
fmt.Println("hello https://tool.lu/")
c := make(chan FInput,3)
defer close(c)
var wg sync.WaitGroup
wg.Add(3)
go F(c,wg) //服务开启监听
go A(c,wg) //发起服务请求
go B(c,wg) //发起服务请求
wg.Wait()
fmt.Println("X https://tool.lu/ is finish")
}
func F (c chan FInput,wg sync.WaitGroup){
fmt.Println("F https://tool.lu/ len:",len(c))
for{
i, ok := <-c
fmt.Println("F https://tool.lu/ ok:",ok)
fmt.Println("F https://tool.lu/ param:",i.param)
if ok{
i.res = i.param + " is executed "
i.callback <- i
}
}
fmt.Println("F https://tool.lu/ is finish")
wg.Done()
}
func A (c chan FInput,wg sync.WaitGroup){
cb:=make(chan FInput)
defer close(cb)
var p FInput
p.callback = cb
p.param = "A"
c<-p
i, ok := <-cb
if ok{
fmt.Println("A https://tool.lu/ res ok:",ok)
fmt.Println("A https://tool.lu/ res param:",i.param)
fmt.Println("A https://tool.lu/ res value:",i.res)
}
fmt.Println("A https://tool.lu/ is finish")
wg.Done()
}
func B (c chan FInput,wg sync.WaitGroup){
cb:=make(chan FInput)
defer close(cb)
var p FInput
p.callback = cb
p.param = "B"
c<-p
i, ok := <-cb
if ok{
fmt.Println("B https://tool.lu/ res ok:",ok)
fmt.Println("B https://tool.lu/ res param:",i.param)
fmt.Println("B https://tool.lu/ res value:",i.res)
}
fmt.Println("B https://tool.lu/ is finish")
wg.Done()
}
其实对比上述的各种架构模式设计,都是各具特色,优缺兼并。在进行架构选型的时候,想到不同的模式只是第一步。在此前提下,才能更进一步的挑选。
随着 chatGPT 进入大众的视野,”大模型航道“ 变得赤手可热,而且其独特的衍变进程更是一骑绝尘。毫不客气的说,未来的各种应用都将会是更智能、更大模型…
下一章讲介绍 “入口服务” 如何和 ”大模型“ 相结合,设计面向未来的架构模式,敬请期待!