/easymicro

an easy use framework for go micro service

Primary LanguageGo

easymicro

  • 一个简单易用、好理解的微服务治理框架,实现上参照了net/rpc、rpcx、grpc等诸多优秀的rpc框架,底层数据传输协议使用rpcx协议格式,框架封装则主要在net/rpc库的基础上,增加超时访问、心跳保活、断开重连、元数据传递、服务发现、熔断、限流以及指标统计和链路追踪等特性;服务端支持网关模式,可以处理本服务的http访问请求;同时还提供了一个通用的网关实现,可以用于快速搭建自己的微服务集群。

  • 依赖管理

    • go mod
  • 快速上手

    • 直接运行examples中的例子即可
  • 名词解释

    • server : rpc服务端的一个实例
    • client: rpc客户端的一个实例
    • rpcclient: rpc客户端到服务端到一个连接
    • 网关代理:每个服务可以启用网关代理模式,自身支持http到rpc的转换
    • 独立网关:集成服务发现组件,提供所有服务的http协议到rpc协议的转换
  • rpcclient

    • rpc客户端到服务端到一个连接

    • 当创建一个rpcclient时,会开启两个goroutine

      	go client.input()  //用于接收服务端发送到数据
      	go client.keepalive()  //用于连接保活
    • 保活设计

      • 根据配置的时间间隔,发送心跳包到对端,当本地有数据发送时,则推迟心跳包到发送,如果心跳包发送失败(会尝试重发几次),则根据当前连接的状态做相应的处理。
      • 当rpcclient关闭时,会退出keepalive goroutine。
  • 超时

    	ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
    	log.Infof("client call at time %d", time.Now().Unix())
    	err = client.Call(ctx, "Mul", args, reply)
    	if err != nil {
    		//log.Fatalf("failed to call: %v", err)
    		log.Error(err)
    	}
  • 元数据传递

    • 客户端发送和获取

      ctx := metadata.NewClientMdContext(context.Background(), map[string]string{
      		"testmd":  "md",
      		"testMd2": "md2",
      	})
      
      	mdFromServer := map[string]string{}
      	err = client.Call(ctx, "Mul", args, reply, easyclient.GetMetadataFromServer(&mdFromServer))
      	if err != nil {
      		log.Error(err)
      	}
    • 服务端发送和获取

      	md, b := metadata.FromClientMdContext(ctx)
      	if b {
      		log.Infof("get md %+v from context", md)
      	}
      	reply.C = args.A * args.B
      	server.SendMetaData(ctx, map[string]string{
      		"server": "hi this is server md",
      	})
  • 熔断

      cli, err := client.NewClient("tcp", ":8972", "Arith", client.SetCircuitBreaker(gobreaker.Settings{
    		Name:          "test_circuit",
      	MaxRequests:   5,
    		Timeout:       5 * time.Second,
    		OnStateChange: client.NewOnStateChange(),
    	}))
  • 限流

    • 使用令牌桶算法
      cli, err := client.NewClient("tcp", ":8972", "Arith", client.SetRateLimiter(ratelimit.NewBucketWithQuantum(1*time.Second, 5, 1)))
    
    	if err != nil {
    		panic(err)
    	}
    defer cli.Close()
  • 服务发现

    • 目前仅支持etcd

    • 客户端

      	cli, err := client.NewDiscoveryClient("Arith", dis.NewEtcdDiscoveryMaster([]string{
          		"http://127.0.0.1:22379",
          	}, "Arith"))
      • 服务端

         	s := server.NewServer(server.SetEtcdDiscovery([]string{
        		"http://127.0.0.1:22379",
        	}, "127.0.0.1:8972"))
        
        	s.RegisterName("Arith", new(Arith))
        	s.Serve("tcp", ":8972")
  • 网关代理模式

    • 服务端

      	s := server.NewServer(server.SetGateWayMode())
      	s.RegisterName("Arith", new(Arith))
      	s.Serve("tcp", ":8972")
    • 使用curl命令验证

    curl -H "Easymro-Version:1.1" -H "Easymicro-SerializeTypymicro-Messageid:1" -X POST --data '{"a":73,"b":1500}' http://127.0.0.1:8972/Arith/mul

    
    
    
  • 独立网关

    • 根据给定的服务发现地址,路由所有的服务
    	gw := gateway.NewGateway(":8887", []string{"http://127.0.0.1:22379"})
    	gw.StartGateway()
    • 使用curl命令验证

    curl -H "Easymro-Version:1.1" -H "Easymicro-SerializeType:1" -H "Easymicro-Messageid:1" -X POST --data '{"a":73,"b":1500}' http://127.0.0.1:8887/Arith/mul

    
    
  • 性能

    • 在请求处理上,采用了goroutine池的方式,没有使用per request per goroutine的模式

    • 主要跟grpc对比了一下,1000个并发,5000000条数据,每条消息大小581B,性能差不多是grpc的两倍多

    • grpc

      2019/11/27 12:28:16 concurrency: 1000
      requests per client: 5000
      
      2019/11/27 12:28:16 message size: 581 bytes
      
      2019/11/27 12:33:11 took 294315 ms for 5000000 requests
      2019/11/27 12:33:15 sent     requests    : 5000000
      2019/11/27 12:33:15 received requests    : 5000000
      2019/11/27 12:33:15 received requests_OK : 5000000
      2019/11/27 12:33:15 throughput  (TPS)    : 16988
      2019/11/27 12:33:15 mean: 58657349 ns, median: 55538530 ns, max: 7140185427 ns, min: 282498 ns, p99: 164440052 ns
      2019/11/27 12:33:15 mean: 58 ms, median: 55 ms, max: 7140 ms, min: 0 ms, p99: 164 ms
      
      
    • easymicro

      2019-11-27 13:40:19.722522 I | took 124799 ms for 5000000 requests
      2019/11/27 13:40:19 rpcclient.go:403: INFO : client input goroutine exit
      2019/11/27 13:40:19 rpcclient.go:308: INFO : client keepalive goroutine exit
      2019-11-27 13:40:23.597393 I | sent     requests    : 5000000
      2019-11-27 13:40:23.597422 I | received requests    : 5000000
      2019-11-27 13:40:23.597429 I | received requests_OK : 5000000
      2019-11-27 13:40:23.597435 I | throughput  (TPS)    : 40064
      2019-11-27 13:40:23.597466 I | mean: 24768440 ns, median: 22327754 ns, max: 3023957318 ns, min: 207311 ns, p99: 65509680 ns
      2019-11-27 13:40:23.597475 I | mean: 24 ms, median: 22 ms, max: 3023 ms, min: 0 ms, p99: 65 ms