micro--go-micro
1bb25d6e7f
* feat: add agent platform showcase and blog post Add a complete platform example (Users, Posts, Comments, Mail) that mirrors micro/blog, demonstrating how existing microservices become AI-accessible through MCP with zero code changes. Includes blog post "Your Microservices Are Already an AI Platform" walking through real agent workflows: signup, content creation, commenting, tagging, and cross-service messaging. https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc * refactor: rename handler types to drop redundant Service suffix UserService → Users, PostService → Posts, CommentService → Comments, MailService → Mail. Matches micro/blog naming convention. https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc * refactor: consolidate top-level directories, reduce framework bloat Move internal/non-public packages behind internal/ or into their parent packages where they belong: - deploy/ → gateway/mcp/deploy/ (Helm charts belong with the gateway) - profile/ → service/profile/ (preset plugin profiles are a service concern) - scripts/ → internal/scripts/ (install script is not public API) - test/ → internal/test/ (test harness is not public API) - util/ → internal/util/ (internal helpers shouldn't be imported externally) Also fixes CLAUDE.md merge conflict markers and updates project structure documentation. All import paths updated. Build and tests pass. https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc * refactor: redesign model package to match framework conventions Rename model.Database interface to model.Model (consistent with client.Client, server.Server, store.Store). Remove generics in favor of interface{}-based API with reflection. Key changes: - model.Model interface: Register once, CRUD infers table from type - DefaultModel + NewModel() + package-level convenience functions - Schema registered via Register(&User{}), no per-call schema passing - Memory implementation as default (in model package, like store) - memory/sqlite/postgres backends updated for new interface - protoc-gen-micro generates RegisterXModel() instead of generic factory - All docs, blog, and README updated https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc --------- Co-authored-by: Claude <noreply@anthropic.com>
217 行
3.3 KiB
Go
217 行
3.3 KiB
Go
package transport
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
log "go-micro.dev/v5/logger"
|
|
"go-micro.dev/v5/internal/util/buf"
|
|
)
|
|
|
|
type httpTransportClient struct {
|
|
dialOpts DialOptions
|
|
conn net.Conn
|
|
ht *httpTransport
|
|
|
|
// request must be stored for response processing
|
|
req chan *http.Request
|
|
buff *bufio.Reader
|
|
addr string
|
|
|
|
// local/remote ip
|
|
local string
|
|
remote string
|
|
reqList []*http.Request
|
|
|
|
sync.RWMutex
|
|
|
|
once sync.Once
|
|
|
|
closed bool
|
|
}
|
|
|
|
func (h *httpTransportClient) Local() string {
|
|
return h.local
|
|
}
|
|
|
|
func (h *httpTransportClient) Remote() string {
|
|
return h.remote
|
|
}
|
|
|
|
func (h *httpTransportClient) Send(m *Message) error {
|
|
logger := h.ht.Options().Logger
|
|
|
|
header := make(http.Header)
|
|
for k, v := range m.Header {
|
|
header.Set(k, v)
|
|
}
|
|
|
|
b := buf.New(bytes.NewBuffer(m.Body))
|
|
defer func() {
|
|
if err := b.Close(); err != nil {
|
|
logger.Logf(log.ErrorLevel, "failed to close buffer: %v", err)
|
|
}
|
|
}()
|
|
|
|
req := &http.Request{
|
|
Method: http.MethodPost,
|
|
URL: &url.URL{
|
|
Scheme: "http",
|
|
Host: h.addr,
|
|
},
|
|
Header: header,
|
|
Body: b,
|
|
ContentLength: int64(b.Len()),
|
|
Host: h.addr,
|
|
Close: h.dialOpts.ConnClose,
|
|
}
|
|
|
|
if !h.dialOpts.Stream {
|
|
h.Lock()
|
|
if h.closed {
|
|
h.Unlock()
|
|
return io.EOF
|
|
}
|
|
|
|
h.reqList = append(h.reqList, req)
|
|
|
|
select {
|
|
case h.req <- h.reqList[0]:
|
|
h.reqList = h.reqList[1:]
|
|
default:
|
|
}
|
|
h.Unlock()
|
|
}
|
|
|
|
// set timeout if its greater than 0
|
|
if h.ht.opts.Timeout > time.Duration(0) {
|
|
if err := h.conn.SetDeadline(time.Now().Add(h.ht.opts.Timeout)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return req.Write(h.conn)
|
|
|
|
}
|
|
|
|
// Recv receives a message.
|
|
func (h *httpTransportClient) Recv(msg *Message) (err error) {
|
|
if msg == nil {
|
|
return errors.New("message passed in is nil")
|
|
}
|
|
|
|
var req *http.Request
|
|
|
|
if !h.dialOpts.Stream {
|
|
|
|
var rc *http.Request
|
|
var ok bool
|
|
|
|
h.Lock()
|
|
select {
|
|
case rc, ok = <-h.req:
|
|
default:
|
|
}
|
|
|
|
if !ok {
|
|
if len(h.reqList) == 0 {
|
|
h.Unlock()
|
|
return io.EOF
|
|
}
|
|
|
|
rc = h.reqList[0]
|
|
h.reqList = h.reqList[1:]
|
|
}
|
|
h.Unlock()
|
|
|
|
req = rc
|
|
}
|
|
|
|
// set timeout if its greater than 0
|
|
if h.ht.opts.Timeout > time.Duration(0) {
|
|
if err = h.conn.SetDeadline(time.Now().Add(h.ht.opts.Timeout)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
h.Lock()
|
|
defer h.Unlock()
|
|
|
|
if h.closed {
|
|
return io.EOF
|
|
}
|
|
|
|
rsp, err := http.ReadResponse(h.buff, req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func() {
|
|
if err2 := rsp.Body.Close(); err2 != nil {
|
|
err = errors.Wrap(err2, "failed to close body")
|
|
}
|
|
}()
|
|
|
|
b, err := io.ReadAll(rsp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if rsp.StatusCode != http.StatusOK {
|
|
return errors.New(rsp.Status + ": " + string(b))
|
|
}
|
|
|
|
msg.Body = b
|
|
|
|
if msg.Header == nil {
|
|
msg.Header = make(map[string]string, len(rsp.Header))
|
|
}
|
|
|
|
for k, v := range rsp.Header {
|
|
if len(v) > 0 {
|
|
msg.Header[k] = v[0]
|
|
} else {
|
|
msg.Header[k] = ""
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *httpTransportClient) Close() error {
|
|
if !h.dialOpts.Stream {
|
|
h.once.Do(
|
|
func() {
|
|
h.Lock()
|
|
h.buff.Reset(nil)
|
|
h.closed = true
|
|
h.Unlock()
|
|
close(h.req)
|
|
},
|
|
)
|
|
|
|
return h.conn.Close()
|
|
}
|
|
|
|
err := h.conn.Close()
|
|
h.once.Do(
|
|
func() {
|
|
h.Lock()
|
|
h.buff.Reset(nil)
|
|
h.closed = true
|
|
h.Unlock()
|
|
close(h.req)
|
|
},
|
|
)
|
|
|
|
return err
|
|
}
|