作者:李俱顺
原文:https://www.4async.com/2021/03/2021-03-11-running-dapr-without-container/
前一段时间一直关注的dapr
正式发布了v1.0版本(实际上本文发布时还更新了v1.0.1),代表dapr
在某些程度上进入稳定状态,可以尝试在实际中进行运用。作为我一直关注的项目,在第一时间中进行了尝试,并试图引入实际项目中,本文则是针对这些的一些先期测试内容.
dapr
最早是由微软开源的(不愧是你),一个可移植的、事件驱动的程序运行时,它使任何开发者都能轻松地构建运行在云和边缘的弹性、无状态/有状态的应用程序,并且可以灵活支持多种开发语言。换而言之,在我看来,dapr
可以作为一个serverless
落地方案看待和处理,对程序而言,只关注提供的store和消息队列接口,无需关心架构层面更多内容。
不过在官方的示例教程中,使用的环境为容器环境部署和管理dapr。实际上,除了在容器环境或者容器集群环境下,dapr
可以配置为在本地机器上以自托管模式运行。
dapr
安装可以通过官方的dapr-cli
实现,dapr-cli
可以通过一键安装命令快速安装:
# wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
Your system is linux_amd64
Dapr CLI is detected:
main: line 86: 43656 Segmentation fault $DAPR_CLI_FILE --version
Reinstalling Dapr CLI - /usr/local/bin/dapr...
Getting the latest Dapr CLI...
Installing v1.0.0 Dapr CLI...
Downloading https://github.com/dapr/cli/releases/download/v1.0.0/dapr_linux_amd64.tar.gz ...
dapr installed into /usr/local/bin successfully.
CLI version: 1.0.0
Runtime version: n/a
To get started with Dapr, please visit https://docs.dapr.io/getting-started/
可以通过输入dapr
命令确认dapr-cli
程序是否被正常安装成功。
接下来使用dapr-cli安装所有的runtime等应用。
# dapr init --slim
⌛ Making the jump to hyperspace...
↘ Downloading binaries and setting up components...
Dapr runtime installed to /root/.dapr/bin, you may run the following to add it to your path if you want to run daprd directly:
export PATH=$PATH:/root/.dapr/bin
✅ Downloaded binaries and completed components set up.
ℹ️ daprd binary has been installed to /root/.dapr/bin.
ℹ️ placement binary has been installed to /root/.dapr/bin.
✅ Success! Dapr is up and running. To get started, go here: https://aka.ms/dapr-getting-started
# dapr --version
CLI version: 1.0.0
Runtime version: 1.0.1
在官方文档中,如果选择使用init
命令初始化,dapr-cli
将会自动尝试使用容器环境管理相关程序,只有添加--slim
参数才会选择本地化运行。更多用法可以参考dapr help init
帮助。默认程序相关内容会安装在$HOME/.dapr
目录下,这里因为我为了简便使用了root
用户,因此程序命令所在目录为/root/.dapr/bin
,共安装了如下命令:
# ls ~/.dapr/bin
daprd dashboard placement web
从文件名可以看出来daprd
是deamon进程,dashboard
就是管理面板,placement
是用于管理actor
分布方案和密钥范围的工具。官方文档中提到在安装后会使用Reids
作为默认的存储和pub/sub组件,但是我实际安装下来其实是并没没有的,不知道是不是文档有些过期导致的。这时如果按照官方文档的例子进行操作启动程序并尝试在存储中保存数据,则会出现报错的情况:
// 第一个session中执行:
# dapr run --app-id myapp --dapr-http-port 3500
// 第二个session中执行:
# curl -X POST -H "Content-Type: application/json" -d '[{ "key": "name", "value": "Bruce Wayne"}]' http://localhost:3500/v1.0/state/statestore
{"errorCode":"ERR_STATE_STORES_NOT_CONFIGURED","message":"state store is not configured"}
不过实际上添加组件在dapr
中也是比较简单的,可以通过在$HOME/.dapr/components
下添加对应yaml
文件实现。
我们可以在官方文档
中找到一个Redis
组件配置模版,可以快速使用:
# redis-store.yml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: redis-store
namespace: default
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: 127.0.0.1:6379
- name: redisPassword
value: ""
当然我们也可以使用Redis Stream
功能做pub/sub功能,虽然这个功能已经GA,但是介于Redis Stream
的特点,你需要谨慎使用这个功能,这里只是因为是演示所以无所谓:
# redis-pubsub.yml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: redis-pubsub
namespace: default
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: 127.0.0.1:6379
- name: redisPassword
value: ""
- name: consumerID
value: "myGroup"
这里我们定义了一个store
名叫做redis-store
,所以我们要把上面的命令修改一下:
# curl -X POST -H "Content-Type: application/json" -d '[{ "key": "name", "value": "Bruce Wayne"}]' http://localhost:3500/v1.0/state/redis-store
// 获取存储内容
# curl http://localhost:3500/v1.0/state/redis-store/name
"Bruce Wayne"
同时也可以通过redis-cli
获取Redis
中存储的内容:
# redis-cli
127.0.0.1:6379> keys *
1) "myapp||name"
127.0.0.1:6379> hgetall "myapp||name"
1) "data"
2) "\"Bruce Wayne\""
3) "version"
4) "1"
我们在添加Redis
作为存储时还额外添加了Redis
支持发布/订阅功能,这个功能如何实现呢?这里可能就需要编写额外程序实现了。我们这里采用官方的例子进行。订阅在dapr
中有两种形式,一种是采用yaml
声明组件形式,另外一种则可以通过编写代码形式实现。当然第一种方式和第二种方式互有优劣,前者更适合无缝集成,后者方便开发控制。这里为了演示直观性直接采用了编写代码方式实现。
package main
import (
"io"
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/dapr/subscribe", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, []map[string]string{
{
"pubsubname": "redis-pubsub",
"topic": "deathStarStatus",
"route": "dsstatus",
},
})
})
r.POST("/dsstatus", func(c *gin.Context) {
b, _ := io.ReadAll(c.Request.Body)
defer c.Request.Body.Close()
log.Println(string(b))
c.JSON(http.StatusOK, map[string]interface{}{"success": true})
})
r.Run("127.0.0.1:5000")
}
使用如下命令启动编译后的daprdemo
,注意指定文件名时需要填写路径或者在$PATH
中:
# dapr --app-id subapp --app-port 5000 run ~/daprdemo
在程序启动日志中我们可以看到dapr
会尝试访问一些默认的endpoint
读取可能的配置:
INFO[0000] application discovered on port 5000 app_id=subapp instance=127.0.0.1 scope=dapr.runtime type=log ver=1.0.1
== APP == [GIN] 2021/03/11 - 10:45:02 | 404 | 949ns | 127.0.0.1 | GET "/dapr/config"
INFO[0000] application configuration loaded app_id=subapp instance=127.0.0.1 scope=dapr.runtime type=log ver=1.0.1
INFO[0000] actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s app_id=subapp instance=127.0.0.1 scope=dapr.runtime.actor type=log ver=1.0.1
== APP == [GIN] 2021/03/11 - 10:45:02 | 200 | 540.891µs | 127.0.0.1 | GET "/dapr/subscribe"
INFO[0000] app is subscribed to the following topics: [deathStarStatus] through pubsub=redis-pubsub app_id=subapp instance=127.0.0.1 scope=dapr.runtime type=log ver=1.0.1
WARN[0000] redis streams: BUSYGROUP Consumer Group name already exists app_id=subapp instance=127.0.0.1 scope=dapr.contrib type=log ver=1.0.1
INFO[0000] dapr initialized. Status: Running. Init Elapsed 49.674504ms app_id=subapp instance=127.0.0.1 scope=dapr.runtime type=log ver=1.0.1
接下来我们尝试使用dapr-cli
对我们之前启动的myapp
发送消息:
dapr publish --publish-app-id myapp --pubsub redis-pubsub --topic deathStarStatus --data '{"status": "completed"}'
在程序日志中获取到的输出为:
== APP == [GIN] 2021/03/11 - 10:45:05 | 200 | 122.15µs | 127.0.0.1 | POST "/dsstatus"
== APP == 2021/03/11 10:45:05 {"id":"9c237504-7cab-4a13-8582-92d9130fd016","source":"myapp","pubsubname":"redis-pubsub","traceid":"00-fba669a086f84650e882e3cadc55082c-ea466c080e359e68-00","data":{"status":"completed"},"specversion":"1.0","datacontenttype":"application/json","type":"com.dapr.event.sent","topic":"deathStarStatus"}
当然,除了pub/sub方式,我们也可以借助dapr
提供的路由功能,直接进行服务调用:
# curl http://127.0.0.1:3500/v1.0/invoke/subapp/method/dsstatus -X POST
{"success":true}
其他的组件功能则可以参考官方文档中描述进行配置即可。
dapr
是一个功能强大的serverless
运行时,除了上面提到的面向消息和请求存储的功能以外,还可以控制程序的HTTP
请求与gRPC
请求等等。除了这些功能外,还包含了服务的管理,还有可观测性支持等功能,是一个非常有潜力的运行时选择。
相关文章:
Dapr能否引领云原生中间件的未来?
云原生 | 阿里巴巴的Dapr实践与探索
Dapr | 云原生的抽象与实现
Dapr 可视化指南
Dapr 知多少 | 分布式应用运行时
Dapr 正式发布 1.0
Dapr 交通流量控制示例
Dapr是如何简化微服务的开发和部署
微软开源微服务运行时Dapr,赋能云原生应用开发
YARP实现Dapr服务调用的反向代理
Dapr微服务应用开发系列0:概述
Dapr微服务应用开发系列1:环境配置
Dapr微服务应用开发系列2:Hello World与SDK初接触
Dapr微服务应用开发系列3:服务调用构件块
Dapr微服务应用开发系列4:状态管理构件块
Dapr微服务应用开发系列5:发布订阅构建块
Windows环境下Dapr入门
通过Dapr实现一个简单的基于.net的微服务电商系统
通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解
通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr
通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布
通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理
通过Dapr实现一个简单的基于.net的微服务电商系统(六)——一步一步教你如何撸Dapr之Actor服务
通过Dapr实现一个简单的基于.net的微服务电商系统(七)——一步一步教你如何撸Dapr之服务限流
通过Dapr实现一个简单的基于.net的微服务电商系统(八)——一步一步教你如何撸Dapr之链路追踪
通过Dapr实现一个简单的基于.net的微服务电商系统(九)——一步一步教你如何撸Dapr之OAuth2授权
通过Dapr实现一个简单的基于.net的微服务电商系统(九)——一步一步教你如何撸Dapr之OAuth2授权-百度版
通过Dapr实现一个简单的基于.net的微服务电商系统(十)——一步一步教你如何撸Dapr之绑定
通过Dapr实现一个简单的基于.net的微服务电商系统(十一)——一步一步教你如何撸Dapr之自动扩/缩容
WebAssembly + Dapr = 下一代云原生运行时?
dapr 应用开发 | 环境配置
乘风破浪,.Net Core遇见Dapr,为云原生而生的分布式应用运行时
Dapr案例之高德 Serverless 平台建设及实践