apisix源码分析-配置文件(2)

apisix源码分析-配置文件(2)

启动后会读取配置文件

apisix/core/profile.lua

配置文件放在 apisix_home/conf 下。

在执行读取配置文件会使用一个 os.getenv(“APISIX_PROFILE“) 来决定读取哪个配置文件

比如设置了 APISIX_PROFILE = dev

读取的配置文件为 apisix_home/conf/-dev

配置文件会读取 yaml 文件后,转换成 lua 里的 table 对象,通过 scheme 来校验。

scheme

core/scheme.lua

所有的传入的参数验证都会通过 scheme 来进行校验:

  • 读取配置文件校验
  • admin api 写入 etcd 的配置校验。
  • 插件配置校验。

这里用到了 jsonscheme 的 lua 库对参数进行校验,在 scheme_def.lua 中能够找到所有 apisix 的配置验证模版。插件的验证模版在每个插件中。

scheme 是一个非常棒的设计,用约定的方式定义了输入的数据,对数据数据做 scheme 的校验.保证输入输出的正确性.

主配置文件的生成

apisix 的 nginx.conf 配置是由 cli/ngx_tpl.lua 生成的,tpl 文件和上面读取到的 conf 中得到的配置文件做变量替换后得到具体的 nginx.conf 文件.

以下是和 http 有关的阶段配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
	 upstream apisix_backend {
server 0.0.0.1;

balancer_by_lua_block {
apisix.http_balancer_phase()
}

keepalive 320;
keepalive_requests 1000;
keepalive_timeout 60s;
}


init_by_lua_block {
require "resty.core"
apisix = require("apisix")

local dns_resolver = { "127.0.0.11", }
local args = {
dns_resolver = dns_resolver,
}
apisix.http_init(args)
}

init_worker_by_lua_block {
apisix.http_init_worker()
}

exit_worker_by_lua_block {
apisix.http_exit_worker()
}
server {
ssl_certificate_by_lua_block {
apisix.http_ssl_phase()
}
access_by_lua_block {
apisix.http_access_phase()
}

proxy_pass $upstream_scheme://apisix_backend$upstream_uri;

mirror /proxy_mirror;

header_filter_by_lua_block {
apisix.http_header_filter_phase()
}

body_filter_by_lua_block {
apisix.http_body_filter_phase()
}

log_by_lua_block {
apisix.http_log_phase()
}

}
}
apisix源码分析-启动(1)

apisix源码分析-启动(1)

启动

1
2
3
4
5
6
7
8
9
/bin/apisix
/cli/
apisix.lua
env.lua
etcd.lua
file.lua
ngx_tpl.lua
ops.lua
util.lua

执行 /bin/apisix $* 实际执行 lua apisix/cli/apisix.lua $*

cli

`apisix.lua` :
  1. env.lua 环境变量
1
2
3
4
5
6
7
apisix_home : apisixe 安装目录
is_root_path : 是否为 root 目录
openresty_args : openresty 启动
openresty -p apisix_home -c apisix_home/conf/nginx.conf
pkg_cpath_org:
pkg_path_org :
min_etcd_version : etcd 版本
1. `ops.lua` :执行启动关闭等 ops.execute(env,arg) 在执行 `apisix start | stop | quit | restart | reload` 会调用 openresty 的执行命令,比如 `apisix reload` 会执行 `openresty -c nginx.conf -s reload`

apisix init

cli/ops/init()

负责读取检查准备apisix配置文件,

- 检查yaml配置文件
- 检查admin key
- 检查 openresty 版本
- 检查插件列表
- 检查promtheus
- 检查多端口
- 检查 ssl
- ...

所有检查的内容会存储在 sys_conf 的变量里,通过模版变量替换。

cli/ngx_tpl.lua 生成 conf/nginx.conf 文件,

1
2
3
4
local conf_render = template.compile(ngx_tpl)
local ngxconf = conf_render(sys_conf)
local ok, err = util.write_file(env.apisix_home .. "/conf/nginx.conf",
ngxconf)

apisix init_etcd : etcd.lua

检查 etcd 的版本,验证授权信息

以上就是 cli 启动的过程,总结就是检查运行环境,通过配置文件生成 nginx.conf 。

apisix源码分析-前言(0)

apisix源码分析-前言(0)

apisix 是什么

Apache APISIX® – Cloud-Native API Gateway

apisix 是一个动态、实时、高性能的 API 网关。

发展

当使用 nginx 做网关、代理、均衡负载时。想要对 nginx 添加一些需求。

tengine : 使用 nginx 的插件方式来实现原生扩展。

openresty : 实现了 nginx lua 的扩展: openresty/lua-nginx-module ,使用 lua 来扩展 nginx 功能。

openresty 的出现让我们能够方便快捷的扩展 nginx 的功能。tengine 也通过添加 lua-nginx-module 扩展实现了相关能力。

在学习 apisix 之前,先要了解 openresty 的执行阶段,如下图:

Untitled.png

初始化

initinit_worker 阶段分别对应到 nginx 的 master 进程和 worker 进程的初始化。

请求

ssl_certificate 对应到证书认证阶段,当使用 https 访问时会最先进入到这个阶段。

set_by_lua 变量设置阶段,可以在 nginx.conf 里设置一些变量,在这个阶段生效后,在之后的阶段通过 ngx.var. 访问

rewrite 重写阶段,在 ngx_http_rewrite_module 运行后执行。

access 访问阶段,在 ngx_http_access_module 运行后执行。

响应

content 内容处理

balancer 均衡负载

header_filter 返回 header 处理

body_filter 返回 body 处理

日志

log 在写日志之前运行

通过 openresty 对 nginx 功能进行扩展,可以对不同的阶段进行介入,实现需求。 当在各个阶段实功能不断迭代时,功能的边界会越来越模糊,导致代码混乱,无法管理。

所以我们提出了需求来解决问题:

  • 不同的功能要有清晰的边界(插件化)
  • 对不同的路由能够启用不同的功能(路由管理)
  • 能够动态的管理后端服务 (动态化)
  • 能够对证书管理
  • ……

apisixkong 就是基于 lua-nginx-module 实现的框架,将 route、 server 、plugin 、ssl 等等,能够动态的管理起来。 以下是我认为的 apisix 和 kong 最大的区别点:

apisix kong
配置同步 使用 etcd 存储配置,通过 watch 同步配置,毫秒级变更配置。 使用配置文件或数据库(postgres),定期轮询同步配置,5秒同步一次
路由匹配 使用 radixtree 存储路由,高性能。 前缀匹配,性能一般
插件 队列或有向无环图 队列
nginx ocsp cache 分析

nginx ocsp cache 分析

本文分析 nginx ocsp cache 的原理,用于回答相关业务的解惑

缓存时间

OCSP 的缓存时间和证书 OCSP Response 的 Next Update 时间相关.如果证书未提供 Next Update 时间,过期时间=当前时间+3600s.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// nextupdate 存在时设置为 nextupdate ,不存在时设置为 NGX_MAX_TIME_T_VALUE
//ngx_ssl_ocsp_verify
if (nextupdate) {
ctx->valid = ngx_ssl_stapling_time(nextupdate);
if (ctx->valid == (time_t) NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"invalid nextUpdate time in certificate status");
goto error;
}

} else {
ctx->valid = NGX_MAX_TIME_T_VALUE;
}
// 在存储到缓存中时,如果时间为 NGX_MAX_TIME_T_VALUE ,则是当前时间 +3600s

// ngx_ssl_ocsp_cache_store ocsp cache 存储
valid = ctx->valid;
now = ngx_time();
// 过期时间小于当前,不缓存,直接返回
if (valid < now) {
return NGX_OK;
}
// 过期时间未默认值是设置为 3600s
if (valid == NGX_MAX_TIME_T_VALUE) {
valid = now + 3600;
}


Thisupdate 和 nextupdate 的时间由 OCSP_resp_find_status 从证书中获取, OCSP_resp_find_status 是 openssl 提供的函数.

https://github.com/openssl/openssl/blob/e3d897d3fa3b48bb835fab0665a435469beea7ae/crypto/ocsp/ocsp_cl.c#L248

single = OCSP_resp_get0(bs, i); // 负责获取证书信息 .

调用

1
2
3
4
5
6
7
8
#define sk_OCSP_SINGLERESP_value( st,
i
)SKM_sk_value(OCSP_SINGLERESP, (st), (i))

#defineSKM_sk_value(type, st, i) ((type *)sk_value(CHECKED_STACK_OF(type, st), i))



OCSP_single_get0_status 获取状态后直接返回值,并没有做对应的值转换或者判断.

存储的 key 和谁有关

Ocsp 缓存使用使用证书的 Serial Number 作为key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ngx_ssl_ocsp_create_key
static ngx_int_t
ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx)
{
u_char *p;
......

ctx->key.data = p; // 设置p 为key 默认设置长度 60
ctx->key.len = 60;

......
// 获取 serial number
serial = X509_get_serialNumber(ctx->cert);
if (serial->length > 20) {
return NGX_ERROR;
}
// 将证书得 serial 复制到p中
p = ngx_cpymem(p, serial->data, serial->length);
ngx_memzero(p, 20 - serial->length);

......

return NGX_OK;
}

Ocsp 存储的大小

Ocsp 的存储使用 nginx 的共享内存来存储,关于 nginx 共享内存可以看 OpenResty 和 Nginx 如何分配和管理内存

在OCSP 的数据在存储到红黑树中只存储了数据的状态和过期时间,并不存储整张证书.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ngx_ssl_ocsp_cache_store  ocsp cache 存储


// node 分配的内存块
node->node.str.len = ctx->key.len;
node->node.str.data = (u_char *) node + sizeof(ngx_ssl_ocsp_cache_node_t);
// 将OCSP ctx->key 存储到 node.str
ngx_memcpy(node->node.str.data, ctx->key.data, ctx->key.len);
// node.key 为 ctx->key 的 hash 值
node->node.node.key = hash;
// 证书的状态
node->status = ctx->status;
// 证书的过期时间
node->valid = valid;
// 将node插入到树中
ngx_rbtree_insert(&cache->rbtree, &node->node.node);
// 将数据加入到 LRU 队列
ngx_queue_insert_head(&cache->expire_queue, &node->queue);

由于无法直接读取到 nginx 创建的共享内存空间,我们在 lua 里面创建一个共享对象来验证占用.

1
2
3
4
5
6
7
8
typedef struct {
ngx_str_node_t node; // 16 bytes
ngx_queue_t queue; // 32 bytes
int status; // 4 bytes
time_t valid; // 8 bytes
} ngx_ssl_ocsp_cache_node_t;
size_t size = sizeof(ngx_ssl_ocsp_cache_node_t); // 60 bytes

写入30w 个key value 长度为 60 的字符串,内存占用 79.078125 M , 当把字符串长度扩展到80时,内存占用为 79.078125 M, 和 60 是一致的. (实际分配了 128 bytes 的内存)

以上的验证可能并不精确,但是可以大概描述 ocsp cache 在内存中的存储的大致量. 当 cache 数为 500M 时 ,大致可以缓存 180 w 个.

缓存满了的淘汰策略

淘汰策略使用 LRU (最近最少) .

当 cache 缓存满了后, 会自动将新的添加到队列的头部,删除尾部的数据.

Ocsp stapling 只会下发 good 的状态.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static void
ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
{
time_t now;
ngx_str_t response;
ngx_ssl_stapling_t *staple;

staple = ctx->data;
now = ngx_time();

if (ngx_ssl_ocsp_verify(ctx) != NGX_OK) {
goto error;
}
-- 如果校验的状不是 GOOD 报错退出
**if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"certificate status \"%s\" in the OCSP response",
OCSP_cert_status_str(ctx->status));
goto error;
}**

/* copy the response to memory not in ctx->pool */

response.len = ctx->response->last - ctx->response->pos;
response.data = ngx_alloc(response.len, ctx->log);
...
openresty lua_resty_counter 源码

openresty lua_resty_counter 源码

无锁的累加库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
local ngx_shared = ngx.shared
local pairs = pairs
local ngx = ngx
local error = error
local setmetatable = setmetatable
local tonumber = tonumber
-- table 清理函数
local clear_tab
do
local ok
ok, clear_tab = pcall(require, "table.clear")
if not ok then
clear_tab = function(tab)
for k in pairs(tab) do
tab[k] = nil
end
end
end
end

local _M = {
_VERSION = '0.2.1'
}
local mt = { __index = _M }

-- local cache of counters increments
-- worker 全局存储,key 为 shdict_name ,value 为对应的 table
-- <shdict_name>={}
local increments = {}
-- boolean flags of per worker sync timers
-- 用来记录是否开启的自动同步 timer_started[shdict_name]=true
local timer_started = {}

local id

-- 同步函数: 将 worker 的计数器同步到 ngx.shared 中
local function sync(_, self)
local err, _, forcible
local ok = true
-- 循环 worker 全局字典,将 increments 的计数同步到 dict 中
for k, v in pairs(self.increments) do
_, err, forcible = self.dict:incr(k, v, 0)
if forcible then
ngx.log(ngx.ERR, "increasing counter in shdict: lru eviction: key=", k)
ok = false
end
if err then
ngx.log(ngx.ERR, "error increasing counter in shdict key: ", k, ", err: ", err)
ok = false
end
end
-- 同步完成后清理字典,并设置 error_metric_name 为 1
clear_tab(self.increments)
if ok == false then
self.dict:incr(self.error_metric_name, 1, 0)
end

return ok
end


function _M.new(shdict_name, sync_interval, error_metric_name)
id = ngx.worker.id()

if not ngx_shared[shdict_name] then
error("shared dict \"" .. (shdict_name or "nil") .. "\" not defined", 2)
end

if not increments[shdict_name] then
increments[shdict_name] = {}
end
-- 构建当前对象的计数器
local self = setmetatable({
dict = ngx_shared[shdict_name],
increments = increments[shdict_name],
error_metric_name = error_metric_name,
}, mt)

-- 更具设置的同步时间,设置定时同步 ngx.timer.every
if sync_interval then
sync_interval = tonumber(sync_interval)
if not sync_interval or sync_interval < 0 then
error("expect sync_interval to be a positive number", 2)
end
if not timer_started[shdict_name] then
ngx.log(ngx.DEBUG, "start timer for shdict ", shdict_name, " on worker ", id)
ngx.timer.every(sync_interval, sync, self)
timer_started[shdict_name] = true
end
end

return self
end
-- 主动同步内容
function _M:sync()
return sync(false, self)
end
-- incr 只执行 increments 的累加
function _M:incr(key, step)
step = step or 1
local v = self.increments[key]
if v then
step = step + v
end

self.increments[key] = step
return true
end
-- reset 清理 dict
function _M:reset(key, number)
if not number then
return nil, "expect a number at #2"
end
return self.dict:incr(key, -number, number)
end
-- get 获取 dict 的内容
function _M:get(key)
return self.dict:get(key)
end

function _M:get_keys(max_count)
return self.dict:get_keys(max_count)
end

return _M
对七层接入的一些思考

对七层接入的一些思考

做了近两年的七层接入的事情,到月底做一个阶段性的结束.借用一句话, 既往不念,纵情向前.

需要做的事情:

  • 在 openresty 上笔记上记了很多的内容,做一个整理.
  • 把一些技术方面的思路,重新整理成完整的库.

如果拖的时间太久可能会对技术细节的遗忘,懒得在这方面投入经历.

tengine 和 openresty

tengine 是 nginx 的 fork

openresty 是 nginx 的 plugin

两者是不同的技术方向, tengine 的技术方向在 16 年之前非常优秀,但是能不用就不用吧.首先 tengine 在 19 年后就没有在更新维护,相比来说 openresty 的社区活跃得多.

不过今年 tengine 时隔 4 年,今年发布了 2.0.4 的版本,在对 nginx 1.21 openresty 1.21 tengine 2.0.4 的 ocsp 测试中, tengine 的 ocsp 不生效.

去年在查看相关插件时, tengine 对 nginx 的改动几乎没有办法平滑的更新 nginx 的版本. 社区的不活跃也导致 bug 的修复几乎没有.

如果是 nginx 做均衡负载产品,使用 c 技术栈来做由于变化不多,有一定可取性.但是目前大家开始做融合, 均衡负载、网关、mash 做融合,使用 openresty lua 方案要比 c 写组件的方式在开发维护成本要低的多.

apisix 和 kong

apisix 和 kong 两个都是建立在 openresty 上的开源产品, apisix 是在看到了 kong 的缺点后出现的解决方案.我理解的 apisix 优于 kong 的几点:

  • apisix 使用 etcd watch 作为配置下发, kong 使用数据库轮询. ms 级和 s 级的区别.
  • apisix 的路由使用 rds_tree 性能要比 kong 的高很多.
  • apisix 的代码相比 kong 要简洁很多.

作为两个建立在 openresty 生态上的开源产品,相互直接也有很多的借鉴, 比如 apisix 的 dns 服务发现使用的库是 kong 开源的.

在做引擎的替换时,我几乎看完了 apisix 、kong 已经生态上开源库的代码, 为什么说几乎看完呢,首先这个生态和其他开发语言相比,要小众得多,而多数的开源库都是来自 openresty apisix kong 官方,剩下的一小部分来自其他的开发者,另外就是 openresty lua 确实是非常简单.

nginx 和 envoy

我没有用过 envoy ,只是看过相关的文档. 这里的描述可能是错的.

nginx 和 envoy 的架构不同,在性能上实际相差不大,nginx 成熟稳定,enovy 年轻激情.

envoy + wasm / openresty + lua 两种不同的技术栈.

相对来说 openresty 要简单一些. envoy 的配置动态下发方式是 openresty 一大优势,在很多时候我都知在想办法解决怎么才能让 nginx 少 reload 或者不 reload.

envoy 对云原生的支持要比 nginx 优秀. apisix 也在这方面做了很多工作.

日志监控

日志可以看 apisix 的实现,写本地或者使用 tcp 的方式,tcp 的方式可能更好一些,会少一个日志收集组件. 可能会出现丢日志的情况.

监控由于 promehtus 几乎成为了云原生监控的规范.apisix使用的 resty-lua-prometheus 的库使用了单独的进程来做 metrics 输出,原因是 metrics 拉指标存在排序和一些计算,导致内存和 cpu 的异常占用,我通过一些技巧,对此进行重写后,性能非常可观.

从数据包看网络协议

从数据包看网络协议

数据包是怎么流转的 – OSI 网络模型

Untitled.png

数据包在每一层的作用

Untitled.png

TCP/IP 模型更好地体现网络工作的逻辑方式

在 TCP/IP 模型中,四层包括:

  • 4. 应用程序层:这大致相当于 OSI 模型中的第 7 层。
  • 3. 传输层:对应于 OSI 模型中的第 4 层。
  • 2. 互联网层: 对应于 OSI 模型中的第 3 层。
  • 1. 网络访问层:结合了 OSI 模型中第 1 层和第 2 层的过程。

网络访问层:结合了 OSI 模型中第 1 层和第 2 层的过程

Untitled.png

帧头部 : 源 MAC 地址 目标 MAC 地址 Type

以太网MAC帧格式

Untitled.png

MTU(最大传输单元): MTU相当于在发寄快递的时候对包裹的限制,这个限制是不同的链路层对应的物理层的限制。

MAC帧中的数据长度规定为46-1500字节,ARP(地址解析协议)数据包的长度不够46字节要在后面补填充位,如果大于1500字节,必须要求网络层进行分片。
最大值1500称为以太网的最大传输单元(MTU)不同的网络类型有不同的MTU
如果一个数据包从以太网路由到拨号链路上,数据包长度大于拨号链路的MTU,则需要对数据包进行分片。

ARP协议: ARP是介于链路层到网络层之间的协议,它的作用是为了解析IP地址到MAC地址的映射

分析

Untitled.png

eth.type == 0x0800

eth.addr == ff:ff:ff:ff:ff:ff

互联网层: 对应于 OSI 模型中的第 3 层。

四元组 源地址:源端口 目标地址:目标端口

Netfilter 框架

iptables

NAT

使私有网络能够和公网互联。

Untitled.png

应用 LVS

传输层:对应于 OSI 模型中的第 4 层。

TCP && UDP

Untitled.png

TCP

Untitled.png

UDP

Untitled.png

TCP 三次握手 四次挥手 粘包 拆包

UDP

问题: 同一个端口能否同时监听 TCP 和 UDP

应用层协议设计 ,如果是你怎么来设计一个协议。

  1. 包分隔符:
  2. 包头
    1. 版本号
    2. 长度
    3. code
    4. 消息ID
    5. option
    6. 预留位
  3. 消息体
  4. 其他

请求包 / 响应包

Untitled.png

应用程序层:这大致相当于 OSI 模型中的第 7 层

HTTP 协议

Untitled.png

tcpdump 和 wireshark

libpocp

tcpdump -i eth0 port 80 -w result.pocp

tcpdump -i en0 udp -w upd.pcap

参考

什么是MTU(Maximum Transmission Unit)?MTU设置为多少合适?

数据链路层详解_HanSion.Z-CSDN博客_数据链路层

什么是网络层_以及网络传播协议中OSI模型与TCP/IP模型的区别 | Cloudflare

GPT最佳实践记录

GPT最佳实践记录

文章来源说明: openai 官方文档,连接查看参考文档

本文只是对官方文档的摘录和学习.

获取更好的结果的六种策略

1. 写清楚说明

  • 在查询种包含详细信息已获得更相关答案
1
2
Prompt:帮我写一个排序函数 
优化后: 使用 golang 编写一个冒泡排序,并解释每一行代码的作用以及为什么这么写
  • 设定角色: 让GPT扮演特定角色,比如很有意思的智囊团
1
假设你是我的智囊团,团内有6个不同的董事做我的教练。6个董事分别是乔布斯、伊隆马斯克、马云、柏拉图、Ray Dalio 和慧能大师,他们都有自己个性、世界观、价值观.对问题有不同的看法、建议和意见。我会在这里说出我的处境和我的决策,请分别以这6个身份,以他们的视角来审视我的决策,给出他们的评判和建议。
  • 使用定界符标记输入的部分:用来清晰的标记不同的输入内容.

三重引号、XML 标记、章节标题等分隔符可以帮助区分要区别对待的文本部分。

1
2
3
4
5
6
7
8
1. 总结引号内的内容 
‘’‘
<需要总结的内容〉
’‘’

2 你将获得一对关于同一主题的文章(用 XML 标记分隔)。先总结一下每篇文章的论点。然后指出他们中的哪一个提出了更好的论点并解释原因。
<article>在这里插入第一篇文章</article>
<article>在这里插入第二篇文章</article>
  • 指定任务完成所需要的步骤;对于复杂任务,把它分解成一系列清晰的步骤,这样GPT会更容易理解。
1
2
3
4
5
使用一下分步响应用户的输入.
第一步:总结引号内的文本,格式为:
[总结]:<总结内容>
第二步:为第一步的总结生成观点
[观点]:<内容>
  • 提供示例;在合适的情况下,提供示例可以让GPT更清楚地了解你的需求。
  • 指定输出所需要的长度
1
用 **大约 50 个单词/两个段落** 总结由三重引号分隔的文本。"""在此插入文本"""

-

2. 提供参考文本

1
2
3
使用由引号分隔的提供的文章来回答问题。如果在文章中找不到答案,写“我找不到答案”。
'''<插入文章,每篇文章用三重引号分隔>'''
问题:<在此处插入问题>

提供参考能够让内容限定在某个范围

1
2
-Prompt:“讲述拿破仑的历史。”
-优化后:“根据史蒂芬·克拉克的书《拿破仑:人生、立场和遗产》,讲述拿破仑的历史。

3. 将复杂的任务拆分为更简单的子任务

处理复杂任务时,将其分解为更简单的子任务通常更有效。这样不仅可以降低错误率,还可以创建一个工作流,其中每个任务建立在前一个任务的结果上。

  • 如果需要大量独立指令处理任务,可以讲指令分为一系列的阶段.
1
2
3
4
5
6
您将收到需要在技术支持环境中进行故障排除的客户服务查询。通过以下方式帮助用户: 
- 要求他们检查所有进出路由器的电缆是否已连接。请注意,电缆随时间松动是很常见的。
- 如果所有电缆都已连接但问题仍然存在,请询问他们使用的是哪种路由器型号 - 现在您将建议他们如何重新启动他们的设备:
- 如果型号是 MTD-327J,建议他们按下红色按钮并按住它 5 秒钟,然后等待 5 分钟,然后再测试连接。
- 如果型号是 MTD-327S,建议他们拔下并重新插入,然后等待 5 分钟,然后再测试连接。
- 如果客户的问题在重启设备并等待 5 分钟后仍然存在,请通过输出 {"IT support requested"} 将他们连接到 IT 支持。
  • 分段总结长文档,并递归构建完整摘要

要总结一个很长的文档,比如一本书,我们可以使用一系列查询来总结文档的每个部分。节摘要可以被连接和总结生成摘要的摘要。这个过程可以递归地进行,直到总结了整个文档。

4. 给 GPT 时间“思考”

将简单的询问,修改为带有 ’思考‘ 的提问

1
2
-Prompt 判断学生的答案是否正确。
-优化后 首先想出你自己解决问题的方法。然后将您的解决方案与学生的解决方案进行比较,并评估学生的解决方案是否正确。在您自己完成问题之前,不要判断学生的解决方案是否正确。

这里有点类似观点3,将问题修改为多个子问题,

1
2
3
4
1. 首先找到自己的答案
2. 将答案和用户的回答对比,评估是否正确
3. 如果用户错误,在不给出答案的情况下,给出提示
4. 如果用户步骤正确,继续执行直到结果正确.

这里也可以做一些限定范围

1
2
-Prompt:“为什么天空是蓝色的?”
-让GPT“思考”的查询:“当我们看天空时,我们通常看到蓝色。这是因为大气和光的相互作用。请从光的散射和大气的组成两个方面,解释为什么天空在大多数情况下呈现蓝色。”

询问模型是否有遗漏内容

1
2
3
4
5
6
7
8
系统:
您将获得一份由三重引号分隔的文件。你的任务是选择与以下问题相关的摘录:“人工智能历史上发生了哪些重大的范式转变。” 确保摘录包含解释它们所需的所有相关上下文——换句话说,不要提取缺少重要上下文的小片段。以 JSON 格式提供输出,如下所示:[{"excerpt": "..."}, ... {"excerpt": "..."}]
用户:
"""<在此处插入文档>"""
助手:
[{“摘录”:“模型在这里写了一个摘录”},... {“摘录”:“模型在这里写了另一个摘录”}]
用户:
有更多相关的摘录吗?注意不要重复摘录。还要确保摘录包含解释它们所需的所有相关上下文——换句话说,不要提取缺少重要上下文的小片段。

5. 使用外部工具

GPT 提供了一些插件,利用插件可以得到更加准确的结果. 比如时间 、天气、金融数据等.

6. 系统地测试更改

这里我并没有做一些记录,需要明确的是 GPT 是一个语言生成工具,并不是知识生成工具.

它生成的内容可能是胡乱编造的,虽然 GPT4 在这方面做的更好. 但是从原理上讲,是不可避免的,毕竟人类说话也会胡说八道.

所以我们在使用时需要依靠它,让它帮我们完成80%的工作,剩下留给自己,把自己想象成公司的老板,雇佣了一个叫 GPT 的员工.

参考文章

bookmark

nginx TLS 重放和禁用压缩

重放

TLS 通道本身使用 MAC(消息验证代码)来防止重放攻击

Untitled.png

https://zh.wikipedia.org/wiki/傳輸層安全性協定

TLS 1.3 0-RTT

nginx 1.15.3+ 中 提供了 ssl_early_data 来禁用TLS1.3 early_data . 默认关闭. 默认情况下 Nginx 因为安全原因,没有开启 TLS 1.3 0-RTT,可以通过添加 ssl_early_data on; 指令开启 0-RTT.

如需打开需要配置

1
2
ssl_early_data on;
proxy_set_header Early-Data $ssl_early_data;

http://nginx.org/en/docs/http/ngx_http_ssl_module.html

https://blog.cloudflare.com/introducing-0-rtt/

https://medium.com/@hnasr/the-danger-of-0-rtt-a815d2b99ac6

压缩

CRIME 攻击 : 可以针对 SSL/TLS 协议和 SPDY 协议执行 CRIME 攻击.利用SSL/TLS协议和SPDY协议的压缩机制中的弱点来解密网站设置的HTTPS cookie。然后,这会迫使用户的浏览器将 HTTPS 请求转发到恶意网站并在执行攻击时访问该网站。之后,攻击者控制新请求的路径。

影响

TLS 1.0 应用程序使用 TLS 压缩、谷歌的 SPDY 协议、支持 SPDY 的旧版 Mozilla Firefox 以及支持 TLS 和 SPDY 的旧版 Google Chrome。

nginx

nginx 发版记录中 1.3.2 起禁用所有 openssl 压缩

Untitled.png

openssl 1.0.0

nginx 1.1.6

起禁用 ssl 压缩

https://mailman.nginx.org/pipermail/nginx/2012-September/035600.html

proxy_next_upstream 和 get_last_failure 的关系

proxy_next_upstream

用来指定什么情况需要做重试的操作。

default: proxy_next_upstream error timeout;

默认情况,出现了 error 或者 timeout 的情况触发重试。

invalid_header : 服务器返回了空或者无效的响应

http_500 503 503 504 403 404 429 : 返回指定代码时的响应

non_idempotent: 对非幂等情况也会重试,如果需要对 POST 等请求重试,需要加上。

get_last_failure

openresty 的 balancer.get_last_failure 函数,用来获取上一次请求的结果。

当 next_upstream 重试起作用时,检索上一次失败的详细信息。 当上一次失败时,它返回一个描述该尝试的字符串,以及描述该尝试状态码。

返回值: state_name,state_code

stage_name可能会有

  • next: 由于后端服务器发送的错误状态码而失败,源端的响应市相同的,这意味着后端连接还可以重复使用。
  • failed: 与后端服务器通信出现错误。这种情况下,后端连接不能被重用。

当 proxy_next_upstream 配置不同的值, get_last_failure 返回什么?

  1. 直接指向错误的ip,返回 502 。

    1
    state_code=502  state_name=failed  
  2. 让服务端主动返回502。未进行重试

  3. 配置 proxy_next_upstream error timeout http_502 http_404 non_idempotent;

    1
    2
    state_code = 502   state_name = failed 
    state_code = 404 state_name = next
  4. 把所有支持的都配置上。

proxy_next_upstream error timeout http_502 http_500 http_503 http_504 http_403 http_404 http_429 non_idempotent;

1
2
3
4
5
6
state_code = 502   state_name = failed 
state_code = 503 state_name = failed
state_code = 504 state_name = failed
state_code = 403 state_name = next
state_code = 404 state_name = next
state_code = 429 state_name = failed

结论

服务端错误会返回 failed。当服务端主动返回对应 code 时,如果配置了对应code 的重试, 403 404 返回 next ,5xx 和 429 返回 failed。 在 lua 进行转发时对这两种情况都需要做下一步的尝试。