美文网首页
Skywalking源码分析之OAP

Skywalking源码分析之OAP

作者: pq217 | 来源:发表于2023-09-27 15:20 被阅读0次

    概述

    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",
            {
                ...
    

    相关文章

      网友评论

          本文标题:Skywalking源码分析之OAP

          本文链接:https://www.haomeiwen.com/subject/hnukbdtx.html