基于envoy ads的动态配置服务发现,直接上代码。
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"os"
"os/signal"
"time"
"github.com/envoyproxy/go-control-plane/envoy/api/v2"
envoyapi "github.com/envoyproxy/go-control-plane/envoy/api/v2"
envoycore "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
envoyendpoint "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"
envoylistener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"
envoyroute "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
envoyhcm "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2"
discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2"
envoycache "github.com/envoyproxy/go-control-plane/pkg/cache"
envoyserver "github.com/envoyproxy/go-control-plane/pkg/server"
envoyutil "github.com/envoyproxy/go-control-plane/pkg/util"
grpc "google.golang.org/grpc"
)
type hash struct{}
func (hash) ID(node *envoycore.Node) string {
if node == nil {
return "unknown"
}
return node.Cluster + "/" + node.Id
}
func createListener() *envoyapi.Listener {
manager := &envoyhcm.HttpConnectionManager{
StatPrefix: "http",
RouteSpecifier: &envoyhcm.HttpConnectionManager_Rds{
Rds: &envoyhcm.Rds{
RouteConfigName: "hello_route",
ConfigSource: envoycore.ConfigSource{
ConfigSourceSpecifier: &envoycore.ConfigSource_ApiConfigSource{
ApiConfigSource: &envoycore.ApiConfigSource{
ApiType: envoycore.ApiConfigSource_GRPC,
GrpcServices: []*envoycore.GrpcService{{
TargetSpecifier: &envoycore.GrpcService_EnvoyGrpc_{
EnvoyGrpc: &envoycore.GrpcService_EnvoyGrpc{
ClusterName: "xds_cluster",
},
},
}},
},
},
},
},
},
HttpFilters: []*envoyhcm.HttpFilter{{
Name: "envoy.router",
}},
}
filterConfig, err := envoyutil.MessageToStruct(manager)
if err != nil {
panic(err.Error())
}
listener := &envoyapi.Listener{
Name: "listener_0",
Address: envoycore.Address{
Address: &envoycore.Address_SocketAddress{
SocketAddress: &envoycore.SocketAddress{
Address: "0.0.0.0",
PortSpecifier: &envoycore.SocketAddress_PortValue{PortValue: 80},
},
},
},
FilterChains: []envoylistener.FilterChain{{
Filters: []envoylistener.Filter{{
Name: "envoy.http_connection_manager",
ConfigType: &envoylistener.Filter_Config{
Config: filterConfig,
},
}},
}},
}
return listener
}
func createRouteConfig() *envoyapi.RouteConfiguration {
routeconfig := &envoyapi.RouteConfiguration{
Name: "hello_route",
VirtualHosts: []envoyroute.VirtualHost{{
Name: "hello_service",
//Domains: []string{"hello.local"},
Domains: []string{"*"},
Routes: []envoyroute.Route{{
Match: envoyroute.RouteMatch{
PathSpecifier: &envoyroute.RouteMatch_Prefix{
Prefix: "/",
},
},
Action: &envoyroute.Route_Route{
Route: &envoyroute.RouteAction{
ClusterSpecifier: &envoyroute.RouteAction_Cluster{
Cluster: "hello_cluster",
},
},
},
}},
}},
}
return routeconfig
}
func createCluster() *envoyapi.Cluster {
connectionTimeout := time.Duration(60*1000) * time.Millisecond
cluster := &envoyapi.Cluster{
Name: "hello_cluster",
ConnectTimeout: connectionTimeout,
LbPolicy: envoyapi.Cluster_ROUND_ROBIN,
ClusterDiscoveryType: &envoyapi.Cluster_Type{
Type: envoyapi.Cluster_EDS,
},
EdsClusterConfig: &envoyapi.Cluster_EdsClusterConfig{
EdsConfig: &envoycore.ConfigSource{
ConfigSourceSpecifier: &envoycore.ConfigSource_ApiConfigSource{
ApiConfigSource: &envoycore.ApiConfigSource{
ApiType: envoycore.ApiConfigSource_GRPC,
GrpcServices: []*envoycore.GrpcService{{
TargetSpecifier: &envoycore.GrpcService_EnvoyGrpc_{
EnvoyGrpc: &envoycore.GrpcService_EnvoyGrpc{
ClusterName: "xds_cluster",
},
},
}},
},
},
},
},
}
return cluster
}
func createEndpoint(endp MyEndpoint) *envoyapi.ClusterLoadAssignment {
clusterLoadAssignment := &envoyapi.ClusterLoadAssignment{
ClusterName: endp.name,
Endpoints: []envoyendpoint.LocalityLbEndpoints{{
LbEndpoints: []envoyendpoint.LbEndpoint{{
HostIdentifier: &envoyendpoint.LbEndpoint_Endpoint{
Endpoint: &envoyendpoint.Endpoint{
Address: &envoycore.Address{
Address: &envoycore.Address_SocketAddress{
SocketAddress: &envoycore.SocketAddress{
Address: endp.address,
PortSpecifier: &envoycore.SocketAddress_PortValue{PortValue: endp.port},
},
},
},
},
},
}},
}},
}
return clusterLoadAssignment
}
func createSnapshot(endp MyEndpoint) envoycache.Snapshot {
var endpoints []envoycache.Resource
endpoints = append(endpoints, createEndpoint(endp))
var clusters []envoycache.Resource
clusters = append(clusters, createCluster())
var routes []envoycache.Resource
routes = append(routes, createRouteConfig())
var listeners []envoycache.Resource
listeners = append(listeners, createListener())
return envoycache.NewSnapshot(endp.version, endpoints, clusters, routes, listeners)
}
func run(listen string, endp MyEndpoint) error {
snapshotCache := envoycache.NewSnapshotCache(false, hash{}, nil)
server := envoyserver.NewServer(snapshotCache, Callback{})
err := snapshotCache.SetSnapshot("cluster.local/node0", createSnapshot(endp))
if err != nil {
return err
}
grpcServer := grpc.NewServer()
discovery.RegisterAggregatedDiscoveryServiceServer(grpcServer, server)
envoyapi.RegisterEndpointDiscoveryServiceServer(grpcServer, server)
envoyapi.RegisterClusterDiscoveryServiceServer(grpcServer, server)
envoyapi.RegisterRouteDiscoveryServiceServer(grpcServer, server)
envoyapi.RegisterListenerDiscoveryServiceServer(grpcServer, server)
lsn, err := net.Listen("tcp", listen)
if err != nil {
return err
}
go func() {
log.Printf("start grpc server version:%s", endp.version)
grpcServer.Serve(lsn)
}()
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
<-quit
log.Println("stopping grpc server...")
grpcServer.Stop()
//grpcServer.GracefulStop()
return nil
}
func main() {
var listen string
flag.StringVar(&listen, "listen", ":20000", "listen port")
flag.Parse()
log.Printf("Starting server with -listen=%s", listen)
end0 := MyEndpoint{
version: "0",
name: "hello_cluster",
address: "127.0.0.1",
port: 8080,
}
err := run(listen, end0)
if err != nil {
fmt.Println(os.Stderr, err)
os.Exit(1)
}
end1 := MyEndpoint{
version: "1",
name: "hello_cluster",
address: "127.0.0.1",
port: 8081,
}
err = run(listen, end1)
if err != nil {
fmt.Println(os.Stderr, err)
os.Exit(1)
}
}
type MyEndpoint struct {
version string
name string
address string
port uint32
}
type Callback struct{}
func (cb Callback) OnStreamOpen(context.Context, int64, string) error { return nil }
func (cb Callback) OnStreamClosed(int64) {}
func (cb Callback) OnStreamRequest(int64, *v2.DiscoveryRequest) error {
return nil
}
func (cb Callback) OnStreamResponse(int64, *v2.DiscoveryRequest, *v2.DiscoveryResponse) {}
func (cb Callback) OnFetchRequest(context.Context, *v2.DiscoveryRequest) error { return nil }
func (cb Callback) OnFetchResponse(*v2.DiscoveryRequest, *v2.DiscoveryResponse) {}
go.mod文件
module envoyprac
go 1.12
require (
github.com/envoyproxy/go-control-plane v0.8.1
github.com/gogo/protobuf v1.2.1
google.golang.org/grpc v1.21.1
)
envoy yaml配置文件:
node:
id: node0
cluster: cluster.local
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address:
protocol: TCP
address: 127.0.0.1
port_value: 9901
static_resources:
clusters:
- name: xds_cluster
connect_timeout: 0.25s
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: xds_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: {address: 127.0.0.1, port_value: 20000 }
dynamic_resources:
ads_config:
api_type: GRPC
grpc_services:
- envoy_grpc:
cluster_name: xds_cluster
cds_config:
ads: {}
lds_config:
ads: {}
参考https://github.com/tjtjtj/envoyprac