docs: update readme

This commit is contained in:
zhuyasen 2025-07-27 15:53:20 +08:00
parent af791196cf
commit 236b72bebb
13 changed files with 196 additions and 59 deletions

View File

@ -18,7 +18,7 @@ Common gin middleware libraries, including:
### Logging middleware
You can set the maximum length for printing, add a request id field, ignore print path, customize [zap](go.uber.org/zap) log.
You can set the maximum length for printing, add a request id field, ignore print path, customize [zap](https://github.com/uber-go/zap) log.
```go
import (
@ -126,6 +126,12 @@ func NewRouter() *gin.Engine {
r.Use(middleware.CircuitBreaker(
//middleware.WithValidCode(http.StatusRequestTimeout), // add error code 408 for circuit breaker
//middleware.WithDegradeHandler(handler), // add custom degrade handler
//middleware.WithBreakerOption(
//circuitbreaker.WithSuccess(75), // default 60
//circuitbreaker.WithRequest(200), // default 100
//circuitbreaker.WithBucket(20), // default 10
//circuitbreaker.WithWindow(time.Second*5), // default 3s
//),
))
// ......

View File

@ -43,7 +43,7 @@ func (o *circuitBreakerOptions) apply(opts ...CircuitBreakerOption) {
}
// WithGroup with circuit breaker group.
// NOTE: implements generics circuitbreaker.CircuitBreaker
// Deprecated: use WithBreakerOption instead
func WithGroup(g *group.Group) CircuitBreakerOption {
return func(o *circuitBreakerOptions) {
if g != nil {
@ -52,6 +52,17 @@ func WithGroup(g *group.Group) CircuitBreakerOption {
}
}
// WithBreakerOption set the circuit breaker options.
func WithBreakerOption(opts ...circuitbreaker.Option) CircuitBreakerOption {
return func(o *circuitBreakerOptions) {
if len(opts) > 0 {
o.group = group.NewGroup(func() interface{} {
return circuitbreaker.NewBreaker(opts...)
})
}
}
}
// WithValidCode http code to mark failed
func WithValidCode(code ...int) CircuitBreakerOption {
return func(o *circuitBreakerOptions) {

View File

@ -26,9 +26,16 @@ func runCircuitBreakerHTTPServer() string {
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(CircuitBreaker(WithGroup(group.NewGroup(func() interface{} {
return circuitbreaker.NewBreaker()
})),
r.Use(CircuitBreaker(
WithGroup(group.NewGroup(func() interface{} {
return circuitbreaker.NewBreaker()
})),
WithBreakerOption(
circuitbreaker.WithSuccess(75), // default 60
circuitbreaker.WithRequest(200), // default 100
circuitbreaker.WithBucket(20), // default 10
circuitbreaker.WithWindow(time.Second*5), // default 3s
),
WithValidCode(http.StatusForbidden),
WithDegradeHandler(degradeHandler),
))

View File

@ -5,15 +5,34 @@
### Example of use
```go
import "github.com/go-dev-frame/sponge/pkg/grpc/client"
package main
conn, err := client.NewClient(context.Background(), "127.0.0.1:8282",
//client.WithServiceDiscover(builder),
//client.WithLoadBalance(),
//client.WithSecure(credentials),
//client.WithUnaryInterceptor(unaryInterceptors...),
//client.WithStreamInterceptor(streamInterceptors...),
)
import (
"context"
"fmt"
"github.com/go-dev-frame/sponge/pkg/grpc/client"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
func main() {
conn, err := client.NewClient("127.0.0.1:8282",
//client.WithServiceDiscover(getDiscovery(), false),
//client.WithLoadBalance(),
//client.WithSecure(credentials),
//client.WithUnaryInterceptor(unaryInterceptors...),
//client.WithStreamInterceptor(streamInterceptors...),
)
if err != nil {
panic(err)
}
greeterClient := pb.NewGreeterClient(conn)
reply, err := greeterClient.SayHello(context.Background(), &pb.HelloRequest{Name: "Alice"})
if err != nil {
panic(err)
}
fmt.Printf("Greeting: %s\n", reply.GetMessage())
conn.Close()
}
```
Examples of practical use https://github.com/zhufuyi/grpc_examples/blob/main/usage/client/main.go

View File

@ -8,13 +8,19 @@ import (
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/resolver"
"github.com/go-dev-frame/sponge/pkg/servicerd/discovery"
"github.com/go-dev-frame/sponge/pkg/servicerd/registry"
)
// Option client option func
type Option func(*options)
type options struct {
builders []resolver.Builder
builders []resolver.Builder
iDiscovery registry.Discovery
isInsecure bool
isLoadBalance bool
credentials credentials.TransportCredentials
unaryInterceptors []grpc.UnaryClientInterceptor
@ -33,9 +39,17 @@ func (o *options) apply(opts ...Option) {
}
// WithServiceDiscover set service discover
func WithServiceDiscover(builders ...resolver.Builder) Option {
func WithServiceDiscover(d registry.Discovery, isInsecure bool) Option {
return func(o *options) {
o.builders = builders
o.iDiscovery = d
o.isInsecure = isInsecure
}
}
// WithServiceDiscoverBuilder set service discover builder
func WithServiceDiscoverBuilder(builder ...resolver.Builder) Option {
return func(o *options) {
o.builders = builder
}
}
@ -83,7 +97,15 @@ func NewClient(endpoint string, opts ...Option) (*grpc.ClientConn, error) {
// service discovery
if len(o.builders) > 0 {
dialOptions = append(dialOptions, grpc.WithResolvers(o.builders...))
dialOptions = append(dialOptions, grpc.WithResolvers(o.builders...)) // higher priority
} else {
if o.iDiscovery != nil {
dialOptions = append(dialOptions, grpc.WithResolvers(
discovery.NewBuilder(
o.iDiscovery,
discovery.WithInsecure(o.isInsecure),
)))
}
}
// load balance option

View File

@ -8,8 +8,20 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/resolver"
"github.com/go-dev-frame/sponge/pkg/servicerd/registry"
)
func getDiscovery() registry.Discovery {
//endpoint = "discovery:///" + grpcClientCfg.Name // format: discovery:///serverName
//cli, err := consulcli.Init(cfg.Consul.Addr, consulcli.WithWaitTime(time.Second*5))
//if err != nil {
// panic(fmt.Sprintf("consulcli.Init error: %v, addr: %s", err, cfg.Consul.Addr))
//}
//return consul.New(cli)
return nil
}
type builder struct{}
func (b *builder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
@ -34,7 +46,8 @@ var streamInterceptors = []grpc.StreamClientInterceptor{
func TestNewClient(t *testing.T) {
conn, err := NewClient("127.0.0.1:50082",
WithServiceDiscover(new(builder)),
WithServiceDiscover(getDiscovery(), false),
WithServiceDiscoverBuilder(new(builder)),
WithLoadBalance(),
WithSecure(insecure.NewCredentials()),
WithUnaryInterceptor(unaryInterceptors...),

View File

@ -211,6 +211,12 @@ func setDialOptions() []grpc.DialOption {
interceptor.UnaryServerCircuitBreaker(
//interceptor.WithValidCode(codes.DeadlineExceeded), // add error code for circuit breaker
//interceptor.WithUnaryServerDegradeHandler(handler), // add custom degrade handler
//interceptor.WithBreakerOption(
//circuitbreaker.WithSuccess(75), // default 60
//circuitbreaker.WithRequest(200), // default 100
//circuitbreaker.WithBucket(20), // default 10
//circuitbreaker.WithWindow(time.Second*5), // default 3s
//),
),
)
options = append(options, option)

View File

@ -47,7 +47,7 @@ func (o *circuitBreakerOptions) apply(opts ...CircuitBreakerOption) {
}
// WithGroup with circuit breaker group.
// NOTE: implements generics circuitbreaker.CircuitBreaker
// Deprecated: use WithBreakerOption instead
func WithGroup(g *group.Group) CircuitBreakerOption {
return func(o *circuitBreakerOptions) {
if g != nil {
@ -56,6 +56,17 @@ func WithGroup(g *group.Group) CircuitBreakerOption {
}
}
// WithBreakerOption set the circuit breaker options.
func WithBreakerOption(opts ...circuitbreaker.Option) CircuitBreakerOption {
return func(o *circuitBreakerOptions) {
if len(opts) > 0 {
o.group = group.NewGroup(func() interface{} {
return circuitbreaker.NewBreaker(opts...)
})
}
}
}
// WithValidCode rpc code to mark failed
func WithValidCode(code ...codes.Code) CircuitBreakerOption {
return func(o *circuitBreakerOptions) {

View File

@ -3,6 +3,7 @@ package interceptor
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
@ -18,6 +19,12 @@ func TestUnaryClientCircuitBreaker(t *testing.T) {
WithGroup(group.NewGroup(func() interface{} {
return circuitbreaker.NewBreaker()
})),
WithBreakerOption(
circuitbreaker.WithSuccess(75), // default 60
circuitbreaker.WithRequest(200), // default 100
circuitbreaker.WithBucket(20), // default 10
circuitbreaker.WithWindow(time.Second*5), // default 3s
),
WithValidCode(codes.PermissionDenied),
)

View File

@ -5,22 +5,45 @@
### Example of use
```go
import "github.com/go-dev-frame/sponge/pkg/grpc/server"
package main
port := 8282
registerFn := func(s *grpc.Server) {
pb.RegisterGreeterServer(s, &greeterServer{})
}
server.Run(port, registerFn,
//server.WithSecure(credentials),
//server.WithUnaryInterceptor(unaryInterceptors...),
//server.WithStreamInterceptor(streamInterceptors...),
//server.WithServiceRegister(func() {}),
//server.WithStatConnections(metrics.WithConnectionsLogger(logger.Get()), metrics.WithConnectionsGauge()), // show connections or set prometheus metrics
)
import (
"context"
"fmt"
"github.com/go-dev-frame/sponge/pkg/grpc/server"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
select{}
type greeterServer struct {
pb.UnimplementedGreeterServer
}
func (s *greeterServer) SayHello(_ context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
fmt.Printf("Received: %v\n", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
port := 8282
registerFn := func(s *grpc.Server) {
pb.RegisterGreeterServer(s, &greeterServer{})
// Register other services here
}
fmt.Printf("Starting server on port %d\n", port)
srv, err := server.Run(port, registerFn,
//server.WithSecure(credentials),
//server.WithUnaryInterceptor(unaryInterceptors...),
//server.WithStreamInterceptor(streamInterceptors...),
//server.WithServiceRegister(srFn), // register service address to Consul/Etcd/Zookeeper...
//server.WithStatConnections(metrics.WithConnectionsLogger(logger.Get()), metrics.WithConnectionsGauge()),
)
if err != nil {
panic(err)
}
defer srv.Stop()
select {}
}
```
Examples of practical use https://github.com/zhufuyi/grpc_examples/blob/main/usage/server/main.go

View File

@ -14,8 +14,8 @@ import (
// RegisterFn register object
type RegisterFn func(srv *grpc.Server)
// ServiceRegisterFn service register
type ServiceRegisterFn func()
// ServiceRegisterFn used to register service address to Consul/ETCD/Nacos/Zookeeper...
type ServiceRegisterFn func() error
// Option set server option
type Option func(*options)
@ -95,15 +95,15 @@ func customInterceptorOptions(o *options) []grpc.ServerOption {
return opts
}
// Run grpc server
func Run(port int, registerFn RegisterFn, options ...Option) {
// Run grpc server with options, registerFn is the function to register object to the server
func Run(port int, registerFn RegisterFn, options ...Option) (*grpc.Server, error) {
o := defaultServerOptions()
o.apply(options...)
// listening on TCP port
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
panic(err)
return nil, err
}
if o.isShowConnections {
@ -116,9 +116,11 @@ func Run(port int, registerFn RegisterFn, options ...Option) {
// register object to the server
registerFn(srv)
// register service to target
// register service address to Consul/ETCD/Nacos/Zookeeper...
if o.serviceRegisterFn != nil {
o.serviceRegisterFn()
if err = o.serviceRegisterFn(); err != nil {
return nil, err
}
}
go func() {
@ -128,4 +130,6 @@ func Run(port int, registerFn RegisterFn, options ...Option) {
panic(err)
}
}()
return srv, nil
}

View File

@ -18,6 +18,15 @@ var fn = func(s *grpc.Server) {
// pb.RegisterGreeterServer(s, &greeterServer{})
}
var srFn = func() error {
//iRegistry, instance, err := consul.NewRegistry(cfg.Consul.Addr, id, cfg.App.Name, []string{instanceEndpoint})
//if err != nil {
// return err
//}
// return iRegistry.Register(ctx, instance)
return nil
}
var unaryInterceptors = []grpc.UnaryServerInterceptor{
func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
return nil, nil
@ -32,13 +41,14 @@ var streamInterceptors = []grpc.StreamServerInterceptor{
func TestRun(t *testing.T) {
port, _ := utils.GetAvailablePort()
Run(port, fn,
srv, _ := Run(port, fn,
WithSecure(insecure.NewCredentials()),
WithUnaryInterceptor(unaryInterceptors...),
WithStreamInterceptor(streamInterceptors...),
WithServiceRegister(func() {}),
WithServiceRegister(srFn),
WithStatConnections(metrics.WithConnectionsLogger(logger.Get()), metrics.WithConnectionsGauge()),
)
defer srv.Stop()
t.Log("grpc server started", port)
time.Sleep(time.Second * 2)

View File

@ -8,9 +8,7 @@ Support `mysql`, `postgresql`, `sqlite`.
## Examples of use
### mysql
#### Initializing the connection
### Mysql
```go
import "github.com/go-dev-frame/sponge/pkg/sgorm/mysql"
@ -18,7 +16,7 @@ Support `mysql`, `postgresql`, `sqlite`.
var dsn = "root:123456@(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
// case 1: connect to the database using the default settings
gdb, err := mysql.Init(dsn)
db, err := mysql.Init(dsn)
// case 2: customised settings to connect to the database
db, err := mysql.Init(
@ -81,18 +79,18 @@ Tidb is mysql compatible, just use **mysql.Init**.
import "github.com/go-dev-frame/sponge/pkg/sgorm/sqlite"
func InitSqlite() {
opts := []sgorm.Option{
sgorm.WithMaxIdleConns(10),
sgorm.WithMaxOpenConns(100),
sgorm.WithConnMaxLifetime(time.Duration(10) * time.Minute),
sgorm.WithLogging(logger.Get()),
sgorm.WithLogRequestIDKey("request_id"),
opts := []sqlite.Option{
sqlite.WithMaxIdleConns(10),
sqlite.WithMaxOpenConns(100),
sqlite.WithConnMaxLifetime(time.Duration(10) * time.Minute),
sqlite.WithLogging(logger.Get()),
sqlite.WithLogRequestIDKey("request_id"),
}
dbFile: = "test.db"
db, err := sgorm.Init(dbFile, opts...)
db, err := sqlite.Init(dbFile, opts...)
if err != nil {
panic("sgorm.Init error: " + err.Error())
panic("sqlite.Init error: " + err.Error())
}
}
```
@ -159,6 +157,6 @@ func (table *User) TableName() string {
<br>
### gorm User Guide
### Gorm Guide
- https://gorm.io/zh_CN/docs/index.html