package protocol import ( "context" "fmt" "net" "sync" "time" ) // Client permet d'appeler un microservice via Unix socket. type Client struct { socketPath string conn net.Conn mu sync.Mutex } // NewClient crée un nouveau client. func NewClient(socketPath string) *Client { return &Client{ socketPath: socketPath, } } // Connect établit la connexion au socket. func (c *Client) Connect() error { c.mu.Lock() defer c.mu.Unlock() if c.conn != nil { return nil // Déjà connecté } conn, err := net.Dial("unix", c.socketPath) if err != nil { return fmt.Errorf("dial %s: %w", c.socketPath, err) } c.conn = conn return nil } // Close ferme la connexion. func (c *Client) Close() error { c.mu.Lock() defer c.mu.Unlock() if c.conn != nil { err := c.conn.Close() c.conn = nil return err } return nil } // Call envoie une requête et attend la réponse. func (c *Client) Call(ctx context.Context, req *Request) (*Response, error) { c.mu.Lock() defer c.mu.Unlock() if c.conn == nil { return nil, fmt.Errorf("not connected") } // Timeout timeout := time.Duration(req.TimeoutMs) * time.Millisecond if timeout == 0 { timeout = 5 * time.Second } deadline := time.Now().Add(timeout) c.conn.SetDeadline(deadline) // Encoder et envoyer la requête data, err := Encode(req) if err != nil { return nil, fmt.Errorf("encode request: %w", err) } if err := writeMessage(c.conn, data); err != nil { c.conn = nil // Connexion cassée return nil, fmt.Errorf("write request: %w", err) } // Lire la réponse respData, err := readMessage(c.conn) if err != nil { c.conn = nil // Connexion cassée return nil, fmt.Errorf("read response: %w", err) } resp, err := DecodeResponse(respData) if err != nil { return nil, fmt.Errorf("decode response: %w", err) } return resp, nil } // CallAction raccourci pour appeler une action. func (c *Client) CallAction(ctx context.Context, action string, params map[string]any) (*Response, error) { req := NewRequest(action, params) return c.Call(ctx, req) } // Pool gère un pool de connexions vers un service. type Pool struct { socketPath string clients chan *Client maxSize int } // NewPool crée un pool de connexions. func NewPool(socketPath string, size int) *Pool { return &Pool{ socketPath: socketPath, clients: make(chan *Client, size), maxSize: size, } } // Get obtient un client du pool. func (p *Pool) Get() (*Client, error) { select { case client := <-p.clients: return client, nil default: // Créer un nouveau client client := NewClient(p.socketPath) if err := client.Connect(); err != nil { return nil, err } return client, nil } } // Put remet un client dans le pool. func (p *Pool) Put(client *Client) { select { case p.clients <- client: // OK, remis dans le pool default: // Pool plein, fermer le client client.Close() } } // Call obtient un client, exécute l'appel, et remet le client. func (p *Pool) Call(ctx context.Context, req *Request) (*Response, error) { client, err := p.Get() if err != nil { return nil, err } resp, err := client.Call(ctx, req) if err != nil { client.Close() return nil, err } p.Put(client) return resp, nil } // Close ferme tous les clients du pool. func (p *Pool) Close() { close(p.clients) for client := range p.clients { client.Close() } }