{"id":38272430,"url":"https://github.com/tedcy/sheep","last_synced_at":"2026-01-17T01:50:52.726Z","repository":{"id":57531444,"uuid":"165344535","full_name":"tedcy/sheep","owner":"tedcy","description":" micro service lib, based on grpc go","archived":false,"fork":false,"pushed_at":"2019-09-25T02:59:26.000Z","size":135,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-06-20T15:46:14.223Z","etag":null,"topics":["go","grpc","microservice"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tedcy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-12T04:05:40.000Z","updated_at":"2021-11-22T11:41:18.000Z","dependencies_parsed_at":"2022-09-06T16:30:49.100Z","dependency_job_id":null,"html_url":"https://github.com/tedcy/sheep","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tedcy/sheep","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tedcy%2Fsheep","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tedcy%2Fsheep/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tedcy%2Fsheep/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tedcy%2Fsheep/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tedcy","download_url":"https://codeload.github.com/tedcy/sheep/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tedcy%2Fsheep/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28491678,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T00:50:05.742Z","status":"ssl_error","status_checked_at":"2026-01-17T00:43:11.982Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","grpc","microservice"],"created_at":"2026-01-17T01:50:51.915Z","updated_at":"2026-01-17T01:50:52.689Z","avatar_url":"https://github.com/tedcy.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"sheep库是一个基于grpc的go服务化框架\n\nsheep库的[c++版本](https://github.com/tedcy/sheep_cpp#3221-http-client-in-grpc-server)在这里\n\n- [1 功能](#1-)\n- [2 安装](#2-)\n- [3 使用](#3-)\n    - [3.1 grpc-client](#31-grpc-client)\n    - [3.2 server](#32-server)\n        - [3.2.1 配置](#321-)\n        - [3.2.1 grpc-server](#321-grpc-server)\n        - [3.2.2 http-server](#322-http-server)\n- [4 extends库使用](#4-extends)\n    - [4.1 extends/config      配置文件解析](#41-extendsconfig------)\n    - [4.2 extends/log         日志库](#42-extendslog---------)\n        - [4.2.1 分不同文件打印](#421-)\n        - [4.2.2 不分不同文件打印](#422-)\n    - [4.3 extends/coster      调用时长统计](#43-extendscoster------)\n    - [4.4 服务端拦截器](#44-)\n    - [4.5 flow](#45-flow)\n\n## 1 功能\n* grpc原生客户端风格的服务化封装\n* 客户端可选的负载均衡策略\n* 客户端可选的熔断器\n* 服务端可选的多个限流策略\n\n可复用基础库:\n* github.com/tedcy/sheep/watcher  独立服务发现封装  \n* github.com/tedcy/sheep/breaker  独立熔断器实现  \n* github.com/tedcy/sheep/limiter  独立限流器实现  \n\n额外功能extends库(github.com/tedcy/sheep/extends):\n* config        读取配置  \n* coster        调用时长统计  \n* log           日志统计  \n* flow          责任链模式封装，适合复杂逻辑请求处理  \n\n注意：目前服务发现只支持etcd组件\n\n## 2 安装\n```\ngo get github.com/tedcy/sheep\n```\n\n## 3 使用\n\n### 3.1 grpc-client\n\n客户端目前只支持grpc(http也不需要服务发现)  \n\n具体可参考example/client/client.go\n\n```\n//先设置DialConfig，一共4个参数\n//EnableBreak\n//BalancerType          负载均衡策略，设置client.RespTimeBalancer，则根据调用超时时间反比设置调用权重\n//Timeout               调用超时时间\n//TargetPath            可被watcher解析的字串，目前只支持etcd，格式为etcd://xxxx:2379,xxxx:2379,xxxx:2379/注册路径\nc := \u0026client.DialConfig{}\n\nc.TargetPath = \"etcd://172.16.176.38:2379/test1\"\n\nconn, err := client.DialContext(context.Background(), c)\n//判断err\n\n//接下来使用和原生grpc相同\nrealConn := pb.NewGreeterClient(conn)\n\nresp, err := realConn.SayHello(context.Background(), \u0026pb.HelloRequest{Name : \"name\"})\n//判断err\n\nfmt.Println(\"resp: \" + resp.Message)\n```\n\n### 3.2 server\n\n服务端目前支持http和grpc，两者的配置部分是一样的，使用方式不同\n\n#### 3.2.1 配置\n\nsheep\\_server.ServerConfig  \n* Addr  \n监听地址\n* Type\nserver类型，可选http或grpc  \n* WatcherAddrs  \nwatcher地址，这里是etcd地址列表  \netcd://xxxx:2379,xxxx:2379,xxxx:2379\n* WatcherTimeout  \n和watcher连接的超时时间，决定了临时节点多久下线\n* WatcherPath  \n在wathcer上注册路径\n* LimiterType  \n限流器类型，可选  \nlimiter.QueueLengthLimiterType，固定请求队列长度限流  \nlimiter.InvokeTimeLimiterType，根据调用时间限制限流  \n* Limit  \n限制值  \n对QueueLengthLimiterType来说，是请求队列个数  \n对InvokeTimeLimiterType来说，是限制的延时毫秒数  \n\n* Interceptors  \n自定义拦截器，下面单独说明  \n* Opt  \n额外的服务端选项，目前只支持grpc, grpc高并发需要带这个选项，否则会因为stream太少报错  \n```\nconfig.Opt = \u0026grpc.GrpcServerOpt{\n\tGrpcOpts: append([]real_grpc.ServerOption{}, real_grpc.MaxConcurrentStreams(10000)),\n}\n```\n\n#### 3.2.1 grpc-server\n\n具体参考example/server/grpc/server.go\n\n类的实现部分，实现proto生成的方法\n\n```\nimport (\n    pb \"google.golang.org/grpc/examples/helloworld/helloworld\"\n)\ntype server struct {\n}\nvar gResp *pb.HelloReply = \u0026pb.HelloReply{Message: \"Hello\"}\nfunc (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {\n    return gResp, nil\n}\n```\n\n使用部分\n\n```\nimport (\n    sheep_server \"github.com/tedcy/sheep/server\"\n    \"github.com/tedcy/sheep/server/real_server/grpc\"\n    real_grpc \"google.golang.org/grpc\"\n)\nconfig := \u0026sheep_server.ServerConfig{}\nconfig.Addr = \":50051\"\nconfig.Type = \"grpc\"\nconfig.Opt = \u0026grpc.GrpcServerOpt{\n    GrpcOpts: append([]real_grpc.ServerOption{}, real_grpc.MaxConcurrentStreams(10000)),\n}\nrealS := \u0026server{}\ns, err := sheep_server.New(context.Background(), config)\n//判断err\n\n//退出关闭服务\ndefer s.Close()\n\n//获取server的grpc注册器\nh, ok := s.GetRegisterHandler().(*real_grpc.Server)\n//判断ok\n\n//把realS注册到grpcServer上\npb.RegisterGreeterServer(h, realS)\n\n//服务开启，这里阻塞主协程不会退出\nerr = s.Serve()\n//判断err\n```\n\n#### 3.2.2 http-server\n\n参考example/server/http/server.go\n\n服务端结构体需要实现HttpHandlerI的三个方法\n\n**注：http服务端的设计较为复杂，是为了将解析部分剥离出来，让grpc和http服务端代码可以灵活转换，让内网的grpc服务可以随时对外服务**\n\n```\ntype HttpMap interface{\n\tGet(key string) string\n}\ntype HttpReq struct {\n\treq *http.Request\n\tHeaders HttpMap\n\tQueryStrs HttpMap \n\tBody io.Reader\n\tPath string\n}\ntype ResponseWriter interface {\n\tHeader() http.Header\n\tWrite([]byte) (int, error)\n\tWriteHeader(int)\n}\ntype HttpHandlerI interface{\n    //对http请求做解析，解析结果返回req对象，会传给Handler的req\n\tDecode(httpReq *HttpReq) (req interface{},err error)\n    //处理Handler解析的结果，返回resp对象\n\tHandler(ctx context.Context, req interface{}) (resp interface{}, err error)\n    //对Handler返回的resp对象做处理，进行返回\n\tEncode(resp interface{}, outputErr error, rw ResponseWriter) (err error)\n}\n```\n\n```\nimport (\n    \"github.com/tedcy/sheep/server/real_server/http\"\n    \"io/ioutil\"\n)\ntype server struct {\n}\nfunc (s *server) Decode(httpReq *http.HttpReq) (req interface{},err error) {\n\tb, err := ioutil.ReadAll(httpReq.Body)\n\tif err != nil {\n\t\treturn\n\t}\n\treq = b\n\treturn\n}\nfunc (s *server) Encode(resp interface{}, outputErr error, rw http.ResponseWriter) error {\n\tif outputErr != nil {\n\t\trw.Write([]byte(outputErr.Error()))\n\t\treturn outputErr\n\t}\n\t_, err := rw.Write(resp.([]byte))\n\treturn err\n}\nfunc (s *server) Handler(ctx context.Context, req interface{}) (resp interface{}, err error) {\n\tresp = req\n\treturn\n}\n```\n\n```\nimport (\n    sheep_server \"github.com/tedcy/sheep/server\"\n)\nconfig := \u0026sheep_server.ServerConfig{}\nconfig.Addr = \"127.0.0.1:80\"\nconfig.Type = \"http\"\nrealS := \u0026server{}\n\ns, err := sheep_server.New(context.Background(), config)\n//判断err\n\n//将服务端实现注册到http路径上去，method部分大小写兼容\nerr = s.Register(\"GET:/test\", realS)\n//判断err\n\n//服务开启，这里阻塞主协程不会退出\nerr := s.Serve()\n//判断err\n```\n\n## 4 extends库使用\n\n### 4.1 extends/config      配置文件解析\n\n目前是对toml库的封装，不能选择其他格式\n\n```\nconfigs := map[string]interface{} {\n    \"conf/project.toml\" : \u0026Project,\n    \"conf/redis.toml\" : \u0026Redis,\n}\nfor file, cfg := range configs {\n    err := config.Read(cfg, file)\n    //判断err\n}\n```\n\nPS: toml格式非常简单，很适合写配置文件\n\n基本规则如下，我没用过这个以外的语法\n\n* k = v, v如果是字符串加\"\"\n* 某个结构体，缩进2个空格以后[结构体变量名]，换行再缩进2个空格\n\n```\nClickBaseUrl = \"\"\nWinBaseUrl = \"\"\n  [ServerConfig]\n    Addr = \":8235\"\n    Type = \"http\"\n    LimiterType = 2\n    Limit = 100\n```\n\n### 4.2 extends/log         日志库\n\n日志库优点是支持自定义分文件，缺点是不支持按日志级别打印。\n\n\n```\nlog.GetLog().Infoln()\n```\n\nGetLog函数中的参数是为了传入LogOption，来进行分不同文件  \n如果不传也可以，会自动获取协程变量来选择文件，如果没有设置协程变量会使用默认全局变量\n\n#### 4.2.1 分不同文件打印\n我一般配合config库使用  \n\n比如bidding.toml\n```\nLogCategory = \"file\"\nIgnore = false\nOutputFile = \"log/bidding\"\nHeaderFormat = \"$L $datetime-ms $gid $file:$line] \"\nRotateCategory = \"size\"\nRotateValue = 1912602624\nRotateSuffixFormat = \".P$pid.$datetime\"\n```\n\n```\n//解析LogOption\nvar LogBidding = log.DefaultLogOption()\nerr := config.Read(\u0026LogBidding, \"bidding.toml\")\n\n//将LogOption绑定在一个LogKey的变量上\ntype biddingLogKeyT struct{}\nvar biddingLogKey biddingLogKeyT\nlog.BindOption(biddingLogKey, LogBidding)\n\n//下面两行一般会放在服务端拦截器中\n//逻辑执行前设置协程变量\nlog.SetGlsDefaultKey(\u0026LogBidding)\n//逻辑结束后清理协程变量\ndefer log.CleanupGlsDefaultKey(\u0026LogBidding)\n```\n\n#### 4.2.2 不分不同文件打印\n\n嫌麻烦的话按下面写可以修改默认全局变量进行打印\n```\nlogOpt := log.DefaultLogOption()\nlogOpt.LogCategory = \"file\"\nlg, err := log.MakeLogger(logOpt)\n//判断err\nlog.SetLog(lg)\n```\n\n### 4.3 extends/coster      调用时长统计\n\n```\n//调用时长分类变量\nvar CosterRequest string = \"1\"\n\n//逻辑执行前通过coster管理器单例创建一个costerOnce对象，此时开始计时\ncosterOnce := coster.GetInstance().Start()\n\n//逻辑结束后通过coster管理器对CosterRequest结束costerOnce对应的计时，结束计数\ndefer coster.GetInstance().GetCoster(CosterRequest).End(costerOnce)\n\n//统计最近五分钟平均数\nlast := time.Now().Add(-time.Minute * 5)\ncoster.GetInstance().GetCoster(CosterRequest).GetAverage(last)\n```\n\n目前每秒进行一次统计，也就是300次统计的平均值，但是如果当时统计值为0，不会计入结果  \n* GetAverage是平均值\n* GetMost是众数  \n按阶段分类，0-9阶段，10-99阶段，100-999阶段最多的阶段，除以这个阶段的阶段最小值抹掉小数点，再乘100，得到最多的值  \n例如0,10,100,101,200,众数就是先算最多的阶段，100-999  \n在100,101,200中，除以100，为1,1,2，再乘100，100,100,200，得到众数100  \n* GetMax是取最大值  \n\n### 4.4 服务端拦截器\n\n为了让设计更灵活\n\n分类日志，字段解析的日志打印，统计延时等功能我都放在了服务端拦截器中\n\n配合extends库实现\n\n\n```\nimport (\n    sheep_server \"github.com/tedcy/sheep/server\"\n)\n//sheep_server的配置文件加上拦截器\nvar config sheep_server.ServerConfig\nconfig.Interceptors = append([]server_common.ServerInterceptor{}, serverInterceptor)\n\n//参数\n//ctx       上下文信息\n//req       传入的请求\n//handler   被拦截的handler\n//resp      传出的resp\n//err       报错信息\nfunc serverInterceptor(ctx context.Context, req interface{},\n    handler server_common.ServerHandler) (resp interface{}, err error) {\n    var logKey interface{}\n    //服务端通过ctx.Value(\"serviceName\").(string)获取到路径名称\n    //grpc服务端的serviceName是proto生成代码的Desc描述类\n    //http服务端的serviceName是自己注册的Api路径\n    serviceName := ctx.Value(\"serviceName\").(string)\n    switch serviceName {\n    case \"/proto.BidService/Handler\":\n        //每个实现包把它自己的LogKey封装成了GetLogKey()\n        logKey = bapi.GetLogKey()\n    case \"GET:/imp\":\n        logKey = iapi.GetLogKey()\n    case \"GET:/click\":\n        logKey = capi.GetLogKey()\n    }\n\n    //日志的LogOption\n    log.SetGlsDefaultKey(logKey)\n    //释放LogOption\n    defer log.CleanupGlsDefaultKey(logKey)\n\n    //对请求json序列化进行打印\n    breq, _ := json.Marshal(req)\n    log.GetLog().Debugln(serviceName, string(breq))\n\n    //执行被拦截的handler\n    resp, err = handler(ctx, req)\n\n    //如果出错打印错误信息，否则打印resp\n    if err != nil {\n        log.GetLog().Errorln(err)\n    }else {\n        bresp, _ := json.Marshal(resp)\n        log.GetLog().Debugln(string(bresp))\n    }\n    return\n}\n```\n\n### 4.5 flow\n\n见doc/flow.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftedcy%2Fsheep","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftedcy%2Fsheep","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftedcy%2Fsheep/lists"}