您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

再见,ELK

时间:2020-12-23 14:49:43  来源:  作者:

Loki 作为一个新兴的日志解决方案,现在越来越受到关注。经过调研比较,我们正在将日志服务底层逐步从 ES 替换为 Loki 。

再见,ELK

图片来自 Pexels

本文基于我们对 Loki 的使用和理解,从它产生的背景、解决的问题、采用的方案、系统架构、实现逻辑等做一些剖析,希望对关注 Loki 的小伙伴们提供一些帮助。

在日常的系统可视化监控过程中,当监控探知到指标异常时,我们往往需要对问题的根因做出定位。

但监控数据所暴露的信息是提前预设、高度提炼的,在信息量上存在着很大的不足,它需要结合能够承载丰富信息的日志系统一起使用。

当监控系统探知到异常告警,我们通常在 Dashboard 上根据异常指标所属的集群、主机、实例、应用、时间等信息圈定问题的大致方向,然后跳转到日志系统做更精细的查询,获取更丰富的信息来最终判断问题根因。

在如上流程中,监控系统和日志系统往往是独立的,使用方式具有很大差异。比如监控系统 Prometheus 比较受欢迎,日志系统多采用 ES+Kibana 。

他们具有完全不同的概念、不同的搜索语法和界面,这不仅给使用者增加了学习成本,也使得在使用时需在两套系统中频繁做上下文切换,对问题的定位迟滞。

此外,日志系统多采用全文索引来支撑搜索服务,它需要为日志的原文建立反向索引,这会导致最终存储数据相较原始内容成倍增长,产生不可小觑的存储成本。

并且,不管数据将来是否会被搜索,都会在写入时因为索引操作而占用大量的计算资源,这对于日志这种写多读少的服务无疑也是一种计算资源的浪费。

Loki 则是为了应对上述问题而产生的解决方案,它的目标是打造能够与监控深度集成、成本极度低廉的日志系统。

Loki 日志方案

低使用成本

①数据模型

在数据模型上,Loki 参考了 Prometheus ,数据由标签、时间戳、内容组成,所有标签相同的数据属于同一日志流:

  • 标签,描述日志所属集群、服务、主机、应用、类型等元信息, 用于后期搜索服务。
  • 时间戳,日志的产生时间。
  • 内容,日志的原始内容。

具有如下结构:

{ 
  "stream": {  
    "label1": "value1", 
    "label1": "value2" 
  }, # 标签 
  "values": [ 
    ["<timestamp nanoseconds>","log content"], # 时间戳,内容 
    ["<timestamp nanoseconds>","log content"] 
  ] 
} 

Loki 还支持多租户,同一租户下具有完全相同标签的日志所组成的集合称为一个日志流。

在日志的采集端使用和监控时序数据一致的标签,这样在可以后续与监控系统结合时使用相同的标签,也为在 UI 界面中与监控结合使用做快速上下文切换提供数据基础。

LogQL:Loki 使用类似 Prometheus 的 PromQL 的查询语句 logQL ,语法简单并贴近社区使用习惯,降低用户学习和使用成本。

语法例子如下:

{file="debug.log""} |= "err" 

流选择器:{label1="value1", label2="value2"}, 通过标签选择日志流, 支持等、不等、匹配、不匹配等选择方式。过滤器:|= "err",过滤日志内容,支持包含、不包含、匹配、不匹配等过滤方式。

这种工作方式类似于 find+grep,find 找出文件,grep 从文件中逐行匹配:

find . -name "debug.log" | grep err 

logQL 除支持日志内容查询外,还支持对日志总量、频率等聚合计算。

Grafana:在 Grafana 中原生支持 Loki 插件,将监控和日志查询集成在一起,在同一 UI 界面中可以对监控数据和日志进行 side-by-side 的下钻查询探索,比使用不同系统反复进行切换更直观、更便捷。

再见,ELK

 

此外,在 Dashboard 中可以将监控和日志查询配置在一起,这样可同时查看监控数据走势和日志内容,为捕捉可能存在的问题提供更直观的途径。

低存储成本

只索引与日志相关的元数据标签,而日志内容则以压缩方式存储于对象存储中, 不做任何索引。

相较于 ES 这种全文索引的系统,数据可在十倍量级上降低,加上使用对象存储,最终存储成本可降低数十倍甚至更低。

方案不解决复杂的存储系统问题,而是直接应用现有成熟的分布式存储系统,比如 S3、GCS、Cassandra、BigTable 。

Loki 架构

整体上 Loki 采用了读写分离的架构,由多个模块组成:

  • Promtail、Fluent-bit、Fluentd、Rsyslog 等开源客户端负责采集并上报日志。
  • Distributor:日志写入入口,将数据转发到 Ingester。
  • Ingester:日志的写入服务,缓存并写入日志内容和索引到底层存储。
  • Querier:日志读取服务,执行搜索请求。
  • QueryFrontend:日志读取入口,分发读取请求到 Querier 并返回结果。
  • Cassandra/BigTable/DnyamoDB/S3/GCS:索引、日志内容底层存储。
  • Cache:缓存,支持 redis/Memcache/本地 Cache。

其主体结构如下图所示:

再见,ELK

 

Distributor:作为日志写入的入口服务,其负责对上报数据进行解析、校验与转发。

它将接收到的上报数解析完成后会进行大小、条目、频率、标签、租户等参数校验,然后将合法数据转发到 Ingester 服务,其在转发之前最重要的任务是确保同一日志流的数据必须转发到相同 Ingester 上,以确保数据的顺序性。

Hash 环:Distributor 采用一致性哈希与副本因子相结合的办法来决定数据转发到哪些 Ingester 上。

Ingester 在启动后,会生成一系列的 32 位随机数作为自己的 Token ,然后与这一组 Token 一起将自己注册到 Hash 环中。

在选择数据转发目的地时,Distributor 根据日志的标签和租户 ID 生成 Hash,然后在 Hash 环中按 Token 的升序查找第一个大于这个 Hash 的 Token ,这个 Token 所对应的 Ingester 即为这条日志需要转发的目的地。

如果设置了副本因子,顺序的在之后的 Token 中查找不同的 Ingester 做为副本的目的地。

Hash 环可存储于 etcd、consul 中。另外 Loki 使用 Memberlist 实现了集群内部的 KV 存储,如不想依赖 etcd 或 consul ,可采用此方案。

输入输出:Distributor 的输入主要是以 HTTP 协议批量的方式接受上报日志,日志封装格式支持 JSON 和 PB ,数据封装结构:

[ 
  { 
   "stream": {  
     "label1": "value1", 
     "label1": "value2" 
   }, 
   "values": [ 
     ["<timestamp nanoseconds>","log content"], 
     ["<timestamp nanoseconds>","log content"] 
   ] 
  }, 
  ...... 
] 

Distributor 以 grpc 方式向 ingester 发送数据,数据封装结构:

{ 
  "streams": [ 
    { 
      "labels": "{label1=value1, label2=value2}", 
      "entries": [ 
          {"ts": <unix epoch in nanoseconds>, "line:":"<log line>" }, 
          {"ts": <unix epoch in nanoseconds>, "line:":"<log line>" }, 
      ] 
    } 
    .... 
   ] 
} 

①Ingester

作为 Loki 的写入模块,Ingester 主要任务是缓存并写入数据到底层存储。根据写入数据在模块中的生命周期,ingester 大体上分为校验、缓存、存储适配三层结构。

②校验

Loki 有个重要的特性是它不整理数据乱序,要求同一日志流的数据必须严格遵守时间戳单调递增顺序写入。

所以除对数据的长度、频率等做校验外,至关重要的是日志顺序检查。

Ingester 对每个日志流里每一条日志都会和上一条进行时间戳和内容的对比,策略如下:

  • 与上一条日志相比,本条日志时间戳更新,接收本条日志。
  • 与上一条日志相比,时间戳相同内容不同,接收本条日志。
  • 与上一条日志相比,时间戳和内容都相同,忽略本条日志。
  • 与上一条日志相比,本条日志时间戳更老,返回乱序错误。

③缓存

日志在内存中的缓存采用多层树形结构对不同租户、日志流做出隔离。同一日志流采用顺序追加方式写入分块:

  • Instances:以租户的 userID 为键 Instance 为值的 Map 结构。
  • Instance:一个租户下所有日志流(stream)的容器。
  • Streams:以日志流的指纹(streamFP)为键,Stream 为值的 Map 结构。
  • Stream:一个日志流所有 Chunk 的容器。
  • Chunks:Chunk 的列表。
  • Chunk:持久存储读写最小单元在内存态的结构。
  • Block:Chunk 的分块,为已压缩归档的数据。
  • HeadBlock:尚在开放写入的分块。
  • Entry:单条日志单元,包含时间戳(timestamp)和日志内容(line)

整体结构如下:

再见,ELK

 

Chunks:在向内存写入数据前,ingester 首先会根据租户ID(userID)和由标签计算的指纹(streamPF)定位到日志流(stream)及 Chunks。

Chunks 由按时间升序排列的 chunk 组成,最后一个 chunk 接收最新写入的数据,其他则等刷写到底层存储。

当最后一个 chunk 的存活时间或数据大小超过指定阈值时,Chunks 尾部追加新的 chunk 。

Chunk:Chunk 为 Loki 在底层存储上读写的最小单元在内存态下的结构。其由若干 block 组成,其中 headBlock 为正在开放写入的 block ,而其他 Block 则已经归档压缩的数据。

Block:Block 为数据的压缩单元,目的是为了在读取操作那里避免因为每次解压整个 Chunk 而浪费计算资源,因为很多情况下是读取一个 chunk 的部分数据就满足所需数据量而返回结果了。

Block 存储的是日志的压缩数据,其结构为按时间顺序的日志时间戳和原始内容,压缩可采用 gzip、snAppy 、lz4 等方式。

HeadBlock:正在接收写入的特殊 block ,它在满足一定大小后会被压缩归档为 Block ,然后新 headBlock 会被创建。

存储适配:由于底层存储要支持 S3、Cassandra、BigTable、DnyamoDB 等系统,适配层将各种系统的读写操作抽象成统一接口,负责与他们进行数据交互。

④输出

Loki 以 Chunk 为单位在存储系统中读写数据。在持久存储态下的 Chunk 具有如下结构:

  • meta:封装 chunk 所属 stream 的指纹、租户 ID,开始截止时间等元信息。
  • data:封装日志内容,其中一些重要字段。
  • encode 保存数据的压缩方式。
  • block-N bytes 保存一个 block 的日志数据。
  • #blocks section byte offset 单元记录 #block 单元的偏移量。
  • #block 单元记录一共有多少个 block。
  • #entries 和 block-N bytes 一一对应,记录每个 block 里有日式行数、时间起始点,blokc-N bytes 的开始位置和长度等元信息。
再见,ELK

 

Chunk 数据的解析顺序:

  • 根据尾部的 #blocks section byte offset 单元得到 #block 单元的位置。
  • 根据 #block 单元记录得出 chunk 里 block 数量。
  • 从 #block 单元所在位置开始读取所有 block 的 entries、mint、maxt、offset、len 等元信息。
  • 顺序的根据每个 block 元信息解析出 block 的数据。

⑤索引

Loki 只索引了标签数据,用于实现标签→日志流→Chunk 的索引映射, 以分表形式在存储层存储。

表结构如下:

CREATE TABLE IF NOT EXISTS Table_N ( 
    hash text, 
    range blob, 
    value blob, 
    PRIMARY KEY (hash, range) 
 ) 

Table_N,根据时间周期分表名;hash, 不同查询类型时使用的索引;range,范围查询字段;value,日志标签的值。

数据类型:Loki 保存了不同类型的索引数据用以实现不同映射场景,对于每种类型的映射数据,Hash/Range/Value 三个字段的数据组成如下图所示:

再见,ELK

 

seriesID 为日志流 ID,shard 为分片,userID 为租户 ID,labelName 为标签名,labelValueHash 为标签值 hash,chunkID 为 chunk 的 ID,chunkThrough 为 chunk 里最后一条数据的时间这些数据元素在映射过程中的作用在 Querier 环节的[查询流程]((null))做详细介绍。

上图中三种颜色标识的索引类型从上到下分别为:

  • 数据类型 1:用于根据用户 ID 搜索查询所有日志流的 ID。
  • 数据类型 2:用于根据用户 ID 和标签查询日志流的 ID。
  • 数据类型 3:用于根据日志流 ID 查询底层存储 Chunk 的 ID。

除了采用分表外,Loki 还采用分桶、分片的方式优化索引查询速度。

分桶:

再见,ELK

 

以天分割:bucketID = timestamp / secondsInDay。

以小时分割:bucketID = timestamp / secondsInHour。

分片:将不同日志流的索引分散到不同分片,shard = seriesID% 分片数。

Chunk 状态:Chunk 作为在 Ingester 中重要的数据单元,其在内存中的生命周期内分如下四种状态:

  • Writing:正在写入新数据。
  • Waiting flush:停止写入新数据,等待写入到存储。
  • Retain:已经写入存储,等待销毁。
  • Destroy:已经销毁。

四种状态之间的转换以 writing→waiting flush→retain→destroy 顺序进行。

状态转换时机:

  • 协作触发:有新的数据写入请求。
  • 定时触发:刷写周期触发将 chunk 写入存储,回收周期触发将 chunk 销毁。

writing 转为 waiting flush:chunk 初始状态为 writing,标识正在接受数据的写入,满足如下条件则进入到等待刷写状态:

  • chunk 空间满(协作触发)。
  • chunk 的存活时间(首末两条数据时间差)超过阈值 (定时触发)。
  • chunk 的空闲时间(连续未写入数据时长)超过设置 (定时触发)。

waiting flush 转为 etain:Ingester 会定时的将等待刷写的 chunk 写到底层存储,之后这些 chunk 会处于”retain“状态,这是因为 ingester 提供了对最新数据的搜索服务,需要在内存里保留一段时间,retain 状态则解耦了数据的刷写时间以及在内存中的保留时间,方便视不同选项优化内存配置。

destroy,被回收等待 GC 销毁:总体上,Loki 由于针对日志的使用场景,采用了顺序追加方式写入,只索引元信息,极大程度上简化了它的数据结构和处理逻辑,这也为 Ingester 能够应对高速写入提供了基础。

Querier:查询服务的执行组件,其负责从底层存储拉取数据并按照 LogQL 语言所描述的筛选条件过滤。它可以直接通过 API 提供查询服务,也可以与 queryFrontend 结合使用实现分布式并发查询。

⑥查询类型

查询类型如下:

  • 范围日志查询
  • 单日志查询
  • 统计查询
  • 元信息查询

在这些查询类型中,范围日志查询应用最为广泛,所以下文只对范围日志查询做详细介绍。

并发查询:对于单个查询请求,虽然可以直接调用 Querier 的 API 进行查询,但很容易会由于大查询导致 OOM,为应对此种问题 querier 与 queryFrontend 结合一起实现查询分解与多 querier 并发执行。

再见,ELK

 

每个 querier 都与所有 queryFrontend 建立 grpc 双向流式连接,实时从 queryFrontend 中获取已经分割的子查询求,执行后将结果发送回 queryFrontend。

具体如何分割查询及在 querier 间调度子查询将在 queryFrontend 环节介绍。

⑧查询流程

先解析 logQL 指令,然后查询日志流 ID 列表。

Loki 根据不同的标签选择器语法使用了不同的索引查询逻辑,大体分为两种:

=,或多值的正则匹配=~,工作过程如下:

以类似下 SQL 所描述的语义查询出标签选择器里引用的每个标签键值对所对应的日志流 ID(seriesID)的集合。

SELECT * FROM Table_N WHERE hash=? AND range>=?    AND value=labelValue 

hash 为租户 ID(userID)、分桶(bucketID)、标签名(labelName)组合计算的哈希值;range 为标签值(labelValue)计算的哈希值。

将根据标签键值对所查询的多个 seriesID 集合取并集或交集求最终集合。

比如,标签选择器{file="app.log", level=~"debug|error"}的工作过程如下:

  • 查询出 file="app.log",level="debug", level="error" 三个标签键值所对应的 seriesID 集合,S1 、S2、S3。
  • 根据三个集合计算最终 seriesID 集合 S = S1∩cap (S2∪S3)。

!=,=~,!~,工作过程如下:

以如下 SQL 所描述的语义查询出标签选择器里引用的每个标签所对应 seriesID 集合。

SELECT * FROM Table_N WHERE hash = ? 

hash 为租户 ID(userID)、分桶(bucketID)、标签名(labelName)。

根据标签选择语法对每个 seriesID 集合进行过滤。

将过滤后的集合进行并集、交集等操作求最终集合。

比如,{file~="MySQL*", level!="error"} 的工作过程如下:

  • 查询出标签“file”和标签"level"对应的 seriesID 的集合,S1、S2。
  • 求出 S1 中 file 的值匹配 mysql*的子集 SS1,S2 中 level 的值!="error"的子集 SS2。
  • 计算最终 seriesID 集合 S = SS1∩SS2。

以如下 SQL 所描述的语义查询出所有日志流所包含的 chunk 的 ID:

SELECT * FROM Table_N Where hash = ? 

hash 为分桶(bucketID)和日志流(seriesID)计算的哈希值。

根据 chunkID 列表生成遍历器来顺序读取日志行:遍历器作为数据读取的组件,其主要功能为从存储系统中拉取 chunk 并从中读取日志行。其采用多层树形结构,自顶向下逐层递归触发方式弹出数据。

再见,ELK

 

具体结构如上图所示:

  • batch Iterator:以批量的方式从存储中下载 chunk 原始数据,并生成 iterator 树。
  • stream Iterator:多个 stream 数据的遍历器,其采用堆排序确保多个 stream 之间数据的保序;
  • chunks Iterator:多个 chunk 数据的遍历器,同样采用堆排序确保多个 chunk 之间保序及多副本之间的去重。
  • blocks Iterator:多个 block 数据的遍历器。
  • block bytes Iterator:block 里日志行的遍历器。

从 Ingester 查询在内存中尚未写入到存储中的数据:由于 Ingester 是定时的将缓存数据写入到存储中,所以 Querier 在查询时间范围较新的数据时,还会通过 grpc 协议从每个 ingester 中查询出内存数据。

需要在 ingester 中查询的时间范围是可配置的,视 ingester 缓存数据时长而定。

上面是日志内容查询的主要流程。至于指标查询的流程与其大同小异,只是增加了指标计算的遍历器层用于从查询出的日志计算指标数据。其他两种则更为简单,这里不再详细展开。

QueryFrontend:Loki 对查询采用了计算后置的方式,类似于在大量原始数据上做 grep,所以查询势必会消耗比较多的计算和内存资源。

如果以单节点执行一个查询请求的话很容易因为大查询造成 OOM、速度慢等性能瓶颈。

为解决此问题,Loki 采用了将单个查询分解在多个 querier 上并发执行方式,其中查询请求的分解和调度则由 queryFrontend 完成。

再见,ELK

 

queryFrontend 在 Loki 的整体架构上处于 querier 的前端,它作为数据读取操作的入口服务,其主要的组件及工作流程如上图所示:

  • 分割 Request:将单个查询分割成子查询 subReq 的列表。
  • Feeder:将子查询顺序注入到缓存队列 Buf Queue。
  • Runner:多个并发的运行器将 Buf Queue 中的查询并注入到子查询队列,并等待返回查询结果。
  • Querier 通过 grpc 协议实时从子查询队列弹出子查询,执行后将结果返回给相应的 Runner。
  • 所有子请求在 Runner 执行完毕后汇总结果返回 API 响应。

⑨查询分割

queryFrontend 按照固定时间跨度将查询请求分割成多个子查询。比如,一个查询的时间范围是 6 小时,分割跨度为 15 分钟,则查询会被分为 6*60/15=24 个子查询。

⑩查询调度

Feeder:Feeder 负责将分割好的子查询逐一的写入到缓存队列 Buf Queue,以生产者/消费者模式与下游的 Runner 实现可控的子查询并发。

Runner:从 Buf Queue 中竞争方式读取子查询并写入到下游的请求队列中,并处理来自 Querier 的返回结果。

Runner 的并发个数通过全局配置控制,避免因为一次分解过多子查询而对 Querier 造成巨大的徒流量,影响其稳定性。

子查询队列:队列是一个二维结构,第一维存储的是不同租户的队列,第二维存储同一租户子查询列表,它们都是以 FIFO 的顺序组织里面的元素的入队出队。

分配请求:queryFrontend 是以被动方式分配查询请求,后端 Querier 与 queryFrontend 实时的通过 grpc 监听子查询队列,当有新请求时以如下顺序在队列中弹出下一个请求:

  • 以循环的方式遍历队列中的租户列表,寻找下一个有数据的租户队列。
  • 弹出该租户队列中的最老的请求。

总结

Loki 作为一个正在快速发展的项目,最新版本已到 2.0,相较 1.6 增强了诸如日志解析、Ruler、Boltdb-shipper 等新功能,不过基本的模块、架构、数据模型、工作原理上已处于稳定状态。

希望本文的这些尝试性的剖析能够能够为大家提供一些帮助,如文中有理解错误之处,欢迎批评指正。

作者:张海军

编辑:陶家龙

出处:转载自公众号京东智联云开发者(ID:JDC_Developers)



Tags:ELK   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
Grafana Loki 是一个日志聚合工具,它是功能齐全的日志堆栈的核心。图片来自 包图网先看看结果有多轻量吧: Loki 是一个为有效保存日志数据而优化的数据存储。日志数据的高效索...【详细内容】
2021-09-14  Tags: ELK  点击:(97)  评论:(0)  加入收藏
在我们的服务器上通常会生成各种日志文件,比如系统日志、 应用日志、安全日志。当系统发生故障时,工程师需要登录到服务器上,在日志里查找故障原因。如果定位到处理请求的服务...【详细内容】
2021-06-01  Tags: ELK  点击:(159)  评论:(0)  加入收藏
开始之前先了解一下什么是ELK ELK:大型分布式日志分析系统ELK elasticsearch(存储日志)+logstash(收集日志)+kibana(展示数据)Elasticsearch:Elasticsearch (ES)是一个基于Luce...【详细内容】
2021-04-30  Tags: ELK  点击:(186)  评论:(0)  加入收藏
本文介绍使用docker安装部署Filebeat与Elasticsearch、Logstash、Kibana(简称ELK)全家桶7.5.1。如果熟悉框架的话,直接copy配置文件与docker命令,简单删减和修改路径,即可快速启...【详细内容】
2021-03-22  Tags: ELK  点击:(428)  评论:(0)  加入收藏
Loki 作为一个新兴的日志解决方案,现在越来越受到关注。经过调研比较,我们正在将日志服务底层逐步从 ES 替换为 Loki 。图片来自 Pexels本文基于我们对 Loki 的使用和理解,从它...【详细内容】
2020-12-23  Tags: ELK  点击:(143)  评论:(0)  加入收藏
一般提到监控,很多人就会想到监控服务器运行状态,网络运行状态。其实由于业务需要,服务器和网络设备每时每刻产生的海量日志也同样的重要。 为什么选用ELK? 首先我们来了解一下E...【详细内容】
2020-11-18  Tags: ELK  点击:(134)  评论:(0)  加入收藏
本篇主要讲工作中的真实经历,我们怎么打造亿级日志平台,同时手把手教大家建立起这样一套亿级 ELK 系统。日志平台具体发展历程可以参考上篇 「从 ELK 到 EFK 演进」废话不多...【详细内容】
2020-07-27  Tags: ELK  点击:(55)  评论:(0)  加入收藏
最近,在对公司容器云的日志方案进行设计的时候,发现主流的ELK或者EFK比较重,再加上现阶段对于ES复杂的搜索功能很多都用不上最终选择了Grafana开源的Loki日志系统,下面介绍下Lok...【详细内容】
2020-06-15  Tags: ELK  点击:(88)  评论:(0)  加入收藏
前言起源许多年前,一个刚结婚的名叫 Shay Banon 的失业开发者,跟着他的妻子去了伦敦,他的妻子在那里学习厨师。 在寻找一个赚钱的工作的时候,为了给他的妻子做一个食谱搜索引擎,...【详细内容】
2020-06-13  Tags: ELK  点击:(43)  评论:(0)  加入收藏
本文以api.mingongge.com.cn域名为测试对象进行统计,日志为crm.mingongge.com.cn和risk.mingongge.com.cn请求之和(此二者域名不具生产换环境统计意义),生产环境请根据具体需...【详细内容】
2020-06-12  Tags: ELK  点击:(57)  评论:(0)  加入收藏
▌简易百科推荐
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(1)  评论:(0)  加入收藏
程序是如何被执行的&emsp;&emsp;程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(9)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(19)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(23)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(24)  评论:(0)  加入收藏
一个项目的大部分API,测试用例在参数和参数值等信息会有很多相似的地方。我们可以复制API,复制用例来快速生成,然后做细微调整既可以满足我们的测试需求1.复制API:在菜单发布单...【详细内容】
2021-12-14  AutoMeter    Tags:AutoMeter   点击:(20)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条