文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么在golang中实现负载均衡

2023-06-14 22:32

关注

本篇文章为大家展示了怎么在golang中实现负载均衡,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

golang的优点

golang是一种编译语言,可以将代码编译为机器代码,编译后的二进制文件可以直接部署到目标机器而无需额外的依赖,所以golang的性能优于其他的解释性语言,且可以在golang中使用goroutine来实现并发性,它提供了一个非常优雅的goroutine调度程序系统,可以很容易地生成数百万个goroutine。

1.首先就是服务器的信息

package balancetype Instance struct {    host string    port int}func NewInstance(host string, port int) *Instance {    return &Instance{        host: host,        port: port,    }}func (p *Instance) GetHost() string {    return p.host}func (p *Instance) GetPort() int {    return p.port}

2.接着定义接口

package balancetype Balance interface {        DoBalance([] *Instance,...string) (*Instance,error)}

3.接着,是实现接口,random.go

package balanceimport (    "errors"    "math/rand")func init()  {    RegisterBalance("random",&RandomBalance{})}type RandomBalance struct {}func (p *RandomBalance) DoBalance(insts [] *Instance,key...string) (inst *Instance, err error) {    if len(insts) == 0 {        err = errors.New("no instance")        return    }    lens := len(insts)    index := rand.Intn(lens)    inst = insts[index]    return}

roundrobin.go

package balanceimport (    "errors")func init() {    RegisterBalance("round", &RoundRobinBalance{})}type RoundRobinBalance struct {    curIndex int}func (p *RoundRobinBalance) DoBalance(insts [] *Instance, key ...string) (inst *Instance, err error) {    if len(insts) == 0 {        err = errors.New("no instance")        return    }    lens := len(insts)    if p.curIndex >= lens {        p.curIndex = 0    }    inst = insts[p.curIndex]    p.curIndex++    return}

4 然后,全部交给管理器来管理,这也是为什么上面的文件全部重写了init函数

package balanceimport (    "fmt")type BalanceMgr struct {    allBalance map[string]Balance}var mgr = BalanceMgr{    allBalance: make(map[string]Balance),}func (p *BalanceMgr) registerBalance(name string, b Balance) {    p.allBalance[name] = b}func RegisterBalance(name string, b Balance) {    mgr.registerBalance(name, b)}func DoBalance(name string, insts []*Instance) (inst *Instance, err error) {    balance, ok := mgr.allBalance[name]    if !ok {        err = fmt.Errorf("not fount %s", name)        fmt.Println("not found ",name)        return    }    inst, err = balance.DoBalance(insts)    if err != nil {        err = fmt.Errorf(" %s erros", name)        return    }    return}

下面进行测试:

func main() {    var insts []*balance.Instance    for i := 0; i < 10; i++ {        host := fmt.Sprintf("192.168.%d.%d", rand.Intn(255), rand.Intn(255))        port, _ := strconv.Atoi(fmt.Sprintf("880%d", i))        one := balance.NewInstance(host, port)        insts = append(insts, one)    }    var name = "round"    if len(os.Args) > 1 {        name = os.Args[1]    }    for {        inst, err := balance.DoBalance(name, insts)        if err != nil {            fmt.Println("do balance err")            time.Sleep(time.Second)            continue        }        fmt.Println(inst)        time.Sleep(time.Second)    }}

5.如果想扩展这个,又不入侵原来的代码结构,可以类比上面实现dobalance接口即可

package addimport (    "awesomeProject/test/balance"    "fmt"    "math/rand"    "hash/crc32")func init() {    balance.RegisterBalance("hash", &HashBalance{})}type HashBalance struct {    key string}func (p *HashBalance) DoBalance(insts [] *balance.Instance, key ...string) (inst *balance.Instance, err error) {    defKey := fmt.Sprintf("%d", rand.Int())    if len(key) > 0 {        defKey = key[0]    }    lens := len(insts)    if lens == 0 {        err = fmt.Errorf("no balance")        return    }    hashVal := crc32.Checksum([]byte(defKey), crc32.MakeTable(crc32.IEEE))    index := int(hashVal) % lens    inst = insts[index]    return}

这样就能交给管理器统一管理了,而且不会影响原来的api。

补充:golang grpc配合nginx实现负载均衡

概述

grpc负载均衡有主要有进程内balance, 进程外balance, proxy 三种方式,本文叙述的是proxy方式,以前进程内的方式比较流行,靠etcd或者consul等服务发现来轮询,随机等方式实现负载均衡。

现在nginx 1.13过后正式支持grpc, 由于nginx稳定,高并发量,功能强大,更难能可贵的是部署方便,并且不像进程内balance那样不同的语言要写不同的实现,因此我非常推崇这种方式。

nginx的配置

确认安装版本大于1.13的nginx后打开配置文件,写入如下配置

upstream lb{#负载均衡的grpc服务器地址  server 127.0.0.1:50052;  server 127.0.0.1:50053;  server 127.0.0.1:50054;  #keepalive 500;#这个东西是nginx和rpc服务器群保持长连接的总数,设置可以提高效率,同时避免nginx到rpc服务器之间默认是短连接并发过后造成time_wait过多}server {  listen       9527     http2;  access_log  /var/log/nginx/host.access.log  main;  http2_max_requests 10000;#这里默认是1000,并发量上来会报错,因此设置大一点  #grpc_socket_keepalive on;#这个东西nginx1.5过后支持  location / {    grpc_pass grpc://lb;    error_page 502 = /error502grpc;  }  location = /error502grpc {    internal;    default_type application/grpc;    add_header grpc-status 14;    add_header grpc-message "Unavailable";    return 204;  }}

可以在host.access.log日志文件里面看到数据转发记录

proto文件:

syntax = "proto3"; // 指定proto版本package grpctest;     // 指定包名// 定义Hello服务service Hello {    // 定义SayHello方法    rpc SayHello(HelloRequest) returns (HelloReply) {}}// HelloRequest 请求结构message HelloRequest {    string name = 1;}// HelloReply 响应结构message HelloReply {    string message = 1;}

客户端:

客户端连接地址填写nginx的监听地址,相关代码如下:

package mainimport ( pb "protobuf/grpctest" // 引入proto包 "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" "fmt" "time")const ( // Address gRPC服务地址 Address = "127.0.0.1:9527")func main() { // 连接 conn, err := grpc.Dial(Address, grpc.WithInsecure()) if err != nil {  grpclog.Fatalln(err) } defer conn.Close() // 初始化客户端 c := pb.NewHelloClient(conn) reqBody := new(pb.HelloRequest) reqBody.Name = "gRPC" // 调用方法 for{  r, err := c.SayHello(context.Background(), reqBody)  if err != nil {   grpclog.Fatalln(err)  }  fmt.Println(r.Message)  time.Sleep(time.Second) }}

服务端:

package mainimport ( "net" "fmt" pb "protobuf/grpctest" // 引入编译生成的包 "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/grpclog")const ( // Address gRPC服务地址 Address = "127.0.0.1:50052" //Address = "127.0.0.1:50053" //Address = "127.0.0.1:50054")var HelloService = helloService{}type helloService struct{}func (this helloService) SayHello(ctx context.Context,in *pb.HelloRequest)(*pb.HelloReply,error){ resp := new(pb.HelloReply) resp.Message = Address+" hello"+in.Name+"." return resp,nil}func main(){ listen,err:=net.Listen("tcp",Address) if err != nil{  grpclog.Fatalf("failed to listen: %v", err) } s:=grpc.NewServer() pb.RegisterHelloServer(s,HelloService) grpclog.Println("Listen on " + Address) s.Serve(listen)}

测试

以50052,50053,50054 3个端口启3个服务端进程,运行客户端代码,即可看见如下效果:

怎么在golang中实现负载均衡

上述内容就是怎么在golang中实现负载均衡,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯