parent
25f448794d
commit
feeec630d7
@ -0,0 +1,82 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"context" |
||||
"log" |
||||
"time" |
||||
|
||||
"github.com/go-kratos/kratos/contrib/registry/consul/v2" |
||||
"github.com/go-kratos/kratos/examples/helloworld/helloworld" |
||||
"github.com/go-kratos/kratos/v2/middleware/recovery" |
||||
"github.com/go-kratos/kratos/v2/selector/filter" |
||||
"github.com/go-kratos/kratos/v2/selector/p2c" |
||||
"github.com/go-kratos/kratos/v2/selector/wrr" |
||||
"github.com/go-kratos/kratos/v2/transport/grpc" |
||||
"github.com/go-kratos/kratos/v2/transport/http" |
||||
"github.com/hashicorp/consul/api" |
||||
) |
||||
|
||||
func main() { |
||||
consulCli, err := api.NewClient(api.DefaultConfig()) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
r := consul.New(consulCli) |
||||
|
||||
// new grpc client
|
||||
conn, err := grpc.DialInsecure( |
||||
context.Background(), |
||||
grpc.WithEndpoint("discovery:///helloworld"), |
||||
grpc.WithDiscovery(r), |
||||
// 由于gRPC框架的限制只能使用全局balancer+filter的方式来实现selector
|
||||
// 这里使用weighted round robin算法的balancer+静态version=1.0.0的Filter
|
||||
grpc.WithBalancerName(wrr.Name), |
||||
grpc.WithSelectFilter(filter.Version("1.0.0")), |
||||
) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
defer conn.Close() |
||||
gClient := helloworld.NewGreeterClient(conn) |
||||
|
||||
// new http client
|
||||
hConn, err := http.NewClient( |
||||
context.Background(), |
||||
http.WithMiddleware( |
||||
recovery.Recovery(), |
||||
), |
||||
http.WithEndpoint("discovery:///helloworld"), |
||||
http.WithDiscovery(r), |
||||
// 这里使用p2c算法的balancer+静态version=2.0.0的Filter组成一个selector
|
||||
http.WithSelector( |
||||
p2c.New(p2c.WithFilter(filter.Version("2.0.0"))), |
||||
), |
||||
) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
defer hConn.Close() |
||||
hClient := helloworld.NewGreeterHTTPClient(hConn) |
||||
|
||||
for { |
||||
time.Sleep(time.Second) |
||||
callGRPC(gClient) |
||||
callHTTP(hClient) |
||||
} |
||||
} |
||||
|
||||
func callGRPC(client helloworld.GreeterClient) { |
||||
reply, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: "kratos"}) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
log.Printf("[grpc] SayHello %+v\n", reply) |
||||
} |
||||
|
||||
func callHTTP(client helloworld.GreeterHTTPClient) { |
||||
reply, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: "kratos"}) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
log.Printf("[http] SayHello %s\n", reply.Message) |
||||
} |
@ -0,0 +1,79 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"os" |
||||
|
||||
"github.com/go-kratos/kratos/contrib/registry/consul/v2" |
||||
"github.com/go-kratos/kratos/examples/helloworld/helloworld" |
||||
"github.com/go-kratos/kratos/v2" |
||||
"github.com/go-kratos/kratos/v2/log" |
||||
"github.com/go-kratos/kratos/v2/middleware/logging" |
||||
"github.com/go-kratos/kratos/v2/middleware/recovery" |
||||
"github.com/go-kratos/kratos/v2/transport/grpc" |
||||
"github.com/go-kratos/kratos/v2/transport/http" |
||||
"github.com/hashicorp/consul/api" |
||||
) |
||||
|
||||
// server is used to implement helloworld.GreeterServer.
|
||||
type server struct { |
||||
helloworld.UnimplementedGreeterServer |
||||
} |
||||
|
||||
// SayHello implements helloworld.GreeterServer
|
||||
func (s *server) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) { |
||||
return &helloworld.HelloReply{Message: fmt.Sprintf("Welcome %+v!", in.Name)}, nil |
||||
} |
||||
|
||||
func main() { |
||||
logger := log.NewStdLogger(os.Stdout) |
||||
|
||||
consulClient, err := api.NewClient(api.DefaultConfig()) |
||||
if err != nil { |
||||
log.NewHelper(logger).Fatal(err) |
||||
} |
||||
go runServer("1.0.0", logger, consulClient, 8000) |
||||
go runServer("1.0.0", logger, consulClient, 8010) |
||||
|
||||
runServer("2.0.0", logger, consulClient, 8020) |
||||
} |
||||
|
||||
func runServer(version string, logger log.Logger, client *api.Client, port int) { |
||||
logger = log.With(logger, "version", version, "port:", port) |
||||
log := log.NewHelper(logger) |
||||
|
||||
httpSrv := http.NewServer( |
||||
http.Address(fmt.Sprintf(":%d", port)), |
||||
http.Middleware( |
||||
recovery.Recovery(), |
||||
logging.Server(logger), |
||||
), |
||||
) |
||||
grpcSrv := grpc.NewServer( |
||||
grpc.Address(fmt.Sprintf(":%d", port+1000)), |
||||
grpc.Middleware( |
||||
recovery.Recovery(), |
||||
logging.Server(logger), |
||||
), |
||||
) |
||||
|
||||
s := &server{} |
||||
helloworld.RegisterGreeterServer(grpcSrv, s) |
||||
helloworld.RegisterGreeterHTTPServer(httpSrv, s) |
||||
|
||||
r := consul.New(client) |
||||
app := kratos.New( |
||||
kratos.Name("helloworld"), |
||||
kratos.Server( |
||||
grpcSrv, |
||||
httpSrv, |
||||
), |
||||
kratos.Version(version), |
||||
kratos.Registrar(r), |
||||
) |
||||
|
||||
if err := app.Run(); err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
} |
@ -0,0 +1,82 @@ |
||||
package wrr |
||||
|
||||
import ( |
||||
"context" |
||||
"sync" |
||||
|
||||
"github.com/go-kratos/kratos/v2/selector" |
||||
"github.com/go-kratos/kratos/v2/selector/node/direct" |
||||
) |
||||
|
||||
const ( |
||||
// Name is wrr balancer name
|
||||
Name = "wrr" |
||||
) |
||||
|
||||
var _ selector.Balancer = &Balancer{} // Name is balancer name
|
||||
|
||||
// WithFilter with select filters
|
||||
func WithFilter(filters ...selector.Filter) Option { |
||||
return func(o *options) { |
||||
o.filters = filters |
||||
} |
||||
} |
||||
|
||||
// Option is random builder option.
|
||||
type Option func(o *options) |
||||
|
||||
// options is random builder options
|
||||
type options struct { |
||||
filters []selector.Filter |
||||
} |
||||
|
||||
// Balancer is a random balancer.
|
||||
type Balancer struct { |
||||
mu sync.Mutex |
||||
currentWeight map[string]float64 |
||||
} |
||||
|
||||
// New random a selector.
|
||||
func New(opts ...Option) selector.Selector { |
||||
var option options |
||||
for _, opt := range opts { |
||||
opt(&option) |
||||
} |
||||
|
||||
return &selector.Default{ |
||||
Balancer: &Balancer{ |
||||
currentWeight: make(map[string]float64), |
||||
}, |
||||
NodeBuilder: &direct.Builder{}, |
||||
Filters: option.filters, |
||||
} |
||||
} |
||||
|
||||
// Pick pick a weighted node.
|
||||
func (p *Balancer) Pick(_ context.Context, nodes []selector.WeightedNode) (selector.WeightedNode, selector.DoneFunc, error) { |
||||
if len(nodes) == 0 { |
||||
return nil, nil, selector.ErrNoAvailable |
||||
} |
||||
var totalWeight float64 |
||||
var selected selector.WeightedNode |
||||
var selectWeight float64 |
||||
|
||||
// nginx wrr load balancing algorithm: http://blog.csdn.net/zhangskd/article/details/50194069
|
||||
p.mu.Lock() |
||||
for _, node := range nodes { |
||||
totalWeight += node.Weight() |
||||
cwt := p.currentWeight[node.Address()] |
||||
// current += effectiveWeight
|
||||
cwt += node.Weight() |
||||
p.currentWeight[node.Address()] = cwt |
||||
if selected == nil || selectWeight < cwt { |
||||
selectWeight = cwt |
||||
selected = node |
||||
} |
||||
} |
||||
p.currentWeight[selected.Address()] = selectWeight - totalWeight |
||||
p.mu.Unlock() |
||||
|
||||
d := selected.Pick() |
||||
return selected, d, nil |
||||
} |
Loading…
Reference in new issue