go|九,自写网关(library-apigateway)

网关 library-apigateway,接口访问的统一入口。通过 consul 获取注册的微服务的某一个实例,发起访问请求。如图:
go|九,自写网关(library-apigateway)
文章图片

完整代码:
https://github.com/Justin02180218/micro-kit

包结构 go|九,自写网关(library-apigateway)
文章图片





目前网关的功能只是做接口转发,所以只有一个 transport 层。

代码实现 配置文件

server: port: 80 mode: debug name: "apigateway"consul: addr: "http://consul-server:8500" interval: "10s" timeout: "1s" client: retrymax: 3 retrytimeout: 500


修改 consul.go
func HttpClient(cfg *configs.AppConfig, name, method, path string, logger log.Logger) endpoint.Endpoint { consulAddr := cfg.ConsulConfig.Addr retryMax := cfg.ConsulConfig.Client.RetryMax retryTimeout := cfg.ConsulConfig.Client.RetryTimeout client := connectConsul(consulAddr) instance := consul.NewInstancer(client, logger, name, []string{name}, true) factory := factoryForHttp(method, path) endpointer := sd.NewEndpointer(instance, factory, logger) balancer := lb.NewRoundRobin(endpointer) retry := lb.Retry(retryMax, time.Millisecond*time.Duration(retryTimeout), balancer) return retry }func factoryForHttp(method, path string) sd.Factory { return func(instance string) (endpoint.Endpoint, io.Closer, error) { if !strings.HasPrefix(instance, "http") { instance = "http://" + instance } tgt, err := url.Parse(instance) if err != nil { return nil, nil, err } tgt.Path = pathreturn httptransport.NewClient( method, tgt, utils.EncodeJSONRequest, utils.DecodeJSONResponse, ).Endpoint(), nil, nil } }

【go|九,自写网关(library-apigateway)】
transport层
在 transport 层定义 NewHttpHandler 函数,返回 *gin.Engine,目前采用硬编码的方式添加api接口,后续可以改成页面配置的方式。
func NewHttpHandler(ctx context.Context, cfg *configs.AppConfig, logger log.Logger) *gin.Engine { r := utils.NewRouter(ctx.Value("ginMod").(string)) e := r.Group("/api/user") { e.POST("register", func(c *gin.Context) { register := registers.HttpClient(cfg, "user-service", "POST", "/api/v1/register", logger) kithttp.NewServer( register, utils.DecodeJSONRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) e.GET("findByID", func(c *gin.Context) { findByID := registers.HttpClient(cfg, "user-service", "GET", "/api/v1/findByID", logger) kithttp.NewServer( findByID, utils.DecodeJSONRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) e.GET("findByEmail", func(c *gin.Context) { findByEmail := registers.HttpClient(cfg, "user-service", "GET", "/api/v1/findByEmail", logger) kithttp.NewServer( findByEmail, utils.DecodeJSONRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) e.GET("findBooksByUserID", func(c *gin.Context) { findBooksByUserID := registers.HttpClient(cfg, "user-service", "GET", "/api/v1/findBooksByUserID", logger) kithttp.NewServer( findBooksByUserID, utils.DecodeJSONRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) } e = r.Group("/api/book") { e.POST("save", func(c *gin.Context) { save := registers.HttpClient(cfg, "book-service", "POST", "/api/v1/save", logger) kithttp.NewServer( save, utils.DecodeJSONRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) e.GET("books", func(c *gin.Context) { books := registers.HttpClient(cfg, "book-service", "GET", "/api/v1/books", logger) kithttp.NewServer( books, utils.DecodeJSONRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) e.GET("selectBookByName", func(c *gin.Context) { selectBookByName := registers.HttpClient(cfg, "book-service", "GET", "/api/v1/selectBookByName", logger) kithttp.NewServer( selectBookByName, utils.DecodeJSONRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) e.POST("borrowBook", func(c *gin.Context) { borrowBook := registers.HttpClient(cfg, "book-service", "POST", "/api/v1/borrowBook", logger) kithttp.NewServer( borrowBook, utils.DecodeJSONRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) } return r }


启动服务 main.go
var confFile = flag.String("f", "apigateway.yaml", "user config file") func main() { flag.Parse() var logger log.Logger { logger = log.NewLogfmtLogger(os.Stderr) logger = log.With(logger, "ts", log.DefaultTimestampUTC) logger = log.With(logger, "caller", log.DefaultCaller) } err := configs.Init(*confFile) if err != nil { panic(err) } ctx := context.Background() ctx = context.WithValue(ctx, "ginMod", configs.Conf.ServerConfig.Mode) r := transport.NewHttpHandler(ctx, configs.Conf, logger) errChan := make(chan error) go func() { errChan <- r.Run(fmt.Sprintf(":%s", strconv.Itoa(configs.Conf.ServerConfig.Port))) }() go func() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errChan <- fmt.Errorf("%s", <-c) }() fmt.Println(<-errChan) }


启动
进入 library-apigateway 目录,执行 go run main.go 如图:
go|九,自写网关(library-apigateway)
文章图片


服务成功启动,监听80端口

接口测试
在 /etc/hosts 中配置 api.library.com 域名

使用postman进行接口测试,在这里进行了 findBooksByUserID 的接口测试,结果如图:
go|九,自写网关(library-apigateway)
文章图片


下一篇文章,我们在各个服务中加入分布式链路追踪功能。

完整代码:
https://github.com/Justin02180218/micro-kit
更多【分布式专辑】【架构实战专辑】系列文章,请关注公众号
go|九,自写网关(library-apigateway)
文章图片

    推荐阅读