golang 实现一个负载均衡案例(随机,轮训)

今天用go实现一个简单的负载均衡的算法,虽然简单,还是要写一下。

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

package balance
type 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 balance
type Balance interface {
    /**
    *负载均衡算法
    */
    DoBalance([] *Instance,...string) (*Instance,error)
}

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

package balance
import (
    "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 balance
import (
    "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 balance
import (
    "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 add
import (
    "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 main
import (
 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 main
import (
 "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个服务端进程,运行客户端代码,即可看见如下效果:

负载均衡完美实现, 打开日志文件,可以看到post的地址为 /grpctest.Hello/SayHello,nginx配置为所有请求都按默认 localtion / 转发,因此 nginx再配上合适的路由规则,还可实现更灵活转发,也可达到微服务注册的目的,非常方便。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • Golang 文件操作:删除指定的文件方式

    我就废话不多说了,大家还是直接看代码吧~ package main import "os" func main () { err := os.Remove(logFile) if err != nil { // 删除失败 } else { // 删除成功 } } 补充:[Golang]文件/文件夹一般操作:判断.复制.删除.遍历 1. 检查指定路径是否为文件夹 func IsDir(name string) bool { if info, err := os.Stat(name); e

  • golang 如何删除二进制文件中的源码路径信息

    方法 go v1.13 go build 新增 -trimpath参数,不用以前那么麻烦了. ➜ awesomeProject CGO_ENABLED=0 go build -v -a -ldflags="-w -s" -trimpath \ -o ./hello_word hello_word.go ➜ awesomeProject strings hello_word|grep src ➜ awesomeProject #之前 ➜ awesomeProject go tool ob

  • golang 实现一个restful微服务的操作

    如何用net/http构建一个简单的web服务 Golang提供了简洁的方法来构建web服务 package main import ( "net/http" ) func HelloResponse(rw http.ResponseWriter, request *http.Request) { fmt.Fprintf(w, "Hello world.") } func main() { http.HandleFunc("/", HelloRe

  • golang 如何自动下载所有依赖包

    如何自动下载所有依赖包? 大部分情况下大家下载 Go 项目都是使用go get命令,它除了会下载指定的项目代码,还会去下载这个项目所依赖的所有项目. 但是有的时候我们的项目由于各种原因并不是通过go get下载的,是通过git clone下载的,这样代码下下来就没有依赖包了,没办法编译通过的. 这样的话怎么办呢? go get -d -v ./... -d标志只下载代码包,不执行安装命令: -v打印详细日志和调试日志.这里加上这个标志会把每个下载的包都打印出来: ./...这个表示路径,代表当前

  • golang 通用Contains方法分享

    今天在群里看见有人写了一个InAarry方法,主要作用是判断一个元素是否在slice中,看完后,我感觉方法应该还有扩展的空间 于是自己重新写了一个Contains方法 可以支持 slice,array,map等类型 package main import ( "errors" "fmt" "reflect" ) // 判断obj是否在target中,target支持的类型arrary,slice,map func Contain(obj inte

  • Golang 删除文件并递归删除空目录的操作

    目的: 删除文件之后,如果目录为空,递归删除为空的目录. svr.dataDir指目录的最外层,递归删除目录的时候判断到这一层即可. eg:/Users/danny/data func deleteFileOnDisk(localPath string) { log.Debugf("remove file: %s", localPath) if err := os.Remove(localPath); err != nil { log.Error(err) } dirsList :=

  • golang grpc 负载均衡的方法

    微服务架构里面,每个服务都会有很多节点,如果流量分配不均匀,会造成资源的浪费,甚至将一些机器压垮,这个时候就需要负载均衡,最简单的一种策略就是轮询,顺序依次选择不同的节点访问. grpc 在客户端提供了负载均衡的实现,并提供了服务地址解析和更新的接口(默认提供了 DNS 域名解析的支持),方便不同服务的集成 使用示例 conn, err := grpc.Dial( "", grpc.WithInsecure(), // 负载均衡,使用 consul 作服务发现 grpc.WithBal

  • golang 实现一个负载均衡案例(随机,轮训)

    今天用go实现一个简单的负载均衡的算法,虽然简单,还是要写一下. 1.首先就是服务器的信息 package balance type 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

  • Golang 实现简单随机负载均衡

    目录 负载均衡简介 随机负载均衡 测试 随机负载均衡 负载均衡简介 从设备角度实现负载均衡: 硬件负载均衡: 由专门的 负载均衡器 服务提供商,在你的服务器和外部网络之间架设 服务商的 负载均衡器.服务商提供设备和方案帮你处理流量的均衡,不过因为比较费钱包国内没什么人用 软件负载均衡: 包括家喻户晓的Nginx,LVS,Tengine(阿里版Nginx).优点就是成本比较低,但需要运维去配置.维护.会踩坑,但国内都在用基本上网找就有解决方案. 随机负载均衡 无论是Nginx,LVS 均衡负载的核

  • Nginx+Tomcat负载均衡集群安装配置案例详解

    目录 前言 一.Nginx+Tomcat 二.配置Nginx服务器 三.部署Tomcat应用服务器 总结 前言 介绍Tomcat及Nginx+Tomcat负载均衡集群,Tomcat的应用场景,然后重点介绍Tomcat的安装配置.Nginx+Tomcat负载均衡集案列是应用于生产环境的一套可靠的Web站点解决方案. 一.Nginx+Tomcat 通常情况下,一个Tomcat站点由于可能出现单点故障及无法应付过多客户复杂多样的请求等问题,不能单独应用于生产环境下,所以我们需要一套更可靠的解决方案来完

  • asp.net实现负载均衡

    我的目标是我一个人搭建一个负载均衡网站.不接受这是网络部,或者运维,或者系统部的事情,所有事情都是我一个人来完成,包括掏钱,包括将来发展等等,同时也别告诉我有没有意义,just do  it !给我方案,给我方法. 拥有资源:不够可以买,多了可以先闲着. 现在我已经买了5台阿里云服务器,ip地址分别为 ip地址 名字简称 操作系统 iis服务器 cpu 内存DDR3 机械硬盘 111.13.101.204 ay1 window7 iis7 4核 8GB 1TB 111.13.101.205 ay

  • SpringCloud 服务负载均衡和调用 Ribbon、OpenFeign的方法

    1.Ribbon Spring Cloud Ribbon是基于Netflix Ribbon实现的-套客户端―负载均衡的工具. 简单的说,Ribbon是Netlix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用.Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等.简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器.我们很容易使用Ribbon实现自定义的负载均

  • 使用Golang实现加权负载均衡算法的实现代码

    背景描述 如下图所示,负载均衡做为反向代理,将请求方的请求转发至后端的服务节点,实现服务的请求. 在nginx中可以通过upstream配置server时,设置weight表示对应server的权重. 若存在多个服务节点时,负载均衡如何通过服务节点的权重进行转发. 如下详细说明权重转发算法的实现. 用三个后端服务节点为例说明 设置三个后端服务ServerA,ServerB和ServerC,它们的权重分布是 5,3,1 按照加权负载均衡算法,在一轮(5+3+1=9次)中ServerA占5次,Ser

  • 使用Java实现5种负载均衡算法实例

    目录 前言 概念 几种负载均衡算法图例 轮询算法 加权轮询法 加权随机法 随机法 IP_Hash算法 总结 前言 负载均衡是为了解决并发情况下,多个请求访问,把请求通过提前约定好的规则转发给各个server.其中有好几个种经典的算法.在用java代码编写这几种算法之前,先来了解一下负载均衡这个概念. 概念 负载均衡是将客户端请求访问,通过提前约定好的规则转发给各个server.其中有好几个种经典的算法,下面我们用Java实现这几种算法. 几种负载均衡算法图例 主要的负载均衡算法是图中这些,在代码

  • Nginx+Tomcat负载均衡集群的实现示例

    目录 引言 一.案例概述 二.环境部署 三.Nginx 主机安装 四.Tomcat 安装及配置 1. 安装 Tomcat 2. Tomcat 服务器1配置 3. Tomcat 服务器2配置 五.Nginx server 配置 六.验证结果 总结 引言 通常情况下,一个 Tomcat 站点由于可能出现单点故障以及无法应付过多客户复杂多样的请求等问题,不能单独应用于生产环境中,所以需要一套更可靠的解决方案来完善 Web 站点架构. 一.案例概述 Nginx 是一款非常优秀的 http 服务器软件,它

  • 服务器的负载均衡nginx+tomcat实现动静分离

    目录 案例环境 案例实施: 部署tomcat服务器 部署nginx服务器 部署nginx-lb服务器 本文实验内容nginx+tomcat实现网站的动静分离功能 案例环境 主机信息 架构图 案例实施: 部署tomcat服务器 两台tomcat服务器上准备jdk和tomcat jdk下载 tomcat下载 mkdir /tools cd /tools ls apache-tomcat-8.5.70.tar.gz jdk-8u181-linux-x64.tar.gz 安装jdk mkdir -p /

  • Java实现5种负载均衡算法(小结)

    目录 概念 轮询算法 加权轮询法 加权随机法 随机法 IP_Hash算法 概念 负载均衡是将客户端请求访问,通过提前约定好的规则转发给各个server.其中有好几个种经典的算法,下面我们用Java实现这几种算法. 轮询算法 轮询算法按顺序把每个新的连接请求分配给下一个服务器,最终把所有请求平分给所有的服务器. 优点:绝对公平 缺点:无法根据服务器性能去分配,无法合理利用服务器资源. package com.monkeyjava.learn.basic.robin; import com.goog

随机推荐