利用go-kit组件进行服务注册与发现和健康检查的操作

在go的微服务架构中

使用go-kit组件进行开发微服务

type Reg struct {
	Host string
	Port int
	Client consul.Client
}
func MakeReg (host string , port int) (*Reg , error) {
	reg := api.DefaultConfig()
	reg.Address = host + ":" + strconv.Itoa(port)
	apiclient , err = api.NewClient(reg)
	if err != nil {
		return nil , err
	}
	client := consul.NewClient(apiclient)
	return &Reg{Host : host , Port : port ,Client : client} , nil
}
func (r Reg) Resiter (servicename , tag , host , seviceid ,checkinter ,healthcheckhttp ,deregisterafter string , port int) bool {
	congig := api.AgentServiceRegistration{
		Port : port ,
		Address : host ,
		Name := servicename,
		ID := serviceid,
		Ckeck : &api.AgentServiceCheck{
			Interval : checkinter,
			HTTP : "http://" + host + ":" + healthcheckhttp ,
			DeregisterCriticalServiceAfter : deregisterafter,
		}
	}
	if err := r.Client.Register(&config) ; err != nil {
		fmt.Println(err)
		return false
	}
	return true
}
func (r Reg) Deregister (serviceid string) bool {
	dreg := api.AgentServiceRegistration{ID : serviceid}
	if err != r.Client.Deregister(&config) ; err != nil {
		fmt.Println(err)
		return false
	}
	return true
}
func (r Reg) discover (servicename , tag string ,passingonly bool) ( []*api.ServiceEntry ,error ) {
	if entries ,_ ,err := r.Client.Service(servicename , tag , passingonly , nil) ;err != nil {
		return nil ,err
	}else{
		return entries , nil
	}
}

补充:go-kit 与 grpc 结合实现注册发现与负载均衡

介绍

grpc提供了简单的负载均衡,需要自己实现服务发现resolve。我们既然要使用go-kit来治理微服务,那么我们就使用go-kit的注册发现、负载均衡机制。

go-kit官方【stringsvc3】例子中使用的负载均衡方案是通过服务端转发进行,翻找下源码go-kit的服务注册发现、负载均衡在【sd】包中。下面我们介绍怎么通过go-kit进行客户端负载均衡。

go-kit提供的注册中心

1、 etcd

2、 consul

3、 eureka

4、 zookeeper

go-kit提供的负载均衡

1、 random[随机]

2、 roundRobin[轮询]

只需实现Balancer接口,我们可以很容易的增加其它负载均衡机制

type Balancer interface {
   Endpoint() (endpoint.Endpoint, error)
}

etcd注册发现

etcd和zookeeper类似是一个高可用、强一致性的存储仓库,拥有服务发现功能。 我们就通过go-kit提供的etcd包来实现服务注册发现

服务端代码

服务注册

1、连接注册中心

2、注册当前服务

var (
   //etcd服务地址
   etcdServer = "127.0.0.1:2379"
   //服务的信息目录
   prefix     = "/services/book/"
   //当前启动服务实例的地址
   instance   = "127.0.0.1:50052"
   //服务实例注册的路径
   key        = prefix + instance
   //服务实例注册的val
   value      = instance
   ctx        = context.Background()
   //服务监听地址
   serviceAddress = ":50052"
)
//etcd的连接参数
options := etcdv3.ClientOptions{
   DialTimeout: time.Second * 3,
   DialKeepAlive: time.Second * 3,
}
//创建etcd连接
client, err := etcdv3.NewClient(ctx, []string{etcdServer}, options)
if err != nil {
   panic(err)
}
// 创建注册器
registrar := etcdv3.NewRegistrar(client, etcdv3.Service{
   Key:   key,
   Value: value,
}, log.NewNopLogger())
// 注册器启动注册
registrar.Register()

完整代码

package main
import (
   "grpc-test/pb"
   "context"
   grpc_transport "github.com/go-kit/kit/transport/grpc"
   "github.com/go-kit/kit/endpoint"
   "google.golang.org/grpc"
   "net"
   "github.com/go-kit/kit/sd/etcdv3"
   "github.com/go-kit/kit/log"
   "time"
)
type BookServer struct {
   bookListHandler  grpc_transport.Handler
   bookInfoHandler  grpc_transport.Handler
}
//通过grpc调用GetBookInfo时,GetBookInfo只做数据透传, 调用BookServer中对应Handler.ServeGRPC转交给go-kit处理
func (s *BookServer) GetBookInfo(ctx context.Context, in *book.BookInfoParams) (*book.BookInfo, error) {
   _, rsp, err := s.bookInfoHandler.ServeGRPC(ctx, in)
   if err != nil {
      return nil, err
   }
   return rsp.(*book.BookInfo),err
}
//通过grpc调用GetBookList时,GetBookList只做数据透传, 调用BookServer中对应Handler.ServeGRPC转交给go-kit处理
func (s *BookServer) GetBookList(ctx context.Context, in *book.BookListParams) (*book.BookList, error) {
   _, rsp, err := s.bookListHandler.ServeGRPC(ctx, in)
   if err != nil {
      return nil, err
   }
   return rsp.(*book.BookList),err
}
//创建bookList的EndPoint
func makeGetBookListEndpoint() endpoint.Endpoint {
   return func(ctx context.Context, request interface{}) (interface{}, error) {
      //请求列表时返回 书籍列表
      bl := new(book.BookList)
      bl.BookList = append(bl.BookList, &book.BookInfo{BookId:1,BookName:"21天精通php"})
      bl.BookList = append(bl.BookList, &book.BookInfo{BookId:2,BookName:"21天精通java"})
      return bl,nil
   }
}
//创建bookInfo的EndPoint
func makeGetBookInfoEndpoint() endpoint.Endpoint {
   return func(ctx context.Context, request interface{}) (interface{}, error) {
      //请求详情时返回 书籍信息
      req := request.(*book.BookInfoParams)
      b := new(book.BookInfo)
      b.BookId = req.BookId
      b.BookName = "21天精通php"
      return b,nil
   }
}
func decodeRequest(_ context.Context, req interface{}) (interface{}, error) {
   return req, nil
}
func encodeResponse(_ context.Context, rsp interface{}) (interface{}, error) {
   return rsp, nil
}
func main() {
   var (
      //etcd服务地址
      etcdServer = "127.0.0.1:2379"
      //服务的信息目录
      prefix     = "/services/book/"
      //当前启动服务实例的地址
      instance   = "127.0.0.1:50052"
      //服务实例注册的路径
      key        = prefix + instance
      //服务实例注册的val
      value      = instance
      ctx        = context.Background()
      //服务监听地址
      serviceAddress = ":50052"
   )
   //etcd的连接参数
   options := etcdv3.ClientOptions{
      DialTimeout: time.Second * 3,
      DialKeepAlive: time.Second * 3,
   }
   //创建etcd连接
   client, err := etcdv3.NewClient(ctx, []string{etcdServer}, options)
   if err != nil {
      panic(err)
   }
   // 创建注册器
   registrar := etcdv3.NewRegistrar(client, etcdv3.Service{
      Key:   key,
      Value: value,
   }, log.NewNopLogger())
   // 注册器启动注册
   registrar.Register()
   bookServer := new(BookServer)
   bookListHandler := grpc_transport.NewServer(
      makeGetBookListEndpoint(),
      decodeRequest,
      encodeResponse,
   )
   bookServer.bookListHandler = bookListHandler
   bookInfoHandler := grpc_transport.NewServer(
      makeGetBookInfoEndpoint(),
      decodeRequest,
      encodeResponse,
   )
   bookServer.bookInfoHandler = bookInfoHandler
   ls, _ := net.Listen("tcp", serviceAddress)
   gs := grpc.NewServer(grpc.UnaryInterceptor(grpc_transport.Interceptor))
   book.RegisterBookServiceServer(gs, bookServer)
   gs.Serve(ls)
}

客户端代码

客户端流程

1、 连接注册中心

2、 获取提供的服务

3、 监听服务目录变化,目录变化更新本地缓存

4、 创建负载均衡器

5、 获取请求的 endPoint

完整代码

package main
import (
   "context"
   "github.com/go-kit/kit/sd/etcdv3"
   "time"
   "github.com/go-kit/kit/sd"
   "github.com/go-kit/kit/log"
   "github.com/go-kit/kit/endpoint"
   "io"
   "github.com/go-kit/kit/sd/lb"
   "grpc-test/pb"
   "fmt"
   "google.golang.org/grpc"
)
func main() {
   var (
      //注册中心地址
      etcdServer = "127.0.0.1:2379"
      //监听的服务前缀
      prefix     = "/services/book/"
      ctx        = context.Background()
   )
   options := etcdv3.ClientOptions{
      DialTimeout: time.Second * 3,
      DialKeepAlive: time.Second * 3,
   }
   //连接注册中心
   client, err := etcdv3.NewClient(ctx, []string{etcdServer}, options)
   if err != nil {
      panic(err)
   }
   logger := log.NewNopLogger()
   //创建实例管理器, 此管理器会Watch监听etc中prefix的目录变化更新缓存的服务实例数据
   instancer, err := etcdv3.NewInstancer(client, prefix, logger)
   if err != nil {
      panic(err)
   }
   //创建端点管理器, 此管理器根据Factory和监听的到实例创建endPoint并订阅instancer的变化动态更新Factory创建的endPoint
   endpointer := sd.NewEndpointer(instancer, reqFactory, logger)
   //创建负载均衡器
   balancer := lb.NewRoundRobin(endpointer)
   /**
   我们可以通过负载均衡器直接获取请求的endPoint,发起请求
   reqEndPoint,_ := balancer.Endpoint()
   */
   /**
   也可以通过retry定义尝试次数进行请求
   */
   reqEndPoint := lb.Retry(3, 3*time.Second, balancer)
   //现在我们可以通过 endPoint 发起请求了
   req := struct{}{}
   if _, err = reqEndPoint(ctx, req); err != nil {
      panic(err)
   }
}
//通过传入的 实例地址  创建对应的请求endPoint
func reqFactory(instanceAddr string) (endpoint.Endpoint, io.Closer, error) {
   return func(ctx context.Context, request interface{}) (interface{}, error) {
      fmt.Println("请求服务: ", instanceAddr)
      conn, err := grpc.Dial(instanceAddr, grpc.WithInsecure())
      if err != nil {
         fmt.Println(err)
         panic("connect error")
      }
      defer conn.Close()
      bookClient := book.NewBookServiceClient(conn)
      bi,_:=bookClient.GetBookInfo(context.Background(),&book.BookInfoParams{BookId:1})
      fmt.Println("获取书籍详情")
      fmt.Println("bookId: 1", " => ", "bookName:", bi.BookName)
      bl,_ := bookClient.GetBookList(context.Background(), &book.BookListParams{Page:1, Limit:10})
      fmt.Println("获取书籍列表")
      for _,b := range bl.BookList {
         fmt.Println("bookId:", b.BookId, " => ", "bookName:", b.BookName)
      }
      return nil,nil
   },nil,nil
} 

测试

请求测试

请求服务: 127.0.0.1:50052
获取书籍详情
bookId: 1  =>  bookName: 21天精通php
获取书籍列表
bookId: 1  =>  bookName: 21天精通php
bookId: 2  =>  bookName: 21天精通java

负载均衡测试

1、 修改server的注册监听端口,启动多个server

instance   = "127.0.0.1:50052"
serviceAddress = ":50052"

2、client发起多次请求

req := struct{}{}
for i := 1; i <= 8; i++ {
   if _, err = reqEndPoint(ctx, req); err != nil {
      panic(err)
   }
}

通过返回结果中记录的请求地址,我们可以看到已经按照轮询的方式请求不同的微服务实例。

请求服务:  127.0.0.1:50051
        获取书籍详情
        bookId: 1  =>  bookName: 21天精通php
        获取书籍列表
        bookId: 1  =>  bookName: 21天精通php
        bookId: 2  =>  bookName: 21天精通java
请求服务:  127.0.0.1:50052
        获取书籍详情
        bookId: 1  =>  bookName: 21天精通php
        获取书籍列表
        bookId: 1  =>  bookName: 21天精通php
        bookId: 2  =>  bookName: 21天精通java
请求服务:  127.0.0.1:50051
        获取书籍详情
        bookId: 1  =>  bookName: 21天精通php
        获取书籍列表
        bookId: 1  =>  bookName: 21天精通php
        bookId: 2  =>  bookName: 21天精通java
请求服务:  127.0.0.1:50052
        获取书籍详情
        bookId: 1  =>  bookName: 21天精通php
        获取书籍列表
        bookId: 1  =>  bookName: 21天精通php
        bookId: 2  =>  bookName: 21天精通java
请求服务:  127.0.0.1:50051
        获取书籍详情
        bookId: 1  =>  bookName: 21天精通php
        获取书籍列表
        bookId: 1  =>  bookName: 21天精通php
        bookId: 2  =>  bookName: 21天精通java
请求服务:  127.0.0.1:50052
        获取书籍详情
        bookId: 1  =>  bookName: 21天精通php
        获取书籍列表
        bookId: 1  =>  bookName: 21天精通php
        bookId: 2  =>  bookName: 21天精通java
请求服务:  127.0.0.1:50051
        获取书籍详情
        bookId: 1  =>  bookName: 21天精通php
        获取书籍列表
        bookId: 1  =>  bookName: 21天精通php
        bookId: 2  =>  bookName: 21天精通java
请求服务:  127.0.0.1:50052
        获取书籍详情
        bookId: 1  =>  bookName: 21天精通php
        获取书籍列表
        bookId: 1  =>  bookName: 21天精通php
        bookId: 2  =>  bookName: 21天精通java
Process finished with exit code 0

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

(0)

相关推荐

  • go语言中切片与内存复制 memcpy 的实现操作

    Go 语言原则上不支持内存的直接操作访问,但是提供了切片功能. 最初我以为切片就是动态数组,实际程序设计过程中发现,切片是提供数组一个内存片段的一个合法的手段,利用切片功能,实际上我们可以自由访问数组的任何一个片段,因而可以借助 copy 函数,实现内存复制. 不同类型之间的数据复制,可以借助 unsafe 取出变量地址,类型转换为数组后,利用数组切片,实现内存复制. 不罗嗦了,示例代码如下: package main import ( "fmt" "unsafe"

  • Go语言中的UTF-8实现

    计算机刚诞生的时候,计算机内的字符可以全部由 ASCII 来表示,ASCII 字符的长度是 7 位,可以表示 128 个字符,对于美国等国家来说是够了,但是对于世界上的其他国家,特别是东亚国家,文字不是由字母组成,汉字就有几万个,ASCII 码根本不够用. 字符本质就是对应计算机中的一个数值,既然不够用,那么解决方法就是把这个范围扩大,Unicode 的出现就解决了这个问题,它包括了世界上所有的字符,每一个字符都对应一个数值,这个数值被称之为 Unicode 码点. 但是 Unicode 也不是

  • golang中的空slice案例

    golang中允许对值为 nil 的 slice 添加元素 package main func main() { var s []int s = append(s, 1) } 运行成功~ 补充:golang slice 详解 一.数组切片的使用 func main() { //1.基于数组创建数组切片 var array [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} var slice = array[1:7] //array[startInd

  • go-kit组件使用hystrix中间件的操作

    使用go-kit中间件时,一般在endpoint中进行中间件的开发. 在endpoint层插入hystrix中间件的插入. endpoint.go func MakeEndpoint (svc services.StringService) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(*St

  • Go语言切片前或中间插入项与内置copy()函数详解

    内置append()函数能够在切片末尾位置添加新的项,假设要在切片的前面或者中间某位置插入特定项,可以这样实现 看下代码: package main import "fmt" func main() { s := []string{"M","N","O","P","Q","R"} x := InsertStringSliceCopy(s,[]string{"

  • Goland 断点调试Debug的操作

    第一步:进入编辑模式 第二步:开始进行编辑 第三步:就可以进行调式了 补充:goland断点调试报错 一.运行报错 runnerw.exe: CreateProcess failed with error 216 \(no message available\) 解决办法: 查看package是不是main,同时必须有个main函数,作为程序运行的主入口 查看GOOS是不是本系统的,比如windows,得设置为同一个,cmd中使用set GOOS=windows,goland中设置方法如下: 二

  • golang中切片copy复制和等号复制的区别介绍

    结论: copy复制会比等号复制慢.但是copy复制为值复制,改变原切片的值不会影响新切片.而等号复制为指针复制,改变原切片或新切片都会对另一个产生影响. 测试复制速度: func TestArr1(t *testing.T) { var a []int for i := 0; i < 100000000; i++ { a = append(a, i) } start := time.Now().UnixNano() var b = make([]int, 1000000) copy(b, a)

  • 利用go-kit组件进行服务注册与发现和健康检查的操作

    在go的微服务架构中 使用go-kit组件进行开发微服务 type Reg struct { Host string Port int Client consul.Client } func MakeReg (host string , port int) (*Reg , error) { reg := api.DefaultConfig() reg.Address = host + ":" + strconv.Itoa(port) apiclient , err = api.NewC

  • Asp.Net Core使用Ocelot结合Consul实现服务注册和发现

    目录 1.服务注册与发现(Service Discovery) 2.Consul 3.Asp.Net Core向Consul注册服务实例 4.项目演示 4.1APIGateway项目 4.2Common项目 4.3APIServiceA项目 4.4APIServiceB项目 4.5项目运行 1.服务注册与发现(Service Discovery) 服务注册:我们通过在每个服务实例写入注册代码,实例在启动的时候会先去注册中心(例如Consul.ZooKeeper.etcd.Eureka)注册一下,

  • 微服务架构之服务注册与发现功能详解

    目录 微服务的注册与发现 1.服务注册 2.服务发现 3.注册中心 4.现下的主流注册中心 4.1 Eureka 4.1.1 介绍 4.1.2 整体架构 4.1.3 接入Spring Cloud 4.2 ZooKeeper 4.2.1 介绍 4.2.2 整体架构 4.2.3 接入Dubbo生态 4.3 Consul 4.3.1 介绍 4.3.2 整体架构 4.3.3 生态对接 4.4 总结对比 详解微服务架构及其演进史 微服务全景架构全面瓦解 微服务架构拆分策略详解 微服务的注册与发现 我们前面

  • SpringCloud服务注册和发现组件Eureka

    本篇文章,我们来讲解springcloud的服务注册和发现组件,上一章节我们讲解了如何搭建springcloud的多模块项目,已经新建了springcloud-eureka-server,springcloud-eureka-client两个模块,本章节就在这基础上直接使用. 想要了解的请参考:一起来学Spring Cloud | 第一章 :如何搭建一个多模块的springcloud项目 一.Eureka简介: 1.1 什么是eureka Eureka是一个基于REST的服务,主要用于AWS云中

  • 详解springcloud之服务注册与发现

    本次分享的是关于springcloud服务注册与发现的内容,将通过分别搭建服务中心,服务注册,服务发现来说明:现在北京这边很多创业公司都开始往springcloud靠了,可能是由于文档和组件比较丰富的原因吧,毕竟是一款目前来说比较完善的微服务架构:本次分享希望能给大家带来好的帮助: Eureka服务中心 Provider注册服务 Consumer发现服务 Eureka服务中心高可用 Eureka服务中心 就我现在了解到并且用的比较多的注册中心有zookeeper和Eureka,我的上上篇文章分享

  • 详解golang consul-grpc 服务注册与发现

    在微服务架构里面,每个小服务都是由很多节点组成,节点的添加删除故障希望能对下游透明,因此有必要引入一种服务的自动注册和发现机制,而 consul 提供了完整的解决方案,并且内置了对 GRPC 以及 HTTP 服务的支持 总体架构 服务调用: client 直连 server 调用服务 服务注册: 服务端将服务的信息注册到 consul 里 服务发现: 客户端从 consul 里发现服务信息,主要是服务的地址 健康检查: consul 检查服务器的健康状态 服务注册 服务端将服务信息注册到 con

  • SpringCloud实现Eureka服务注册与发现

    GitHub地址:https://github.com/yudiandemingzi/spring-cloud-study 一.Eureka概述 1.Eureka特点 (1) Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移. (2) Eureka 主管服务注册与发现,在微服务中,以后了这两者,只需要使用服务的标识符(==就是那个在每个服务的yml文件中取得服务名称==), 就可以访问到服务,不需要修改服务调用的配置文件. (3) Eureka遵循AP原则(

  • SpringBoot的服务注册与发现示例

    微服务 实践"微服务"自然要学习如何做服务注册与发现 基于SpringBoot来进行微服务的学习,自然选择了与之息息相关的SpringCloud;当然可以选择其他的技术进行,比如dubbo 也可以用zookeeper来实现服务注册与发现,至于zookeeper来实现此功能好还是不好,各家之言都有 SpringCloud Spring Cloud provides tools for developers to quickly build some of the common patte

  • SpringCloud之服务注册与发现Spring Cloud Eureka实例代码

    一.Spring Cloud简介 Spring Cloud是一个基千SpringBoot实现的微服务架构开发 工具.它为微服务架构中涉及的 配置管理.服务治理. 断路器. 智能路由.微代理. 控制总线. 全局锁. 决策竞选.分布式会话和集群状态管理等操作提供了一种简单的开发方式. Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品,还可能会新增),如下所述. Spring Cloud Config: 配置管理工具.Spring Cloud Netflix: 核心组件

  • SpringCloud Eureka实现服务注册与发现

    前言 Eureka是一种基于REST(具像状态传输)的服务,主要用于AWS云中定位服务,以实现中间层服务器的负载平衡和故障转移.本文记录一个简单的服务注册与发现实例. GitHub地址:https://github.com/Netflix/eureka 官网文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.1.0.RC2/single/spring-cloud-netflix.html Eureka-Ser

随机推荐