Code Monkey home page Code Monkey logo

blog's People

Contributors

yyt030 avatar

Watchers

 avatar

blog's Issues

influxDB学习

概述

InfluxDB是一个用于存储和分析时序数据的开源数据库,该数据库使用go开发,小巧灵活,依赖少,主要有以下几点特点:

  • 内置http接口,方便使用
  • 数据可打标记tag,查询灵活
  • 类SQL的查询语句
  • 安装简单,无依赖
  • 实时查询,读写效率非常高

安装

influxDB默认使用下面的网络端口:

  • TCP端口8086用作InfluxDB的客户端和服务端的http api通信
  • TCP端口8088给备份和恢复数据的RPC服务使用
yum install influxdb

使用指南

写数据

写数据有两种方式,一种使用CLI,一种使用HTTP restful API方式,重点介绍下HTTP

  • create db
curl -i -XPOST http://localhost:8086/query --data-urlencode "q=CREATE DATABASE mydb"
  • 写数据点
curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary 'cpu_load_short,host=server01,region=us-west value=0.64 1434055562000000000'

它的组成部分有measurement,tags,fields和timestamp。measurement是InfluxDB必须的,严格地说,tags是可选的,但是对于大部分数据都会包含tags用来区分数据的来源,让查询变得容易和高效。tag的key和value都必须是字符串。fields的key也是必须的,而且是字符串,默认情况下field的value是float类型的。timestamp在这个请求行的最后,是一个从1/1/1970 UTC开始到现在的一个纳秒级的Unix time,它是可选的,如果不传,InfluxDB会使用服务器的本地的纳米级的timestamp来作为数据的时间戳,注意无论哪种方式,在InfluxDB中的timestamp只能是UTC时间

  • 一次写多个数据点

要想同时发送多个数据点到多个series(在InfluxDB中measurement加tags组成了一个series),可以用新的行来分开这些数据点。这种批量发送的方式可以获得更高的性能

无模式设计

InfluxDB是一个无模式(schemaless)的数据库,你可以在任意时间添加measurement,tags和fields。注意:如果你试图写入一个和之前的类型不一样的数据(例如,filed字段之前接收的是数字类型,现在写了个字符串进去),那么InfluxDB会拒绝这个数据。+

HTTP返回值概要

  • 2xx:如果你写了数据后收到HTTP 204 No Content,说明写入成功了!
  • 4xx:表示InfluxDB不知道你发的是什么鬼。
  • 5xx:系统过载或是应用受损。

查询数据

http接口查询

curl -G 'http://localhost:8086/query?pretty=true' --data-urlencode "db=mydb" --data-urlencode "q=SELECT \"value\" FROM \"cpu_load_short\" WHERE \"region\"='us-west'"

数据采样和保留

InfluxDB每秒可以处理数十万的数据点。如果要长时间地存储大量的数据,对于存储会是很大的压力。一个很自然的方式就是对数据进行采样,对于高精度的裸数据存储较短的时间,而对于低精度的的数据可以保存得久一些甚至永久保存。

InfluxDB提供了两个特性——连续查询(Continuous Queries简称CQ)和保留策略(Retention Policies简称RP),分别用来处理数据采样和管理老数据的。

定义

  • Continuous Query (CQ)是在数据库内部自动周期性跑着的一个InfluxQL的查询,CQs需要在SELECT语句中使用一个函数,并且一定包括一个GROUP BY time()语句。
  • Retention Policy (RP)是InfluxDB数据架构的一部分,它描述了InfluxDB保存数据的时间。InfluxDB会比较服务器本地的时间戳和你数据的时间戳,并删除比你在RPs里面用DURATION设置的更老的数据。单个数据库中可以有多个RPs但是每个数据的RPs是唯一的。

查询语法

数据查询语法

select 子句

SELECT <field_key>[,<field_key>,<tag_key>] FROM <measurement_name>[,<measurement_name>]
  • 从单个measurement查询所有的field和tag
 SELECT * FROM "h2o_feet"
  • 从单个measurement中查询特定tag和field
 SELECT "level description","location","water_level" FROM "h2o_feet"
  • 从单个measurement中选择特定的tag和field,并提供其标识符类型
SELECT "level description"::field,"location"::tag,"water_level"::field FROM "h2o_feet"
  • 从单个measurement查询所有field
SELECT *::field FROM "h2o_feet"
  • 从measurement中选择一个特定的field并执行基本计算
SELECT ("water_level" * 2) + 4 from "h2o_feet"
  • 从多个measurement中查询数据
SELECT * FROM "h2o_feet","h2o_pH"
  • 从完全限定的measurement中选择所有数据
SELECT * FROM "NOAA_water_database"."autogen"."h2o_feet"

where 子句

  • 查询有特定field的key value的数据
SELECT * FROM "h2o_feet" WHERE "water_level" > 8
  • 查询有特定field的key value为字符串的数据
SELECT * FROM "h2o_feet" WHERE "level description" = 'below 3 feet'
  • 查询有特定field的key value并且带计算的数据
SELECT * FROM "h2o_feet" WHERE "water_level" + 2 > 11.9
  • 查询有特定tag的key value的数据
SELECT "water_level" FROM "h2o_feet" WHERE "location" = 'santa_monica'
  • 查询有特定tag的key value以及特定field的key value的数据
SELECT "water_level" FROM "h2o_feet" WHERE "location" <> 'santa_monica' AND (water_level < -0.59 OR water_level > 9.95)
  • 根据时间戳来过滤数据
SELECT * FROM "h2o_feet" WHERE time > now() - 7d

group by子句

GROUP BY子句后面可以跟用户指定的tags或者是一个时间间隔。

  • 对单个tag作group by
SELECT MEAN("water_level") FROM "h2o_feet" GROUP BY "location"
  • 对多个tag作group by
SELECT MEAN("index") FROM "h2o_quality" GROUP BY location,randtag
  • GROUP BY时间间隔
SELECT COUNT("water_level") FROM "h2o_feet" WHERE "location"='coyote_creek' AND time >= '2015-08-18T00:00:00Z' AND time <= '2015-08-18T00:30:00Z' GROUP BY time(12m)
SELECT COUNT("water_level") FROM "h2o_feet" WHERE time >= '2015-08-18T00:00:00Z' AND time <= '2015-08-18T00:30:00Z' GROUP BY time(12m),"location"

高级GROUP BY time()语法

SELECT <function>(<field_key>) FROM_clause WHERE <time_range> GROUP BY time(<time_interval>,<offset_interval>),[tag_key] [fill(<fill_option>)]
  • 查询结果间隔按18分钟group by,并将预设时间边界向前移动
SELECT "water_level" FROM "h2o_feet" WHERE "location"='coyote_creek' AND time >= '2015-08-18T00:00:00Z' AND time <= '2015-08-18T00:54:00Z'

SELECT MEAN("water_level") FROM "h2o_feet" WHERE "location"='coyote_creek' AND time >= '2015-08-18T00:06:00Z' AND time <= '2015-08-18T00:54:00Z' GROUP BY time(18m,6m)
SELECT MEAN("water_level") FROM "h2o_feet" WHERE "location"='coyote_creek' AND time >= '2015-08-18T00:06:00Z' AND time <= '2015-08-18T00:54:00Z' GROUP BY time(18m)
  • 查询结果按12分钟间隔group by,并将预设时间界限向后移动
SELECT MEAN("water_level") FROM "h2o_feet" WHERE "location"='coyote_creek' AND time >= '2015-08-18T00:06:00Z' AND time <= '2015-08-18T00:54:00Z' GROUP BY time(18m,-12m)
  • 查询结果按12分钟间隔group by,并将预设时间边界向前移动
SELECT COUNT("water_level") FROM "h2o_feet" WHERE "location"='coyote_creek' AND time >= '2015-08-18T00:06:00Z' AND time < '2015-08-18T00:18:00Z' GROUP BY time(12m,6m)

GROUP BY time()加fill()

默认情况下,没有数据的GROUP BY time()间隔返回为null作为输出列中的值。fill()更改不含数据的时间间隔返回的值。请注意,如果GROUP(ing)BY多个对象(例如,tag和时间间隔),那么fill()必须位于GROUP BY子句的末尾。
fill的参数

  • 任一数值:用这个数字返回没有数据点的时间间隔
  • linear:返回没有数据的时间间隔的线性插值结果。
  • none: 不返回在时间间隔里没有点的数据
  • previous:返回时间隔间的前一个间隔的数据
SELECT MEAN("tadpoles") FROM "pond" WHERE time >= '2016-11-11T21:00:00Z' AND time <= '2016-11-11T22:06:00Z' GROUP BY time(12m) fill(linear)

INTO子句

SELECT_clause INTO <measurement_name> FROM_clause [WHERE_clause] [GROUP_BY_clause]

正则表达式

InluxDB支持在以下场景使用正则表达式:

  • 在SELECT中的field key和tag key;
  • 在FROM中的measurement
  • 在WHERE中的tag value和字符串类型的field value
  • 在GROUP BY中的tag key
  1. 在SELECT中使用正则表达式指定field key和tag key
SELECT /l/ FROM "h2o_feet" LIMIT 1
  1. 在SELECT中使用正则表达式指定函数里面的field key
SELECT DISTINCT(/level/) FROM "h2o_feet" WHERE "location" = 'santa_monica' AND time >= '2015-08-18T00:00:00.000000000Z' AND time <= '2015-08-18T00:12:00Z'
  1. 在FROM中使用正则表达式指定measurement
SELECT MEAN("degrees") FROM /temperature/
  1. 在WHERE中使用正则表达式指定tag value
SELECT MEAN(water_level) FROM "h2o_feet" WHERE "location" =~ /[m]/ AND "water_level" > 3
  1. 在WHERE中使用正则表达式指定无值的tag
SELECT * FROM "h2o_feet" WHERE "location" !~ /./
  1. 在WHERE中使用正则表达式指定有值的tag
SELECT MEAN("water_level") FROM "h2o_feet" WHERE "location" =~ /./
  1. 在WHERE中使用正则表达式指定一个field value
SELECT MEAN("water_level") FROM "h2o_feet" WHERE "location" = 'santa_monica' AND "level description" =~ /between/
  1. 在GROUP BY中使用正则表达式指定tag key
SELECT FIRST("index") FROM "h2o_quality" GROUP BY /l/

schema查询语法

InfluxQL是一种类似SQL的查询语言,用于与InfluxDB中的数据进行交互。下面我们要介绍一些有用的查询schema的语法:

  • SHOW DATABASES
  • SHOW RETENTION POLICIES
  • SHOW SERIES
  • SHOW MEASUREMENTS
  • SHOW TAG KEYS
  • SHOW TAG VALUES
  • SHOW FIELD KEYS

函数

  • COUNT(): 返回非空字段值得数目
  • DISTINCT(): 返回field value的不同值列表
  • MEAN(): 返回字段的平均值
  • MEDIAN(): 返回排好序的字段的中位数
  • MODE():返回字段中出现频率最高的值
  • SPREAD(): 返回字段中最大和最小值的差值
  • STDDEV(): 返回字段的标准差
  • SUM():返回字段值的和
  • BOTTOM():返回最小的N个field值
  • FIRST(): 返回时间戳最早的值
  • LAST():返回时间戳最近的值
  • MAX():返回最大的字段值
  • MIN():返回最小的字段值
  • PERCENTILE():返回较大百分之N的字段值
  • SAMPLE():返回N个随机抽样的字段值
  • TOP():返回最大的N个field值

prometheus基本使用

下载

从prometheus官网上下载最新的版本,解压

tar -xf prometheus-2.0.0.linux-amd64.tar.gz
cd prometheus-*

配置

prometheus是完整的监控产品,它通过http的方式收集指标,它也可以监控自身的指标数据。
配置job和每个job要收集的目标metric数据,配置文件分两级job、target,在k8sz中监控主要配置api-server和etcd的metric地址;
编辑prometheus.yml配置文件,具体如下:

# my global config
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ['192.168.56.3:9090']
  - job_name: 'kubernetes'
    scrape_interval: 5s
    static_configs:
      - targets: ['192.168.56.4:8080']
  - job_name: 'etcd-server'
    scrape_interval: 5s
    static_configs:
      - targets: ['192.168.56.4:2379','192.168.56.5:2379','192.168.56.6:2379']

启动prometheus

./prometheus --config.file=prometheus.yml

然后就可以通过http://:9090访问

建议通过systemd服务启动prometheus

# /etc/systemd/system/prometheus.service
[Unit]
Description=prometheus
After=network.target
[Service]
Type=simple
#User=prometheus
ExecStart=/root/prometheus-2.0.0.linux-amd64/prometheus --config.file=/root/prometheus-2.0.0.linux-amd64/prometheus.yml --storage.tsdb.path=/var/lib/prometheus
Restart=on-failure
[Install]
WantedBy=multi-user.target

启动prometheus
systemctl start prometheus

监控prometheus服务器

为收集监控服务器上的CPU、内存、磁盘、io等信息,要安装node-exporter,该exporter的作用是收集机器系统数据,充当agent的作用;

  • 安装node_exporter
  • 配置node_exporter service文件
# /etc/systemd/system/node_exporter.service
[Unit]
Description=node_exporter
After=network.target
[Service]
Type=simple
#User=prometheus
ExecStart=/root/prometheus-2.0.0.linux-amd64/node_exporter-0.15.2.linux-amd64/node_exporter
Restart=on-failure
[Install]
WantedBy=multi-user.target
  • 启动node_exporter
systemctl start node_exporter

添加prometheus yaml文件中的监控点

- job_name: 'linux'
    scrape_interval: 5s
    static_configs:
      - targets: ['localhost:9100']

重启prometheus

systemctl restart prometheus

绘图展示

prometheus自带的监控界面比较简单,可以配合go实现的开源grafana,界面功能强大,美观;

docker run -d --name=grafana -p 3000:3000 grafana/grafana

通过http://:3000访问grafana,缺省帐号/密码为admin/admin

简单代码实现词云

#!/usr/bin/env python
# coding: utf-8

__author__ = 'yueyt'

import jieba
import numpy as np
import xlrd
from PIL import Image
from matplotlib import pyplot as plt
from wordcloud import WordCloud, STOPWORDS


def excel2text(excel_file):
    data = xlrd.open_workbook(excel_file)
    table = data.sheets()[0]
    nrows = table.nrows
    content = []
    for i in range(nrows):
        row_values = table.row_values(i)
        if len(row_values[-1]) > 0:
            content.append(row_values[-1])

    # remove null content
    return content


def gen_text_file(filename, text):
    with open(filename, mode='w', encoding='utf-8') as f:
        f.write('{}\n'.format(text))


def text2word(text):
    return jieba.lcut(text, cut_all=True)


def get_cloud_word(text):
    alice_mask = np.array(Image.open("data/bank.jpg"))
    stopwords = set(STOPWORDS)
    stopwords.add('该条')
    stopwords.add('什么')
    wc = WordCloud(font_path='wqy-microhei.ttc', stopwords=stopwords, background_color="white", max_words=300,
                   mask=alice_mask).generate(text)
    plt.imshow(wc, interpolation='bilinear')
    plt.axis('off')
    plt.show()


def get_most_count(text, n):
    from collections import Counter
    total_counts = Counter(text)
    return total_counts.most_common(n)


if __name__ == '__main__':
    excel_file = 'data/评论详情_20170401_20170630.xlsx'
    content = excel2text(excel_file)
    content = ' '.join(content).replace('\n', ' ')
    words = text2word(content)
    get_cloud_word(' '.join(words))

Go语言实现json2xml转换器

什么是antlr

antlr(ANother Tool for Language Recognition)是一个强大的语法分析器生成工具,它可用于读取,处理,执行和翻译结构化的文本和二进制文件。目前,该工具广泛应用于学术和工业生产领域,同时也是众多语言,工具和框架的基础。
今天我们就用这个工具实现一个go语言版的json2xml转换器;

antlr的作用

关于一门语言的语法描述叫做grammar, 该工具能够为该语言生成语法解析器,并自动建立语法分析数AST,同时antlr也能自动生成数的遍历器,极大地降低手工coding 语法解析器的成本;

实践开始

言归正传,拿json2xml为例,实现一个工具;

安装

以macOS为例

brew install antlr

编辑json语言解析语法

// Derived from http://json.org
grammar JSON;

json:   object
    |   array
    ;

object
    :   '{' pair (',' pair)* '}'    # AnObject
    |   '{' '}'                     # NullObject
    ;

array
    :   '[' value (',' value)* ']'  # ArrayOfValues
    |   '[' ']'                     # NullArray
    ;

pair:   STRING ':' value ;

value
    :   STRING		# String
    |   NUMBER		# Atom
    |   object  	# ObjectValue
    |   array  		# ArrayValue
    |   'true'		# Atom
    |   'false'		# Atom
    |   'null'		# Atom
    ;

LCURLY : '{' ;
LBRACK : '[' ;
STRING :  '"' (ESC | ~["\\])* '"' ;
fragment ESC :   '\\' (["\\/bfnrt] | UNICODE) ;
fragment UNICODE : 'u' HEX HEX HEX HEX ;
fragment HEX : [0-9a-fA-F] ;
NUMBER
    :   '-'? INT '.' INT EXP?   // 1.35, 1.35E-9, 0.3, -4.5
    |   '-'? INT EXP            // 1e10 -3e4
    |   '-'? INT                // -3, 45
    ;
fragment INT :   '0' | '1'..'9' '0'..'9'* ; // no leading zeros
fragment EXP :   [Ee] [+\-]? INT ; // \- since - means "range" inside [...]
WS  :   [ \t\n\r]+ -> skip ;

上面是依照antlr4的语法格式编辑的文件

  • antlr4文件语法也比较简单:
    • 以grammar关键字开头,名字与文件名相匹配
    • 语法分析器的规则必须以小写的字母开头
    • 词法分析器的规则必须用大写开头
    • | 管道符号分割同一语言规则的若干备选分支,使用圆括号把一些符号组成子规则。
  • 涉及到的几个专有名词:
    • 语言: 语言即一个有效语句的集合,语句由词组组成,词组由子词组组成,一次循环类推;
    • 语法: 语法定义语言的语意规则, 语法中每条规则定义一种词组结构;
    • 语法分析树: 以树状的形式代表的语法的层次结构;根结点对应语法规则的名字,叶子节点代表语句中的符号或者词法符号。
    • 词法分析器: 将输入的字符序列分解成一系列的词法符号。一个词法分析器负责分析词法;
    • 语法分析器: 检查语句结构是否符合语法规范或者是否合法。分析的过程类似走迷宫,一般都是通过对比匹配完成。
    • 自顶向下语法分析器: 是语法分析器的一种实现,每条规则都对应语法分析器中的一个函数;
    • 前向预测: 语法分析器使用前向预测来进行决策判断,具体指将输入的符号于每个备选分支的起始字符进行比较;

生成解析基础代码

# antlr4 -Dlanguage=Go -package json2xml JSON.g4

使用antlr生成目标语言为Go, package名为json2xml的基础代码

生成的文件如下:

$ tree
├── JSON.g4
├── JSON.interp             # 语法解析中间文件
├── JSON.tokens             # 语法分析tokens流文件
├── JSONLexer.interp        # 词法分析中间文件
├── JSONLexer.tokens        # 词法分析tokens流文件
├── json_base_listener.go   # 默认是listener模式文件
├── json_lexer.go           # 词法分析器
├── json_listener.go        # 抽象listener接口文件
├── json_parser.go          # parser解析器文件

实现解析器(listener例子)

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"testing"

	"c2j/parser/json2xml"

	"github.com/antlr/antlr4/runtime/Go/antlr"
)

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
}

type j2xConvert struct {
	*json2xml.BaseJSONListener
	xml map[antlr.Tree]string
}

func NewJ2xConvert() *j2xConvert {
	return &j2xConvert{
		&json2xml.BaseJSONListener{},
		make(map[antlr.Tree]string),
	}
}

func (j *j2xConvert) setXML(ctx antlr.Tree, s string) {
	j.xml[ctx] = s
}

func (j *j2xConvert) getXML(ctx antlr.Tree) string {
	return j.xml[ctx]
}

// j2xConvert methods
func (j *j2xConvert) ExitJson(ctx *json2xml.JsonContext) {
	j.setXML(ctx, j.getXML(ctx.GetChild(0)));
}

func (j *j2xConvert) stripQuotes(s string) string {
	if s == "" || ! strings.Contains(s, "\"") {
		return s
	}
	return s[1 : len(s)-1]
}

func (j *j2xConvert) ExitAnObject(ctx *json2xml.AnObjectContext) {
	sb := strings.Builder{}
	sb.WriteString("\n")
	for _, p := range ctx.AllPair() {
		sb.WriteString(j.getXML(p))
	}
	j.setXML(ctx, sb.String())
}

func (j *j2xConvert) ExitNullObject(ctx *json2xml.NullObjectContext) {
	j.setXML(ctx, "")
}

func (j *j2xConvert) ExitArrayOfValues(ctx *json2xml.ArrayOfValuesContext) {
	sb := strings.Builder{}
	sb.WriteString("\n")
	for _, p := range ctx.AllValue() {
		sb.WriteString("<element>")
		sb.WriteString(j.getXML(p))
		sb.WriteString("</element>")
		sb.WriteString("\n")
	}
	j.setXML(ctx, sb.String())
}

func (j *j2xConvert) ExitNullArray(ctx *json2xml.NullArrayContext) {
	j.setXML(ctx, "")
}

func (j *j2xConvert) ExitPair(ctx *json2xml.PairContext) {
	tag := j.stripQuotes(ctx.STRING().GetText())
	v := ctx.Value()
	r := fmt.Sprintf("<%s>%s</%s>\n", tag, j.getXML(v), tag)
	j.setXML(ctx, r)
}

func (j *j2xConvert) ExitObjectValue(ctx *json2xml.ObjectValueContext) {
	j.setXML(ctx, j.getXML(ctx.Object()))
}

func (j *j2xConvert) ExitArrayValue(ctx *json2xml.ArrayValueContext) {
	j.setXML(ctx, j.getXML(ctx.Array()))
}

func (j *j2xConvert) ExitAtom(ctx *json2xml.AtomContext) {
	j.setXML(ctx, ctx.GetText())
}

func (j *j2xConvert) ExitString(ctx *json2xml.StringContext) {
	j.setXML(ctx, j.stripQuotes(ctx.GetText()))
}

func TestJSON2XMLVisitor(t *testing.T) {
	f, err := os.Open("testdata/json2xml/t.json")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	content, err := ioutil.ReadAll(f)
	if err != nil {
		panic(err)
	}

	// Setup the input
	is := antlr.NewInputStream(string(content))

	// Create lexter
	lexer := json2xml.NewJSONLexer(is)
	stream := antlr.NewCommonTokenStream(lexer, antlr.LexerDefaultTokenChannel)

	// Create parser and tree
	p := json2xml.NewJSONParser(stream)
	p.BuildParseTrees = true
	tree := p.Json()

	// Finally AST tree
	j2x := NewJ2xConvert()
	antlr.ParseTreeWalkerDefault.Walk(j2x, tree)
	log.Println(j2x.getXML(tree))
}

上面代码比较简单,看注释就好;

一般流程如下:

  • 新建输入流
  • 新建词法分析器
  • 生成token流,存储词法分析器生成的词法符号tokens
  • 新建语法分析器parser,处理tokens
  • 然后针对语法规则,开始语法分析
  • 最后通过默认提供的Walker,进行AST的遍历

其中针对中间生成的参数和结果如何存放?好办,直接定义个map,map键以Tree存放;

xml map[antlr.Tree]string

Listener和Visitor

antlr生成的代码有两种默认,默认是listener实现,要生成visitor,需要另加参数-visitor。
这两种机制的区别在于,监听器的方法会被antlr提供的遍历器对象自动调用,而visitor模式的方法中,必须显示调用visit方法来访问子节点。如果忘记调用的话,对应的子树就不会被访问。

总结

antlr是一个强大的工具,能让常见的语法解析工作事半功倍,效率极高。同时,该工具使语法分析过程和程序本身高度分离,提供足够的灵活性和可操控性。

wrk http benchmark tool 使用

简介

wrk是一种现代的HTTP基准测试工具,可在单个多核CPU上运行时产生显着的负载。它将多线程设计与可扩展的事件通知系统(如epoll和kqueue)相结合.同时,该工具可以hook LUA脚本,定制化一些报告等特殊化请求, 是现代http 压测的首选;

如何使用

help

wrk --help
Usage: wrk <options> <url>                            
  Options:                                            
    -c, --connections <N>  Connections to keep open  连接数 
    -d, --duration    <T>  Duration of test          测试持续时间
    -t, --threads     <N>  Number of threads to use  开启线程数目 
                                                      
    -s, --script      <S>  Load Lua script file      加载的lua脚本
    -H, --header      <H>  Add header to request     
        --latency          Print latency statistics   
        --timeout     <T>  Socket/request timeout    请求超时时间
    -v, --version          Print version details      
                                                      
  Numeric arguments may include a SI unit (1k, 1M, 1G)
  Time arguments may include a time unit (2s, 2m, 2h)

cmd

wrk -t12 -c400 -d30s http://127.0.0.1:8000

结果分析

先列举下结果的字段说明

项目 名称 说明
Avg 平均值 每次测试的平均值
Stdev 标准偏差 结果的离散程度,越高说明越不稳定
Max 最大值 最大的一次结果
+/- Stdev 正负一个标准差占比 结果的离散程度,越大越不稳定

Latency: 可以理解为响应时间
Req/Sec: 每个线程每秒钟的完成的请求数

一般我们来说我们主要关注平均值和最大值.
标准差标志着样本本身离散程度. 如果比较大,说明系统性能波动大

Running 30s test @ http://127.0.0.1:9000
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   186.42ms  153.84ms   1.78s    90.69%
    Req/Sec    76.16     76.79   333.00     80.96%
  16545 requests in 30.10s, 2.60MB read
  Socket errors: connect 0, read 52, write 0, timeout 190
Requests/sec:    549.71
Transfer/sec:     88.58KB

定制脚本

具体参见LUA for wrk

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.