Skywalking源码分析之OAP

概述

OAP是skywalking的服务端,负责接受探针上报的数据、提供数据分析的接口等,本文重点描述OAP如何为Web展示界面提供接口

armeria

skywalking 使用微框架armeria处理http请求及路由转发

armeria顶替的是tomcat或者spring-boot-web的功能

构建armeria的位置:HTTPServer, 初始化代码如下:

public void initialize() {
    final String contextPath = StringUtils.stripEnd(config.getContextPath(), "/");
    // 初始化Armeria
    sb = com.linecorp.armeria.server.Server
        .builder()
        .serviceUnder(contextPath + "/docs", DocService.builder().build())
        .workerGroup(config.getMaxThreads())
        .http(new InetSocketAddress( // http请求
            config.getHost(), // Socket host
            config.getPort() // Socket port
        ))

整个web项目只提供一个接口,即Post graphql, 并把请求转至graphql服务, 代码在GraphQLQueryHandler

@Blocking
@Post("/graphql")
public HttpResponse graphql(
    final ServiceRequestContext ctx,
    final HttpRequest req) throws Exception {
    return graphqlService.serve(ctx, req);
}

graphql

graphql是一种用于API的查询语言,skywalking使用它实现同一接口获取不同资源,根据参数描述所需资源的功能

graphql顶替的是restful协议

传统项目使用spring-web实现restful,skywalking使用graphql-java实现graphql

语言

graphql使用schema文件(文件后缀为.graphqls)来进行资源描述,文件的内容使用的就是graphql语言

后端开发人员使用schema文件描述系统提供的资源、数据,并描述出资源接受的参数类型即返回的数据类型、字段描述,类似于接口文档(使用graphql就不需要接口文档了)

前端人员通过查看schema文件就知道后端提供了什么资源,可以通过唯一的一个请求接口传递不同的参数描述需要什么资源,需要返回值中的哪些字段等

一个简单的schema文件例子

type User {
    id: Long!
    name: String!
    age: Int
    role: Role
}

type Role {
    id: Long!
    name: String!
}

extend type Query {
    listUsers(name: String): [User!]!
    listRoles(name: String): [Role!]!
}

该文件使用graphql语言编写,意思是后端提供两个列表查询资源,一个是用户列表:listUsers,一个是角色列表:listRoles

其中用户列表接受name参数,类型是String,返回是一个User数组,[]代表数组,且数组元素和数组本身都不为空, !代表不为空,其中User对象的字段在type User定义,包含id,name,age,role四个字段,其中id和name不为空,role字段返回的是一个type Role对象

此时即可发起如下请求来获取用户资源:

{
    "query": "query getUsers($name: String) {
      users : listUsers(name: $name) {
        value: name
        role
      }
    }",
    "variables": {
        "name": "wmf"
    }
}

解释

1.query 代表请求描述

  • query getUsers($name: String) 这只是对请求的一个描述,其中getUsers应该是请求的名字,($name: String)是对资源参数一个描述,需要与schema文件一致
  • users: listUsers 指定请求资源,对应schema文件中的listUsers, users对请求数组的key起的别名,默认就是资源名
  • value: name, role,描述请求只要name、role两个字段,其中name重命名为value

2.variables 即变量传参

  • name: 即变量名,后面的value就是参数值

java

skywalking 使用graphql-java完成graphql到方法的映射,相关代码在server-query-plugin.query-graphql-plugin模块下,使用如下:

schema文件

文件位置在 server-query-plugin\query-graphql-plugin\resources\query-protocol文件夹下
文件以.graphqls结尾,如

  • common.graphqls 通用资源描述
  • metadata-v2.graphqls 元数据资源描述
  • trace.graphqls 链路跟踪资源描述

与java方法对应

每个schema文件描述了多个资源Query,如:listServices(获取所有服务), findService(搜索服务),这些资源与实际的java代码相对应,即请求指定该资源java就执行对应方法返回数据

Query资源的映射代码写在GraphQLQueryProvider中,通过schema文件文件名与java类进行匹配(方法自动按名称匹配), 如下

 schemaBuilder.file("query-protocol/common.graphqls")
                .resolvers(new Query(), new Mutation(), new HealthQuery(getManager()))
                ...省略
                .file("query-protocol/metadata-v2.graphqls")
                .resolvers(new MetadataQueryV2(getManager()))
                ...
                .scalars(ExtendedScalars.GraphQLLong);

代码中metadata-v2.graphqls文件对应到MetadataQueryV2类,metadata-v2.graphqls包含如下描述

extend type Query {
    listLayers: [String!]!
    listServices(layer: String!): [Service!]!
    ...

两个资源对应MetadataQueryV2类的如下方法(名称匹配)

public Set<String> listLayers() throws IOException {
    return getMetadataQueryService().listLayers();
}

public List<Service> listServices(final String layer) throws IOException {
    return getMetadataQueryService().listServices(layer, null);
}

skywalking通过graphql-java完成schema与java类的自动匹配

架构

以获取服务列表为例:

Query层(server-query-plugin)

上面这些与schema文件对应的类,如MetadataQueryV2,就类似传统MVC框架中的Controller

Service层(server-core)

Query层接受请求后会转交给Service层处理

public List<Service> listServices(final String layer) throws IOException {
    return getMetadataQueryService().listServices(layer, null);
}
Dao层(server-storage-plugin)

Service层通过调用Dao层完成对数据库的操作,如下

public List<Service> listServices(final String layer, final String group) throws IOException {
    return this.combineServices(getMetadataQueryDAO().listServices(layer, group));
}

Dao层最终获取数据的方式就是拼装sql去调用数据库(没有使用ORM框架),链接池使用了Hikari

容器

skywalking没有使用spring容器,而是自己实现了一个简单容器,实际上就是一个HashMap作为对象容器:ModuleManager,调用它的provider()方法可获取ModuleProvider,容器就定义在这个对象里,如下

private final Map<Class<? extends Service>, Service> services = new HashMap<>();

当想获取某个对象时,与spring一样不需new,而是调用调用ModuleProvider的getService(Class)方法,如下

this.metadataQueryDAO = moduleManager.find(StorageModule.NAME).provider().getService(IMetadataQueryDAO.class);

Query>Service>Dao层就是通过容器来获取下层对象,初始化时手动注入到本对象的属性中,以便请求到达时并执行下层方法

vue

前端基于VUE3,TS,请求的格式即graphql语言,没有使用特殊工具,只是简单的使用axios发起请求

但graphql的相关工具自己也有封装,即graphql类:index.ts

class Graphql {
  private queryData = "";
  public query(queryData: string) {
    this.queryData = queryData;
    return this;
  }
  public params(variablesData: unknown): AxiosPromise<void> {
    return axios // 使用axios
      .post(
        "/graphql",
        {
            ...
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容