网关 library-apigateway,接口访问的统一入口。通过 consul 获取注册的微服务的某一个实例,发起访问请求。如图:
完整代码:
https://github.com/Justin02180218/micro-kit
包结构
目前网关的功能只是做接口转发,所以只有一个 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 如图:
服务成功启动,监听80端口
接口测试
在 /etc/hosts 中配置 api.library.com 域名
使用postman进行接口测试,在这里进行了 findBooksByUserID 的接口测试,结果如图:
下一篇文章,我们在各个服务中加入分布式链路追踪功能。
完整代码:
https://github.com/Justin02180218/micro-kit
更多【分布式专辑】【架构实战专辑】系列文章,请关注公众号