微服务网关
2020-06-15 11:45:00 0 举报
AI智能生成
微服务网关
作者其他创作
大纲/内容
积木拼装
构建配置更新模块
代码实现
功能测试
接入tcp服务代理
代码整合
功能测试
接入http服务代理
代码整合
功能测试
在线配置管理
实现一套AdminLTE后台
实现服务接口
实现租户接口
构建积木
网络基础知识(为何需要补充网络基础)
OSI七层网络模型 与 TCP/IP四层协议模型
经典应用
应用层:HTTP、FTP、Telnet、HTTP2、
传输层:TCP、UDP
网络层:ICMP、ARP
数据链路层
层次体系结构图
协议和数据包
整个数据包结构图
经典协议数据包图
TCP协议图
HTTP协议图
websocket协议图
tcp 协议拓展
三次握手、四次挥手
为啥time_wait需要等待2MSL?
MSL是啥?
(Maximum Segment Lifetime,30秒--1分钟)
四次握手图
1、确保ack发送成功可能对方buffer慢了丢包了都要重发
2、防止已失效报文重新请求连接。
参照:https://blog.csdn.net/q1007729991/article/details/69686781#commentBox
https://www.cnblogs.com/pengmn/p/10836784.html
为啥服务器会出现大量close_wait?
现象图:Too many open files。https://www.cnblogs.com/grey-wolf/p/10936657.html
直接上图贴代码未close样例
tcp 流量、拥塞控制
为什么会出现粘包、如何拆包?
回顾数据包结构
了解缓冲区的定位
传输过程图(带报文细节)
为啥会粘包?
应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包。
应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包。
进行MSS(最大报文长度)大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆包。
接收方法不及时读取套接字缓冲区数据,这将发生粘包。
如何拆包?
使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容。
设置消息边界,服务端从网络流中按消息编辑分离出消息内容,一般使用‘\n’。
更为复杂的协议,如mqtt、json、protobuf
示例代码:
定义codec形式:https://segmentfault.com/a/1190000013493942
贴上自己的核心逻辑实现
如何获取完整数据报文?
定义数据格式:msg_header+content_len+content
编码_encode
解码_decode
tcp_client
1、连接服务器
2、数据编码
tcp_server
1.监听端口
2.接收请求
3.创建独立协程
4. 数据解码
拥塞控制图
窗口图
发送者与接收者图
流量控制
为啥使用流量控制
流量控制图
代码实操
udp服务器与客户端,代码演示
服务端
step 1 监听服务器
step 2 循环读取消息内容
step 3 回复数据
客户端
step 1 连接服务器
step 2 发送数据
step 3 接收数据
tcp服务器与客户端,代码演示
服务器
1、监听端口
2、建立套接字连接
3、创建处理协程
客户端
1、连接服务器
2、读取命令行输入
3、一直读取直到读到\n
4、读取Q时停止
5、回复服务器信息
客户端未执行close,会一致使连接处于 ESTABLISHED
服务端未执行close, 会使服务端连接处于 close_wait状态,客户端处理 fin_wait2状态
http服务器与客户端,代码演示
服务器
创建路由器
设置路由规则
创建服务器
监听端口并提供服务
客户端
创建连接池
创建客户端
请求数据
读取内容
服务器源码分析
理解函数一等公民
注册路由
type ServeMux struct{}
mu sync.RWMutex
m map[string]muxEntry
http.ServeMux.HandleFunc
Handle
mux.m[pattern] = e
开启服务
http.Server.ListenAndServe
Serve(l net.Listener)
go c.serve
处理请求
http.serverHandler{c.server}.ServeHTTP(w, w.req)
mux.Handler(r)
mux.handler(r.Host, r.URL.Path)
mux.match(path)
v, ok := mux.m[path]
客户端源码分析
主要结构体
type Client struct {}
Transport RoundTripper
Timeout time.Duration
type RoundTripper interface {}
RoundTrip(*Request) (*Response, error) 请求下游接口
请求流程
func (c *Client) Get(url string)
c.Do(req)
c.do(req)
c.send(req, deadline)
send(req, c.transport(), deadline)
resp, err = rt.RoundTrip(req)
func (t *Transport) roundTrip(req *Request)
Transport 连接池
结构体
type Transport struct {
idleMu sync.Mutex
wantIdle bool // 用户是否已关闭所有的空闲连接
idleConn map[connectMethodKey][]*persistConn // 保存从connect 到persistConn的映射
idleConnCh map[connectMethodKey]chan *persistConn
}
connectMethodKey{ //参照func (cm *connectMethod) key() 加深理解
proxy: proxyStr, //代理url, 浏览器透明代理
scheme: cm.targetScheme, //协议 http、https
addr: targetAddr, //代理base的url, 下游服务base地址
onlyH1: cm.onlyH1, //是否http1.1
}
roundTrip流程
func (t *Transport) roundTrip(req *Request)
pconn, err := t.getConn(treq, cm)
type persistConn struct {
br *bufio.Reader // from conn
bw *bufio.Writer // to conn
reqch chan requestAndChan //read by readLoop
writech chan writeRequest //read by writeLoop
}
pc, idleSince := t.getIdleConn(cm) 尝试获取闲置的连接
select
case <-t.incHostConnCount(cmKey): 确认每主机是否有限制?
case pc := <-t.getIdleConnCh(cm): 尝试获取连接
case <-req.Context().Done(): 一边监听取消事件
go func() { pc, err := t.dialConn(ctx, cm) 异步创建连接
go pconn.readLoop() 一边读数据
监听 pconn.reqch,写入conn
go pconn.writeLoop() 一边写数据
监听 pconn.writech,写入conn
select
case v := <-dialc: 新增连接成功
case pc := <-idleConnCh: 有可用连接
resp, err = pconn.roundTrip(treq)
pc.writech <- writeRequest
默认Client、Transport配置
超时时间概览图
Keepalived时间与IdleConnTimeout区别
server端连接 是由 客户端指定 keepalived 产生的
client端连接 是由 transport连接池设置的
keepalive是发送探活报时间间隔 https://blog.csdn.net/qq_42316690/article/details/80463992
网络代理
什么是网络代理
代理用户请求信息:用户真实请求通过网络代理完成转发到达目标服务器,目标服务器相应后再通过网络代理回传。
代理类型
正向代理:
是一种客户端的代理技术,帮助客户端访问无法访问的服务资源,可以隐藏用户真实IP。
比如:浏览器上设置web代理
正向代理图
代理原理图分析
先准备代码(上图),然后就是配置浏览器器(上图),然后就是请求浏览器正向代理功能演示
这种正向代理技术不是我们研究的重点
核心代码逻辑
代理接收客户端请求,复制原请求对象,并根据数据配置新请求的各种参数
把新请求发送到服务端,并接收到服务器端返回
代理服务器对相应做一些处理,然后返回给客户端
反向代理:
是一种服务端的代理技术,帮助服务器做负载均衡、缓存、提供安全校验等,可以隐藏服务器真实IP。
比如:LVS技术、nginx_pass等
反向代理图
代理原理图分析
先准备代码(上图),然后演示结果也直接贴图,
这个功能比较复杂我们稍后再详细讲述,引入http代理原理分析
核心代码逻辑
代理接收客户端请求,更新请求服务器信息
把新请求发送到服务器,并接收到服务器端返回
拷贝返回请求头header信息,给客户端
拷贝返回请求内容,给客户端
更加详细的原理阐述 https://www.cnblogs.com/xuepei/p/10437114.html
http代理
首先来个http代理的串讲,他们之间的相互关系
首先回顾一下上面章节讲到反向代理代码实现及原理
当前实现的http代理问题
无法设置:错误回调及错误日志处理
无法:更改代理返回内容
不支持:负载均衡
不支持:url重写
不支持:限流、熔断、
不支持:数据统计
不支持:权限验证
不支持:websocket协议
使用ReverseProxy实现一个http代理
代码实现
只做一个简单功能演示
可以实现的功能有:
连接池功能
支持随机的负载均衡
贴图
后续我们会讲解各种负载均衡策略
更改内容支持
贴图
错误信息回调
贴图
url重写功能
贴图
支持websocket服务
后续讲解
支持https代理
后续讲解
都有哪些实现细节,我们一一来看
看看它的难点有哪些?
了解技术细节有利于我们分析问题
首先补充一下header知识:http header特殊头部详解
虽然不常用,但是有利于理解 http代理实现细节
引出为啥 connection hop 头?原理图说明为好
"X-Forwarded-For"
如果一个请求经过了多个代理服务器,那么每一个代理服务器的IP地址都会被依次记录在内
格式:<clientIP>,<proxy1>,<proxy1>
图片说明:https://www.cnblogs.com/diaosir/p/6890825.html
"Connection"
去除标准的逐段传输(hop-by-hop)头
这些头定义了发出者和第一个实体之间的连接,而不是和目的地节点的连接。
一般值为:keep-alived/close
图片说明
"TE"
不常用了解即可
request header
表示希望使用的传输编码类型
TE: trailers, deflate;q=0.5
trailers:期望在采用分块传输编码相应中接收挂载字段,
deflate:zlib编码,
q:优先级排序
"Trailer"
不常用了解即可
response header
允许发送方在消息后面添加额外的元信息
参见示例代码:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Trailer
再次补充一下statusCode知识:
100
在使用curl做POST的时候, 当要POST的数据大于1024字节的时候
curl并不会直接就发起POST请求, 而是会分为俩步
1. 发送一个请求, 包含一个Expect:100-continue, 询问Server使用愿意接受数据
2. 接收到Server返回的100-continue应答以后, 才把数据POST给Server
http://www.laruence.com/2011/01/20/1840.html
101
状态码表示服务器应客户端升级协议的请求
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
关于websocket 协议升级后期再讲解
明确核心结构体
type ReverseProxy struct {
Director func(*http.Request)
Transport http.RoundTripper
FlushInterval time.Duration
ErrorLog *log.Logger
BufferPool BufferPool
ModifyResponse func(*http.Response) error
ErrorHandler func(http.ResponseWriter, *http.Request, error)
}
核心方法:func (p *ReverseProxy) ServeHTTP
验证是否请求终止
设置请求 ctx 信息
深拷贝header
修改req
Upgrade头的特殊处理
追加clientIp信息
向下游请求数据
处理升级协议请求
移除逐段头部
修改返回内容
拷贝头部的数据
写入状态码
周期刷新到内容到response
拓展ReverseProxy功能
负载轮询
随机负载均衡
轮询负载均衡
加权负载均衡
算法分析
核心逻辑:每论都计算出最大临时权重节点
每节点有三个变量:
weight 权重
currentWeight 临时权重
effectiveWeight 有效权重
算法逻辑
统计所有有效权重之和
变更节点临时权重为的节点临时权重+节点有效权重
有效权重默认与权重相同,通讯异常时-1, 通讯成功+1,直到恢复到weight大小
选择最大临时权重点节点
变更临时权重为 临时权重-有效权重之和
请求次数分析:参考网页做下分析
一致性hash负载均衡
一致性hash算法
算法目标
新增或减少一台服务器以后,不会造成大面积的访问错误。
同一个资源仍然会映射到之前同样的服务器上。
新增一台服务器时,新的服务器需要尽量分担存储其他服务器的缓存资源。
少一台服务器时,其他服务器也可以尽量分担存储它的缓存资源
算法实现图
构建一个环形空间
crc32算法
把主机映射到环形空间上
对象可以沿顺时针查找cache
添加或移除cache,单调分析
添加虚拟节点,确保平衡性
来源ip
请求url
反向代理拓展负载均衡
使用工厂模式
使用接口封装统计方法
中间件支持
后话,有了中间件就到了网关大显神通的时候了,我们的rpc、grpc、http都离不开它
有了它就可以便捷加入你想要的功能了
学习一遍之后可以触类旁通了
它处在HTTP request和HTTP response之间,用来实现某种功能,比如:日志、权限认证、限流等操作
实现原理
认识洋葱结构图
方法链条
代码实现
责任链模式
实现思路
router 创建中间件链式结构
router 构造基于链表的方法链
router 实现ServeHTTP方法,方便引用调用
具体实现,因涉及多种回调和实现比较复杂一些
我们简单过一下代码不必深究,下面还有一种控制灵活简单的方式
方法切片模式
实现思路
router 用于构建中间件方法数组
routerContext 用于方法回放及方法控制
routerContext 实现ServeHTTP方法,方便引用调用
将核心代理方法传入即可完成代理功能
代码分析
后话,有了中间件就到了网关大显神通的时候了
限流、熔断
高并发三大利器:缓存、降级、限流
限流
限流意义:对某一时间窗口请求数限制,保证系统可用性和稳定性。
漏桶算法 图讲解,考虑重新绘制
令牌桶算法 图讲解
代码示例
限流器提供三种使用方式
Allow
判断当前是否可以取到token
Wait
阻塞等待直到,取到一个token
Reserve
返回需要等待多久才有新的 token,这样就可以等待指定时间执行任务
源码窥探
计算上次请求和当前请求时间差
计算时间差内可生成token数,求得token总数
计算每次请求后剩余 token 数
如果 token 未负数,计算等待时间
更新当前 token 数
https://www.jianshu.com/p/1ecb513f7632
限流代码我们暂时先不接入网关,我们接着往下学习
https://www.cnblogs.com/cjsblog/p/9379516.html
https://mp.weixin.qq.com/s?src=11&timestamp=1579832864&ver=2115&signature=H*QOdcw6ZEby2GpK*5T9cvg6nAX26XlV3rD2BOUWXMuQc5bigapyO6-xO80P3GSQ8lG-9dey2q9zSA6BuG6fcf5VxHuDNIX3w5ui0FcAHYjhEfYPPCgILATcB3CGLToK&new=1
https://mp.weixin.qq.com/s?src=11&timestamp=1579832361&ver=2115&signature=c6202A2BOxKWmK*-n6HM5CScwQy9QCuw1i4h2AMwWUL-rsNZrS3vCnSfB8wnc7AStbmL*IGPfempvhccouDaSzsJk-ou5XMN9fVyOH8b-ezbfBMyQ2hHzH2CDHUvhSFi&new=1
熔断原理
集限流、熔断、容错 类库 hystrix-go
使用方法示例
同步、异步二种方式
源码分析
流程图准备,从下面文档中获取
设置熔断配置 hystrix.ConfigureCommand(
执行熔断方法 hystrix.Do
func GoC(
GetCircuit(name)
newCircuitBreaker(name)
初始化交换统计 c.metrics = newMetricExchange
初始化指标收集器数组 m.metricCollectors = []DefaultMetricCollector
go m.Monitor()
for update := range m.Updates
go m.IncrementMetrics 记录到metric中
初始化执行池 c.executorPool = newExecutorPool(name)
p.Metrics = newPoolMetrics(name)
go m.Monitor()
for u := range m.Updates {
更新时间窗数据
m.Executed.Increment(1)
m.MaxActiveRequests.UpdateMax(float64(u.activeCount))
生产并发数量个桶 p.Tickets <- &struct{}{}
sync.NewCond 通知锁使用方法
验证是否允许请求 cmd.circuit.AllowRequest
或 !circuit.IsOpen()
10秒内请求量小于熔断阈值直接返回false
10秒内错误百分比超过错误阈值量
circuit.setOpen()
或 circuit.allowSingleTest()
指定秒数的探测
获取令牌 cmd.ticket = <-circuit.executorPool.Tickets
go 执行命令 任何一个先执行完就归还ticket
go 执行超时验证 任何一个先执行完就归还ticket
结构体嵌套结构:
type CircuitBreaker struct {
Name string
open bool
forceOpen bool
mutex *sync.RWMutex
openedOrLastTestedTime int64
executorPool *executorPool
metrics *metricExchange
}
执行池结构
type executorPool struct {
Name string
Metrics *poolMetrics
Max int
Tickets chan *struct{}
}
用以10秒以熔断核心数据收集
type poolMetrics struct {
Mutex *sync.RWMutex
Updates chan poolMetricsUpdate
Name string
MaxActiveRequests *rolling.Number //最大活动请求
Executed *rolling.Number //执行统计
}
type Number struct {
Buckets map[int64]*numberBucket
Mutex *sync.RWMutex
}
func (r *Number) Increment(i float64) {
if i == 0 {
return
}
r.Mutex.Lock()
defer r.Mutex.Unlock()
b := r.getCurrentBucket()
b.Value += i
r.removeOldBuckets()
}
//指标交换器
type metricExchange struct {
Name string
Updates chan *commandExecution
Mutex *sync.RWMutex
metricCollectors []metricCollector.MetricCollector
}
用以10秒内统计多种指标
DefaultMetricCollector{
successes *rolling.Number
failures *rolling.Number
timeouts *rolling.Number
}
类库与项目整合
思路很清晰
实现熔断器配置
添加熔断中间件
使用 dashboard
测试时不要使用本地IP,使用局域网ip
https://cloud.tencent.com/developer/article/1454740
http://127.0.0.1:2001/hystrix.stream
http://192.168.3.4:8080/hystrix/
http://192.168.3.4:8080/hystrix/monitor?stream=http%3A%2F%2F192.168.3.4%3A2001%2Fhystrix.stream&title=stream
效果展示
docker run -d -p 8080:9002 --name hystrix-dashboard mlabouardy/hystrix-dashboard:latest
网关实战
核心结构体与配置
权限认证
客户端ip白名单验证
原理
示例
白名单 配置
添加中间件
jwt验证
原理
示例
配置
添加中间件
数据统计
原理
示例
websocket支持测试
ModifyResponse必须要加Upgrade验证判断
构建自测环境
http://127.0.0.1:2002/websocket
https支持测试
1、证书生成步骤 可跳过
2、启动下游机器 demo/proxy/reverse_proxy_http2/real_server
3、设置TLSClientConfig的特殊设置,如果不考虑证书可以跳过即可
https测试
创建证书文件和私钥
openssl genrsa -out server.pem 2048
openssl req -new -x509 -key server.pem -out server.crt -days 3650
curl -k 'https://127.0.0.1:3002/'
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
grpc无法被正常代理
grpc代理
tcp代理支持
tcp代理原理
原理图
实现步骤
构建一个tcp服务器
1、监听服务
2、获取连接请求
3、构建新连接对象并设置超时时间及keepalive
4、设置方法退出时连接关闭
5、调用回调方法
构建一个tcp代理
1、代理实现回调方法结构
2、连接下游服务
3、设置退出时关闭连接
4、一边读数据一边写数据
tcp服务器与tcp代理结合,完整中间件配置,实现基于负载均衡的代理
grpc透明代理支持
grpc基本知识科普
grpc是基于http2协议的
grpc示例demo
http2到grpc数据是如何转换的? 代码分析之
grpc实现透明反向代理难点有哪些?
http与grpc协议不同
grpc本身需要proto结构体才能做转换操作
grpc是双向数据流
grpc代理原理
原理图:要体现原始字节与proto关系
实现步骤
构建一个codec解码器
构建支持原始字节、支持proto的解码器
如果拿到的是原始字节,则不需要解码,如果不是原始字节则需要通过proto转换一下
构建输出方法,设置到server参数中
构建回调函数
构建一个下游连接器
构建一个回调类
获取上游数据写入到下游
获取下游头及数据写入到上游
封装输出方法,设置到server参数中
负载均衡拓展服务发现
分布式数据库
主要当前主流:etcd、zookeeper
原理
代码实现
测试功能
brew services start zookeeper
构建服务发现
认识zookeeper核心功能
认识基本功能
增、删、改、查 api
监听子节点变化
为啥主节点一定能要是持久化节点?
代码演示
监听节点内容变化
zk: version conflict问题
代码演示
与负载均衡中间件整合
观察者模式
优点:逻辑解耦,后期可以拓展N种配置更新方法
1、subject主题对象是模块配置
2、负载均衡器是观察者
3、主题与观察者绑定
功能代码测试
结合步骤:
1、新增构建负载均衡的方法
2、下层负载算法实现 Update
功能代码测试
0 条评论
下一页