Golang-后端总结

什么时候用指针,什么时候用值传递,结构体、尤其sync包下的, 都要用指针传递; 而一些轻量的数据可以使用值传递,也不改变其值;
尽量避免反射,在高性能服务中杜绝反射的使用
多进行复用,使用sync.Pool


线上问题优化:
批量读与写,主要问题: CPU占用过高,主要因为申请释放大量资源造成的;

解决: 使用“sync.Pool”实现了一个对象池,
p:=sync.Pool{
New: new buff(),
}
p.Get().(*buff)
p.Put(buff.clean())


另外使用goroutine并发读写;

服务的高峰 PCT99 从100ms降低到15ms。

 

 

 


微服务,每一个服务都有自己的数据库,微服务划分: 会员认证模块、评论模块、点赞模块等;

gRPC是由Google主导开发的RPC框架,使用HTTP/2协议并用Protobuf作为序列化工具;

要点:
中心化配置
服务发现
日志
分布式探寻
熔断
负载均衡
边缘
监测
安全

 

服务注册和发现, API GATEWAY,REST API; 消息中心,RPC API; 也就是对外RESTFUL服务使用json,对内使用protobuf

 

中心配置: etcd


服务发现: 请求达到网关后,要知道服务的API的IP地址是多少;在生产实践中,主要有两种服务发现机制:客户端发现和服务端发现。
客户端发现:
例如三个客户端的实例向组测服务注册(self-registration心跳模式),该服务可以是访问服务登记表,也就是一个可用服务的数据库,然后客户端使用一种负载均衡算法(一致性哈希),选择一个可用的服务实例然后发起请求。
也使用etcd

 

Go kit套件, 可以在其中使用http,grpc各种传输; 分布式探寻: 使用分区规则进行并行计算; 日志:grpc或kafka进行汇总 ; 统计信息: 各个方法的连接次数,gc四分位点等;


熔断: go-kit的hystrix,主要包含: 1. 请求超时的时间; 2. 允许的最大并发请求数; 3. 熔断开启多久尝试发起一次请求;


负载均衡: 将同一个服务处理的代码分布到三台机器,所有请求先进负载均衡服务器,


网络传输三层结构:
HTTP报文: GET www...
TCP报文: 源地址: 3345(客户端),目的地: 80(负载均衡服务器)
IP数据包: 源地址: ip-客户端,目的地: ip-负载均衡服务器

在负载均衡服务器,将目的地的 TCP端口、IP地址改城 RS1服务的地址;


七层结构就是在三层的基础上, 加上URL,浏览器,语言等;
但是还有一个问题: 请求很短,但是响应返回的是一个html文件; 让Load Balancer只处理请求,让各个服务器把响应直接发给客户端;


解决:
首先让所有的服务器都有同一个IP, 我们把他称为VIP吧(如图中115.39.19.22)。

网络传输四层结构:
以太网帧: 源地址: 11:27:F5:.. 目的地: ???
HTTP报文: GET www...
TCP报文: 源地址: 3345(客户端),目的地: 80 vip
IP数据包: 源地址: ip-客户端,目的地: 115.39.19.22 vip


使用ARP协议,将115.39.19.22 vip 给广播出去, 然后具有此IP机器就会回复自己的MAC地址。


这样请求就能分发出去,响应就能由VIP 直接发给客户端;

 

 

 

 

 

 

tcp讲解:
现在的典型模型是, Non-Block + I/O多路复用。
而Go开发者无需关注socket是否是 non-block的,也无需亲自注册文件描述符的回调,只需在每个连接对应的goroutine中以“block I/O”的方式对待socket处理即可;


l,err := net.Listen("tcp",":8888") // 服务端监听这个端口

for {
c,err := l.Accept() // 相当于在这里进行read阻塞,等待flag;
}

go handleConn(c) // 获取连接后进行处理

 

TCP Socket的连接的建立需要经历客户端和服务端的三次握手的过程。连接建立过程中,服务端是一个标准的Listen + Accept的结构;

// 客户端尝试连接:
conn,err := net.DialTimeout("tcp",":8080",2 * time.Second)

在握手的过程中会经历以下情况:
1. 网络不可达或对方服务未启动; 会立即返回报错;

2. server端的listen backlog队列满;

3. 网络延迟较大,Dial阻塞并超时

 

conn连接上之后, 就可以使用读写,conn.Read(),conn.Write()
TCP是全双工通信,因此每个方向都 有独立的数据缓冲。当发送方将对方的接收缓冲区以及自身的发送缓冲区写满后,Write就会阻塞。
读写都是lock safe的,使用锁完整读,完整写;

 

 

 

 

 

 

 

 

 

rpc讲解:

RPC(Remote ProcedureCall,远程过程调用), 构建于TCP或UDP,或者是HTTP之上,允许直接调用另一台计算机上的程序,而无需额外地为这个调用过程编写网络通信相关代码。

使用:
在RPC服务端,可将一个对象注册为可访问的服务,之后该对象的公开方法就能够以远程的方式提供访问。一个RPC服务端可以注册多个不同类型的对象,但不允许注册同一类型的多个对象。

func (t *T) MethodName(argType T1,replyType *T2) error
第一个参数表示由 RPC 客户端传入的参数,第二个参数表示要返 回给RPC客户端的结果,该方法最后返回一个error 类型的值。

调用RPC客户端的Call(), 同步; 调用RPC客户端的Go(),异步;

如果没有明确指定RPC传输过程中使用何种编码解码器,默认将使用 Go 标准库提供的encoding/gob 包进行数据传输; Gob是二进制编码的数据流,只能用于go语言;

 


arith : 服务对象可以很简单, 比如类型是int或者是interface{},重要的是它输出的方法。

服务端:
rpc.Register(arith)
rpc.HandleHTTP()
l,e := net.Listen("tcp",":1234")
go http.Serve(l,nil)


客户端:
client,err := rpc.DialHTTP("tcp",serverAddress + ":1234")
args := &server.Args{7,8}
var reply int
err = client.Call("Arith.Multiply",args,&reply) // 使用同步


quotient := new(Quotient)
divCall := client.Go("Arith.Divide",&quotient,nil) // 使用异步
replyCall := <-divCall.Done

 

 


负载均衡的rpc方案:

服务端:
var (
//etcd服务地址
etcdServer = "127.0.0.1:2379"
//服务的信息目录
prefix = "Arith.Multiply"
//当前启动服务实例的地址
instance = "127.0.0.1:50052"
//服务实例注册的路径
key = prefix + instance
//服务实例注册的val
value = instance
ctx = context.Background()
//服务监听地址
serviceAddress = ":50052"
)

 

// 将连接信息, 写入到etcd的leader上;
// 创建注册器
registrar := etcdv3.NewRegistrar(client,etcdv3.Service{
Key: key,
Value: value,
},log.NewNopLogger())


// 注册器启动注册
registrar.Register()

 

// 将rpc方法, 添加到tcp端口;
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l,serviceAddress) // 使用服务注册的地址
if e != nil {
fmt.Print("listen error:",e)
}
http.Serve(l,nil)



type Arith int

func (t *Arith) Multiply(args *Args,reply *int) error {
*reply = args.A * args.B
return nil
}

 


客户端:

// 添加etcd地址, 以及需要的服务名称;
var (
//注册中心地址
etcdServer = "127.0.0.1:2379"
//监听的服务前缀
prefix = "Arith.Multiply"
ctx = context.Background()
)

//创建实例管理器,此管理器会Watch监听etc中prefix的目录变化更新缓存的服务实例数据
instancer,err := etcdv3.NewInstancer(client,prefix,logger)


//创建端点管理器, 此管理器根据Factory和监听的到实例创建endPoint并订阅instancer的变化动态更新Factory创建的endPoint
endpointer := sd.NewEndpointer(instancer,reqFactory,logger)
//创建负载均衡器
balancer := lb.NewRoundRobin(endpointer)

//func reqFactory(instanceAddr string) 中client,instanceAddr), client.Call("Arith.Multiply",&reply) 具体调用哪个方法;

 

 

 


MQTT协议:
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上;

服务质量Qos:
“至多一次”,消息发布完全依赖底层TCP/IP网络。会发生消息丢失。
“至少一次”,确保消息到达,但消息重复可能会发生。
“只有一次”,确保消息到达一次。在一些要求比较严格的计费系统中。

特点: 小型传输,只有2字节; Last Will: 通知同一主题下的其他设备, 该设备已断开;

组件: 发布者(Publish)、订阅者(Subscribe)、代理(Broker)
发布者与订阅者都是客户端,发布者可以同时是订阅者;
代理是服务端;


MQTT传输的消息分为:主题(Topic)和负载(payload)两部分;

主题用于订阅者订阅, 负载可以理解为消息的内容。

MQTT客户端: 1. 发布其他客户端可能会订阅的信息; 2. 订阅其它客户端发布的消息;
MQTT服务器(broker): 2. 它是位于消息发布者和订阅者之间,1. 接受来自客户的网络连接; 2. 接受客户发布的应用信息;


连接流程:
每个客户端与服务器建立连接后就是一个会话,订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。

MQTT协议数据包结构:
固定头(Fixed header) 主要是连接信息、可变头(Variable header)、消息体(payload)三部分构成。
MQTT 协议不是双向信任的,它没有提供客户端验证服务端身份的机制。

 


安装broker服务器:
NGINX:nginx-1.11.0.tar.gz
EMQ:emqx-centos6.8-v3.0-beta.4.x86_64.rpm 单节点可接收50-100万连接;


emr:
集群方式采用基于 static 节点列表自动集群:
cluster.static.seeds = [email protected],[email protected]

LDAP认证: 安装证书,service emqx start;


nginx:
http {
server 192.168.1.2:18083;
server 192.168.1.3:18083;
}

 

安装客户端:

连接broker: clinetOptions := mqtt.NewClientOptions().AddBroker("tcp://xxxxx:1883").SetUsername("admin").SetPassword("public")
订阅消息: token := client.Subscribe("go-test-topic",1,messageSubHandler) token.Wait()
发布消息: token := client.Publish("go-test-topic",false,text) token.Wait()

 

 





##一些想象的实践:


机器数据 -> broker appoll(ssl双向加密) -> raw;
通过至少一次的策略, 防止数据丢失, 在服务端进行简单etl;

服务限流与容灾, 限流防止内存崩溃, 加入阻塞队列, 容灾: 涉及状态机、心跳、重连策略; 使用最大负载从上一状态开始跑; 宕机状态的邮件分发;


熔断 -> 限流 -> appoll -> 服务断掉恢复状态 -> nigix -> vue

 

 

概念
服务雪崩效应
原因:由于延时或负载过高等导致请求积压,占用大量系统资源,服务器达到性能瓶颈,服务提供者不可用
现象:上游服务故障导致下游服务瘫痪,出现连锁故障
应对策略:
扩容
控制流量
熔断
服务降级


hystrix.ConfigureCommand("aaa",hystrix.CommandConfig{
Timeout: 5000,
MaxConcurrentRequests: 5,
})
1、Timeout 【请求超时的时间】

2、ErrorPercentThreshold【允许出现的错误比例】

3、SleepWindow【熔断开启多久尝试发起一次请求】

4、MaxConcurrentRequests【允许的最大并发请求数】

5、RequestVolumeThreshold 【波动期内的最小请求数,默认波动期10S】

相关文章

类型转换 1、int转string 2、string转int 3、string转float ...
package main import s &quot;strings&quot; import...
类使用:实现一个people中有一个sayhi的方法调用功能,代码如...
html代码: beego代码:
1、读取文件信息: 2、读取文件夹下的所有文件: 3、写入文件...
配置环境:Windows7+推荐IDE:LiteIDEGO下载地址:http:...