- sogoctl: supervisor avec health checks et restart auto - sogoway: gateway HTTP, auth JWT, routing par hostname - sogoms-db: microservice MariaDB avec pool par application - Protocol IPC Unix socket JSON length-prefixed - Config YAML multi-application (prokov) - Deploy script pour container Alpine gw3 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
170 lines
3.3 KiB
Go
170 lines
3.3 KiB
Go
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()
|
|
}
|
|
}
|