Merge pull request #558 from micro/network

Networking code
This commit is contained in:
Asim Aslam 2019-07-01 12:12:23 +01:00 committed by GitHub
commit 0971deb9cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 895 additions and 23 deletions

View File

@ -1,26 +1,47 @@
package network
import (
"crypto/sha256"
"fmt"
"io"
"runtime/debug"
"sync"
"time"
gproto "github.com/golang/protobuf/proto"
"github.com/google/uuid"
"github.com/micro/go-micro/codec"
"github.com/micro/go-micro/codec/proto"
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/network/proxy"
"github.com/micro/go-micro/network/resolver"
"github.com/micro/go-micro/network/router"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/util/log"
pb "github.com/micro/go-micro/network/proto"
nreg "github.com/micro/go-micro/network/resolver/registry"
)
type network struct {
options.Options
// router
r router.Router
// resolver use to connect to the network
resolver resolver.Resolver
// proxy
p proxy.Proxy
// router used to find routes in the network
router router.Router
// proxy used to route through the network
proxy proxy.Proxy
// id of this network
id string
// links maintained for this network
// based on peers not nodes. maybe maintain
// node separately or note that links have nodes
mtx sync.RWMutex
links []Link
}
@ -28,31 +49,233 @@ type network struct {
type node struct {
*network
// closed channel
closed chan bool
mtx sync.RWMutex
// the node id
id string
// address of this node
address string
// the node registry
registry registry.Registry
// the base level transport
transport transport.Transport
// the listener
listener transport.Listener
// leases for connections to us
// link id:link
links map[string]*link
}
type link struct {
// the embedded node
*node
// the link id
id string
// queue buffer for this link
queue chan *Message
// the socket for this link
socket *socket
// the lease for this link
lease *pb.Lease
// length and weight of the link
mtx sync.RWMutex
// determines the cost of the link
// based on queue length and roundtrip
length int
weight int
}
type socket struct {
node *node
codec codec.Marshaler
socket transport.Socket
}
// network methods
// lease generates a new lease with a node id/address
// TODO: use a consensus mechanism, pool or some deterministic
// unique prefixing method.
func (n *network) lease() *pb.Lease {
// create the id
id := uuid.New().String()
// create a timestamp
now := time.Now().UnixNano()
// create the address
h := sha256.New()
h.Write([]byte(fmt.Sprintf("%s-%d\n", id, now)))
address := fmt.Sprintf("%x", h.Sum(nil))
// return the node
return &pb.Lease{
Id: id,
Timestamp: now,
Node: &pb.Node{
Id: id,
Address: address,
},
}
}
// lookup returns a list of network records in priority order of local
func (n *network) lookup(r registry.Registry) []*resolver.Record {
// create a registry resolver to find local nodes
rr := nreg.Resolver{Registry: r}
// get all the nodes for the network that are local
localRecords, err := rr.Resolve("network:" + n.Id())
if err != nil {
// we're not in a good place here
}
// if its a local network we never try lookup anything else
if n.Id() == "local" {
return localRecords
}
// now resolve incrementally based on resolvers specified
networkRecords, err := n.resolver.Resolve(n.Id())
if err != nil {
// still not in a good place
}
// return aggregate records
return append(localRecords, networkRecords...)
}
func (n *network) Id() string {
return n.id
}
// Connect connects to the network and returns a new node.
// The node is the callers connection to the network. They
// should advertise this address to people. Anyone else
// on the network should be able to route to it.
func (n *network) Connect() (Node, error) {
return nil, nil
// create a new node
node := new(node)
// closed channel
node.closed = make(chan bool)
// set the nodes network
node.network = n
// initially we have no id
// create an id and address
// TODO: create a real unique id and address
// lease := n.lease()
// set the node id
// node.id = lease.Node.Id
// get the transport we're going to use for our tunnels
t, ok := n.Options.Values().Get("network.transport")
if ok {
node.transport = t.(transport.Transport)
} else {
// TODO: set to quic
node.transport = transport.DefaultTransport
}
// start the node
// we listen on a random address, this is not advertised
// TODO: use util/addr to get something anyone in the same private network can talk to
l, err := node.transport.Listen(":0")
if err != nil {
return nil, err
}
// set the listener
node.listener = l
// TODO: this should be an overlay address
// ideally received via some dhcp style broadcast
node.address = l.Addr()
// TODO: start the router and broadcast advertisements
// receive updates and push them to the network in accept(l) below
// chan, err := n.router.Advertise()
// u <- chan
// socket.send("route", u)
// u := socket.recv() => r.router.Update(u)
// process any incoming messages on the listener
// this is our inbound network connection
node.accept(l)
// register the node with the registry for the network
// TODO: use a registrar or something else for local things
r, ok := n.Options.Values().Get("network.registry")
if ok {
node.registry = r.(registry.Registry)
} else {
node.registry = registry.DefaultRegistry
}
// lookup the network to see if there's any nodes
records := n.lookup(node.registry)
// should we actually do this?
if len(records) == 0 {
// set your own node id
lease := n.lease()
node.id = lease.Node.Id
}
// register self with the network registry
// this is a local registry of nodes separate to the resolver
// maybe consolidate registry/resolver
// TODO: find a way to do this via gossip or something else
if err := node.registry.Register(&registry.Service{
// register with the network id
Name: "network:" + n.Id(),
Nodes: []*registry.Node{
{Id: node.id, Address: node.address},
},
}); err != nil {
node.Close()
return nil, err
}
// create a channel to get links
linkChan := make(chan *link, 1)
// we're going to wait for the first connection
go node.connect(linkChan)
// wait forever to connect
// TODO: do something with the links we receive
<-linkChan
return node, nil
}
// TODO: establish links for peering networks
func (n *network) Peer(Network) (Link, error) {
// New network was created using NewNetwork after receiving routes from a different node
// Connect to the new network and be assigned a node
// Transfer data between the networks
// take other resolver
// order: registry (local), ...resolver
// resolve the network
// periodically connect to nodes resolved in the network
// and add to the network links
return nil, nil
}
@ -64,24 +287,235 @@ func (n *network) Links() ([]Link, error) {
// node methods
// Accept processes the incoming messages on its listener.
// This listener was created with the first call to network.Connect.
// Any inbound new socket here is essentially something else attempting
// to connect to the network. So we turn it into a socket, then process it.
func (n *node) accept(l transport.Listener) error {
return l.Accept(func(sock transport.Socket) {
defer func() {
// close socket
sock.Close()
if r := recover(); r != nil {
log.Log("panic recovered: ", r)
log.Log(string(debug.Stack()))
}
}()
// create a new link
// generate a new link
link := &link{
node: n,
id: uuid.New().String(),
}
// create a new network socket
sk := new(socket)
sk.node = n
sk.codec = proto.Marshaler{}
sk.socket = sock
// set link socket
link.socket = sk
// accept messages on the socket
// blocks forever or until error
if err := link.up(); err != nil {
// TODO: delete link
}
})
}
// connect attempts to periodically connect to new nodes in the network.
// It will only do this if it has less than 3 connections. this method
// is called by network.Connect and fired in a go routine after establishing
// the first connection and creating a node. The node attempts to maintain
// its connection to the network via multiple links.
func (n *node) connect(linkChan chan *link) {
// TODO: adjustable ticker
t := time.NewTicker(time.Second)
var lease *pb.Lease
for {
select {
// on every tick check the number of links and then attempt
// to connect to new nodes if we don't have sufficient links
case <-t.C:
n.mtx.RLock()
// only start processing if we have less than 3 links
if len(n.links) > 2 {
n.mtx.RUnlock()
continue
}
// get a list of link addresses so we don't reconnect
// to the ones we're already connected to
nodes := map[string]bool{}
for _, l := range n.links {
nodes[l.lease.Node.Address] = true
}
n.mtx.RUnlock()
records := n.network.lookup(n.registry)
// for each record check we haven't already got a connection
// attempt to dial it, create a new socket and call
// connect with our existing network lease.
// if its the first call we don't actually have a lease
// TODO: determine how to prioritise local records
// while still connecting to the global network
for _, record := range records {
// skip existing connections
if nodes[record.Address] {
continue
}
// attempt to connect and create a link
// connect to the node
s, err := n.transport.Dial(record.Address)
if err != nil {
continue
}
// create a new socket
sk := &socket{
node: n,
codec: &proto.Marshaler{},
socket: s,
}
// broadcast a "connect" request and get back "lease"
// this is your tunnel to the outside world and to the network
// then push updates and messages over this link
// first connect will not have a lease so we get one with node id/address
l, err := sk.connect(lease)
if err != nil {
s.Close()
continue
}
// set lease for next time
lease = l
// create a new link with the lease and socket
link := &link{
id: uuid.New().String(),
lease: lease,
node: n,
queue: make(chan *Message, 128),
socket: sk,
}
// bring up the link
go link.up()
// save the new link
n.mtx.Lock()
n.links[link.id] = link
n.mtx.Unlock()
// drop this down the link channel to the network
// so it can manage the links
select {
case linkChan <- link:
// we don't wait for anyone
default:
}
}
case <-n.closed:
return
}
}
}
func (n *node) Address() string {
return n.address
}
// Close shutdowns all the links and closes the listener
func (n *node) Close() error {
select {
case <-n.closed:
return nil
default:
close(n.closed)
// shutdown all the links
n.mtx.Lock()
for id, link := range n.links {
link.down()
delete(n.links, id)
}
n.mtx.Unlock()
// deregister self
n.registry.Deregister(&registry.Service{
Name: "network:" + n.network.Id(),
Nodes: []*registry.Node{
{Id: n.id, Address: n.address},
},
})
return n.listener.Close()
}
return nil
}
func (n *node) Accept() (*Message, error) {
// process the inbound cruft
return nil, nil
}
func (n *node) Send(*Message) error {
return nil
func (n *node) Links() ([]Link, error) {
n.mtx.RLock()
defer n.mtx.RUnlock()
var links []Link
for _, l := range n.links {
links = append(links, l)
}
return links, nil
}
func (n *node) Network() Network {
return n.network
}
func (n *node) Send(m *Message) error {
n.mtx.RLock()
defer n.mtx.RUnlock()
var gerr error
// send to all links
// TODO: be smarter
for _, link := range n.links {
// TODO: process the error, do some link flap detection
// blackhold the connection, etc
if err := link.socket.send(m, nil); err != nil {
gerr = err
continue
}
}
return gerr
}
// link methods
// bring up the link
func (l *link) up() error {
// TODO: manage the length/weight of the link
return l.socket.accept()
}
// kill the link
func (l *link) down() error {
return l.socket.close()
}
func (l *link) Length() int {
l.mtx.RLock()
defer l.mtx.RUnlock()
@ -93,3 +527,161 @@ func (l *link) Weight() int {
defer l.mtx.RUnlock()
return l.weight
}
// accept is the state machine that processes messages on the socket
func (s *socket) accept() error {
for {
m := new(transport.Message)
err := s.socket.Recv(m)
if err == io.EOF {
return nil
}
if err != nil {
return err
}
// TODO: pick a reliable header
event := m.Header["Micro-Method"]
switch event {
// connect event
case "connect":
// process connect events from network.Connect()
// these are new connections to join the network
// decode the connection event
conn := new(pb.Connect)
if err := s.codec.Unmarshal(m.Body, conn); err != nil {
// skip error
continue
}
// get the existing lease if it exists
lease := conn.Lease
if lease == nil {
// create a new lease/node
lease = s.node.network.lease()
}
// send back a lease offer for the node
if err := s.send(&Message{
Header: map[string]string{
"Micro-Method": "lease",
},
}, lease); err != nil {
return err
}
// record this mapping of socket to node/lease
s.node.mtx.Lock()
id := uuid.New().String()
s.node.links[id] = &link{
node: s.node,
id: id,
lease: lease,
queue: make(chan *Message, 128),
socket: s,
}
s.node.mtx.Unlock()
// a route update
case "route":
// process router events
// received a lease
case "lease":
// no op as we don't process lease events on existing connections
// these are in response to a connect message
default:
// process all other messages
}
}
}
func (s *socket) close() error {
return s.socket.Close()
}
// connect sends a connect request and waits on a lease.
// this is for a new connection. in the event we send
// an existing lease, the same lease should be returned.
// if it differs then we assume our address for this link
// is different...
func (s *socket) connect(l *pb.Lease) (*pb.Lease, error) {
// send a lease request
if err := s.send(&Message{
Header: map[string]string{
"Micro-Method": "connect",
},
}, &pb.Connect{Lease: l}); err != nil {
return nil, err
}
// create the new things
tm := new(Message)
lease := new(pb.Lease)
// wait for a lease response
if err := s.recv(tm, lease); err != nil {
return nil, err
}
return lease, nil
}
func (s *socket) send(m *Message, v interface{}) error {
tm := new(transport.Message)
tm.Header = m.Header
tm.Body = m.Body
// set the body if not nil
// we're assuming this is network message
if v != nil {
// encode the data
b, err := s.codec.Marshal(v)
if err != nil {
return err
}
// set the content type
tm.Header["Content-Type"] = "application/protobuf"
// set the marshalled body
tm.Body = b
}
// send via the transport socket
return s.socket.Send(&transport.Message{
Header: m.Header,
Body: m.Body,
})
}
func (s *socket) recv(m *Message, v interface{}) error {
if m.Header == nil {
m.Header = make(map[string]string)
}
tm := new(transport.Message)
// receive the transport message
if err := s.socket.Recv(tm); err != nil {
return err
}
// set the message
m.Header = tm.Header
m.Body = tm.Body
// bail early
if v == nil {
return nil
}
// try unmarshal the body
// skip if there's no content-type
if tm.Header["Content-Type"] != "application/protobuf" {
return nil
}
// return unmarshalled
return s.codec.Unmarshal(m.Body, v.(gproto.Message))
}

View File

@ -3,9 +3,14 @@ package network
import (
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/network/proxy"
"github.com/micro/go-micro/network/proxy/mucp"
"github.com/micro/go-micro/network/resolver"
"github.com/micro/go-micro/network/resolver/registry"
"github.com/micro/go-micro/network/router"
)
// Network is an interface defining a network
// Network defines a network interface
type Network interface {
options.Options
// Id of this node
@ -14,16 +19,20 @@ type Network interface {
Connect() (Node, error)
// Peer with a neighboring network
Peer(Network) (Link, error)
// Retrieve list of connections
// Retrieve list of peers
Links() ([]Link, error)
}
// Node represents a single node on a network
type Node interface {
// Node is a network. Network is a node.
Network
// Id of the node
Id() string
// Address of the node
Address() string
// The network of the node
Network() Network
// Links to other nodes
Links() ([]Link, error)
// Close the network connection
Close() error
// Accept messages on the network
@ -36,9 +45,9 @@ type Node interface {
type Link interface {
// remote node the link is to
Node
// length of link which dictates speed
// length defines the speed or distance of the link
Length() int
// weight of link which dictates curvature
// weight defines the saturation or usage of the link
Weight() int
}
@ -47,29 +56,55 @@ type Message struct {
// Headers which provide local/remote info
Header map[string]string
// The opaque data being sent
Data []byte
Body []byte
}
var (
// The default network ID is local
DefaultNetworkId = "local"
DefaultId = "local"
// just the standard network element
DefaultNetwork = NewNetwork()
)
// NewNetwork returns a new network
// NewNetwork returns a new network interface
func NewNetwork(opts ...options.Option) Network {
options := options.NewOptions(opts...)
// new network instance
net := &network{
id: DefaultId,
}
// get network id
id, ok := options.Values().Get("network.id")
if ok {
net.id = id.(string)
}
// get router
r, ok := options.Values().Get("network.router")
if ok {
net.router = r.(router.Router)
} else {
net.router = router.DefaultRouter
}
// get proxy
p, ok := options.Values().Get("network.proxy")
if ok {
net.proxy = p.(proxy.Proxy)
} else {
net.proxy = new(mucp.Proxy)
}
return &network{
Options: options,
// fill the blanks
// router: r,
// proxy: p,
// get resolver
res, ok := options.Values().Get("network.resolver")
if ok {
net.resolver = res.(resolver.Resolver)
} else {
net.resolver = new(registry.Resolver)
}
return net
}

View File

@ -0,0 +1,21 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/network/proto/network.proto
package go_micro_network
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package

197
network/proto/network.pb.go Normal file
View File

@ -0,0 +1,197 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/network/proto/network.proto
package go_micro_network
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// A connect message is for connecting to the network
type Connect struct {
// Lease specifies an existing lease to indicate
// we don't need a new address, we just want to
// establish a link.
Lease *Lease `protobuf:"bytes,1,opt,name=lease,proto3" json:"lease,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Connect) Reset() { *m = Connect{} }
func (m *Connect) String() string { return proto.CompactTextString(m) }
func (*Connect) ProtoMessage() {}
func (*Connect) Descriptor() ([]byte, []int) {
return fileDescriptor_4daa91d05ddc28b6, []int{0}
}
func (m *Connect) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Connect.Unmarshal(m, b)
}
func (m *Connect) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Connect.Marshal(b, m, deterministic)
}
func (m *Connect) XXX_Merge(src proto.Message) {
xxx_messageInfo_Connect.Merge(m, src)
}
func (m *Connect) XXX_Size() int {
return xxx_messageInfo_Connect.Size(m)
}
func (m *Connect) XXX_DiscardUnknown() {
xxx_messageInfo_Connect.DiscardUnknown(m)
}
var xxx_messageInfo_Connect proto.InternalMessageInfo
func (m *Connect) GetLease() *Lease {
if m != nil {
return m.Lease
}
return nil
}
// A lease is returned to anyone attempting to connect.
type Lease struct {
// unique lease id
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// timestamp of lease
Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
// the node
Node *Node `protobuf:"bytes,3,opt,name=node,proto3" json:"node,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Lease) Reset() { *m = Lease{} }
func (m *Lease) String() string { return proto.CompactTextString(m) }
func (*Lease) ProtoMessage() {}
func (*Lease) Descriptor() ([]byte, []int) {
return fileDescriptor_4daa91d05ddc28b6, []int{1}
}
func (m *Lease) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Lease.Unmarshal(m, b)
}
func (m *Lease) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Lease.Marshal(b, m, deterministic)
}
func (m *Lease) XXX_Merge(src proto.Message) {
xxx_messageInfo_Lease.Merge(m, src)
}
func (m *Lease) XXX_Size() int {
return xxx_messageInfo_Lease.Size(m)
}
func (m *Lease) XXX_DiscardUnknown() {
xxx_messageInfo_Lease.DiscardUnknown(m)
}
var xxx_messageInfo_Lease proto.InternalMessageInfo
func (m *Lease) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Lease) GetTimestamp() int64 {
if m != nil {
return m.Timestamp
}
return 0
}
func (m *Lease) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
// A node is the network node
type Node struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Node) Reset() { *m = Node{} }
func (m *Node) String() string { return proto.CompactTextString(m) }
func (*Node) ProtoMessage() {}
func (*Node) Descriptor() ([]byte, []int) {
return fileDescriptor_4daa91d05ddc28b6, []int{2}
}
func (m *Node) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Node.Unmarshal(m, b)
}
func (m *Node) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Node.Marshal(b, m, deterministic)
}
func (m *Node) XXX_Merge(src proto.Message) {
xxx_messageInfo_Node.Merge(m, src)
}
func (m *Node) XXX_Size() int {
return xxx_messageInfo_Node.Size(m)
}
func (m *Node) XXX_DiscardUnknown() {
xxx_messageInfo_Node.DiscardUnknown(m)
}
var xxx_messageInfo_Node proto.InternalMessageInfo
func (m *Node) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Node) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
func init() {
proto.RegisterType((*Connect)(nil), "go.micro.network.Connect")
proto.RegisterType((*Lease)(nil), "go.micro.network.Lease")
proto.RegisterType((*Node)(nil), "go.micro.network.Node")
}
func init() {
proto.RegisterFile("micro/go-micro/network/proto/network.proto", fileDescriptor_4daa91d05ddc28b6)
}
var fileDescriptor_4daa91d05ddc28b6 = []byte{
// 192 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x8f, 0x3d, 0x4b, 0xc0, 0x30,
0x10, 0x86, 0xe9, 0x97, 0xa5, 0x27, 0x88, 0x64, 0xd0, 0x0c, 0x0e, 0xa5, 0x53, 0x29, 0x34, 0x15,
0x5d, 0xdc, 0x5d, 0xc5, 0x21, 0xff, 0x20, 0x36, 0x47, 0x09, 0xda, 0x5c, 0x49, 0x02, 0xfe, 0x7d,
0xe9, 0xd5, 0x0f, 0xb0, 0x5b, 0x9e, 0x37, 0xcf, 0xdd, 0xcb, 0xc1, 0xb0, 0xba, 0x39, 0xd0, 0xb4,
0xd0, 0x78, 0x3c, 0x3c, 0xa6, 0x4f, 0x0a, 0xef, 0xd3, 0x16, 0x28, 0xfd, 0x92, 0x62, 0x12, 0xd7,
0x0b, 0x29, 0xb6, 0xd4, 0x77, 0xde, 0x3d, 0x41, 0xfd, 0x4c, 0xde, 0xe3, 0x9c, 0xc4, 0x08, 0xd5,
0x07, 0x9a, 0x88, 0x32, 0x6b, 0xb3, 0xfe, 0xf2, 0xe1, 0x56, 0xfd, 0x97, 0xd5, 0xcb, 0xfe, 0xad,
0x0f, 0xab, 0x33, 0x50, 0x31, 0x8b, 0x2b, 0xc8, 0x9d, 0xe5, 0xa1, 0x46, 0xe7, 0xce, 0x8a, 0x3b,
0x68, 0x92, 0x5b, 0x31, 0x26, 0xb3, 0x6e, 0x32, 0x6f, 0xb3, 0xbe, 0xd0, 0x7f, 0x81, 0x18, 0xa0,
0xf4, 0x64, 0x51, 0x16, 0x5c, 0x72, 0x73, 0x2e, 0x79, 0x25, 0x8b, 0x9a, 0x9d, 0xee, 0x1e, 0xca,
0x9d, 0x4e, 0x0d, 0x12, 0x6a, 0x63, 0x6d, 0xc0, 0x18, 0x79, 0x7f, 0xa3, 0x7f, 0xf0, 0xed, 0x82,
0xef, 0x7c, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x48, 0x8c, 0x11, 0x91, 0x15, 0x01, 0x00, 0x00,
}

View File

@ -0,0 +1,27 @@
syntax = "proto3";
package go.micro.network;
// A connect message is for connecting to the network
message Connect {
// Lease specifies an existing lease to indicate
// we don't need a new address, we just want to
// establish a link.
Lease lease = 1;
}
// A lease is returned to anyone attempting to connect.
message Lease {
// unique lease id
string id = 1;
// timestamp of lease
int64 timestamp = 2;
// the node
Node node = 3;
}
// A node is the network node
message Node {
string id = 1;
string address = 2;
}