/images/avatar.png

Envoy的ext_proc

本篇文章中,我们学习Envoy中的ext_proc功能。

基本功能

首先我们搭建一个最简单的ext_proc,并观察他的基本使用方法。

首先我们搭建一个简单的外部后端。该后端只处理response header,他在原有header的基础上加一个x-extproc-helloheader。

Envoy里流式协议的数据流

定义后端服务

先用protobuf定义你的服务。该服务有一个双向流API BiStream。

syntax = "proto3";
package api;
option go_package = "envoystreaming/api";

service StreamService {
  rpc BiStream(stream Data) returns (stream Data);
}

message Data { string body = 1; }
提示
为了使Go服务能识别到你定义的服务。你需要先运行下面的代码生成服务定义。执行之后,你会看到他生成了名为stream_grpc.pb.go的Go语言服务定义。

protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    api/stream.proto

然后我们用Go语言实现该服务。该服务按顺序从输入流中轮询发送过来的数据。当他发现有数据发送过来的时候,间隔5秒发送一个ACK响应,一共发三个。

C++小抄cheatsheet

参考文档 (References)

  1. C++ QUICK REFERENCE by Matt Mahoney
  2. C++ Quick reference by utkuufuk
  3. C++ cheatsheet by Fechin

预处理器(preprocessor)

                        // 单行comment
                        /* 多行comment */
#include<stdio.h>       // 插入标准头文件
#include "myfile.h"     // 插入当前文件夹文件
#define X some text     // 用some text替换X
#define F(a,b) a+b      // 用1+2替换替换F(1,2)
#define X \
  some text             // 继续行
#undef X                // 移除定义
#if defined(X)          // 条件编译 (#ifdef X)
#else                   // 可选项 (#ifndex X 或 #if !defined(X))
#endif                  // 必须出现在#if和#ifdef后

字面量(literals)

255, 0377, 0xff         // 整数(十进制,八进制,十六进制)decimal,octal,hex
2147483647L, 0x7fffffff1// 长整型数(32位)(Long)
123.0, 1.23e2           // 浮点数(double)
'1', '\141', '\x61'     // 字符(字面量,八进制,十六进制)
'\n', '\\', '\'', '\"'  // 换行符,反斜杠,单引号,双引号
"string\n"              // 以换行符和\0结尾的字符串数组
"hello" "world"         // 组合的字符串
true, false             // 布尔常量1和0

声明(declarations)

int x;                  // 声明x为一个整数(值未定义)
int x=255;              // 声明并初始化x为255
short s; long l;        // 16或32位整数
char c='a';             // 8位字符
unsigned char u=255; signed char s=-1;  // 字符可以是signed也可以是unsigned的
unsigned long x=0xffffffffL;            // short,int,long默认为signed的,除非特别标明
float f; double d;      // 单精度和双精度数(永不为unsigned的)
bool b=true;            // true或者false,也可以用整数1,0
int a, b, c;            // 多个声明
int a[10];              // 一个10个整数的数组,a[0]到a[9]
int a[]={0,1,2};        // 一个初始化了的数组(或者int a[3]={0, 1, 2};)
int a[2][3]={{1,2,3},{4,5,6}};          // 整数的二维数组
char s[]="hello";       // 字符串(6个元素,包括'\0')
int* p;                 // p是一个整数的指针
char* s="hello";        // s是一个未命名的,包含了"hello"的数组的指针
void* p=NULL;           // untyped内存的地址
int& r=x;               // r是整数x的(左值)引用
T a; T& ref=a;          // ref 是 a 的(左值)引用
ref.xxx;                // 实际调用 a.xxx

enum weekend {SAT,SUN}; // weekend是有值SAT和SUN的枚举类型
enum weekend day;       // day是weekend类型的变量
enum weekend {SAT=0,SUN=1};             // 枚举类型显式表示为整数
enum {SAT,SUN} day;     // 匿名枚举 TODO
typedef String char*;   // String s;表示 char* s;
const int c=3;          // 常量必须被初始化,不能被重新赋值
const int* p=a;         // p的内容是常量
int* const p=a;         // p是常量,p的内容不是
const int* const p=a;   // p和p的内容都是常量
const int& cr=x;        // cr是一个常量引用,不能被赋值去修改他所指向的变量x的值
auto x = 10;            // 编译器推导类型

存储类 (storage class)

int x;                  // 自动分配,内存分配仅在处于当前scope的时候存在
static int x;           // 全局声明周期,即使是在local scope中
extern int x;           // 有一个x整型变量存在,但不要在当前位置给他分配空间。它的真正定义和内存分配在程序的其他文件或位置

语句 (statement)

x=y;                      // 所有的表达式都是语句
int x;                    // 声明是语句
;                         // 空语句

{                         // 一个代码块是一个单一语句
  int x;                  // x的scope是从声明开始直到代码块结束
  a;                      // C语言中,所有变量的声明必须在语句前
}
if (x) a;                 // 如果x是true(非0),执行/计算a
else if (y) b;            // 如果非x但是y,执行/计算b
else c;                   // 如果非x且非y,执行/计算c

while (x) a;              // 重复0次或多次,当x为true时

for (x; y; z) a;          // 等价于: x; while(y) {a; z;}

do a; while (x);          // 等价于: a; while(x) a;

switch (x) {              // x必须是整数
  case X1: a;             // 如果x == X1 (X1必须为常量), 跳转到这
  case X2: b;             // 如果x == X2, 跳转到这
  default: c;             // 否则跳转到这(可选)
}
break;                    // 跳出while,do或for loop,或switch
continue;                 // 跳到while, do, or for 循环底
return x;                 // 返回x给caller
try { a; }
catch (T t) { b; }        // 如果a抛出T,跳到这
catch (...) { c; }        // 如果a抛出别的,跳到这

函数(functions)

int f(int x, int);        // f是一个接受两个整数,并返回整数的函数
void f();                 // f是一个不接受参数的过程(procedure)
void f(int a=0);          // f()等价于f(0)
f();                      // 默认返回类型是int
inline f();               // 优化执行速度
f() { statements; }       // 函数的定义必须放在全局作用域,即函数内部不能定义函数
T operator+(T x, T y);    // a+b (当a b为T类型) 调用operator+(a, b)
T operator-(T x);         // -a 调用函数 operator-(a)
T operator++(int);        // 后缀++或--必须带有一个 int 类型的虚拟参数,但是调用时不需传入
extern "C" {void f();}    // f() 在 C中被编译

函数参数和返回值可以是任意类型。函数在被使用之前,必须先进行声明或定义。它可以先被声明,稍后再进行定义。每个程序都由一组全局变量声明和一组函数定义组成(这些内容可以分布在不同的文件中),其中必须包含以下函数之一:

数学中的因式分解技巧

本文章为小蓝本初中篇因式分解教程笔记。

常用公式

\begin{equation*} a^2 - b^2 = (a + b)(a-b),平方差 \end{equation*}

\begin{equation*} a^3 + b^3 = (a + b)(a^2-ab+b^2),立方和 \end{equation*}

\begin{equation*} a^3 - b^3 = (a - b)(a^2+ab+b^2),立方差 \end{equation*}

GCP Log Explorer祛魅

本文我们将研究GCP的Log Explorer里到底能看到多少种日志,每种日志都长什么样,每种日志做什么操作才能被触发。

Audit Log

Audit Log以cloudaudit.googleapis.com起手,并有几种子类,比如: