这是我参与[第五届青训营]的第十七天
在通常用 Go 编写的无状态微服务中,我们需要发现它们。这就是 Hashicorp 的 Consul 提供的帮助。服务在 Consul 中注册,以便其他服务可以通过简单的 DNS 或 HTTP 查询发现它们。
从一个基本的 Web 服务开始,该服务将从 Redis 提供键值数据。
首先,解析port、ttl和addrs命令行标志。最后一个是用.分隔的Redis地址列表;。
func main() {
port := flag.Int("port", 8080, "Port to listen on")
addrsStr := flag.String("addrs", "", "(Required) Redis addrs (may be delimited by ;)")
ttl := flag.Duration("ttl", time.Second*15, "Service TTL check duration")
flag.Parse()
if len(*addrsStr) == 0 {
fmt.Fprintln(os.Stderr, "addrs argument is required")
flag.PrintDefaults()
os.Exit(1)
}
addrs := strings.Split(*addrsStr, ";")
现在,我们创建一个应该实现[Handler]接口并启动它的服务。
s, err := service.New(addrs, *ttl)
if err != nil {
log.Fatal(err)
}
http.Handle("/", s)
l := fmt.Sprintf(":%d", *port)
log.Print("Listening on ", l)
log.Fatal(http.ListenAndServe(l, nil))
import (
"time"
"github.com/go-redis/redis"
)
type Service struct {
Name string
TTL time.Duration
RedisClient redis.UniversalClient
}
Service是一种包含名称、TTL 和 Redis 客户端处理程序的类型。它是这样实例化的:
func New(addrs []string, ttl time.Duration) (*Service, error) {
s := new(Service)
s.Name = "webkv"
s.TTL = ttl
s.RedisClient = redis.NewUniversalClient(&redis.UniversalOptions{
Addrs: addrs,
})
ok, err := s.Check()
if !ok {
return nil, err
}
return s, nil
}
Check方法发出PINGRedis 命令来检查我们是否正常。这将在稍后与 Consul 注册一起使用。
func (s *Service) Check() (bool, error) {
_, err := s.RedisClient.Ping().Result()
if err != nil {
return false, err
}
return true, nil
现在ServeHTTP将调用请求处理方法的实现:
func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
status := 200
key := strings.Trim(r.URL.Path, "/")
val, err := s.RedisClient.Get(key).Result()
if err != nil {
http.Error(w, "Key not found", http.StatusNotFound)
status = 404
}
fmt.Fprint(w, val)
log.Printf("url="%s" remote="%s" key="%s" status=%d\n",
r.URL, r.RemoteAddr, key, status)
}
基本上,我们所做的是从请求中检索 URL 路径并将其用作 Redis“GET”命令的键。之后我们返回值或 404 以防出错。[最后,我们使用logfmt 格式]的快速的结构化日志消息记录请求 。