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