九,自写网关(library-apigateway)

网关 library-apigateway,接口访问的统一入口。通过 consul 获取注册的微服务的某一个实例,发起访问请求。如图:

九,自写网关(library-apigateway)_第1张图片

完整代码:

https://github.com/Justin02180218/micro-kit

包结构

九,自写网关(library-apigateway)_第2张图片

 

 

 

 

目前网关的功能只是做接口转发,所以只有一个 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 = path
        
        return httptransport.NewClient(
            method,
            tgt,
            utils.EncodeJSONRequest,
            utils.DecodeJSONResponse,
        ).Endpoint(), nil, nil
    }
}

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 如图:

九,自写网关(library-apigateway)_第3张图片

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

接口测试

在 /etc/hosts 中配置 api.library.com 域名

使用postman进行接口测试,在这里进行了 findBooksByUserID 的接口测试,结果如图:

九,自写网关(library-apigateway)_第4张图片

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

完整代码:

https://github.com/Justin02180218/micro-kit


更多【分布式专辑】【架构实战专辑】系列文章,请关注公众号

你可能感兴趣的:(go,分布式架构,微服务,go语言,微服务架构,docker,k8s,网关)