ãã®èšäºã§ã¯ãGoã§ãã€ã¯ããµãŒãã¹ãäœæããããã®ããŒã«ãšã©ã€ãã©ãªã®ã»ããã§ããGoãããã®äœ¿çšã«ã€ããŠèª¬æããŸãã ãã®èšäºã¯Goãããã®çŽ¹ä»ã§ãã ç§ã®ããã°ã®æåã®éšåã§ããäŸã®ãœãŒã¹ã³ãŒãã¯ã ããããå ¥æã§ããŸã ã
Goã¯ãæè¿ã®åæ£ã·ã¹ãã ã®éçºã«ãŸããŸãéžã°ããŠããŸãã ã¯ã©ãŠãããŒã¹ã®åæ£ã·ã¹ãã ãéçºããŠããå ŽåããµãŒãã¹ã®ããŸããŸãªç¹å®ã®æ©èœããµããŒãããå¿ èŠãããå ŽåããããŸããããŸããŸãªãã©ã³ã¹ããŒããããã³ã«ïŒ translãHTTPãgRPCãªã© ïŒããã³ãããã®ã¡ãã»ãŒãžãšã³ã³ãŒãã£ã³ã°åœ¢åŒãRPCä¿¡é Œæ§ããã®ã³ã°ããã¬ãŒã¹ãã¡ããªãã¯ãšãããã¡ã€ãªã³ã°ããªã¯ãšã¹ãã®äžæããªã¯ãšã¹ãã®æ°ã®å¶éãã€ã³ãã©ã¹ãã©ã¯ãã£ãžã®çµ±åãããã«ã¯ã¢ãŒããã¯ãã£ã®èª¬æãå«ãŸããŸãã Goã¯ããã®ã·ã³ãã«ããšãéæ³ã®ãªããã¢ãããŒãã®ããã«äººæ°ã®ããèšèªã§ãããããã£ãŠãGoã®ããã±ãŒãžïŒæšæºã©ã€ãã©ãªãªã©ïŒã¯ãå€ãã®ãå éšã®éæ³ããåããæ¬æ Œçãªãã¬ãŒã ã¯ãŒã¯ã䜿çšããããããåæ£ã·ã¹ãã ã®éçºã«ãã§ã«é©ããŠããŸãã å人çã«ãç§[ çŽã ããã Shiju Varghese ]ç§ã¯æ¬æ Œçãªãã¬ãŒã ã¯ãŒã¯ã®äœ¿çšããµããŒãããŠããŸãããéçºè ã«ããå€ãã®èªç±ãäžããã©ã€ãã©ãªã䜿çšããããšã奜ã¿ãŸãã Goãããã¯Goãšã³ã·ã¹ãã ã®ã®ã£ãããåãããã€ã¯ããµãŒãã¹ã®äœææã«ã©ã€ãã©ãªãšããã±ãŒãžã®ã»ããã䜿çšã§ããããã«ããŸãããããã«ãããåæ£ã·ã¹ãã ã§åã ã®ãµãŒãã¹ãèšèšããããã®åªããååã䜿çšã§ããŸãã
Goãããã®æŠèŠ
Goãããã¯ãä¿¡é Œã§ãããµããŒãããããã€ã¯ããµãŒãã¹ãç°¡åã«æ§ç¯ã§ããGoããã±ãŒãžã®ã»ããã§ãã Goãããã¯ã補åã§ãã€ã¯ããµãŒãã¹ãå®è¡ããããã«å¿ èŠãªãã®ã³ã°ãã¡ããªãã¯ããã¬ãŒã¹ãå¶éãå²ã蟌ã¿èŠæ±ãªã©ã®ã¬ã€ã€ãŒã䜿çšããŠãééçã§ä¿¡é Œã§ããã¢ããªã±ãŒã·ã§ã³ã¢ãŒããã¯ãã£ã®ããŸããŸãªã³ã³ããŒãã³ããå®è£ ããããã®ã©ã€ãã©ãªãæäŸããŸãã Goãããã¯ãããŸããŸãªã€ã³ãã©ã¹ãã©ã¯ãã£ãã¡ãã»ãŒãžãšã³ã³ãŒã圢åŒãããŸããŸãªãã©ã³ã¹ããŒãã¬ã€ã€ãŒãšå¯Ÿè©±ããããã®ããŒã«ãå®è£ ãããŠãããããåªããŠããŸãã
ã¯ãŒã«ããµãŒãã¹ãéçºããããã®ã©ã€ãã©ãªã»ããã«å ããŠããµãŒãã¹ã®ã¢ãŒããã¯ãã£ãèšèšããããã®åªããååã®äœ¿çšãæäŸãã奚å±ããŠããŸãã Goãããã¯ã Alistair Cockburnãææ¡ããSOLIDååããµããžã§ã¯ãæåã¢ãããŒãïŒDDDïŒã å è§åœ¢ã¢ãŒããã¯ã㣠ããŸãã¯Jeffrey Palermoã ã ã¿ããã® ã¢ãŒããã¯ã㣠ãã Robert C. Martinã ã ã¯ãªãŒã³ã¢ãŒããã¯ã㣠ããšããŠç¥ããããã®ä»ã®ã¢ãŒããã¯ãã£ã¢ãããŒããé å®ããã®ã«åœ¹ç«ã¡ãŸãã Goãããã¯ããã€ã¯ããµãŒãã¹ãéçºããããã®äžé£ã®ããã±ãŒãžãšããŠèšèšãããŸãããããšã¬ã¬ã³ããªã¢ããªã¹ã®éçºã«ãé©ããŠããŸãã
Architecture Goããã
Goãããã䜿çšããŠéçºãããã¢ããªã±ãŒã·ã§ã³ã®ã¢ãŒããã¯ãã£ã®3ã€ã®äž»èŠãªã¬ãã«ã¯æ¬¡ã®ãšããã§ãã
- 茞éã¬ãã«
- ãšã³ããã€ã³ãã¬ãã«
- ãµãŒãã¹ã¬ãã«
茞éã¬ãã«
åæ£ã·ã¹ãã çšã®ãã€ã¯ããµãŒãã¹ãäœæããå Žåããããã®ãµãŒãã¹ã¯å€ãã®å ŽåãHTTPãgRPCãªã©ã®ããŸããŸãªãã©ã³ã¹ããŒããããã³ã«ã䜿çšããŠçžäºã«éä¿¡ããããNATSãªã©ã®pub / subã·ã¹ãã ã䜿çšããå¿ èŠããããŸãã Goãããã®ãã©ã³ã¹ããŒãå±€ã¯ãç¹å®ã®ãã©ã³ã¹ããŒããããã³ã«ïŒä»¥äžããã©ã³ã¹ããŒãïŒã«é¢é£ä»ããããŠããŸãã Goãããã¯ãHTTPãgRPCãNATSãAMQPãThirftãªã©ããµãŒãã¹ã®ããŸããŸãªãã©ã³ã¹ããŒãããµããŒãããŸãïŒããããã ãããã³ã«çšã«ç¬èªã®ãã©ã³ã¹ããŒããéçºããããšãã§ããŸã ïŒã ãããã£ãŠãGoãããã䜿çšããŠèšè¿°ããããµãŒãã¹ã¯ã䜿çšããããã©ã³ã¹ããŒãã«ã€ããŠäœãç¥ããªãç¹å®ã®ããžãã¹ããžãã¯ã®å®è£ ã«çŠç¹ãåœãŠãããšããããããåããµãŒãã¹ã«å¯ŸããŠç°ãªããã©ã³ã¹ããŒããèªç±ã«äœ¿çšã§ããŸãã ããšãã°ãGoãããã§èšè¿°ããã1ã€ã®ãµãŒãã¹ã¯ãHTTPããã³gRPCãä»ããŠåæã«ã¢ã¯ã»ã¹ã§ããŸãã
ãšã³ããã€ã³ã
ãšã³ããã€ã³ããŸãã¯ãšã³ããã€ã³ãã¯ããµãŒãã¹ãšé¡§å®¢ã®åºæ¬çãªæ§æèŠçŽ ã§ãã Goãããã®äž»ãªéä¿¡ãã¿ãŒã³ã¯RPCã§ãã ãšã³ããã€ã³ãã¯ãå¥åã®RPCã¡ãœãããšããŠæ瀺ãããŸãã Goãããã®åãµãŒãã¹ã¡ãœããã¯ãšã³ããã€ã³ãã«å€æãããããããµãŒããŒãšã¯ã©ã€ã¢ã³ãéã§RCPã¹ã¿ã€ã«ã§éä¿¡ã§ããŸãã åãšã³ããã€ã³ãã¯ããã©ã³ã¹ããŒãå±€ã䜿çšããŠãµãŒãã¹ã¡ãœãããå ¬éããŸãããã©ã³ã¹ããŒãå±€ã¯ãHTTPãgRPCãªã©ã®ããŸããŸãªãã©ã³ã¹ããŒããããã³ã«ã䜿çšããŸãã åå¥ã®ãšã³ããã€ã³ãã¯ãè€æ°ã®ãã©ã³ã¹ããŒãïŒ ç°ãªãããŒãã§çŽPer HTTPããã³gRPC ïŒã䜿çšããŠããµãŒãã¹ã®å€éšã«åæã«å ¬éã§ããŸãã
ãµãŒãã¹
ããžãã¹ããžãã¯ã¯ããµãŒãã¹ã¬ã€ã€ãŒã«å®è£ ãããŸãã Goãããã§äœæããããµãŒãã¹ã¯ãã€ã³ã¿ãŒãã§ãŒã¹ãšããŠèšèšãããŠããŸãã ãµãŒãã¹ã¬ã€ã€ãŒã®ããžãã¹ããžãã¯ã«ã¯ãããžãã¹ããžãã¯ã®ã¡ã€ã³ã³ã¢ãå«ãŸããŸããããã«ã¯ã䜿çšãããŠãããšã³ããã€ã³ãããHTTPãgRPCãªã©ã®ç¹å®ã®ãã©ã³ã¹ããŒããããã³ã«ã«ã€ããŠããŸãã¯ããŸããŸãªçš®é¡ã®ã¡ãã»ãŒãžã®èŠæ±ãšå¿çã®ãšã³ã³ãŒããŸãã¯ãã³ãŒãã«ã€ããŠã®ç¥èã¯å¿ èŠãããŸããã ããã«ãããGoãããã䜿çšããŠèšè¿°ããããµãŒãã¹ã®ã¯ãªãŒã³ãªã¢ãŒããã¯ãã£ãé å®ã§ããŸãã åãµãŒãã¹ã¡ãœããã¯ãã¢ããã¿ãŒã䜿çšããŠãšã³ããã€ã³ãã«å€æãããç¹å®ã®ãã©ã³ã¹ããŒãã䜿çšããŠå€éšã«å ¬éãããŸãã ã¯ãªãŒã³ãªã¢ãŒããã¯ãã£ã䜿çšããããšã«ãããè€æ°ã®ãã©ã³ã¹ããŒããåæã«äœ¿çšããŠåäžã®ã¡ãœãããèšå®ã§ããŸãã
äŸ
次ã«ãç°¡åãªã¢ããªã±ãŒã·ã§ã³ã®äŸã䜿çšããŠãäžèšã®ã¬ã€ã€ãŒãèŠãŠã¿ãŸãããã
ãµãŒãã¹ã®ããžãã¹ããžãã¯
ãµãŒãã¹ã®ããžãã¹ããžãã¯ã¯ãã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšããŠèšèšãããŠããŸãã eã³ããŒã¹ã§ã®æ³šæã®äŸãèŠãŠã¿ãŸãããã
// Service describes the Order service. type Service interface { Create(ctx context.Context, order Order) (string, error) GetByID(ctx context.Context, id string) (Order, error) ChangeStatus(ctx context.Context, id string, status string) error }
OrderãµãŒãã¹ã€ã³ã¿ãŒãã§ã€ã¹ã¯ã泚æãã¡ã€ã³ãšã³ãã£ãã£ãšé£æºããŸãã
// Order represents an order type Order struct { ID string `json:"id,omitempty"` CustomerID string `json:"customer_id"` Status string `json:"status"` CreatedOn int64 `json:"created_on,omitempty"` RestaurantId string `json:"restaurant_id"` OrderItems []OrderItem `json:"order_items,omitempty"` } // OrderItem represents items in an order type OrderItem struct { ProductCode string `json:"product_code"` Name string `json:"name"` UnitPrice float32 `json:"unit_price"` Quantity int32 `json:"quantity"` } // Repository describes the persistence on order model type Repository interface { CreateOrder(ctx context.Context, order Order) error GetOrderByID(ctx context.Context, id string) (Order, error) ChangeOrderStatus(ctx context.Context, id string, status string) error }
ããã§ãOrderãµãŒãã¹ã®ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£ ããŸãã
package implementation import ( "context" "database/sql" "time" "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" "github.com/gofrs/uuid" ordersvc "github.com/shijuvar/gokit-examples/services/order" ) // service implements the Order Service type service struct { repository ordersvc.Repository logger log.Logger } // NewService creates and returns a new Order service instance func NewService(rep ordersvc.Repository, logger log.Logger) ordersvc.Service { return &service{ repository: rep, logger: logger, } } // Create makes an order func (s *service) Create(ctx context.Context, order ordersvc.Order) (string, error) { logger := log.With(s.logger, "method", "Create") uuid, _ := uuid.NewV4() id := uuid.String() order.ID = id order.Status = "Pending" order.CreatedOn = time.Now().Unix() if err := s.repository.CreateOrder(ctx, order); err != nil { level.Error(logger).Log("err", err) return "", ordersvc.ErrCmdRepository } return id, nil } // GetByID returns an order given by id func (s *service) GetByID(ctx context.Context, id string) (ordersvc.Order, error) { logger := log.With(s.logger, "method", "GetByID") order, err := s.repository.GetOrderByID(ctx, id) if err != nil { level.Error(logger).Log("err", err) if err == sql.ErrNoRows { return order, ordersvc.ErrOrderNotFound } return order, ordersvc.ErrQueryRepository } return order, nil } // ChangeStatus changes the status of an order func (s *service) ChangeStatus(ctx context.Context, id string, status string) error { logger := log.With(s.logger, "method", "ChangeStatus") if err := s.repository.ChangeOrderStatus(ctx, id, status); err != nil { level.Error(logger).Log("err", err) return ordersvc.ErrCmdRepository } return nil }
RPCãšã³ããã€ã³ãã®ãªã¯ãšã¹ããšåç
ãµãŒãã¹ã¡ãœããã¯RPCãšã³ããã€ã³ããšããŠå ¬éãããŸãã ãã®ãããRPCãšã³ããã€ã³ããä»ããŠã¡ãã»ãŒãžãéåä¿¡ããããã«äœ¿çšãããã¡ãã»ãŒãžã®ã¿ã€ãïŒ ãããDTO-ããŒã¿è»¢éãªããžã§ã¯ã ïŒã決å®ããå¿ èŠããããŸãã 次ã«ãOrderãµãŒãã¹ã§RPCãšã³ããã€ã³ãã®èŠæ±ããã³å¿çã¿ã€ãã®æ§é ãå®çŸ©ããŸãããã
// CreateRequest holds the request parameters for the Create method. type CreateRequest struct { Order order.Order } // CreateResponse holds the response values for the Create method. type CreateResponse struct { ID string `json:"id"` Err error `json:"error,omitempty"` } // GetByIDRequest holds the request parameters for the GetByID method. type GetByIDRequest struct { ID string } // GetByIDResponse holds the response values for the GetByID method. type GetByIDResponse struct { Order order.Order `json:"order"` Err error `json:"error,omitempty"` } // ChangeStatusRequest holds the request parameters for the ChangeStatus method. type ChangeStatusRequest struct { ID string `json:"id"` Status string `json:"status"` } // ChangeStatusResponse holds the response values for the ChangeStatus method. type ChangeStatusResponse struct { Err error `json:"error,omitempty"` }
RPCãšã³ããã€ã³ããªã©ã®ãµãŒãã¹ã¡ãœããçšã®Goããããšã³ããã€ã³ã
ããžãã¹ããžãã¯ã®ã³ã¢ã¯ãæ®ãã®ã³ãŒãããåé¢ããããµãŒãã¹ã¬ã€ã€ãŒã«é 眮ãããŸãããµãŒãã¹ã¬ã€ã€ãŒã¯ãRPCãšã³ããã€ã³ãã䜿çšããŠå ¬éããããšã³ããã€ã³ããšåŒã°ããGoãããã¢ãã¹ãã©ã¯ã·ã§ã³ã䜿çšããŸãã
Goãããã®ãšã³ããã€ã³ãã¯æ¬¡ã®ããã«ãªããŸãã
type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)
åè¿°ããããã«ããšã³ããã€ã³ãã¯å¥ã®RPCã¡ãœãããè¡šããŸãã åãµãŒãã¹ã¡ãœããã¯ãã¢ããã¿ãŒã䜿çšããŠendpoint.Endpoint
å€æããendpoint.Endpoint
ã OrderãµãŒãã¹ã¡ãœããã®Goããããšã³ããã€ã³ããäœæããŸãããã
import ( "context" "github.com/go-kit/kit/endpoint" "github.com/shijuvar/gokit-examples/services/order" ) // Endpoints holds all Go kit endpoints for the Order service. type Endpoints struct { Create endpoint.Endpoint GetByID endpoint.Endpoint ChangeStatus endpoint.Endpoint } // MakeEndpoints initializes all Go kit endpoints for the Order service. func MakeEndpoints(s order.Service) Endpoints { return Endpoints{ Create: makeCreateEndpoint(s), GetByID: makeGetByIDEndpoint(s), ChangeStatus: makeChangeStatusEndpoint(s), } } func makeCreateEndpoint(s order.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(CreateRequest) id, err := s.Create(ctx, req.Order) return CreateResponse{ID: id, Err: err}, nil } } func makeGetByIDEndpoint(s order.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(GetByIDRequest) orderRes, err := s.GetByID(ctx, req.ID) return GetByIDResponse{Order: orderRes, Err: err}, nil } } func makeChangeStatusEndpoint(s order.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(ChangeStatusRequest) err := s.ChangeStatus(ctx, req.ID, req.Status) return ChangeStatusResponse{Err: err}, nil } }
ãšã³ããã€ã³ãã¢ããã¿ãŒã¯ãå
¥åãžã®ãã©ã¡ãŒã¿ãŒãšããŠã€ã³ã¿ãŒãã§ã€ã¹ãåãå
¥ããGoãããendpoint.Enpoint
æœè±¡åã«å€æããŸããåã
ã®ãµãŒãã¹ã¡ãœããããšã³ããã€ã³ãã«ããEnpointã ãã®ã¢ããã¿ãŒé¢æ°ã¯ãèŠæ±ã®æ¯èŒãšåå€æãè¡ãããµãŒãã¹ã¡ãœãããåŒã³åºããå¿çã¡ãã»ãŒãžãè¿ããŸãã
func makeCreateEndpoint(s order.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(CreateRequest) id, err := s.Create(ctx, req.Order) return CreateResponse{ID: id, Err: err}, nil } }
HTTPã䜿çšãããµãŒãã¹ã®å ¬é
ãµãŒãã¹ãäœæãããµãŒãã¹ã¡ãœãããå ¬éããããã®RPCãšã³ããã€ã³ãã«ã€ããŠèª¬æããŸããã 次ã«ãä»ã®ãµãŒãã¹ãRCPãšã³ããã€ã³ããåŒã³åºãããšãã§ããããã«ããµãŒãã¹ãå€éšã«å ¬éããå¿ èŠããããŸãã ãµãŒãã¹ãå ¬éããã«ã¯ããªã¯ãšã¹ããåãå ¥ãããµãŒãã¹ã®ãã©ã³ã¹ããŒããããã³ã«ã決å®ããå¿ èŠããããŸãã Goãããã¯ãHTTPãgRPCãNATSãAMQPãThriftãªã©ã®ããŸããŸãªãã©ã³ã¹ããŒããããã«ãµããŒãããŸãã
äŸãšããŠããµãŒãã¹ã«HTTPãã©ã³ã¹ããŒãã䜿çšããŸãã goãããããã±ãŒãžgithub.com/go-kit/kit/transport/httpã¯ãHTTPãªã¯ãšã¹ããåŠçããæ©èœãæäŸããŸãã transport/http
ããã±ãŒãžã®NewServer
é¢æ°ã¯ã NewServer
ãå®è£
ããæäŸããããšã³ããã€ã³ããã©ããããæ°ããhttpãµãŒããŒãäœæããŸãã
以äžã¯ãGoãããã®ãšã³ããã€ã³ããHTTPãªã¯ãšã¹ããåŠçããHTTPãã©ã³ã¹ããŒãã«å€æããã³ãŒãã§ãã
package http import ( "context" "encoding/json" "errors" "github.com/shijuvar/gokit-examples/services/order" "net/http" "github.com/go-kit/kit/log" kithttp "github.com/go-kit/kit/transport/http" "github.com/gorilla/mux" "github.com/shijuvar/gokit-examples/services/order/transport" ) var ( ErrBadRouting = errors.New("bad routing") ) // NewService wires Go kit endpoints to the HTTP transport. func NewService( svcEndpoints transport.Endpoints, logger log.Logger, ) http.Handler { // set-up router and initialize http endpoints r := mux.NewRouter() options := []kithttp.ServerOption{ kithttp.ServerErrorLogger(logger), kithttp.ServerErrorEncoder(encodeError), } // HTTP Post - /orders r.Methods("POST").Path("/orders").Handler(kithttp.NewServer( svcEndpoints.Create, decodeCreateRequest, encodeResponse, options..., )) // HTTP Post - /orders/{id} r.Methods("GET").Path("/orders/{id}").Handler(kithttp.NewServer( svcEndpoints.GetByID, decodeGetByIDRequest, encodeResponse, options..., )) // HTTP Post - /orders/status r.Methods("POST").Path("/orders/status").Handler(kithttp.NewServer( svcEndpoints.ChangeStatus, decodeChangeStausRequest, encodeResponse, options..., )) return r } func decodeCreateRequest(_ context.Context, r *http.Request) (request interface{}, err error) { var req transport.CreateRequest if e := json.NewDecoder(r.Body).Decode(&req.Order); e != nil { return nil, e } return req, nil } func decodeGetByIDRequest(_ context.Context, r *http.Request) (request interface{}, err error) { vars := mux.Vars(r) id, ok := vars["id"] if !ok { return nil, ErrBadRouting } return transport.GetByIDRequest{ID: id}, nil } func decodeChangeStausRequest(_ context.Context, r *http.Request) (request interface{}, err error) { var req transport.ChangeStatusRequest if e := json.NewDecoder(r.Body).Decode(&req); e != nil { return nil, e } return req, nil } func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { if e, ok := response.(errorer); ok && e.error() != nil { // Not a Go kit transport error, but a business-logic error. // Provide those as HTTP errors. encodeError(ctx, e.error(), w) return nil } w.Header().Set("Content-Type", "application/json; charset=utf-8") return json.NewEncoder(w).Encode(response) }
transport/http
ããã±ãŒãžã®NewServer
é¢æ°ã䜿çšããŠNewServer
ãäœæããŸããããã«ããããšã³ããã€ã³ããšãªã¯ãšã¹ããã³ãŒãé¢æ°ïŒ type DecodeRequestFunc func
ã®å€ãè¿ãïŒããã³ã¬ã¹ãã³ã¹ãšã³ã³ãŒãã£ã³ã°ïŒ type EncodeReponseFunc func
ïŒãæäŸãããŸãã
以äžã¯ã DecodeRequestFunc
ãšEncodeResponseFunc
äŸã§ãã
// For decoding request type DecodeRequestFunc func(context.Context, *http.Request) (request interface{}, err error)
// For encoding response type EncodeResponseFunc func(context.Context, http.ResponseWriter, interface{}) error
HTTPãµãŒããŒã®éå§
æåŸã«ãHTTPãµãŒããŒãå®è¡ããŠãªã¯ãšã¹ããåŠçã§ããŸãã äžèšã®NewService
é¢æ°ã¯ãHTTPãµãŒããŒãšããŠå®è¡ã§ããhttp.Handler
ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ããŸãã
func main() { var ( httpAddr = flag.String("http.addr", ":8080", "HTTP listen address") ) flag.Parse() var logger log.Logger { logger = log.NewLogfmtLogger(os.Stderr) logger = log.NewSyncLogger(logger) logger = level.NewFilter(logger, level.AllowDebug()) logger = log.With(logger, "svc", "order", "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller, ) } level.Info(logger).Log("msg", "service started") defer level.Info(logger).Log("msg", "service ended") var db *sql.DB { var err error // Connect to the "ordersdb" database db, err = sql.Open("postgres", "postgresql://shijuvar@localhost:26257/ordersdb?sslmode=disable") if err != nil { level.Error(logger).Log("exit", err) os.Exit(-1) } } // Create Order Service var svc order.Service { repository, err := cockroachdb.New(db, logger) if err != nil { level.Error(logger).Log("exit", err) os.Exit(-1) } svc = ordersvc.NewService(repository, logger) } var h http.Handler { endpoints := transport.MakeEndpoints(svc) h = httptransport.NewService(endpoints, logger) } errs := make(chan error) go func() { c := make(chan os.Signal) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errs <- fmt.Errorf("%s", <-c) }() go func() { level.Info(logger).Log("transport", "HTTP", "addr", *httpAddr) server := &http.Server{ Addr: *httpAddr, Handler: h, } errs <- server.ListenAndServe() }() level.Error(logger).Log("exit", <-errs) }
ããã§ããµãŒãã¹ãéå§ããããã©ã³ã¹ããŒãã¬ãã«ã§HTTPãããã³ã«ã䜿çšãããŸãã å¥ã®ãã©ã³ã¹ããŒãã䜿çšããŠåããµãŒãã¹ãéå§ã§ããŸãïŒããšãã°ãgRPCãŸãã¯Apache Thriftã䜿çšããŠãµãŒãã¹ãå ¬éã§ããŸãïŒã
å ¥éèšäºã§ã¯ãGoãããããªããã£ããæ¢ã«ååã«äœ¿çšããŠããŸãããééçã§ä¿¡é Œæ§ã®é«ããã¿ãŒã³ã®ã·ã¹ãã ãäœæããããã®ããå€ãã®æ©èœããµãŒãã¹æ€åºãè² è·åæ£ãªã©ãæäŸããŸãã Goãããã®ãããããã³ãã®ä»ã®äºé ã«ã€ããŠã¯ã次ã®èšäºã§èª¬æããŸãã
ãœãŒã¹ã³ãŒã
ãµã³ãã«ã®ãœãŒã¹ã³ãŒãå šäœã¯ãGitHubã§ããã§è¡šç€ºã§ããŸãã
Goãããã®ããã«ãŠã§ã¢
Goãããã¯ãéå±€åãªã©ã®ã·ã¹ãã èšèšã®åªããååã䜿çšããåŸåããããŸãã ããã«ãŠã§ã¢ã䜿çšããŠããµãŒãã¹ã³ã³ããŒãã³ããšãšã³ããã€ã³ãã®åé¢ãå¯èœã§ã ïŒ çŽã¬ãŒã³ã¡ãã£ãšãŒã¿ãŒãã¿ãŒã³ ïŒã Goãããã®ããã«ãŠã§ã¢ã¯ããã®ã³ã°ããªã¯ãšã¹ãã®äžæããªã¯ãšã¹ãæ°ã®å¶éãããŒããã©ã³ã·ã³ã°ãŸãã¯åæ£ãã¬ãŒã¹ãªã©ããµãŒãã¹ãšãšã³ããã€ã³ããã©ããããæ©èœïŒåé¢ã³ã³ããŒãã³ãïŒãè¿œå ã§ãã匷åãªã¡ã«ããºã ãæäŸããŸãã
以äžã¯ã Goãããã® Webãµã€ãã®åçã§ããGoãããã®ããã«ãŠã§ã¢ã䜿çšããå
žåçãªããªããªã³ã¢ãŒããã¯ãã£ããšããŠæãããŠããŸãã
Spring Boot Microservices Syndromeã«æ³šæããŠãã ãã
Goããããšåæ§ã«ãSpring Bootã¯Javaã®äžçã«ããããã€ã¯ããµãŒãã¹ããŒã«ãããã§ãã ãã ããGoããããšã¯ç°ãªããSpring Bootã¯éåžžã«æçãããã¬ãŒã ã¯ãŒã¯ã§ãã ãŸããå€ãã®Javaéçºè ã¯ãSpring Bootã䜿çšããŠã䜿çšããã®è¯å®çãªãã£ãŒãããã¯ã䌎ãJavaã¹ã¿ãã¯ã䜿çšããŠã¯ãŒã«ããµãŒãã¹ãäœæããŸãã ãã€ã¯ããµãŒãã¹ã®äœ¿çšã誀ã£ãŠè§£éããå€ãã®éçºããŒã ãããŸãããããã¯Spring BootãšOSS Netflixã䜿çšããŠã®ã¿éçºã§ããåæ£ã·ã¹ãã ãéçºããéã®ãã¿ãŒã³ãšããŠãã€ã¯ããµãŒãã¹ãèªèããŸããã
ãããã£ãŠãGoããããäœããã®ãã¬ãŒã ã¯ãŒã¯ãªã©ã®ããŒã«ã»ããã䜿çšãããšãèšèšãã³ãã¬ãŒããšããŠéçºããã€ã¯ãã»ãã¥ãªãã£ã«åããããšãã§ããŸãã ãã€ã¯ããµãŒãã¹ã¯ãã³ãã³ããšã·ã¹ãã ã®äž¡æ¹ã®ã¹ã±ãŒãªã³ã°ã®åé¡ãå€ã解決ããŸããããã€ã¯ããµãŒãã¹ããŒã¹ã®ã·ã¹ãã ã®ããŒã¿ã¯ããŸããŸãªããŒã¿ããŒã¹ã«æ£åšããŠãããããå€ãã®åé¡ãçºçããŸãã ããã¯ãã¹ãŠããµããžã§ã¯ãé åã®åé¡ãšã·ã¹ãã ã®ã³ã³ããã¹ãã«äŸåããŸãã ã¯ãŒã«ãªã®ã¯ããã€ã¯ããµãŒãã¹ãäœæããããã®ããŒã«ãšããŠèšèšãããGoãããããã·ã¹ãã ã®åªããã¢ãŒããã¯ãã£èšèšã§äœæããããšã¬ã¬ã³ããªã¢ããªã¹ã®äœæã«ãé©ããŠããããšã§ãã
ãŸããèŠæ±ã®äžæãå¶éãªã©ã®Goãããã®æ©èœã¯ãIstioãªã©ã®ãµãŒãã¹ã¡ãã·ã¥ãã©ãããã©ãŒã ã§ãå©çšã§ããŸãã ãããã£ãŠãIstioã®ãããªãã®ã䜿çšããŠãã€ã¯ãã»ãã¥ãªãã£ãå®è¡ããå ŽåãGoãããã®äžéšã¯å¿ èŠãªããããããŸãããããµãŒãã¹ã¡ãã·ã¥ã䜿çšããŠãµãŒãã¹ééä¿¡ãäœæããã®ã«ååãªãã£ã³ãã«å¹ ããããšã¯éããŸãã1ã€ã®ã¬ãã«ãšè¿œå ã®è€éãã
PS
翻蚳ã®èè ã¯ã åæã®èè ã®æèŠãå ±æããããšã¯ã§ããŸããããã®èšäºã¯ããã·ã¢èªã³ãã¥ããã£Goã®ã¿ã察象ãšããæè²ç®çã§ç¿»èš³ãããŠããŸãã
UPD
ããã¯ç¿»èš³ã»ã¯ã·ã§ã³ã®æåã®èšäºã§ãããã翻蚳ã«é¢ãããã£ãŒãããã¯ã«æè¬ããŸãã