草庐IT

颠覆微服务架构?谷歌最新开源Service Weaver初体验

凉凉的知识库 2023-03-28 原文
本文转载自微信公众号「凉凉的知识库」,作者凉凉的知识库 。转载本文请联系凉凉的知识库公众号。

合久必分,分久必合,技术圈也是如此。在大家纷纷从单体应用过渡到微服务的时候,谷歌携带着新时代的“单体”应用框架Service Weaver来了!代码仓库位于:https://github.com/ServiceWeaver/weaver 才发布没几天已经超过了2.5k star,不得不感慨谷歌的号召力。

谷歌称此框架为模块化单体(modular monolith),谷歌为什么会在这个时候提出如此标新立异的框架?它究竟有什么独特之处?让我们来速速体验下吧。

安装

因为Service Weaver使用了泛型,且声明的依赖版本为1.19。所以本地安装的go版本需要大于1.19

$ go install github.com/ServiceWeaver/weaver/cmd/weaver@latest

如果你设置了正确的$GOPATH/bin路径到你的PATH中那么你可以直接运行

$ weaver --help
USAGE

weaver generate // weaver code generator
weaver single <command> ... // for single process deployments
weaver multi <command> ... // for multiprocess deployments
...

教程

创建项目

$ mkdir hello/
$ cd hello/
$ go mod init github.com/liangwt/serviceweaver/hello

启动服务

先来创建一个最简单的HTTP服务。与Go内置的HTTP server的使用方式非常类似,唯一的区别是创建端口监听的方式不同,Service Weaver需要使用github.com/ServiceWeaver/weaver包提供的函数来创建监听

package main

import (
"context"
"fmt"
"log"
"net/http"

"github.com/ServiceWeaver/weaver"
)

func main() {
// Get a network listener on address "localhost:12345".
root := weaver.Init(context.Background())
opts := weaver.ListenerOptions{LocalAddress: "localhost:12345"}
lis, err := root.Listener("hello", opts)
if err != nil {
log.Fatal(err)
}
fmt.Printf("hello listener available on %v\n", lis)

// Serve the /hello endpoint.
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!\n", r.URL.Query().Get("name"))
})
http.Serve(lis, nil)
}

// 内置Go http server使用方式
// func main() {
// lis, err := net.Listen("tcp", "localhost:12345")
// if err != nil {
// log.Fatal(err)
// }

// // Serve the /hello endpoint.
// http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
// fmt.Fprintf(w, "Hello, %s!\n", r.URL.Query().Get("name"))
// })
// http.Serve(lis, nil)
// }

执行也和普通的代码没有区别

$ go mod tidy
$ go run .
hello listener available on 127.0.0.1:12345
╭───────────────────────────────────────────────────╮
│ app : hello │
│ deployment : f4bd112d-c90d-409c-a90f-5022fa5a7b3f │
╰───────────────────────────────────────────────────╯

当服务启动之后我们就可以调用对应端口,直到这里依旧和普通的HTTP server没有区别

$ curl 'localhost:12345/hello?name=Weaver'

Hello, Weaver!

组件(Components)

组件是Service Weaver中一个独特的概念

什么是组件?一个应用会有多个组件,每一个组件就是一个Go的interface。

下面的反转字符串type Reverser interface就是一个组件,type reverser struct是它的一个实现。需要注意一点reverser struct组合了weaver.Implements[Reverser]用以实现Service Weaver要求的其他接口

// Reverser component.
type Reverser interface {
Reverse(context.Context, string) (string, error)
}

// Implementation of the Reverser component.
type reverser struct{
weaver.Implements[Reverser]
}

func (r *reverser) Reverse(_ context.Context, s string) (string, error) {
runes := []rune(s)
n := len(runes)
for i := 0; i < n/2; i++ {
runes[i], runes[n-i-1] = runes[n-i-1], runes[i]
}
return string(runes), nil
}

组件该怎么用?我们可以用weaver.Get()获取一个组件的实例。例如:

func main() {
// Get a network listener on address "localhost:12345".
...
fmt.Printf("hello listener available on %v\n", lis)

// Get a client to the Reverser component.
reverser, err := weaver.Get[Reverser](root)
if err != nil {
log.Fatal(err)
}

// Serve the /hello endpoint.
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
reversed, err := reverser.Reverse(r.Context(), r.URL.Query().Get("name"))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
fmt.Fprintf(w, "Hello, %s!\n", reversed)
})
http.Serve(lis, nil)
}

这次在运行代码前我们得先执行下面命令,文件中会多一个 weaver_gen.go 文件

$ weaver generate .

重新运行代码

$ go run .
hello listener available on 127.0.0.1:12345
╭───────────────────────────────────────────────────╮
│ app : hello │
│ deployment : 7581bbb5-21c1-4cd3-8192-3fa2ac2bfc70 │
╰───────────────────────────────────────────────────╯

调用对应的接口就会获得反转的字符串响应

$ curl 'localhost:12345/hello?name=Weaver'

Hello, revaeW!

对组件的看法

组件的概念,给我的第一感觉是非常像依赖注入中的IOC容器(不知道什么IOC的同学可以自行网上搜索充电)。

很多语言的很多框架都有依赖注入的功能:定义一个接口,实现这个接口,从IOC容器中获取这个接口的实例。借助依赖注入来实现依赖解耦,简化对象创建流程等

Service Weaver定义组件的概念也有其自身的目的,稍后体验过多进程执行的之后,会对组件有一点更深的理解

多进程执行

先创建一个TOML文件weaver.toml,内容如下

[serviceweaver]
binary = "./hello"

编译并使用weaver multi deploy来运行我们的应用

$ go build
$ weaver multi deploy weaver.toml
╭───────────────────────────────────────────────────╮
│ app : hello │
│ deployment : c42d8b31-e6e3-4e41-a56e-6e18956927d3 │
╰───────────────────────────────────────────────────╯
S0308 22:33:22.289059 stdout 451b9a05 ] hello listener available on 127.0.0.1:12345
S0308 22:33:22.289190 stdout 2c79c310 ] hello listener available on 127.0.0.1:12345

调用对应的接口依旧会获得反转的字符串响应

$ curl 'localhost:12345/hello?name=Weaver'

Hello, revaeW!

你可能会说这和go run .有啥区别?对于返回的响应来说确实没有区别,但在运行机制上两者已经有了巨大的差别。

当我们执行go run .时,我们的应用包含所有的组件运行在一个进程中,组件的调用就是Go中的正常的函数调用

当我们执行weaver multi deploy weaver.toml时,我们的应用中的组件会变的像微服务一样,运行中多个进程中,此时组件的调用就通过RPC的方式了

更进一步,既然组件都可以运行中在不同的进程中,自然也可以运行在不同的机器中

Service Weaver目前仅提供了GKE(Google Kubernetes Engine)的支持。快速将应用部署到GKE的不同容器中

$ weaver gke deploy weaver.toml

对多进程的看法

此时再回顾下对组件的看法,weaver.Get:当单进程部署时,它返回一个接口的本地实例,当多进程部署时,它返回一个RPC的client

Service Weaver称自己为模块化单体(modular monolith)。通过组件这个概念让你写代码和部署代码的动作分离开,你可以按照单体的方式写代码,然后在其他进程或者机器按照微服务的方式运行组件的进程

总结

因为也是刚刚接触到这个框架,很多细节还不太理解,目前业界更是没有实践落地。这里说些我自己的看法,也欢迎大家批评指正

单体向微服务的演进不是由于某个单一的原因造成,团队的分工,庞大的代码,复杂的依赖等诸多原因造就了现在流行微服务架构。Service Weaver也并没有要取代传统微服务,在不同团队间使用传统微服务,在同一个团队内部或者同一功能的服务使用模块化单体是一种新的选择

? 关于模块化单体(modular monolith)中的单体

单体的开发确实会降低我们的心智负担,我们不用关心不同模块之间的RPC协议,模块之间的服务发现,在本地测试的时候也不需要专门构建其他依赖微服务的模块

Monorepo 仓库能到达一样的效果么?部分能,我们可以把一套微服务放到一个Monorepo中,并使用一个BFF服务对外提供统一的访问入口,能实现类似于单体开发的体验。但我们依旧需要关心RPC协议、服务发现等一系列的问题

? 关于模块化单体(modular monolith)中的模块

有了Service Weaver我们就可以不用关心服务拆分了么?不是的,我们依旧面临着服务拆分,传统的微服务拆分到不同的代码仓库,Service Weaver拆分到了不同的逻辑(组件)。所以传统的服务拆分的方法论和经验,在这里依旧适用

? 模块化单体(modular monolith)之外的东西

Service Weaver也提供了Logging、Metrics、Tracing、Profiling的基本能力,这部分没啥特殊的,绝大部分的RPC框架都集成了类似的能力

Service Weaver提供了Google Kubernetes Engine (GKE) 部署的能力,如果你的公司恰好使用标准的k8s服务,或许不难扩展。如果你的公司使用了非标准的k8s,如何与现有的部署系统结合也是个需要考虑的问题

有关颠覆微服务架构?谷歌最新开源Service Weaver初体验的更多相关文章

  1. 报告回顾丨模型进化狂飙,DetectGPT能否识别最新模型生成结果? - 2

    导读语言模型给我们的生产生活带来了极大便利,但同时不少人也利用他们从事作弊工作。如何规避这些难辨真伪的文字所产生的负面影响也成为一大难题。在3月9日智源Live第33期活动「DetectGPT:判断文本是否为机器生成的工具」中,主讲人Eric为我们讲解了DetectGPT工作背后的思路——一种基于概率曲率检测的用于检测模型生成文本的工具,它可以帮助我们更好地分辨文章的来源和可信度,对保护信息真实、防止欺诈等方面具有重要意义。本次报告主要围绕其功能,实现和效果等展开。(文末点击“阅读原文”,查看活动回放。)Ericmitchell斯坦福大学计算机系四年级博士生,由ChelseaFinn和Chri

  2. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  3. ruby - Ruby 和 Ruby on Rails 中的三层架构 - 2

    我是一名决定学习Ruby和RubyonRails的ASP.NETMVC开发人员。我已经有所了解并在RoR上创建了一个网站。在ASP.NETMVC上开发,我一直使用三层架构:数据层、业务层和UI(或表示)层。尝试在RubyonRails应用程序中使用这种方法,我发现没有关于它的信息(或者也许我只是找不到它?)。也许有人可以建议我如何在RubyonRails上创建或使用三层架构?附言我使用ruby​​1.9.3和RubyonRails3.2.3。 最佳答案 我建议在制作RoR应用程序时遵循RubyonRails(RoR)风格。Rails

  4. ruby-on-rails - 使用 gmaps4rails 动态加载谷歌地图标记 - 2

    如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail

  5. 西安华为OD面试体验 - 2

    西安华为OD面试体验开始投简历技术面试进展工作进展开始投简历去年一整年一直在考研和工作之间纠结,感觉自己的状态好像当时的疫情一样差劲。之前刚毕业的时候投了个大厂的简历,结果一面写算法的时候太拉跨了,虽然知道时dfs但是代码熟练度不够,放在平时给足时间自己可以调试通过,但是熟练度不够那面试当时就写不出来被刷了。说真的算法学到后期我感觉最重要的是熟练度和背板子(对于我这种普通玩家来说),面试题如果一上来短时间内想不出思路就完蛋了。然后由于当时找的工作不是很理想就又想考研了。但是考研是有风险的,我自我感觉自己可能冲不上那个学校,而找工作一个没成可以继续找嘛。本着抱着试试看的态度在boss上投了简历,

  6. 最新版人脸识别小程序 图片识别 生成二维码签到 地图上选点进行位置签到 计算签到距离 课程会议活动打卡日常考勤 上课签到打卡考勤口令签到 - 2

    技术选型1,前端小程序原生MINA框架cssJavaScriptWxml2,管理后台云开发Cms内容管理系统web网页3,数据后台小程序云开发云函数云开发数据库(基于MongoDB)云存储4,人脸识别算法基于百度智能云实现人脸识别一,用户端效果图预览老规矩我们先来看效果图,如果效果图符合你的需求,就继续往下看,如果不符合你的需求,可以跳过。1-1,登录注册页可以看到登录页有注册入口,注册页如下我们的注册,需要管理员审核,审核通过后才可以正常登录使用小程序1-2,个人中心页登录成功以后,我们会进入个人中心页我们在个人中心页可以注册人脸,因为我们做人脸识别签到,需要先注册人脸才可以进行人脸比对,进

  7. ruby-on-rails - 具有六边形架构和 DCI 模式的框架和数据库适配器 - 2

    我尝试用Ruby设计一个基于Web的应用程序。我开发了一个简单的核心应用程序,在没有框架和数据库的情况下在六边形架构中实现DCI范例。核心六边形中有小六边形和网络,数据库,日志等适配器。每个六边形都在没有数据库和框架的情况下自行运行。在这种方法中,我如何提供与数据库模型和实体类的关系作为独立于数据库的关系。我想在将来将框架从Rails更改为Sinatra或数据库。事实上,我如何在这个核心Hexagon中实现完全隔离的rails和mongodb的数据库适配器或框架适配器。有什么想法吗? 最佳答案 ROM呢?(Ruby对象映射器)。还有

  8. ruby - 从谷歌开发者网站下载后,client_secret.json 为空 - 2

    我正在尝试从googleAPI下载client_secret.json。我正在执行https://developers.google.com/gmail/api/quickstart/ruby中列出的步骤.使用此向导在GoogleDevelopersConsole中创建或选择项目并自动启用API。在左侧边栏中,选择同意屏幕。选择电子邮件地址并输入产品名称(如果尚未设置),然后单击“保存”按钮。在左侧边栏中,选择凭据并点击创建新客户端ID。选择应用程序类型已安装应用程序,已安装应用程序类型为其他,然后单击“创建客户端ID”按钮。点击新客户端ID下的下载JSON按钮。将此文件移动到您的工作

  9. ruby - 如何在 OSX 上正确更新系统 ruby​​ 版本到最新版本 (2.2.1) - 2

    只是想更新到最新版本的Ruby。在ruby​​-lang.org/en/documentation/installation/#homebrew上,我发现你应该可以通过自制软件来完成:brewinstallruby但是,当我在“更新”后列出ruby​​版本(ruby-v)时,它仍然是旧版本2.0.0。Hermes:~Sancho$ruby-vruby2.0.0p481(2014-05-08revision45883)[universal.x86_64-darwin13]我碰巧列出了/usr/local/bin/的内容,我可以看到一个符号链接(symboliclink):ruby->..

  10. python - 开源 Twitter 克隆(在 Ruby/Python 中) - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭6年前。Improvethisquestion是否有任何用Ruby或Python编写的生产就绪的开源Twitter克隆?我对功能丰富的实现更感兴趣,而不仅仅是简单的Twitter消息(例如:API、FBconnect、通知等)谢谢!

随机推荐