grafana 的主体架构是如何设计的?
grafana 是非常强大的可视化项目,它最早从 kibana 生成出来,渐渐也已经形成了自己的生态了。研究完 grafana 生态之后,只有一句话:可视化,grafana 就够了。
这篇就想了解下它的主体架构是如何设计的。如果你对 grafana 有兴趣,不妨让这篇成为入门读物。
入口代码
grafana 的最外层就是一个 build.go,它并不是真正的入口,它只是用来编译生成 grafana-server 工具的。
grafana 会生成两个工具,grafana-cli 和 grafana-server。
go run build.go build-server 其实就是运行
go build ./pkg/cmd/grafana-server -o ./bin/xxx/grafana-server
这里可以划重点学习一下:
如果你的项目要生成多个命令行工具,又或者有多个参数,又或者有多个操作,使用 makefile 已经很复杂了,我们是可以这样直接写个 build.go 或者 main.go 在最外层,来负责编译的事情。
所以真实的入口在 ./pkg/cmd/grafana-server/main.go 中。可以跟着这个入口进入。
设计结构
这篇不说细节,从宏观角度说下 grafana 的设计结构。带着这个架构再去看 granfana 才更能理解其中一些细节。
grafana 中最重要的结构就是 Service。 grafana 设计的时候希望所有的功能都是 Service。是的,所有,包括用户认证 UserAuthTokenService,日志 LogsService, 搜索 LoginService,报警轮训 Service。 所以,这里需要设计出一套灵活的 Service 执行机制。
理解这套 Service 机制就很重要了。这套机制有下列要处理的地方:
注册机制
首先,需要有一个 Service 的注册机制。
grafana 提供的是一种有优先级的,服务注册机制。grafana 提供了 pkg/registry 包。
在 Service 外层包了一个结构,包含了服务的名字和服务的优先级。
type Descriptor struct {
Name string
Instance Service
InitPriority Priority
}
这个包提供的三个注册方法:
RegisterServiceWithPriority
RegisetrService
Register
这三个注册方法都是把 Descriptior(本质也就是 Service)注册到一个全局的数组中。
取的时候也很简单,就是把这个全局数组按照优先级排列就行。
那么什么时候执行注册操作呢?答案就是在每个 Service 的 init() 函数中进行注册操作。所以我们可以看到代码中有很多诸如:
_ "github.com/grafana/grafana/pkg/services/ngalert"
_ "github.com/grafana/grafana/pkg/services/notifications"
_ "github.com/grafana/grafana/pkg/services/provisioning"
的 import 操作,就是为了注册服务的。
Service 的类型
如果我们自己定义 Service,差不多定义一个 interface 就好了,但是实际这里是有问题的。我们有的服务需要的是后端启动,有的服务并不需要后端启动,而有的服务需要先创建一个数据表才能启动,而有的服务需要根据配置文件判断是否开启。要定义一个 Service 接口满足这些需求,其实也是可以的,只是比较丑陋,而 grafana 的写法就非常优雅了。
grafana 定义了基础的 Service 接口,仅仅需要实现一个 Init() 方法:
type Service interface {
Init() error
}
而定义了其他不同的接口,比如需要后端启动的服务:
type BackgroundService interface {
Run(ctx context.Context) error
}
需要数据库注册的服务:
type DatabaseMigrator interface {
AddMigration(mg *migrator.Migrator)
}
需要根据配置决定是否启动的服务:
type CanBeDisabled interface {
IsDisabled() bool
}
在具体使用的时候,根据判断这个 Service 是否符合某个接口进行判断。
service, ok := svc.Instance.(registry.BackgroundService)
if !ok {
continue
}
这样做的优雅之处就在于在具体定义 Service 的时候就灵活很多了。不会定义很多无用的方法实现。
这个也是 golang 鸭子类型的好处。
Service 的依赖
这里还有一个麻烦的地方,Service 之间是有互相依赖的。比如 sqlstore.SQLStore 这个服务,是负责数据存储的。它会在很多服务中用到,比如用户权限认证的时候,需要去数据存储中获取用户信息。那么这里如果在每个 Service 初始化的时候进行实例化,也是颇为痛苦的事情。
grafana 使用的是 facebook 的 inject.Graph 包处理这种依赖的问题的。https://github.com/facebookarchive/inject。
这个 inject 包使用的是依赖注入的解决方法,把一堆实例化的实例放进包里面,然后使用反射技术,对于一些结构中有指定 tag 标签的字段,就会把对应的实例注入进去。
比如 grafana 中的:
type UserAuthTokenService struct {
SQLStore *sqlstore.SQLStore `inject:""`
ServerLockService *serverlock.ServerLockService `inject:""`
Cfg *setting.Cfg `inject:""`
log log.Logger
}
这里可以看到 SQLStore 中有额外的注入 tag。那么在 pkg/server/server.go 中的
services := registry.GetServices()
if err := s.buildServiceGraph(services); err != nil {
return err
}
这里会把所有的 Service (包括这个 UserAuthTokenService) 中的 inject 标签标记的字段进行依赖注入。
这样就完美解决了 Service 的依赖问题。
Service 的运行
Service 的运行在 grafana 中使用的是 errgroup, 这个包是 "golang.org/x/sync/errgroup"。
使用这个包,不仅仅可以并行 go 执行 Service,也能获取每个 Service 返回的 error,在最后 Wait 的时候返回。
大体代码如下:
s.childRoutines.Go(func() error {
...
err := service.Run(s.context)
...
})
}
defer func() {
if waitErr := s.childRoutines.Wait(); waitErr != nil && !errors.Is(waitErr, context.Canceled) {
s.log.Error("A service failed", "err", waitErr)
if err == nil {
err = waitErr
}
}
}()
总结
理解了 Service 机制之后,grafana 的主流程就很简单明了了。如图所示。当然,这个只是 grafana 的主体流程,它的每个 Service 的具体实现还有待研究。
Original: https://www.cnblogs.com/yjf512/p/14169141.html
Author: 轩脉刃
Title: grafana 的主体架构是如何设计的?
相关阅读
Title: UVA12130 Summits(BFS + 贪心)
UVA12130 Summits(BFS + 贪心)
题目大意:
给你一个h ∗ w 的矩阵,矩阵的每一个元素都有一个值,代表这个位置的高度。
题目要求你找出这个图中有多少个位置是峰值点。从每一个点(高度H)出发遍历这个图有一个要求。就是走过的点的高度不能小于等于H - d;成为峰值点的要求就是从这个点出发走到的位置不能有高度大于H的。
解题思路:
由于图非常大。用dfs肯定不行。将这些点依照高度从大到小的排序。然后每一个点作为起点来遍历,假设找到比这个点大的点就说明不是峰值点。
而且遍历的过程中就会将途中走过的点标记上它能到的最大高度。假设下次要找的这个点已经被标记过了。就说明这个点能够到达更大的高度。肯定不是峰值点,就不须要遍历。
代码:
<span class="hljs-preprocessor">#<span class="hljs-keyword">include</span> <cstdio></cstdio></span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">include</span> <cstring></cstring></span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">include</span> <algorithm></algorithm></span>
<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> maxn = <span class="hljs-number">505</span>;
<span class="hljs-keyword">int</span> G[maxn][maxn];
<span class="hljs-keyword">int</span> vis[maxn][maxn];
<span class="hljs-keyword">int</span> n, m, d;
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> dir[<span class="hljs-number">4</span>][<span class="hljs-number">2</span>] = {{<span class="hljs-number">0</span>, -<span class="hljs-number">1</span>}, {<span class="hljs-number">0</span>, <span class="hljs-number">1</span>}, {-<span class="hljs-number">1</span>, <span class="hljs-number">0</span>}, {<span class="hljs-number">1</span>, <span class="hljs-number">0</span>}};
<span class="hljs-keyword">struct</span> Node {
<span class="hljs-keyword">int</span> x, y, v;
}node[maxn * maxn], q[maxn * maxn];
<span class="hljs-keyword">int</span> cmp (<span class="hljs-keyword">const</span> Node &a, <span class="hljs-keyword">const</span> Node &b) {
<span class="hljs-keyword">return</span> a.v > b.v;
}
<span class="hljs-keyword">int</span> BFS (<span class="hljs-keyword">int</span> k) {
<span class="hljs-keyword">int</span> front , rear;
<span class="hljs-keyword">int</span> cnt = <span class="hljs-number">1</span>;
front = <span class="hljs-number">0</span>;
rear = <span class="hljs-number">1</span>;
q[front] = node[k];
vis[node[k].x][node[k].y] = node[k].v;
<span class="hljs-keyword">bool</span> flag = <span class="hljs-number">1</span>;
<span class="hljs-keyword">while</span> (front < rear) {
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">4</span>; i++) {
<span class="hljs-keyword">int</span> newx = q[front].x + dir[i][<span class="hljs-number">0</span>];
<span class="hljs-keyword">int</span> newy = q[front].y + dir[i][<span class="hljs-number">1</span>];
<span class="hljs-keyword">if</span> (newx < <span class="hljs-number">0</span> || newx >= n || newy < <span class="hljs-number">0</span> || newy >= m)
<span class="hljs-keyword">continue</span>;
<span class="hljs-keyword">if</span> (G[newx][newy] > node[k].v) {
flag = <span class="hljs-number">0</span>;
<span class="hljs-keyword">continue</span>;
}
<span class="hljs-keyword">if</span> (vis[newx][newy] == node[k].v || node[k].v - G[newx][newy] >= d)
<span class="hljs-keyword">continue</span>;
vis[newx][newy] = node[k].v;
<span class="hljs-keyword">if</span> (G[newx][newy] == node[k].v)
cnt++;
<span class="hljs-keyword">else</span>
G[newx][newy] = node[k].v;
q[rear].x = newx;
q[rear++].y = newy;
}
front++;
}
<span class="hljs-keyword">if</span> (flag)
<span class="hljs-keyword">return</span> cnt;
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
<span class="hljs-keyword">int</span> main () {
<span class="hljs-keyword">int</span> T;
<span class="hljs-built_in">scanf</span> (<span class="hljs-string">"%d"</span>, &T);
<span class="hljs-keyword">while</span> (T--) {
<span class="hljs-built_in">scanf</span> (<span class="hljs-string">"%d%d%d"</span>, &n, &m, &d);
<span class="hljs-keyword">int</span> cnt = <span class="hljs-number">0</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < n; i++)
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j < m; j++) {
<span class="hljs-built_in">scanf</span> (<span class="hljs-string">"%d"</span>, &G[i][j]);
node[cnt].x = i;
node[cnt].y = j;
node[cnt++].v = G[i][j];
}
sort (node, node + cnt, cmp);
<span class="hljs-built_in">memset</span> (vis, -<span class="hljs-number">1</span>, <span class="hljs-keyword">sizeof</span> (vis));
<span class="hljs-keyword">int</span> ans = <span class="hljs-number">0</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < cnt; i++) {
<span class="hljs-keyword">if</span> (vis[node[i].x][node[i].y] == -<span class="hljs-number">1</span>) {
ans += BFS(i);
}
}
<span class="hljs-built_in">printf</span> (<span class="hljs-string">"%d\n"</span>, ans);
}
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
Original: https://www.cnblogs.com/hrhguanli/p/5121133.html
Author: hrhguanli
Title: UVA12130 Summits(BFS + 贪心)