Compare commits

...

162 Commits

Author SHA1 Message Date
Asim Aslam
6ec32805d0 Don't allow socket close while writing h2 headers 2019-09-10 18:26:12 -07:00
Asim Aslam
3e3bbe3fd0 Merge pull request #751 from micro/link
Move link to tunnel/
2019-09-10 08:16:35 -07:00
Asim Aslam
b5eea02f7a Move link to tunnel/ 2019-09-10 08:12:28 -07:00
Asim Aslam
08c6f60b0f Merge pull request #746 from micro/plugin
Support plugin loading
2019-09-10 05:38:10 -07:00
Asim Aslam
065c7d5616 fix plugin init 2019-09-10 05:32:49 -07:00
Asim Aslam
a5ce3e32da Support plugin loading on service.Init 2019-09-09 20:17:36 -07:00
Asim Aslam
3bfbcd5e6a Add default plugin loader 2019-09-09 19:43:13 -07:00
Asim Aslam
b6c6b13277 Support plugin loading 2019-09-09 19:09:28 -07:00
Asim Aslam
04b31d374c Merge pull request #745 from micro/registry-service
Add service registry
2019-09-09 13:05:46 -07:00
Asim Aslam
e828a099c5 Merge pull request #744 from micro/mdns-domain
Use .micro domain for mdns
2019-09-09 11:59:37 -07:00
Asim Aslam
2c16c7e62f Fix build breaks 2019-09-09 09:25:47 -07:00
Asim Aslam
1f44d7a4a1 Add registry handler 2019-09-09 09:20:17 -07:00
Asim Aslam
b076ef906a Add service registry 2019-09-09 08:57:57 -07:00
Asim Aslam
c669a2b155 Use .micro domain for mdns 2019-09-09 05:11:25 -07:00
Asim Aslam
48a3e51aca Merge pull request #742 from micro/unlock
unlock before sending the message to avoid deadlock
2019-09-06 17:06:39 +01:00
Asim Aslam
e8aaca27d3 unlock before sending the message to avoid deadlock 2019-09-06 16:57:17 +01:00
Asim Aslam
5596407144 Merge pull request #741 from milosgajdos83/list-nodes
List nodes now works properly. Send solicit message on ControlChannel
2019-09-06 15:18:25 +01:00
Milos Gajdos
7971b1b7f9 Remove debug logs 2019-09-06 15:12:23 +01:00
Milos Gajdos
dafbacbdcb Properly handle the list of the nodes. Send solicit on ControlChannel 2019-09-06 15:09:49 +01:00
Asim Aslam
df5657dcd1 Merge pull request #737 from milosgajdos83/buffered-advertchan
Lets make advert channel buffered so we don't lose adverts
2019-09-05 19:19:03 +01:00
Milos Gajdos
bb595c85b2 Lets make advert channel buffered so we don't lose adverts 2019-09-05 19:05:47 +01:00
Asim Aslam
bc6187ea89 Merge pull request #734 from micro/tunnel
Update tunnel to send discovery on connect and multicast messages. An…
2019-09-05 18:19:37 +01:00
Asim Aslam
ed1faa7a5c Add a discover ticker, announce on connect and refactor 2019-09-05 18:13:02 +01:00
Asim Aslam
1d9298ae2b Merge pull request #736 from milosgajdos83/solicit-routes
Solicit routes when new node is discovered
2019-09-05 18:08:49 +01:00
Milos Gajdos
dddfb6f878 Fixed typos and simplified map iteration 2019-09-05 17:59:14 +01:00
Milos Gajdos
ec354934e3 Move Errors to separate init block 2019-09-05 17:44:47 +01:00
Milos Gajdos
b01c8e06e0 Update error name to ErrClientNotFound 2019-09-05 17:43:59 +01:00
Asim Aslam
97b1071f7e Merge pull request #735 from huangzhhui/patch-1
Fixed the link of Chinese documentation
2019-09-05 17:43:03 +01:00
Asim Aslam
1527a84297 Shorten multicast discovery 2019-09-05 17:40:41 +01:00
Milos Gajdos
5ddfd911ba Replace send message code by one network method 2019-09-05 17:18:16 +01:00
黄朝晖
2310ee424c Update README.zh-cn.md 2019-09-05 23:52:54 +08:00
Milos Gajdos
2522d8cb96 Send solicit message when new neighbour is discovered 2019-09-05 16:04:44 +01:00
Asim Aslam
d198765c6c Put back close of listener 2019-09-05 15:23:19 +01:00
Asim Aslam
1840b5bd74 Update tunnel to send discovery on connect and multicast messages. Announce as broadcast 2019-09-05 15:16:11 +01:00
Milos Gajdos
9161b20d6b Add Solicit method to router interface
When calling Solicit, router lists all the routes and advertise them
straight away
2019-09-05 13:23:33 +01:00
Asim Aslam
a1ba1482c5 Only set link if not multicast 2019-09-05 07:41:19 +01:00
Asim Aslam
d0761e0a1b Merge pull request #733 from milosgajdos83/freeze-graph
Freeze network graph when building full network topology
2019-09-05 07:21:53 +01:00
Milos Gajdos
4b1a7abb42 Freeze network graph when building full network topology
Also added some comments and debug logs
2019-09-05 00:16:22 +01:00
Asim Aslam
e33bd17894 Merge pull request #732 from micro/massive-cruft
Fix massive cruft in tunnel dial to set the link on discovered
2019-09-04 20:27:07 +01:00
Asim Aslam
cc5d811a83 add comment to tunnel link selection 2019-09-04 20:19:53 +01:00
Asim Aslam
e15389febb Fix massive cruft in tunnel dial to set the link on discovered 2019-09-04 20:18:26 +01:00
Asim Aslam
6d63c3777f Merge pull request #731 from micro/tunnel
Add some fixes
2019-09-04 18:53:48 +01:00
Asim Aslam
d8a1b47954 Remove lock from link 2019-09-04 18:48:43 +01:00
Asim Aslam
b9a2f719a0 Add some fixes 2019-09-04 18:46:20 +01:00
Asim Aslam
46a9767648 Merge pull request #730 from milosgajdos83/advert-lastseen
Update node.lastSeen properly. Set node.lastSeen when processing advert
2019-09-04 18:13:43 +01:00
Milos Gajdos
dd9f42e3b9 Update lastSeen timestamp properly. Set lastSeen when processing advert 2019-09-04 18:02:13 +01:00
Asim Aslam
f2c8492c77 Merge pull request #729 from micro/tunnel
Tunnel session management and unicast/multicast
2019-09-04 16:25:38 +01:00
Asim Aslam
407381912b Don't try discover on multicast, don't block existing sessions on listen 2019-09-04 15:55:37 +01:00
Asim Aslam
d559ce9da2 Provide Links() method in Tunnel 2019-09-04 15:41:57 +01:00
Asim Aslam
7ab3934eb7 add message comment 2019-09-04 12:18:37 +01:00
Asim Aslam
0075477df0 make tunnel broker use multicast 2019-09-04 12:18:31 +01:00
Asim Aslam
d5be2136ad cleanup new message creation 2019-09-04 12:16:31 +01:00
Asim Aslam
c718b8bf93 Move vars and comment 2019-09-04 12:00:11 +01:00
Asim Aslam
a24818ee54 Fix typo 2019-09-04 11:58:25 +01:00
Asim Aslam
66db0ac52c Move announce into session 2019-09-04 11:58:03 +01:00
Asim Aslam
b9c437fbfe Tunnel discover/announce/open/session/close 2019-09-04 09:48:05 +01:00
Asim Aslam
147899283c Merge pull request #728 from wuyumin/master
Update config source README file
2019-09-04 09:16:04 +01:00
Yumin Wu
5b991cd2c2 Update config source README file 2019-09-04 15:49:58 +08:00
Yumin Wu
bb64f94313 .gitignore file for develop tools 2019-09-04 15:47:46 +08:00
Milos Gajdos
4f4b3d3bae Send connect message to NetworkChannel once we are not at caller mercy 2019-09-03 19:51:52 +01:00
Asim Aslam
eb4a709195 Merge branch 'master' of ssh://github.com/micro/go-micro into tunnel 2019-09-03 17:20:39 +01:00
Asim Aslam
6c21b31226 Merge pull request #727 from milosgajdos83/bug-overhaul
Major bug overhaul in how we handle network.Nodes and related handler
2019-09-03 17:20:15 +01:00
Milos Gajdos
6eb6d050ed Major bug overhaul in how we handle network.Nodes and related handler 2019-09-03 16:39:27 +01:00
Asim Aslam
6c7582a6be Move message to session 2019-09-03 15:56:37 +01:00
Milos Gajdos
3ea4490d6c Don't preallocate the slice if you don't index later on. 2019-09-03 15:02:30 +01:00
Asim Aslam
b50c44a758 Merge pull request #726 from milosgajdos83/prune-nodes
Prune nodes that have not announced themselves for certain time period.
2019-09-03 10:28:31 +01:00
Milos Gajdos
ec6318befc Prune nodes that have not announced themselves for certain time period. 2019-09-03 10:00:14 +01:00
Asim Aslam
5440325a18 Merge pull request #724 from milosgajdos83/efficient-bfs-queue
Make Nodes() BFS implementation efficient
2019-09-03 07:43:37 +01:00
Milos Gajdos
fb13877904 Make Nodes() BFS implementation efficient 2019-09-03 02:58:17 +01:00
Asim Aslam
2f5e3c66b9 Merge pull request #723 from milosgajdos83/sort-nodes-search
Sort the returned slice of nodes before searching
2019-09-02 20:13:20 +01:00
Milos Gajdos
a8d4299df9 Sort the returned slice of nodes before searching
See docs:
https://golang.org/pkg/sort/#Search
2019-09-02 20:00:52 +01:00
Asim Aslam
90745c14f2 Merge pull request #722 from milosgajdos83/net-handler
[WIP] Network handler
2019-09-02 17:15:38 +01:00
Milos Gajdos
86665454e7 Implementation of Nodes method. First take on full handler 2019-09-02 17:06:21 +01:00
Milos Gajdos
4f5a849211 Added Nodes method to Network interface 2019-09-02 12:40:05 +01:00
Milos Gajdos
bf53c16e4b Rough outline of Network introspection interface 2019-09-02 12:40:05 +01:00
Asim Aslam
6c3631728b Merge pull request #721 from micro/tunnel
Separate lookup nodes and setup nodes
2019-09-02 12:10:24 +01:00
Asim Aslam
2cdfed359f Separate lookup nodes and setup nodes 2019-09-02 12:05:47 +01:00
Asim Aslam
956be5c59d Merge pull request #717 from micro/client-stream
use with stream for client connection
2019-09-02 07:36:41 +01:00
Asim Aslam
52d9d75dfa use with stream for client connection 2019-08-31 18:26:48 +01:00
Asim Aslam
0d94784e72 Add some tunnel comments 2019-08-31 17:32:20 +01:00
Asim Aslam
65c2de5a79 Merge pull request #716 from micro/tunnel
Rename Tunnel ID to Channel
2019-08-31 16:32:41 +01:00
Asim Aslam
6fa9d7270f Rename Tunnel ID to Channel 2019-08-30 20:05:00 +01:00
Asim Aslam
140c830af1 Merge pull request #715 from milosgajdos83/net-debug
Add proto definitions for network introspection.
2019-08-30 12:38:24 +01:00
Milos Gajdos
b37837ad92 Add proto definitions for network introspection. 2019-08-30 12:29:26 +01:00
Asim Aslam
10b64af0b3 Merge pull request #713 from milosgajdos83/route-loop-break
Avoid setting routes that route back to the node without its being direct GW to dest
2019-08-30 11:33:17 +01:00
Asim Aslam
5d01284574 Merge pull request #714 from wuyumin/master
Load consul source
2019-08-30 10:15:37 +01:00
Yumin Wu
ff81e4b246 Load consul source 2019-08-30 16:20:58 +08:00
Milos Gajdos
e955e3f798 Avoid routes that route back to node without its being direct GW to dest 2019-08-30 00:04:46 +01:00
Asim Aslam
a17a8b3372 Merge branch 'master' of ssh://github.com/micro/go-micro 2019-08-29 17:21:49 +01:00
Asim Aslam
e1d56fbf58 switch warn to error logging 2019-08-29 17:21:43 +01:00
Milos Gajdos
e7d8cdda44 Avoid duplicate debug logs. 2019-08-29 16:58:07 +01:00
Asim Aslam
690640eeeb Merge pull request #712 from milosgajdos83/route-update
Only emit table event if table.Update actually happens
2019-08-29 16:30:27 +01:00
Milos Gajdos
4f788c6fc7 Only emit the events when actually deleting the route 2019-08-29 16:25:21 +01:00
Milos Gajdos
f50bd400f8 Only emit event if Update actually happens 2019-08-29 16:21:30 +01:00
Asim Aslam
b457ec1990 Merge pull request #711 from milosgajdos83/node-neighbours
Don't override the neighbours.
2019-08-29 15:48:13 +01:00
Milos Gajdos
ffa6b551f4 Don't override the neighbours. 2019-08-29 15:42:07 +01:00
Asim Aslam
3d03fe4076 Fix panic for nil slice 2019-08-29 15:09:01 +01:00
Asim Aslam
6eecb199e9 Merge pull request #710 from micro/nodes
add the ability to provide seed nodes to the network
2019-08-29 15:00:51 +01:00
Asim Aslam
7479515099 add the ability to provide seed nodes to the network 2019-08-29 14:53:30 +01:00
Asim Aslam
6e3d53e1ee Merge pull request #709 from micro/tunnel-arp
Tunnel Direction Fix
2019-08-29 13:13:25 +01:00
Asim Aslam
721c5e6857 fix broken build 2019-08-29 13:11:20 +01:00
Asim Aslam
7d033818cf if the service name is blank, barf 2019-08-29 13:10:06 +01:00
Asim Aslam
00ab58f61b Fix loopback cruft 2019-08-29 12:42:27 +01:00
Asim Aslam
b3aef71fdb Merge pull request #708 from milosgajdos83/route-metric
Set the route.Metric before updating routing table
2019-08-29 12:28:43 +01:00
Milos Gajdos
8606f1e143 Set the route.Metric before updating routing table 2019-08-29 11:45:47 +01:00
Asim Aslam
927fac2cec Merge pull request #706 from milosgajdos83/neighbour-map
Broadcast neighbourhood
2019-08-28 23:16:42 +01:00
Asim Aslam
6ab86c9e57 Don't process unless connected, and only fire loopback messages back up the loopback 2019-08-28 23:12:22 +01:00
Milos Gajdos
db8e2620cb Make tunnel channel clients key-able. Neighbour map simplified.
tunClient is a map of tunnel clients keyed on tunnel channel name.
Neighbour map is now a cimple map of nodes which contains its nodes.
2019-08-28 23:11:26 +01:00
Milos Gajdos
d09b7dbbef Broadcast neighbourhood; fix critical bugs in channel connections
This commit introduces neighbourhood announcements which allows to
maintaing neighbour map if each next-hop node.

It also fixes a critical bug when accepting connections for a particular
tunnel channel.
2019-08-28 20:11:19 +01:00
Asim Aslam
a4f5772555 add network field to the routes 2019-08-28 08:41:19 +01:00
Asim Aslam
731f6f74dd Merge pull request #703 from milosgajdos83/net-id
Adds network id. Skips processing routes when router is the origin.
2019-08-28 08:05:19 +01:00
Milos Gajdos
5e7208119e Adds network id. Skips processing routes when router is the origin. 2019-08-27 23:08:35 +01:00
Asim Aslam
470304ef87 Merge pull request #701 from h-hy/master
Check last for the address binded in lo interface (LVS , DR mode)
2019-08-27 18:08:52 +01:00
huanghaoyan
a6ab4d7b4b check last for the address bind in lo interface. 2019-08-27 23:35:27 +08:00
Asim Aslam
87b56d46ac Use tunnel transport and set server address 2019-08-27 13:21:36 +01:00
Milos Gajdos
371b23d055 Introduce DefaultLink; dont hardcode name of the link 2019-08-27 11:36:46 +01:00
Asim Aslam
f97565ef0a Merge pull request #685 from milosgajdos83/default-network
Default network implementation
2019-08-27 11:02:55 +01:00
Asim Aslam
0888d2fbbc Add grpc content-type 2019-08-27 08:13:58 +01:00
Asim Aslam
443fc0ebde Merge pull request #700 from micro/h2-grpc
H2 grpc
2019-08-26 15:55:31 +01:00
huanghaoyan
35e7b9551f ignore Loopback Address (LVS,DR mode) 2019-08-26 21:48:40 +08:00
Asim Aslam
6daf4fda72 Full support for grpc server side 2019-08-26 12:33:59 +01:00
Asim Aslam
36623bfe50 Improve stream processing 2019-08-25 19:30:22 +01:00
Asim Aslam
6128d18ee0 checkpoint fixing data race to process h2 and grpc requests 2019-08-24 20:12:04 +01:00
Asim Aslam
abadb2211e Merge pull request #698 from micro/tunnel-broker
Add a tunnel broker
2019-08-24 14:37:20 +01:00
Asim Aslam
ca267f73de add a tunnel broker 2019-08-24 09:46:55 +01:00
Asim Aslam
d8608b2343 Merge pull request #697 from micro/static-resolver
Add a static network node resolver
2019-08-23 22:05:02 +01:00
Milos Gajdos
ed8d28c9ab Set Route.Link to "network" not Route.Network. Oops! 2019-08-23 21:08:18 +01:00
Milos Gajdos
88e47b9b06 Dont bail when unable to resolve network nodes. 2019-08-23 17:48:14 +01:00
Asim Aslam
1b0295de0d Add a static network node resolver 2019-08-23 17:24:21 +01:00
Milos Gajdos
9448d7c164 Set Route.Network to "network" and Router.Gateway to network.Address 2019-08-23 16:01:57 +01:00
Milos Gajdos
8c3eec9f2a Set the default resolver to registry 2019-08-23 15:14:16 +01:00
Milos Gajdos
e53484302c Added ControlChannel tunnel.Listener to process incoming messages 2019-08-23 15:14:16 +01:00
Milos Gajdos
db89fc4efe Set server name to the correct value. 2019-08-23 15:14:16 +01:00
Milos Gajdos
e1599b0f17 Set server name. Set default network name. 2019-08-23 15:14:16 +01:00
Milos Gajdos
a09d5d2e9a Add Address method. Start and Stop router/server. 2019-08-23 15:14:16 +01:00
Milos Gajdos
6c1f1d66f7 Switch received messages on the right header 2019-08-23 15:14:16 +01:00
Milos Gajdos
a6e1287b27 Replaced incorrect proto import path 2019-08-23 15:14:15 +01:00
Milos Gajdos
fcec6e8eae First attempt to implement default network interface 2019-08-23 15:14:15 +01:00
Milos Gajdos
30dd3f54f0 Make router.Table docs consistent 2019-08-23 15:14:15 +01:00
Milos Gajdos
6beae23afd First commit. Outline of the default network. 2019-08-23 15:14:15 +01:00
Asim Aslam
718780367e Merge pull request #696 from milosgajdos83/server-idempotent
Make server Start() and Stop() idempotent
2019-08-23 15:12:33 +01:00
Milos Gajdos
ba99f037fb Lock started flag when changing it. 2019-08-23 15:07:08 +01:00
Milos Gajdos
80dc0b97a9 Make server starts and stops idempotent 2019-08-23 15:00:57 +01:00
Asim Aslam
1a32e3a11d Merge pull request #695 from micro/proxy-link
Support multiple clients in the proxy as Links
2019-08-23 14:48:49 +01:00
Asim Aslam
955dc2a23d change where we order the routes 2019-08-23 14:11:53 +01:00
Asim Aslam
934b8eb86d Error as link not found 2019-08-23 14:09:57 +01:00
Asim Aslam
b7f510ff64 support links in the proxy 2019-08-23 14:05:11 +01:00
Asim Aslam
353eade6c3 Update client proto 2019-08-23 12:06:11 +01:00
Asim Aslam
a133e61c2d Merge pull request #694 from milosgajdos83/tunnel-loopback-sleep
Lock when setting loopback flag and receiving keepalives
2019-08-22 17:35:03 +01:00
Milos Gajdos
99d39e743b Lock when setting loopback flag and receiving keepalives 2019-08-22 16:31:37 +01:00
Asim Aslam
0cdac2aa36 Merge pull request #689 from milosgajdos83/router-stop
Make router.Stop idempotent
2019-08-21 21:25:48 +01:00
Milos Gajdos
75871287a1 Make stop idempotent. Small refactoring. Router name is memory. 2019-08-21 21:10:42 +01:00
Asim Aslam
fb750a0bb1 Don't start the router if its already running 2019-08-21 18:58:56 +01:00
Asim Aslam
c6e15ef2d1 rename server, set version to timestamp 2019-08-21 15:43:46 +01:00
Asim Aslam
f787cc0ee0 Merge pull request #687 from micro/tunnel
Add tunnel address
2019-08-21 13:01:28 +01:00
Asim Aslam
c2d85a6e1f Add tunnel address 2019-08-21 12:55:10 +01:00
Milos Gajdos
86f0c06fac Removed filewatch counter test. 2019-08-21 11:26:41 +01:00
Asim Aslam
0aea8e3163 Merge pull request #686 from milosgajdos83/config-watcher
Introduce ErrStoppedWatcher for source.Source Watchers and fixed test
2019-08-21 11:06:14 +01:00
Milos Gajdos
4ea27517b5 Source watcher ErrStoppedWatcher and fixed test 2019-08-20 22:32:47 +01:00
Asim Aslam
f8e68ae101 Add string method to tunnel 2019-08-20 17:21:35 +01:00
Asim Aslam
f848041c49 Add a message type to the tunnel 2019-08-20 17:20:21 +01:00
Asim Aslam
dfbd730b8c Fix mucp service option passing 2019-08-20 16:48:09 +01:00
82 changed files with 6158 additions and 847 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,7 @@
# Develop tools
/.vscode/
/.idea/
# Binaries for programs and plugins
*.exe
*.exe~

View File

@@ -32,5 +32,5 @@ Go Micro把分布式系统的各种细节抽象出来。下面是它的主要特
## 快速上手
更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/go-micro_cn.html)。
更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/cn/)。

View File

@@ -30,6 +30,7 @@ var (
"application/proto": protoCodec{},
"application/protobuf": protoCodec{},
"application/octet-stream": protoCodec{},
"application/grpc": protoCodec{},
"application/grpc+json": jsonCodec{},
"application/grpc+proto": protoCodec{},
"application/grpc+bytes": bytesCodec{},

View File

@@ -31,37 +31,37 @@ var _ context.Context
var _ client.Option
var _ server.Option
// Client API for Micro service
// Client API for Client service
type MicroService interface {
type ClientService interface {
// Call allows a single request to be made
Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
// Stream is a bidirectional stream
Stream(ctx context.Context, opts ...client.CallOption) (Micro_StreamService, error)
Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error)
// Publish publishes a message and returns an empty Message
Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error)
}
type microService struct {
type clientService struct {
c client.Client
name string
}
func NewMicroService(name string, c client.Client) MicroService {
func NewClientService(name string, c client.Client) ClientService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.client"
}
return &microService{
return &clientService{
c: c,
name: name,
}
}
func (c *microService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
req := c.c.NewRequest(c.name, "Micro.Call", in)
func (c *clientService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
req := c.c.NewRequest(c.name, "Client.Call", in)
out := new(Response)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
@@ -70,16 +70,16 @@ func (c *microService) Call(ctx context.Context, in *Request, opts ...client.Cal
return out, nil
}
func (c *microService) Stream(ctx context.Context, opts ...client.CallOption) (Micro_StreamService, error) {
req := c.c.NewRequest(c.name, "Micro.Stream", &Request{})
func (c *clientService) Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error) {
req := c.c.NewRequest(c.name, "Client.Stream", &Request{})
stream, err := c.c.Stream(ctx, req, opts...)
if err != nil {
return nil, err
}
return &microServiceStream{stream}, nil
return &clientServiceStream{stream}, nil
}
type Micro_StreamService interface {
type Client_StreamService interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
@@ -87,27 +87,27 @@ type Micro_StreamService interface {
Recv() (*Response, error)
}
type microServiceStream struct {
type clientServiceStream struct {
stream client.Stream
}
func (x *microServiceStream) Close() error {
func (x *clientServiceStream) Close() error {
return x.stream.Close()
}
func (x *microServiceStream) SendMsg(m interface{}) error {
func (x *clientServiceStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *microServiceStream) RecvMsg(m interface{}) error {
func (x *clientServiceStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *microServiceStream) Send(m *Request) error {
func (x *clientServiceStream) Send(m *Request) error {
return x.stream.Send(m)
}
func (x *microServiceStream) Recv() (*Response, error) {
func (x *clientServiceStream) Recv() (*Response, error) {
m := new(Response)
err := x.stream.Recv(m)
if err != nil {
@@ -116,8 +116,8 @@ func (x *microServiceStream) Recv() (*Response, error) {
return m, nil
}
func (c *microService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) {
req := c.c.NewRequest(c.name, "Micro.Publish", in)
func (c *clientService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) {
req := c.c.NewRequest(c.name, "Client.Publish", in)
out := new(Message)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
@@ -126,43 +126,43 @@ func (c *microService) Publish(ctx context.Context, in *Message, opts ...client.
return out, nil
}
// Server API for Micro service
// Server API for Client service
type MicroHandler interface {
type ClientHandler interface {
// Call allows a single request to be made
Call(context.Context, *Request, *Response) error
// Stream is a bidirectional stream
Stream(context.Context, Micro_StreamStream) error
Stream(context.Context, Client_StreamStream) error
// Publish publishes a message and returns an empty Message
Publish(context.Context, *Message, *Message) error
}
func RegisterMicroHandler(s server.Server, hdlr MicroHandler, opts ...server.HandlerOption) error {
type micro interface {
func RegisterClientHandler(s server.Server, hdlr ClientHandler, opts ...server.HandlerOption) error {
type client interface {
Call(ctx context.Context, in *Request, out *Response) error
Stream(ctx context.Context, stream server.Stream) error
Publish(ctx context.Context, in *Message, out *Message) error
}
type Micro struct {
micro
type Client struct {
client
}
h := &microHandler{hdlr}
return s.Handle(s.NewHandler(&Micro{h}, opts...))
h := &clientHandler{hdlr}
return s.Handle(s.NewHandler(&Client{h}, opts...))
}
type microHandler struct {
MicroHandler
type clientHandler struct {
ClientHandler
}
func (h *microHandler) Call(ctx context.Context, in *Request, out *Response) error {
return h.MicroHandler.Call(ctx, in, out)
func (h *clientHandler) Call(ctx context.Context, in *Request, out *Response) error {
return h.ClientHandler.Call(ctx, in, out)
}
func (h *microHandler) Stream(ctx context.Context, stream server.Stream) error {
return h.MicroHandler.Stream(ctx, &microStreamStream{stream})
func (h *clientHandler) Stream(ctx context.Context, stream server.Stream) error {
return h.ClientHandler.Stream(ctx, &clientStreamStream{stream})
}
type Micro_StreamStream interface {
type Client_StreamStream interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
@@ -170,27 +170,27 @@ type Micro_StreamStream interface {
Recv() (*Request, error)
}
type microStreamStream struct {
type clientStreamStream struct {
stream server.Stream
}
func (x *microStreamStream) Close() error {
func (x *clientStreamStream) Close() error {
return x.stream.Close()
}
func (x *microStreamStream) SendMsg(m interface{}) error {
func (x *clientStreamStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *microStreamStream) RecvMsg(m interface{}) error {
func (x *clientStreamStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *microStreamStream) Send(m *Response) error {
func (x *clientStreamStream) Send(m *Response) error {
return x.stream.Send(m)
}
func (x *microStreamStream) Recv() (*Request, error) {
func (x *clientStreamStream) Recv() (*Request, error) {
m := new(Request)
if err := x.stream.Recv(m); err != nil {
return nil, err
@@ -198,6 +198,6 @@ func (x *microStreamStream) Recv() (*Request, error) {
return m, nil
}
func (h *microHandler) Publish(ctx context.Context, in *Message, out *Message) error {
return h.MicroHandler.Publish(ctx, in, out)
func (h *clientHandler) Publish(ctx context.Context, in *Message, out *Message) error {
return h.ClientHandler.Publish(ctx, in, out)
}

View File

@@ -191,23 +191,23 @@ func init() {
var fileDescriptor_7d733ae29171347b = []byte{
// 270 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x3f, 0x4f, 0xc3, 0x30,
0x10, 0xc5, 0xeb, 0xfe, 0x4b, 0x39, 0x2a, 0x21, 0x9d, 0x18, 0x4c, 0x06, 0x54, 0x32, 0x65, 0xc1,
0x45, 0x30, 0x23, 0x86, 0xce, 0x95, 0x50, 0x40, 0xac, 0x28, 0x71, 0x4f, 0xc1, 0x52, 0x6a, 0x9b,
0xd8, 0xad, 0x94, 0xef, 0xc8, 0x87, 0x42, 0x38, 0x29, 0x45, 0xd0, 0x2e, 0x6c, 0xf7, 0xee, 0x67,
0xbd, 0x3b, 0xbf, 0x83, 0x74, 0xad, 0x64, 0x6d, 0xe6, 0xa5, 0xb9, 0x6e, 0x0b, 0x59, 0x29, 0xd2,
0x7e, 0x6e, 0x6b, 0xe3, 0x77, 0x42, 0x04, 0x81, 0x67, 0xa5, 0x11, 0xe1, 0x8d, 0x68, 0xdb, 0xc9,
0x16, 0xa2, 0x8c, 0xde, 0x37, 0xe4, 0x3c, 0x72, 0x88, 0x1c, 0xd5, 0x5b, 0x25, 0x89, 0xb3, 0x19,
0x4b, 0x4f, 0xb2, 0x9d, 0xc4, 0x18, 0x26, 0xa4, 0x57, 0xd6, 0x28, 0xed, 0x79, 0x3f, 0xa0, 0x6f,
0x8d, 0x57, 0x30, 0x95, 0x46, 0x7b, 0xd2, 0xfe, 0xd5, 0x37, 0x96, 0xf8, 0x20, 0xf0, 0xd3, 0xae,
0xf7, 0xdc, 0x58, 0x42, 0x84, 0x61, 0x61, 0x56, 0x0d, 0x1f, 0xce, 0x58, 0x3a, 0xcd, 0x42, 0x9d,
0x5c, 0xc2, 0x24, 0x23, 0x67, 0x8d, 0x76, 0x7b, 0xce, 0x7e, 0xf0, 0x17, 0x88, 0x96, 0xe4, 0x5c,
0x5e, 0x12, 0x9e, 0xc3, 0xc8, 0x1b, 0xab, 0x64, 0xb7, 0x55, 0x2b, 0xfe, 0xcc, 0xed, 0x1f, 0x9f,
0x3b, 0xd8, 0xfb, 0xde, 0x7e, 0x30, 0x18, 0x2d, 0xbf, 0x02, 0xc0, 0x7b, 0x18, 0x2e, 0xf2, 0xaa,
0x42, 0x2e, 0x7e, 0x65, 0x22, 0xba, 0x40, 0xe2, 0x8b, 0x03, 0xa4, 0x5d, 0x39, 0xe9, 0xe1, 0x02,
0xc6, 0x4f, 0xbe, 0xa6, 0x7c, 0xfd, 0x4f, 0x83, 0x94, 0xdd, 0x30, 0x7c, 0x80, 0xe8, 0x71, 0x53,
0x54, 0xca, 0xbd, 0x1d, 0x70, 0xe9, 0xfe, 0x1f, 0x1f, 0x25, 0x49, 0xaf, 0x18, 0x87, 0xb3, 0xde,
0x7d, 0x06, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x63, 0x94, 0x1a, 0x02, 0x02, 0x00, 0x00,
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x41, 0x4b, 0xc3, 0x40,
0x10, 0x85, 0xbb, 0x6d, 0x4c, 0xea, 0x58, 0x10, 0x06, 0x0f, 0x6b, 0x0e, 0x52, 0x73, 0xca, 0xc5,
0x54, 0xf4, 0x2c, 0x1e, 0x72, 0x16, 0x24, 0x8a, 0x57, 0x49, 0xb6, 0x43, 0x5c, 0x48, 0x77, 0xd7,
0xec, 0xb6, 0x90, 0x1f, 0xe9, 0x7f, 0x12, 0x36, 0xa9, 0x15, 0x6d, 0x2f, 0xbd, 0xcd, 0x9b, 0x6f,
0x79, 0x33, 0xfb, 0x06, 0xd2, 0x95, 0x14, 0xad, 0x5e, 0xd4, 0xfa, 0xa6, 0x2f, 0x44, 0x23, 0x49,
0xb9, 0x85, 0x69, 0xb5, 0xdb, 0x8a, 0xcc, 0x0b, 0x3c, 0xaf, 0x75, 0xe6, 0xdf, 0x64, 0x7d, 0x3b,
0xd9, 0x40, 0x54, 0xd0, 0xe7, 0x9a, 0xac, 0x43, 0x0e, 0x91, 0xa5, 0x76, 0x23, 0x05, 0x71, 0x36,
0x67, 0xe9, 0x69, 0xb1, 0x95, 0x18, 0xc3, 0x94, 0xd4, 0xd2, 0x68, 0xa9, 0x1c, 0x1f, 0x7b, 0xf4,
0xa3, 0xf1, 0x1a, 0x66, 0x42, 0x2b, 0x47, 0xca, 0xbd, 0xbb, 0xce, 0x10, 0x9f, 0x78, 0x7e, 0x36,
0xf4, 0x5e, 0x3b, 0x43, 0x88, 0x10, 0x54, 0x7a, 0xd9, 0xf1, 0x60, 0xce, 0xd2, 0x59, 0xe1, 0xeb,
0xe4, 0x0a, 0xa6, 0x05, 0x59, 0xa3, 0x95, 0xdd, 0x71, 0xf6, 0x8b, 0xbf, 0x41, 0xf4, 0x44, 0xd6,
0x96, 0x35, 0xe1, 0x05, 0x9c, 0x38, 0x6d, 0xa4, 0x18, 0xb6, 0xea, 0xc5, 0xbf, 0xb9, 0xe3, 0xc3,
0x73, 0x27, 0x3b, 0xdf, 0xbb, 0x2f, 0x06, 0x61, 0xee, 0xbf, 0x8e, 0x0f, 0x10, 0xe4, 0x65, 0xd3,
0x20, 0xcf, 0xfe, 0x84, 0x92, 0x0d, 0x89, 0xc4, 0x97, 0x7b, 0x48, 0xbf, 0x73, 0x32, 0xc2, 0x1c,
0xc2, 0x17, 0xd7, 0x52, 0xb9, 0x3a, 0xd2, 0x20, 0x65, 0xb7, 0x0c, 0x1f, 0x21, 0x7a, 0x5e, 0x57,
0x8d, 0xb4, 0x1f, 0x7b, 0x5c, 0x86, 0x00, 0xe2, 0x83, 0x24, 0x19, 0x55, 0xa1, 0xbf, 0xeb, 0xfd,
0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x76, 0x1f, 0x51, 0x03, 0x02, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -218,59 +218,59 @@ var _ grpc.ClientConn
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// MicroClient is the client API for Micro service.
// ClientClient is the client API for Client service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type MicroClient interface {
type ClientClient interface {
// Call allows a single request to be made
Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
// Stream is a bidirectional stream
Stream(ctx context.Context, opts ...grpc.CallOption) (Micro_StreamClient, error)
Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error)
// Publish publishes a message and returns an empty Message
Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error)
}
type microClient struct {
type clientClient struct {
cc *grpc.ClientConn
}
func NewMicroClient(cc *grpc.ClientConn) MicroClient {
return &microClient{cc}
func NewClientClient(cc *grpc.ClientConn) ClientClient {
return &clientClient{cc}
}
func (c *microClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
func (c *clientClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
out := new(Response)
err := c.cc.Invoke(ctx, "/go.micro.client.Micro/Call", in, out, opts...)
err := c.cc.Invoke(ctx, "/go.micro.client.Client/Call", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *microClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Micro_StreamClient, error) {
stream, err := c.cc.NewStream(ctx, &_Micro_serviceDesc.Streams[0], "/go.micro.client.Micro/Stream", opts...)
func (c *clientClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error) {
stream, err := c.cc.NewStream(ctx, &_Client_serviceDesc.Streams[0], "/go.micro.client.Client/Stream", opts...)
if err != nil {
return nil, err
}
x := &microStreamClient{stream}
x := &clientStreamClient{stream}
return x, nil
}
type Micro_StreamClient interface {
type Client_StreamClient interface {
Send(*Request) error
Recv() (*Response, error)
grpc.ClientStream
}
type microStreamClient struct {
type clientStreamClient struct {
grpc.ClientStream
}
func (x *microStreamClient) Send(m *Request) error {
func (x *clientStreamClient) Send(m *Request) error {
return x.ClientStream.SendMsg(m)
}
func (x *microStreamClient) Recv() (*Response, error) {
func (x *clientStreamClient) Recv() (*Response, error) {
m := new(Response)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
@@ -278,66 +278,66 @@ func (x *microStreamClient) Recv() (*Response, error) {
return m, nil
}
func (c *microClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) {
func (c *clientClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) {
out := new(Message)
err := c.cc.Invoke(ctx, "/go.micro.client.Micro/Publish", in, out, opts...)
err := c.cc.Invoke(ctx, "/go.micro.client.Client/Publish", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// MicroServer is the server API for Micro service.
type MicroServer interface {
// ClientServer is the server API for Client service.
type ClientServer interface {
// Call allows a single request to be made
Call(context.Context, *Request) (*Response, error)
// Stream is a bidirectional stream
Stream(Micro_StreamServer) error
Stream(Client_StreamServer) error
// Publish publishes a message and returns an empty Message
Publish(context.Context, *Message) (*Message, error)
}
func RegisterMicroServer(s *grpc.Server, srv MicroServer) {
s.RegisterService(&_Micro_serviceDesc, srv)
func RegisterClientServer(s *grpc.Server, srv ClientServer) {
s.RegisterService(&_Client_serviceDesc, srv)
}
func _Micro_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
func _Client_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MicroServer).Call(ctx, in)
return srv.(ClientServer).Call(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.client.Micro/Call",
FullMethod: "/go.micro.client.Client/Call",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MicroServer).Call(ctx, req.(*Request))
return srv.(ClientServer).Call(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
func _Micro_Stream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(MicroServer).Stream(&microStreamServer{stream})
func _Client_Stream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(ClientServer).Stream(&clientStreamServer{stream})
}
type Micro_StreamServer interface {
type Client_StreamServer interface {
Send(*Response) error
Recv() (*Request, error)
grpc.ServerStream
}
type microStreamServer struct {
type clientStreamServer struct {
grpc.ServerStream
}
func (x *microStreamServer) Send(m *Response) error {
func (x *clientStreamServer) Send(m *Response) error {
return x.ServerStream.SendMsg(m)
}
func (x *microStreamServer) Recv() (*Request, error) {
func (x *clientStreamServer) Recv() (*Request, error) {
m := new(Request)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
@@ -345,41 +345,41 @@ func (x *microStreamServer) Recv() (*Request, error) {
return m, nil
}
func _Micro_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
func _Client_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Message)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MicroServer).Publish(ctx, in)
return srv.(ClientServer).Publish(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.client.Micro/Publish",
FullMethod: "/go.micro.client.Client/Publish",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MicroServer).Publish(ctx, req.(*Message))
return srv.(ClientServer).Publish(ctx, req.(*Message))
}
return interceptor(ctx, in, info, handler)
}
var _Micro_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.client.Micro",
HandlerType: (*MicroServer)(nil),
var _Client_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.client.Client",
HandlerType: (*ClientServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Call",
Handler: _Micro_Call_Handler,
Handler: _Client_Call_Handler,
},
{
MethodName: "Publish",
Handler: _Micro_Publish_Handler,
Handler: _Client_Publish_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Stream",
Handler: _Micro_Stream_Handler,
Handler: _Client_Stream_Handler,
ServerStreams: true,
ClientStreams: true,
},

View File

@@ -2,8 +2,8 @@ syntax = "proto3";
package go.micro.client;
// Micro is the micro client interface
service Micro {
// Client is the micro client interface
service Client {
// Call allows a single request to be made
rpc Call(Request) returns (Response) {};
// Stream is a bidirectional stream

View File

@@ -96,7 +96,15 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
}
}
c, err := r.pool.Get(address, transport.WithTimeout(opts.DialTimeout))
dOpts := []transport.DialOption{
transport.WithStream(),
}
if opts.DialTimeout >= 0 {
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
}
c, err := r.pool.Get(address, dOpts...)
if err != nil {
return errors.InternalServerError("go.micro.client", "connection error: %v", err)
}

View File

@@ -89,9 +89,22 @@ func (c *Codec) Write(m *codec.Message, b interface{}) error {
m.Header[":authority"] = m.Target
m.Header["content-type"] = c.ContentType
case codec.Response:
m.Header["Trailer"] = "grpc-status, grpc-message"
m.Header["Trailer"] = "grpc-status" //, grpc-message"
m.Header["content-type"] = c.ContentType
m.Header[":status"] = "200"
m.Header["grpc-status"] = "0"
m.Header["grpc-message"] = ""
// m.Header["grpc-message"] = ""
case codec.Error:
m.Header["Trailer"] = "grpc-status, grpc-message"
// micro end of stream
if m.Error == "EOS" {
m.Header["grpc-status"] = "0"
} else {
m.Header["grpc-message"] = m.Error
m.Header["grpc-status"] = "13"
}
return nil
}
// marshal content

View File

@@ -1,7 +1,6 @@
package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
@@ -9,7 +8,6 @@ import (
"testing"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/config/source/env"
"github.com/micro/go-micro/config/source/file"
)
@@ -122,57 +120,3 @@ func TestConfigMerge(t *testing.T) {
actualHost)
}
}
func TestFileChange(t *testing.T) {
// create a temp file
fileName := uuid.New().String() + "testWatcher.json"
f, err := os.OpenFile("."+sep+fileName, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Error(err)
}
defer f.Close()
defer os.Remove("." + sep + fileName)
// load the file
if err := Load(file.NewSource(
file.WithPath("." + sep + fileName),
)); err != nil {
t.Error(err)
}
// watch changes
watcher, err := Watch()
if err != nil {
t.Error(err)
}
changeTimes := 0
go func() {
for {
v, err := watcher.Next()
if err != nil {
t.Error(err)
return
}
changeTimes++
t.Logf("file change%s", string(v.Bytes()))
}
}()
content := map[int]string{}
// change the file
for i := 0; i < 5; i++ {
content[i] = time.Now().String()
bytes, _ := json.Marshal(content)
f.Truncate(0)
f.Seek(0, 0)
if _, err := f.Write(bytes); err != nil {
t.Error(err)
}
time.Sleep(time.Second)
}
if changeTimes != 4 {
t.Error(fmt.Errorf("watcher error: change times %d is not enough", changeTimes))
}
}

View File

@@ -44,6 +44,6 @@ Load the source into config
// Create new config
conf := config.NewConfig()
// Load file source
// Load consul source
conf.Load(consulSource)
```

View File

@@ -1,7 +1,6 @@
package consul
import (
"errors"
"time"
"github.com/hashicorp/consul/api"
@@ -80,7 +79,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
case cs := <-w.ch:
return cs, nil
case <-w.exit:
return nil, errors.New("watcher stopped")
return nil, source.ErrWatcherStopped
}
}

View File

@@ -91,6 +91,6 @@ Load the source into config
// Create new config
conf := config.NewConfig()
// Load file source
// Load env source
conf.Load(src)
```

View File

@@ -86,8 +86,8 @@ func TestEnvvar_Prefixes(t *testing.T) {
}
func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) {
source := NewSource(WithStrippedPrefix("GOMICRO_"))
w, err := source.Watch()
src := NewSource(WithStrippedPrefix("GOMICRO_"))
w, err := src.Watch()
if err != nil {
t.Error(err)
}
@@ -97,7 +97,7 @@ func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) {
w.Stop()
}()
if _, err := w.Next(); err.Error() != "watcher stopped" {
if _, err := w.Next(); err != source.ErrWatcherStopped {
t.Errorf("expected watcher stopped error, got %v", err)
}
}

View File

@@ -1,8 +1,6 @@
package env
import (
"errors"
"github.com/micro/go-micro/config/source"
)
@@ -13,7 +11,7 @@ type watcher struct {
func (w *watcher) Next() (*source.ChangeSet, error) {
<-w.exit
return nil, errors.New("watcher stopped")
return nil, source.ErrWatcherStopped
}
func (w *watcher) Stop() error {

View File

@@ -3,7 +3,6 @@
package file
import (
"errors"
"os"
"github.com/fsnotify/fsnotify"
@@ -36,7 +35,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
// is it closed?
select {
case <-w.exit:
return nil, errors.New("watcher stopped")
return nil, source.ErrWatcherStopped
default:
}
@@ -59,7 +58,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
case err := <-w.fw.Errors:
return nil, err
case <-w.exit:
return nil, errors.New("watcher stopped")
return nil, source.ErrWatcherStopped
}
}

View File

@@ -3,7 +3,6 @@
package file
import (
"errors"
"os"
"github.com/fsnotify/fsnotify"
@@ -36,7 +35,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
// is it closed?
select {
case <-w.exit:
return nil, errors.New("watcher stopped")
return nil, source.ErrWatcherStopped
default:
}
@@ -63,7 +62,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
case err := <-w.fw.Errors:
return nil, err
case <-w.exit:
return nil, errors.New("watcher stopped")
return nil, source.ErrWatcherStopped
}
}

View File

@@ -42,6 +42,6 @@ Load the source into config
// Create new config
conf := config.NewConfig()
// Load file source
// Load flag source
conf.Load(flagSource)
```

View File

@@ -39,6 +39,6 @@ Load the source into config
// Create new config
conf := config.NewConfig()
// Load file source
// Load memory source
conf.Load(memorySource)
```

View File

@@ -2,9 +2,15 @@
package source
import (
"errors"
"time"
)
var (
// ErrWatcherStopped is returned when source watcher has been stopped
ErrWatcherStopped = errors.New("watcher stopped")
)
// Source is the source from which config is loaded
type Source interface {
Read() (*ChangeSet, error)

1005
network/default.go Normal file

File diff suppressed because it is too large Load Diff

111
network/handler/handler.go Normal file
View File

@@ -0,0 +1,111 @@
// Package handler implements network RPC handler
package handler
import (
"context"
"sort"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/network"
pbNet "github.com/micro/go-micro/network/proto"
pbRtr "github.com/micro/go-micro/router/proto"
)
// Network implements network handler
type Network struct {
Network network.Network
}
// ListRoutes returns a list of routing table routes
func (n *Network) ListRoutes(ctx context.Context, req *pbRtr.Request, resp *pbRtr.ListResponse) error {
routes, err := n.Network.Options().Router.Table().List()
if err != nil {
return errors.InternalServerError("go.micro.network", "failed to list routes: %s", err)
}
var respRoutes []*pbRtr.Route
for _, route := range routes {
respRoute := &pbRtr.Route{
Service: route.Service,
Address: route.Address,
Gateway: route.Gateway,
Network: route.Network,
Router: route.Router,
Link: route.Link,
Metric: int64(route.Metric),
}
respRoutes = append(respRoutes, respRoute)
}
resp.Routes = respRoutes
return nil
}
// ListNodes returns a list of all accessible nodes in the network
func (n *Network) ListNodes(ctx context.Context, req *pbNet.ListRequest, resp *pbNet.ListResponse) error {
nodes := n.Network.Nodes()
var respNodes []*pbNet.Node
for _, node := range nodes {
respNode := &pbNet.Node{
Id: node.Id(),
Address: node.Address(),
}
respNodes = append(respNodes, respNode)
}
resp.Nodes = respNodes
return nil
}
// Neighbourhood returns a list of immediate neighbours
func (n *Network) Neighbourhood(ctx context.Context, req *pbNet.NeighbourhoodRequest, resp *pbNet.NeighbourhoodResponse) error {
// extract the id of the node to query
id := req.Id
// if no id is passed, we assume local node
if id == "" {
id = n.Network.Id()
}
// get all the nodes in the network
nodes := n.Network.Nodes()
// sort the slice of nodes
sort.Slice(nodes, func(i, j int) bool { return nodes[i].Id() <= nodes[j].Id() })
// find a node with a given id
i := sort.Search(len(nodes), func(j int) bool { return nodes[j].Id() >= id })
var neighbours []*pbNet.Node
// collect all the nodes in the neighbourhood of the found node
if i < len(nodes) && nodes[i].Id() == id {
for _, neighbour := range nodes[i].Neighbourhood() {
// don't return yourself in response
if neighbour.Id() == n.Network.Id() {
continue
}
pbNeighbour := &pbNet.Node{
Id: neighbour.Id(),
Address: neighbour.Address(),
}
neighbours = append(neighbours, pbNeighbour)
}
}
// requested neighbourhood node
node := &pbNet.Node{
Id: nodes[i].Id(),
Address: nodes[i].Address(),
}
// creaate neighbourhood answer
neighbourhood := &pbNet.Neighbour{
Node: node,
Neighbours: neighbours,
}
resp.Neighbourhood = neighbourhood
return nil
}

View File

@@ -1,2 +1,60 @@
// Package network is for creating internetworks
package network
import (
"time"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/server"
)
var (
// DefaultName is default network name
DefaultName = "go.micro"
// DefaultAddress is default network address
DefaultAddress = ":0"
// ResolveTime defines time interval to periodically resolve network nodes
ResolveTime = 1 * time.Minute
// AnnounceTime defines time interval to periodically announce node neighbours
AnnounceTime = 30 * time.Second
// PruneTime defines time interval to periodically check nodes that need to be pruned
// due to their not announcing their presence within this time interval
PruneTime = 90 * time.Second
)
// Node is network node
type Node interface {
// Id is node id
Id() string
// Address is node bind address
Address() string
// Neighbourhood is node neighbourhood
Neighbourhood() []Node
// Network is the network node is in
Network() Network
}
// Network is micro network
type Network interface {
// Node is network node
Node
// Options returns the network options
Options() Options
// Name of the network
Name() string
// Connect starts the resolver and tunnel server
Connect() error
// Nodes returns list of network nodes
Nodes() []Node
// Close stops the tunnel and resolving
Close() error
// Client is micro client
Client() client.Client
// Server is micro server
Server() server.Server
}
// NewNetwork returns a new network interface
func NewNetwork(opts ...Option) Network {
return newNetwork(opts...)
}

103
network/options.go Normal file
View File

@@ -0,0 +1,103 @@
package network
import (
"github.com/google/uuid"
"github.com/micro/go-micro/network/resolver"
"github.com/micro/go-micro/network/resolver/registry"
"github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/proxy/mucp"
"github.com/micro/go-micro/router"
"github.com/micro/go-micro/tunnel"
)
type Option func(*Options)
// Options configure network
type Options struct {
// Id of the node
Id string
// Name of the network
Name string
// Address to bind to
Address string
// Nodes is a list of seed nodes
Nodes []string
// Tunnel is network tunnel
Tunnel tunnel.Tunnel
// Router is network router
Router router.Router
// Proxy is network proxy
Proxy proxy.Proxy
// Resolver is network resolver
Resolver resolver.Resolver
}
// Id sets the id of the network node
func Id(id string) Option {
return func(o *Options) {
o.Id = id
}
}
// Name sets the network name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// Address sets the network address
func Address(a string) Option {
return func(o *Options) {
o.Address = a
}
}
// Nodes is a list of seed nodes used along
// with resolved node
func Nodes(n ...string) Option {
return func(o *Options) {
o.Nodes = n
}
}
// Tunnel sets the network tunnel
func Tunnel(t tunnel.Tunnel) Option {
return func(o *Options) {
o.Tunnel = t
}
}
// Router sets the network router
func Router(r router.Router) Option {
return func(o *Options) {
o.Router = r
}
}
// Proxy sets the network proxy
func Proxy(p proxy.Proxy) Option {
return func(o *Options) {
o.Proxy = p
}
}
// Resolver is the network resolver
func Resolver(r resolver.Resolver) Option {
return func(o *Options) {
o.Resolver = r
}
}
// DefaultOptions returns network default options
func DefaultOptions() Options {
return Options{
Id: uuid.New().String(),
Name: DefaultName,
Address: DefaultAddress,
Tunnel: tunnel.NewTunnel(),
Router: router.DefaultRouter,
Proxy: mucp.NewProxy(),
Resolver: &registry.Resolver{},
}
}

View File

@@ -0,0 +1,126 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: network.proto
package go_micro_network
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
proto1 "github.com/micro/go-micro/router/proto"
math "math"
)
import (
context "context"
client "github.com/micro/go-micro/client"
server "github.com/micro/go-micro/server"
)
// 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
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ client.Option
var _ server.Option
// Client API for Network service
type NetworkService interface {
ListRoutes(ctx context.Context, in *proto1.Request, opts ...client.CallOption) (*proto1.ListResponse, error)
ListNodes(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, opts ...client.CallOption) (*NeighbourhoodResponse, error)
}
type networkService struct {
c client.Client
name string
}
func NewNetworkService(name string, c client.Client) NetworkService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.network"
}
return &networkService{
c: c,
name: name,
}
}
func (c *networkService) ListRoutes(ctx context.Context, in *proto1.Request, opts ...client.CallOption) (*proto1.ListResponse, error) {
req := c.c.NewRequest(c.name, "Network.ListRoutes", in)
out := new(proto1.ListResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) ListNodes(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) {
req := c.c.NewRequest(c.name, "Network.ListNodes", in)
out := new(ListResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, opts ...client.CallOption) (*NeighbourhoodResponse, error) {
req := c.c.NewRequest(c.name, "Network.Neighbourhood", in)
out := new(NeighbourhoodResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Network service
type NetworkHandler interface {
ListRoutes(context.Context, *proto1.Request, *proto1.ListResponse) error
ListNodes(context.Context, *ListRequest, *ListResponse) error
Neighbourhood(context.Context, *NeighbourhoodRequest, *NeighbourhoodResponse) error
}
func RegisterNetworkHandler(s server.Server, hdlr NetworkHandler, opts ...server.HandlerOption) error {
type network interface {
ListRoutes(ctx context.Context, in *proto1.Request, out *proto1.ListResponse) error
ListNodes(ctx context.Context, in *ListRequest, out *ListResponse) error
Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, out *NeighbourhoodResponse) error
}
type Network struct {
network
}
h := &networkHandler{hdlr}
return s.Handle(s.NewHandler(&Network{h}, opts...))
}
type networkHandler struct {
NetworkHandler
}
func (h *networkHandler) ListRoutes(ctx context.Context, in *proto1.Request, out *proto1.ListResponse) error {
return h.NetworkHandler.ListRoutes(ctx, in, out)
}
func (h *networkHandler) ListNodes(ctx context.Context, in *ListRequest, out *ListResponse) error {
return h.NetworkHandler.ListNodes(ctx, in, out)
}
func (h *networkHandler) Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, out *NeighbourhoodResponse) error {
return h.NetworkHandler.Neighbourhood(ctx, in, out)
}

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

@@ -0,0 +1,438 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: network.proto
package go_micro_network
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
_ "github.com/micro/go-micro/router/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
// Empty request
type ListRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListRequest) Reset() { *m = ListRequest{} }
func (m *ListRequest) String() string { return proto.CompactTextString(m) }
func (*ListRequest) ProtoMessage() {}
func (*ListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{0}
}
func (m *ListRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListRequest.Unmarshal(m, b)
}
func (m *ListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListRequest.Marshal(b, m, deterministic)
}
func (m *ListRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListRequest.Merge(m, src)
}
func (m *ListRequest) XXX_Size() int {
return xxx_messageInfo_ListRequest.Size(m)
}
func (m *ListRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ListRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ListRequest proto.InternalMessageInfo
// ListResponse is returned by ListNodes and ListNeighbours
type ListResponse struct {
Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListResponse) Reset() { *m = ListResponse{} }
func (m *ListResponse) String() string { return proto.CompactTextString(m) }
func (*ListResponse) ProtoMessage() {}
func (*ListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{1}
}
func (m *ListResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListResponse.Unmarshal(m, b)
}
func (m *ListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListResponse.Marshal(b, m, deterministic)
}
func (m *ListResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListResponse.Merge(m, src)
}
func (m *ListResponse) XXX_Size() int {
return xxx_messageInfo_ListResponse.Size(m)
}
func (m *ListResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ListResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ListResponse proto.InternalMessageInfo
func (m *ListResponse) GetNodes() []*Node {
if m != nil {
return m.Nodes
}
return nil
}
// NeighbourhoodRequest is sent to query node neighbourhood
type NeighbourhoodRequest struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NeighbourhoodRequest) Reset() { *m = NeighbourhoodRequest{} }
func (m *NeighbourhoodRequest) String() string { return proto.CompactTextString(m) }
func (*NeighbourhoodRequest) ProtoMessage() {}
func (*NeighbourhoodRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{2}
}
func (m *NeighbourhoodRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NeighbourhoodRequest.Unmarshal(m, b)
}
func (m *NeighbourhoodRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NeighbourhoodRequest.Marshal(b, m, deterministic)
}
func (m *NeighbourhoodRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_NeighbourhoodRequest.Merge(m, src)
}
func (m *NeighbourhoodRequest) XXX_Size() int {
return xxx_messageInfo_NeighbourhoodRequest.Size(m)
}
func (m *NeighbourhoodRequest) XXX_DiscardUnknown() {
xxx_messageInfo_NeighbourhoodRequest.DiscardUnknown(m)
}
var xxx_messageInfo_NeighbourhoodRequest proto.InternalMessageInfo
func (m *NeighbourhoodRequest) GetId() string {
if m != nil {
return m.Id
}
return ""
}
// NeighbourhoodResponse contains node neighbourhood hierarchy
type NeighbourhoodResponse struct {
Neighbourhood *Neighbour `protobuf:"bytes,1,opt,name=neighbourhood,proto3" json:"neighbourhood,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NeighbourhoodResponse) Reset() { *m = NeighbourhoodResponse{} }
func (m *NeighbourhoodResponse) String() string { return proto.CompactTextString(m) }
func (*NeighbourhoodResponse) ProtoMessage() {}
func (*NeighbourhoodResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{3}
}
func (m *NeighbourhoodResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NeighbourhoodResponse.Unmarshal(m, b)
}
func (m *NeighbourhoodResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NeighbourhoodResponse.Marshal(b, m, deterministic)
}
func (m *NeighbourhoodResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_NeighbourhoodResponse.Merge(m, src)
}
func (m *NeighbourhoodResponse) XXX_Size() int {
return xxx_messageInfo_NeighbourhoodResponse.Size(m)
}
func (m *NeighbourhoodResponse) XXX_DiscardUnknown() {
xxx_messageInfo_NeighbourhoodResponse.DiscardUnknown(m)
}
var xxx_messageInfo_NeighbourhoodResponse proto.InternalMessageInfo
func (m *NeighbourhoodResponse) GetNeighbourhood() *Neighbour {
if m != nil {
return m.Neighbourhood
}
return nil
}
// Node is network node
type Node struct {
// node ide
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// node address
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_8571034d60397816, []int{4}
}
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 ""
}
// Connect is sent when the node connects to the network
type Connect struct {
// network mode
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,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_8571034d60397816, []int{5}
}
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) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
// Close is sent when the node disconnects from the network
type Close struct {
// network node
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Close) Reset() { *m = Close{} }
func (m *Close) String() string { return proto.CompactTextString(m) }
func (*Close) ProtoMessage() {}
func (*Close) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{6}
}
func (m *Close) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Close.Unmarshal(m, b)
}
func (m *Close) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Close.Marshal(b, m, deterministic)
}
func (m *Close) XXX_Merge(src proto.Message) {
xxx_messageInfo_Close.Merge(m, src)
}
func (m *Close) XXX_Size() int {
return xxx_messageInfo_Close.Size(m)
}
func (m *Close) XXX_DiscardUnknown() {
xxx_messageInfo_Close.DiscardUnknown(m)
}
var xxx_messageInfo_Close proto.InternalMessageInfo
func (m *Close) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
// Solicit is sent when requesting route advertisement from the network nodes
type Solicit struct {
// network node
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Solicit) Reset() { *m = Solicit{} }
func (m *Solicit) String() string { return proto.CompactTextString(m) }
func (*Solicit) ProtoMessage() {}
func (*Solicit) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{7}
}
func (m *Solicit) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Solicit.Unmarshal(m, b)
}
func (m *Solicit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Solicit.Marshal(b, m, deterministic)
}
func (m *Solicit) XXX_Merge(src proto.Message) {
xxx_messageInfo_Solicit.Merge(m, src)
}
func (m *Solicit) XXX_Size() int {
return xxx_messageInfo_Solicit.Size(m)
}
func (m *Solicit) XXX_DiscardUnknown() {
xxx_messageInfo_Solicit.DiscardUnknown(m)
}
var xxx_messageInfo_Solicit proto.InternalMessageInfo
func (m *Solicit) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
// Neighbour is used to nnounce node neighbourhood
type Neighbour struct {
// network node
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
// neighbours
Neighbours []*Node `protobuf:"bytes,3,rep,name=neighbours,proto3" json:"neighbours,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Neighbour) Reset() { *m = Neighbour{} }
func (m *Neighbour) String() string { return proto.CompactTextString(m) }
func (*Neighbour) ProtoMessage() {}
func (*Neighbour) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{8}
}
func (m *Neighbour) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Neighbour.Unmarshal(m, b)
}
func (m *Neighbour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Neighbour.Marshal(b, m, deterministic)
}
func (m *Neighbour) XXX_Merge(src proto.Message) {
xxx_messageInfo_Neighbour.Merge(m, src)
}
func (m *Neighbour) XXX_Size() int {
return xxx_messageInfo_Neighbour.Size(m)
}
func (m *Neighbour) XXX_DiscardUnknown() {
xxx_messageInfo_Neighbour.DiscardUnknown(m)
}
var xxx_messageInfo_Neighbour proto.InternalMessageInfo
func (m *Neighbour) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
func (m *Neighbour) GetNeighbours() []*Node {
if m != nil {
return m.Neighbours
}
return nil
}
func init() {
proto.RegisterType((*ListRequest)(nil), "go.micro.network.ListRequest")
proto.RegisterType((*ListResponse)(nil), "go.micro.network.ListResponse")
proto.RegisterType((*NeighbourhoodRequest)(nil), "go.micro.network.NeighbourhoodRequest")
proto.RegisterType((*NeighbourhoodResponse)(nil), "go.micro.network.NeighbourhoodResponse")
proto.RegisterType((*Node)(nil), "go.micro.network.Node")
proto.RegisterType((*Connect)(nil), "go.micro.network.Connect")
proto.RegisterType((*Close)(nil), "go.micro.network.Close")
proto.RegisterType((*Solicit)(nil), "go.micro.network.Solicit")
proto.RegisterType((*Neighbour)(nil), "go.micro.network.Neighbour")
}
func init() { proto.RegisterFile("network.proto", fileDescriptor_8571034d60397816) }
var fileDescriptor_8571034d60397816 = []byte{
// 360 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0x41, 0x4f, 0xf2, 0x40,
0x10, 0xfd, 0x28, 0xf0, 0x35, 0x0c, 0x1f, 0x5f, 0xcc, 0x46, 0x4d, 0x53, 0x83, 0x21, 0x7b, 0x40,
0x62, 0xb4, 0x18, 0x08, 0x9e, 0xbc, 0x18, 0x0e, 0x5e, 0x08, 0x87, 0x7a, 0xf3, 0x66, 0xbb, 0x9b,
0xb2, 0x11, 0x3a, 0xb8, 0xbb, 0x8d, 0x7f, 0xc0, 0x1f, 0x6e, 0xba, 0x5d, 0xb0, 0x80, 0x60, 0xb8,
0x75, 0xe6, 0xbd, 0x37, 0x6f, 0xa7, 0xfb, 0x16, 0x5a, 0x29, 0xd7, 0x1f, 0x28, 0xdf, 0x82, 0xa5,
0x44, 0x8d, 0xe4, 0x24, 0xc1, 0x60, 0x21, 0x62, 0x89, 0x81, 0xed, 0xfb, 0xc3, 0x44, 0xe8, 0x59,
0x16, 0x05, 0x31, 0x2e, 0xfa, 0x06, 0xe9, 0x27, 0x78, 0x5b, 0x7c, 0x48, 0xcc, 0x34, 0x97, 0x7d,
0xa3, 0xb4, 0x45, 0x31, 0x86, 0xb6, 0xa0, 0x39, 0x11, 0x4a, 0x87, 0xfc, 0x3d, 0xe3, 0x4a, 0xd3,
0x07, 0xf8, 0x57, 0x94, 0x6a, 0x89, 0xa9, 0xe2, 0xe4, 0x06, 0xea, 0x29, 0x32, 0xae, 0xbc, 0x4a,
0xa7, 0xda, 0x6b, 0x0e, 0xce, 0x83, 0x6d, 0xd7, 0x60, 0x8a, 0x8c, 0x87, 0x05, 0x89, 0x76, 0xe1,
0x74, 0xca, 0x45, 0x32, 0x8b, 0x30, 0x93, 0x33, 0x44, 0x66, 0xa7, 0x92, 0xff, 0xe0, 0x08, 0xe6,
0x55, 0x3a, 0x95, 0x5e, 0x23, 0x74, 0x04, 0xa3, 0x2f, 0x70, 0xb6, 0xc5, 0xb3, 0x76, 0x8f, 0xf9,
0x96, 0x25, 0xc0, 0x68, 0x9a, 0x83, 0x8b, 0x1f, 0x6c, 0x57, 0xb4, 0x70, 0x53, 0x41, 0xef, 0xa0,
0x96, 0x1f, 0x69, 0xdb, 0x93, 0x78, 0xe0, 0xbe, 0x32, 0x26, 0xb9, 0x52, 0x9e, 0x63, 0x9a, 0xab,
0x92, 0x8e, 0xc0, 0x1d, 0x63, 0x9a, 0xf2, 0x58, 0x93, 0x6b, 0xa8, 0xe5, 0x9b, 0x58, 0xdb, 0x7d,
0xdb, 0x1a, 0x0e, 0x1d, 0x42, 0x7d, 0x3c, 0x47, 0xc5, 0x8f, 0x12, 0x8d, 0xc0, 0x7d, 0xc6, 0xb9,
0x88, 0xc5, 0x71, 0x5e, 0x08, 0x8d, 0xf5, 0xc2, 0xc7, 0x08, 0xc9, 0x3d, 0xc0, 0xfa, 0xf7, 0x28,
0xaf, 0x7a, 0xf0, 0x12, 0x4b, 0xcc, 0xc1, 0xa7, 0x03, 0xee, 0xb4, 0x00, 0xc9, 0x13, 0x80, 0xc9,
0x44, 0x1e, 0x1b, 0x45, 0xbc, 0x6f, 0xb5, 0x0d, 0x92, 0xbd, 0x65, 0xbf, 0xbd, 0x83, 0x94, 0xa3,
0x44, 0xff, 0x90, 0x09, 0x34, 0xf2, 0x4e, 0x6e, 0xa6, 0x48, 0x7b, 0xf7, 0x14, 0xa5, 0x20, 0xfa,
0x97, 0xfb, 0xe0, 0xf5, 0xb4, 0x08, 0x5a, 0x1b, 0x21, 0x22, 0xdd, 0x03, 0x29, 0x29, 0xa5, 0xd1,
0xbf, 0xfa, 0x95, 0xb7, 0xf2, 0x88, 0xfe, 0x9a, 0x47, 0x32, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff,
0x59, 0xcf, 0xab, 0xb5, 0x7c, 0x03, 0x00, 0x00,
}

View File

@@ -0,0 +1,64 @@
syntax = "proto3";
package go.micro.network;
import "github.com/micro/go-micro/router/proto/router.proto";
// Network service is usesd to gain visibility into networks
service Network {
rpc ListRoutes(go.micro.router.Request) returns (go.micro.router.ListResponse) {};
rpc ListNodes(ListRequest) returns (ListResponse) {};
rpc Neighbourhood(NeighbourhoodRequest) returns (NeighbourhoodResponse) {};
}
// Empty request
message ListRequest {}
// ListResponse is returned by ListNodes and ListNeighbours
message ListResponse {
repeated Node nodes = 1;
}
// NeighbourhoodRequest is sent to query node neighbourhood
message NeighbourhoodRequest {
string id = 1;
}
// NeighbourhoodResponse contains node neighbourhood hierarchy
message NeighbourhoodResponse {
Neighbour neighbourhood = 1;
}
// Node is network node
message Node {
// node ide
string id = 1;
// node address
string address = 2;
}
// Connect is sent when the node connects to the network
message Connect {
// network mode
Node node = 1;
}
// Close is sent when the node disconnects from the network
message Close {
// network node
Node node = 1;
}
// Solicit is sent when requesting route advertisement from the network nodes
message Solicit {
// network node
Node node = 1;
}
// Neighbour is used to nnounce node neighbourhood
message Neighbour {
// network node
Node node = 1;
// neighbours
repeated Node neighbours = 3;
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/micro/go-micro/network/resolver"
)
// Resolver is a DNS network resolve
type Resolver struct{}
// Resolve assumes ID is a domain name e.g micro.mu

View File

@@ -10,6 +10,7 @@ import (
"github.com/micro/go-micro/network/resolver"
)
// Resolver is a HTTP network resolver
type Resolver struct {
// If not set, defaults to http
Proto string

View File

@@ -6,6 +6,7 @@ import (
"github.com/micro/go-micro/registry"
)
// Resolver is a registry network resolver
type Resolver struct {
// Registry is the registry to use otherwise we use the defaul
Registry registry.Registry

View File

@@ -5,7 +5,7 @@ package resolver
// via the name to connect to. This is done based on Network.Name().
// Before we can be part of any network, we have to connect to it.
type Resolver interface {
// Resolve returns a list of addresses for an name
// Resolve returns a list of addresses for a name
Resolve(name string) ([]*Record, error)
}

View File

@@ -0,0 +1,33 @@
// Package static is a static resolver
package registry
import (
"github.com/micro/go-micro/network/resolver"
)
// Resolver returns a static list of nodes. In the event the node list
// is not present it will return the name of the network passed in.
type Resolver struct {
// A static list of nodes
Nodes []string
}
// Resolve returns the list of nodes
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
// if there are no nodes just return the name
if len(r.Nodes) == 0 {
return []*resolver.Record{
{Address: name},
}, nil
}
var records []*resolver.Record
for _, node := range r.Nodes {
records = append(records, &resolver.Record{
Address: node,
})
}
return records, nil
}

125
plugin/default.go Normal file
View File

@@ -0,0 +1,125 @@
// Package plugin provides the ability to load plugins
package plugin
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
pg "plugin"
"strings"
"text/template"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/server"
"github.com/micro/go-micro/transport"
)
type plugin struct{}
// Init sets up the plugin
func (p *plugin) Init(c *Config) error {
switch c.Type {
case "broker":
pg, ok := c.NewFunc.(func(...broker.Option) broker.Broker)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultBrokers[c.Name] = pg
case "client":
pg, ok := c.NewFunc.(func(...client.Option) client.Client)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultClients[c.Name] = pg
case "registry":
pg, ok := c.NewFunc.(func(...registry.Option) registry.Registry)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultRegistries[c.Name] = pg
case "selector":
pg, ok := c.NewFunc.(func(...selector.Option) selector.Selector)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultSelectors[c.Name] = pg
case "server":
pg, ok := c.NewFunc.(func(...server.Option) server.Server)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultServers[c.Name] = pg
case "transport":
pg, ok := c.NewFunc.(func(...transport.Option) transport.Transport)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultTransports[c.Name] = pg
default:
return fmt.Errorf("Unknown plugin type: %s for %s", c.Type, c.Name)
}
return nil
}
// Load loads a plugin created with `go build -buildmode=plugin`
func (p *plugin) Load(path string) (*Config, error) {
plugin, err := pg.Open(path)
if err != nil {
return nil, err
}
s, err := plugin.Lookup("Plugin")
if err != nil {
return nil, err
}
pl, ok := s.(*Config)
if !ok {
return nil, errors.New("could not cast Plugin object")
}
return pl, nil
}
// Generate creates a go file at the specified path.
// You must use `go build -buildmode=plugin`to build it.
func (p *plugin) Generate(path string, c *Config) error {
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close()
t, err := template.New(c.Name).Parse(tmpl)
if err != nil {
return err
}
return t.Execute(f, c)
}
// Build generates a dso plugin using the go command `go build -buildmode=plugin`
func (p *plugin) Build(path string, c *Config) error {
path = strings.TrimSuffix(path, ".so")
// create go file in tmp path
temp := os.TempDir()
base := filepath.Base(path)
goFile := filepath.Join(temp, base+".go")
// generate .go file
if err := p.Generate(goFile, c); err != nil {
return err
}
// remove .go file
defer os.Remove(goFile)
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil && !os.IsExist(err) {
return fmt.Errorf("Failed to create dir %s: %v", filepath.Dir(path), err)
}
cmd := exec.Command("go", "build", "-buildmode=plugin", "-o", path+".so", goFile)
return cmd.Run()
}

46
plugin/plugin.go Normal file
View File

@@ -0,0 +1,46 @@
// Package plugin provides the ability to load plugins
package plugin
// Plugin is a plugin loaded from a file
type Plugin interface {
// Initialise a plugin with the config
Init(c *Config) error
// Load loads a .so plugin at the given path
Load(path string) (*Config, error)
// Build a .so plugin with config at the path specified
Build(path string, c *Config) error
}
// Config is the plugin config
type Config struct {
// Name of the plugin e.g rabbitmq
Name string
// Type of the plugin e.g broker
Type string
// Path specifies the import path
Path string
// NewFunc creates an instance of the plugin
NewFunc interface{}
}
var (
// Default plugin loader
DefaultPlugin = NewPlugin()
)
// NewPlugin creates a new plugin interface
func NewPlugin() Plugin {
return &plugin{}
}
func Build(path string, c *Config) error {
return DefaultPlugin.Build(path, c)
}
func Load(path string) (*Config, error) {
return DefaultPlugin.Load(path)
}
func Init(c *Config) error {
return DefaultPlugin.Init(c)
}

20
plugin/template.go Normal file
View File

@@ -0,0 +1,20 @@
package plugin
var (
tmpl = `
package main
import (
"github.com/micro/go-micro/plugin"
"{{.Path}}"
)
var Plugin = plugin.Config{
Name: "{{.Name}}",
Type: "{{.Type}}",
Path: "{{.Path}}",
NewFunc: {{.Name}}.{{.NewFunc}},
}
`
)

View File

@@ -10,6 +10,7 @@ import (
"github.com/micro/go-micro/client/grpc"
"github.com/micro/go-micro/codec"
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/server"
)
@@ -61,6 +62,10 @@ func readLoop(r server.Request, s client.Stream) error {
}
}
func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error {
return errors.InternalServerError("go.micro.proxy.grpc", "SendRequest is unsupported")
}
// ServeRequest honours the server.Proxy interface
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
// set default client

View File

@@ -10,6 +10,7 @@ import (
"net/url"
"path"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/proxy"
@@ -44,6 +45,10 @@ func getEndpoint(hdr map[string]string) string {
return ""
}
func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error {
return errors.InternalServerError("go.micro.proxy.http", "SendRequest is unsupported")
}
// ServeRequest honours the server.Router interface
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
if p.Endpoint == "" {

View File

@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"io"
"sort"
"strings"
"sync"
@@ -12,6 +13,7 @@ import (
"github.com/micro/go-micro/codec"
"github.com/micro/go-micro/codec/bytes"
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/router"
"github.com/micro/go-micro/server"
@@ -26,9 +28,12 @@ type Proxy struct {
// Endpoint specifies the fixed service endpoint to call.
Endpoint string
// The client to use for outbound requests
// The client to use for outbound requests in the local network
Client client.Client
// Links are used for outbound requests not in the local network
Links map[string]client.Client
// The router for routes
Router router.Router
@@ -76,7 +81,7 @@ func readLoop(r server.Request, s client.Stream) error {
}
// toNodes returns a list of node addresses from given routes
func toNodes(routes map[uint64]router.Route) []string {
func toNodes(routes []router.Route) []string {
var nodes []string
for _, node := range routes {
address := node.Address
@@ -88,15 +93,37 @@ func toNodes(routes map[uint64]router.Route) []string {
return nodes
}
func (p *Proxy) getRoute(service string) ([]string, error) {
func (p *Proxy) getLink(r router.Route) (client.Client, error) {
if r.Link == "local" || len(p.Links) == 0 {
return p.Client, nil
}
l, ok := p.Links[r.Link]
if !ok {
return nil, errors.InternalServerError("go.micro.proxy", "link not found")
}
return l, nil
}
func (p *Proxy) getRoute(service string) ([]router.Route, error) {
toSlice := func(r map[uint64]router.Route) []router.Route {
var routes []router.Route
for _, v := range r {
routes = append(routes, v)
}
// sort the routes in order of metric
sort.Slice(routes, func(i, j int) bool { return routes[i].Metric < routes[j].Metric })
return routes
}
// lookup the route cache first
p.Lock()
routes, ok := p.Routes[service]
if ok {
p.Unlock()
return toNodes(routes), nil
return toSlice(routes), nil
}
p.Routes[service] = make(map[uint64]router.Route)
p.Unlock()
// lookup the routes in the router
@@ -113,12 +140,16 @@ func (p *Proxy) getRoute(service string) ([]string, error) {
// update the proxy cache
p.Lock()
for _, route := range results {
// create if does not exist
if _, ok := p.Routes[service]; !ok {
p.Routes[service] = make(map[uint64]router.Route)
}
p.Routes[service][route.Hash()] = route
}
routes = p.Routes[service]
p.Unlock()
return toNodes(routes), nil
return toSlice(routes), nil
}
// manageRouteCache applies action on a given route to Proxy route cache
@@ -130,9 +161,6 @@ func (p *Proxy) manageRouteCache(route router.Route, action string) error {
}
p.Routes[route.Service][route.Hash()] = route
case "delete":
if _, ok := p.Routes[route.Service]; !ok {
return fmt.Errorf("route not found")
}
delete(p.Routes[route.Service], route.Hash())
default:
return fmt.Errorf("unknown action: %s", action)
@@ -171,12 +199,31 @@ func (p *Proxy) watchRoutes() {
}
}
func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error {
return errors.InternalServerError("go.micro.proxy", "SendRequest is unsupported")
}
// ServeRequest honours the server.Router interface
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
// service name
service := req.Service()
endpoint := req.Endpoint()
// determine if its local routing
var local bool
// address to call
var addresses []string
// routes
var routes []router.Route
// service name to call
service := req.Service()
// endpoint to call
endpoint := req.Endpoint()
if len(service) == 0 {
return errors.BadRequest("go.micro.proxy", "service name is blank")
}
// are we network routing or local routing
if len(p.Links) == 0 {
local = true
}
// call a specific backend endpoint either by name or address
if len(p.Endpoint) > 0 {
@@ -190,7 +237,7 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
return err
}
// set the address
addresses = addr
routes = addr
// set the name
service = p.Endpoint
}
@@ -201,16 +248,66 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
if err != nil {
return err
}
addresses = addr
routes = addr
}
var opts []client.CallOption
// set address if available
// if the address is already set just serve it
// TODO: figure it out if we should know to pick a link
if len(addresses) > 0 {
opts = append(opts, client.WithAddress(addresses...))
// serve the normal way
return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, client.WithAddress(addresses...))
}
// there's no links e.g we're local routing then just serve it with addresses
if local {
var opts []client.CallOption
// set address if available via routes or specific endpoint
if len(routes) > 0 {
addresses := toNodes(routes)
opts = append(opts, client.WithAddress(addresses...))
}
// serve the normal way
return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, opts...)
}
var gerr error
// we're routing globally with multiple links
// so we need to pick a link per route
for _, route := range routes {
// pick the link or error out
link, err := p.getLink(route)
if err != nil {
// ok let's try again
gerr = err
continue
}
// set the address to call
addresses := toNodes([]router.Route{route})
// do the request with the link
gerr = p.serveRequest(ctx, link, service, endpoint, req, rsp, client.WithAddress(addresses...))
// return on no error since we succeeded
if gerr == nil {
return nil
}
// return where the context deadline was exceeded
if gerr == context.Canceled || gerr == context.DeadlineExceeded {
return err
}
// otherwise attempt to do it all over again
}
// if we got here something went really badly wrong
return gerr
}
func (p *Proxy) serveRequest(ctx context.Context, link client.Client, service, endpoint string, req server.Request, rsp server.Response, opts ...client.CallOption) error {
// read initial request
body, err := req.Read()
if err != nil {
@@ -218,14 +315,14 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
}
// create new request with raw bytes body
creq := p.Client.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType()))
creq := link.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType()))
// not a stream so make a client.Call request
if !req.Stream() {
crsp := new(bytes.Frame)
// make a call to the backend
if err := p.Client.Call(ctx, creq, crsp, opts...); err != nil {
if err := link.Call(ctx, creq, crsp, opts...); err != nil {
return err
}
@@ -238,7 +335,7 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
}
// create new stream
stream, err := p.Client.Stream(ctx, creq, opts...)
stream, err := link.Stream(ctx, creq, opts...)
if err != nil {
return err
}
@@ -300,6 +397,7 @@ func NewSingleHostProxy(endpoint string) *Proxy {
// NewProxy returns a new proxy which will route based on mucp headers
func NewProxy(opts ...options.Option) proxy.Proxy {
p := new(Proxy)
p.Links = map[string]client.Client{}
p.Options = options.NewOptions(opts...)
p.Options.Init(options.WithString("mucp"))
@@ -320,6 +418,12 @@ func NewProxy(opts ...options.Option) proxy.Proxy {
p.Client = client.DefaultClient
}
// get client
links, ok := p.Options.Values().Get("proxy.links")
if ok {
p.Links = links.(map[string]client.Client)
}
// get router
r, ok := p.Options.Values().Get("proxy.router")
if ok {

View File

@@ -13,6 +13,8 @@ import (
// Proxy can be used as a proxy server for go-micro services
type Proxy interface {
options.Options
// SendRequest honours the client.Router interface
SendRequest(context.Context, client.Request, client.Response) error
// ServeRequest honours the server.Router interface
ServeRequest(context.Context, server.Request, server.Response) error
}
@@ -35,3 +37,20 @@ func WithClient(c client.Client) options.Option {
func WithRouter(r router.Router) options.Option {
return options.WithValue("proxy.router", r)
}
// WithLink sets a link for outbound requests
func WithLink(name string, c client.Client) options.Option {
return func(o *options.Values) error {
var links map[string]client.Client
v, ok := o.Get("proxy.links")
if ok {
links = v.(map[string]client.Client)
} else {
links = map[string]client.Client{}
}
links[name] = c
// save the links
o.Set("proxy.links", links)
return nil
}
}

View File

@@ -0,0 +1,76 @@
package handler
import (
"context"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/registry"
pb "github.com/micro/go-micro/registry/proto"
"github.com/micro/go-micro/registry/service"
)
type Registry struct {
// internal registry
Registry registry.Registry
}
func (r *Registry) GetService(ctx context.Context, req *pb.GetRequest, rsp *pb.GetResponse) error {
services, err := r.Registry.GetService(req.Service)
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
for _, srv := range services {
rsp.Services = append(rsp.Services, service.ToProto(srv))
}
return nil
}
func (r *Registry) Register(ctx context.Context, req *pb.Service, rsp *pb.EmptyResponse) error {
err := r.Registry.Register(service.ToService(req))
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
return nil
}
func (r *Registry) Deregister(ctx context.Context, req *pb.Service, rsp *pb.EmptyResponse) error {
err := r.Registry.Deregister(service.ToService(req))
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
return nil
}
func (r *Registry) ListServices(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
services, err := r.Registry.ListServices()
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
for _, srv := range services {
rsp.Services = append(rsp.Services, service.ToProto(srv))
}
return nil
}
func (r *Registry) Watch(ctx context.Context, req *pb.WatchRequest, rsp pb.Registry_WatchStream) error {
watcher, err := r.Registry.Watch(registry.WatchService(req.Service))
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
for {
next, err := watcher.Next()
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
err = rsp.Send(&pb.Result{
Action: next.Action,
Service: service.ToProto(next.Service),
})
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
}
return nil
}

View File

@@ -2,6 +2,8 @@
package mdns
import (
"context"
"github.com/micro/go-micro/registry"
)
@@ -9,3 +11,13 @@ import (
func NewRegistry(opts ...registry.Option) registry.Registry {
return registry.NewRegistry(opts...)
}
// Domain sets the mdnsDomain
func Domain(d string) registry.Option {
return func(o *registry.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, "mdns.domain", d)
}
}

View File

@@ -14,6 +14,11 @@ import (
hash "github.com/mitchellh/hashstructure"
)
var (
// use a .micro domain rather than .local
mdnsDomain = "micro"
)
type mdnsTxt struct {
Service string
Version string
@@ -29,6 +34,8 @@ type mdnsEntry struct {
type mdnsRegistry struct {
opts Options
// the mdns domain
domain string
sync.Mutex
services map[string][]*mdnsEntry
@@ -36,11 +43,25 @@ type mdnsRegistry struct {
func newRegistry(opts ...Option) Registry {
options := Options{
Context: context.Background(),
Timeout: time.Millisecond * 100,
}
for _, o := range opts {
o(&options)
}
// set the domain
domain := mdnsDomain
d, ok := options.Context.Value("mdns.domain").(string)
if ok {
domain = d
}
return &mdnsRegistry{
opts: options,
domain: domain,
services: make(map[string][]*mdnsEntry),
}
}
@@ -66,7 +87,7 @@ func (m *mdnsRegistry) Register(service *Service, opts ...RegisterOption) error
s, err := mdns.NewMDNSService(
service.Name,
"_services",
"",
m.domain+".",
"",
9999,
[]net.IP{net.ParseIP("0.0.0.0")},
@@ -141,7 +162,7 @@ func (m *mdnsRegistry) Register(service *Service, opts ...RegisterOption) error
s, err := mdns.NewMDNSService(
node.Id,
service.Name,
"",
m.domain+".",
"",
port,
[]net.IP{net.ParseIP(host)},
@@ -214,6 +235,8 @@ func (m *mdnsRegistry) GetService(service string) ([]*Service, error) {
p.Context, _ = context.WithTimeout(context.Background(), m.opts.Timeout)
// set entries channel
p.Entries = entries
// set the domain
p.Domain = m.domain
go func() {
for {
@@ -223,7 +246,9 @@ func (m *mdnsRegistry) GetService(service string) ([]*Service, error) {
if p.Service == "_services" {
continue
}
if p.Domain != m.domain {
continue
}
if e.TTL == 0 {
continue
}
@@ -288,6 +313,8 @@ func (m *mdnsRegistry) ListServices() ([]*Service, error) {
p.Context, _ = context.WithTimeout(context.Background(), m.opts.Timeout)
// set entries channel
p.Entries = entries
// set domain
p.Domain = m.domain
var services []*Service
@@ -298,7 +325,9 @@ func (m *mdnsRegistry) ListServices() ([]*Service, error) {
if e.TTL == 0 {
continue
}
if !strings.HasSuffix(e.Name, p.Domain+".") {
continue
}
name := strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+".")
if !serviceMap[name] {
serviceMap[name] = true
@@ -329,9 +358,10 @@ func (m *mdnsRegistry) Watch(opts ...WatchOption) (Watcher, error) {
}
md := &mdnsWatcher{
wo: wo,
ch: make(chan *mdns.ServiceEntry, 32),
exit: make(chan struct{}),
wo: wo,
ch: make(chan *mdns.ServiceEntry, 32),
exit: make(chan struct{}),
domain: m.domain,
}
go func() {

View File

@@ -11,6 +11,8 @@ type mdnsWatcher struct {
wo WatchOptions
ch chan *mdns.ServiceEntry
exit chan struct{}
// the mdns domain
domain string
}
func (m *mdnsWatcher) Next() (*Result, error) {
@@ -46,13 +48,14 @@ func (m *mdnsWatcher) Next() (*Result, error) {
Endpoints: txt.Endpoints,
}
// TODO: don't hardcode .local.
if !strings.HasSuffix(e.Name, "."+service.Name+".local.") {
// skip anything without the domain we care about
suffix := fmt.Sprintf(".%s.%s.", service.Name, m.domain)
if !strings.HasSuffix(e.Name, suffix) {
continue
}
service.Nodes = append(service.Nodes, &Node{
Id: strings.TrimSuffix(e.Name, "."+service.Name+".local."),
Id: strings.TrimSuffix(e.Name, suffix),
Address: fmt.Sprintf("%s:%d", e.AddrV4.String(), e.Port),
Metadata: txt.Metadata,
})

View File

@@ -0,0 +1,224 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/registry/proto/registry.proto
package go_micro_registry
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
client "github.com/micro/go-micro/client"
server "github.com/micro/go-micro/server"
)
// 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
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ client.Option
var _ server.Option
// Client API for Registry service
type RegistryService interface {
GetService(ctx context.Context, in *GetRequest, opts ...client.CallOption) (*GetResponse, error)
Register(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error)
Deregister(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error)
ListServices(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
Watch(ctx context.Context, in *WatchRequest, opts ...client.CallOption) (Registry_WatchService, error)
}
type registryService struct {
c client.Client
name string
}
func NewRegistryService(name string, c client.Client) RegistryService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.registry"
}
return &registryService{
c: c,
name: name,
}
}
func (c *registryService) GetService(ctx context.Context, in *GetRequest, opts ...client.CallOption) (*GetResponse, error) {
req := c.c.NewRequest(c.name, "Registry.GetService", in)
out := new(GetResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryService) Register(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error) {
req := c.c.NewRequest(c.name, "Registry.Register", in)
out := new(EmptyResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryService) Deregister(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error) {
req := c.c.NewRequest(c.name, "Registry.Deregister", in)
out := new(EmptyResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryService) ListServices(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) {
req := c.c.NewRequest(c.name, "Registry.ListServices", in)
out := new(ListResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryService) Watch(ctx context.Context, in *WatchRequest, opts ...client.CallOption) (Registry_WatchService, error) {
req := c.c.NewRequest(c.name, "Registry.Watch", &WatchRequest{})
stream, err := c.c.Stream(ctx, req, opts...)
if err != nil {
return nil, err
}
if err := stream.Send(in); err != nil {
return nil, err
}
return &registryServiceWatch{stream}, nil
}
type Registry_WatchService interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Recv() (*Result, error)
}
type registryServiceWatch struct {
stream client.Stream
}
func (x *registryServiceWatch) Close() error {
return x.stream.Close()
}
func (x *registryServiceWatch) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *registryServiceWatch) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *registryServiceWatch) Recv() (*Result, error) {
m := new(Result)
err := x.stream.Recv(m)
if err != nil {
return nil, err
}
return m, nil
}
// Server API for Registry service
type RegistryHandler interface {
GetService(context.Context, *GetRequest, *GetResponse) error
Register(context.Context, *Service, *EmptyResponse) error
Deregister(context.Context, *Service, *EmptyResponse) error
ListServices(context.Context, *ListRequest, *ListResponse) error
Watch(context.Context, *WatchRequest, Registry_WatchStream) error
}
func RegisterRegistryHandler(s server.Server, hdlr RegistryHandler, opts ...server.HandlerOption) error {
type registry interface {
GetService(ctx context.Context, in *GetRequest, out *GetResponse) error
Register(ctx context.Context, in *Service, out *EmptyResponse) error
Deregister(ctx context.Context, in *Service, out *EmptyResponse) error
ListServices(ctx context.Context, in *ListRequest, out *ListResponse) error
Watch(ctx context.Context, stream server.Stream) error
}
type Registry struct {
registry
}
h := &registryHandler{hdlr}
return s.Handle(s.NewHandler(&Registry{h}, opts...))
}
type registryHandler struct {
RegistryHandler
}
func (h *registryHandler) GetService(ctx context.Context, in *GetRequest, out *GetResponse) error {
return h.RegistryHandler.GetService(ctx, in, out)
}
func (h *registryHandler) Register(ctx context.Context, in *Service, out *EmptyResponse) error {
return h.RegistryHandler.Register(ctx, in, out)
}
func (h *registryHandler) Deregister(ctx context.Context, in *Service, out *EmptyResponse) error {
return h.RegistryHandler.Deregister(ctx, in, out)
}
func (h *registryHandler) ListServices(ctx context.Context, in *ListRequest, out *ListResponse) error {
return h.RegistryHandler.ListServices(ctx, in, out)
}
func (h *registryHandler) Watch(ctx context.Context, stream server.Stream) error {
m := new(WatchRequest)
if err := stream.Recv(m); err != nil {
return err
}
return h.RegistryHandler.Watch(ctx, m, &registryWatchStream{stream})
}
type Registry_WatchStream interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Send(*Result) error
}
type registryWatchStream struct {
stream server.Stream
}
func (x *registryWatchStream) Close() error {
return x.stream.Close()
}
func (x *registryWatchStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *registryWatchStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *registryWatchStream) Send(m *Result) error {
return x.stream.Send(m)
}

View File

@@ -0,0 +1,848 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/registry/proto/registry.proto
package go_micro_registry
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
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
// Service represents a go-micro service
type Service struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
Metadata map[string]string `protobuf:"bytes,3,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Endpoints []*Endpoint `protobuf:"bytes,4,rep,name=endpoints,proto3" json:"endpoints,omitempty"`
Nodes []*Node `protobuf:"bytes,5,rep,name=nodes,proto3" json:"nodes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Service) Reset() { *m = Service{} }
func (m *Service) String() string { return proto.CompactTextString(m) }
func (*Service) ProtoMessage() {}
func (*Service) Descriptor() ([]byte, []int) {
return fileDescriptor_f287a6b809166ad2, []int{0}
}
func (m *Service) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Service.Unmarshal(m, b)
}
func (m *Service) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Service.Marshal(b, m, deterministic)
}
func (m *Service) XXX_Merge(src proto.Message) {
xxx_messageInfo_Service.Merge(m, src)
}
func (m *Service) XXX_Size() int {
return xxx_messageInfo_Service.Size(m)
}
func (m *Service) XXX_DiscardUnknown() {
xxx_messageInfo_Service.DiscardUnknown(m)
}
var xxx_messageInfo_Service proto.InternalMessageInfo
func (m *Service) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Service) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *Service) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
func (m *Service) GetEndpoints() []*Endpoint {
if m != nil {
return m.Endpoints
}
return nil
}
func (m *Service) GetNodes() []*Node {
if m != nil {
return m.Nodes
}
return nil
}
// Node represents the node the service is on
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"`
Port int64 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"`
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
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_f287a6b809166ad2, []int{1}
}
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 (m *Node) GetPort() int64 {
if m != nil {
return m.Port
}
return 0
}
func (m *Node) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
// Endpoint is a endpoint provided by a service
type Endpoint struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Request *Value `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"`
Response *Value `protobuf:"bytes,3,opt,name=response,proto3" json:"response,omitempty"`
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Endpoint) Reset() { *m = Endpoint{} }
func (m *Endpoint) String() string { return proto.CompactTextString(m) }
func (*Endpoint) ProtoMessage() {}
func (*Endpoint) Descriptor() ([]byte, []int) {
return fileDescriptor_f287a6b809166ad2, []int{2}
}
func (m *Endpoint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Endpoint.Unmarshal(m, b)
}
func (m *Endpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Endpoint.Marshal(b, m, deterministic)
}
func (m *Endpoint) XXX_Merge(src proto.Message) {
xxx_messageInfo_Endpoint.Merge(m, src)
}
func (m *Endpoint) XXX_Size() int {
return xxx_messageInfo_Endpoint.Size(m)
}
func (m *Endpoint) XXX_DiscardUnknown() {
xxx_messageInfo_Endpoint.DiscardUnknown(m)
}
var xxx_messageInfo_Endpoint proto.InternalMessageInfo
func (m *Endpoint) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Endpoint) GetRequest() *Value {
if m != nil {
return m.Request
}
return nil
}
func (m *Endpoint) GetResponse() *Value {
if m != nil {
return m.Response
}
return nil
}
func (m *Endpoint) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
// Value is an opaque value for a request or response
type Value struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Values []*Value `protobuf:"bytes,3,rep,name=values,proto3" json:"values,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Value) Reset() { *m = Value{} }
func (m *Value) String() string { return proto.CompactTextString(m) }
func (*Value) ProtoMessage() {}
func (*Value) Descriptor() ([]byte, []int) {
return fileDescriptor_f287a6b809166ad2, []int{3}
}
func (m *Value) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Value.Unmarshal(m, b)
}
func (m *Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Value.Marshal(b, m, deterministic)
}
func (m *Value) XXX_Merge(src proto.Message) {
xxx_messageInfo_Value.Merge(m, src)
}
func (m *Value) XXX_Size() int {
return xxx_messageInfo_Value.Size(m)
}
func (m *Value) XXX_DiscardUnknown() {
xxx_messageInfo_Value.DiscardUnknown(m)
}
var xxx_messageInfo_Value proto.InternalMessageInfo
func (m *Value) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Value) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *Value) GetValues() []*Value {
if m != nil {
return m.Values
}
return nil
}
// Result is returns by the watcher
type Result struct {
Action string `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"`
Service *Service `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"`
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Result) Reset() { *m = Result{} }
func (m *Result) String() string { return proto.CompactTextString(m) }
func (*Result) ProtoMessage() {}
func (*Result) Descriptor() ([]byte, []int) {
return fileDescriptor_f287a6b809166ad2, []int{4}
}
func (m *Result) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Result.Unmarshal(m, b)
}
func (m *Result) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Result.Marshal(b, m, deterministic)
}
func (m *Result) XXX_Merge(src proto.Message) {
xxx_messageInfo_Result.Merge(m, src)
}
func (m *Result) XXX_Size() int {
return xxx_messageInfo_Result.Size(m)
}
func (m *Result) XXX_DiscardUnknown() {
xxx_messageInfo_Result.DiscardUnknown(m)
}
var xxx_messageInfo_Result proto.InternalMessageInfo
func (m *Result) GetAction() string {
if m != nil {
return m.Action
}
return ""
}
func (m *Result) GetService() *Service {
if m != nil {
return m.Service
}
return nil
}
func (m *Result) GetTimestamp() int64 {
if m != nil {
return m.Timestamp
}
return 0
}
type EmptyResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *EmptyResponse) Reset() { *m = EmptyResponse{} }
func (m *EmptyResponse) String() string { return proto.CompactTextString(m) }
func (*EmptyResponse) ProtoMessage() {}
func (*EmptyResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f287a6b809166ad2, []int{5}
}
func (m *EmptyResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EmptyResponse.Unmarshal(m, b)
}
func (m *EmptyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_EmptyResponse.Marshal(b, m, deterministic)
}
func (m *EmptyResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_EmptyResponse.Merge(m, src)
}
func (m *EmptyResponse) XXX_Size() int {
return xxx_messageInfo_EmptyResponse.Size(m)
}
func (m *EmptyResponse) XXX_DiscardUnknown() {
xxx_messageInfo_EmptyResponse.DiscardUnknown(m)
}
var xxx_messageInfo_EmptyResponse proto.InternalMessageInfo
type GetRequest struct {
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetRequest) Reset() { *m = GetRequest{} }
func (m *GetRequest) String() string { return proto.CompactTextString(m) }
func (*GetRequest) ProtoMessage() {}
func (*GetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_f287a6b809166ad2, []int{6}
}
func (m *GetRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetRequest.Unmarshal(m, b)
}
func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic)
}
func (m *GetRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetRequest.Merge(m, src)
}
func (m *GetRequest) XXX_Size() int {
return xxx_messageInfo_GetRequest.Size(m)
}
func (m *GetRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetRequest proto.InternalMessageInfo
func (m *GetRequest) GetService() string {
if m != nil {
return m.Service
}
return ""
}
type GetResponse struct {
Services []*Service `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetResponse) Reset() { *m = GetResponse{} }
func (m *GetResponse) String() string { return proto.CompactTextString(m) }
func (*GetResponse) ProtoMessage() {}
func (*GetResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f287a6b809166ad2, []int{7}
}
func (m *GetResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetResponse.Unmarshal(m, b)
}
func (m *GetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetResponse.Marshal(b, m, deterministic)
}
func (m *GetResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetResponse.Merge(m, src)
}
func (m *GetResponse) XXX_Size() int {
return xxx_messageInfo_GetResponse.Size(m)
}
func (m *GetResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GetResponse proto.InternalMessageInfo
func (m *GetResponse) GetServices() []*Service {
if m != nil {
return m.Services
}
return nil
}
type ListRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListRequest) Reset() { *m = ListRequest{} }
func (m *ListRequest) String() string { return proto.CompactTextString(m) }
func (*ListRequest) ProtoMessage() {}
func (*ListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_f287a6b809166ad2, []int{8}
}
func (m *ListRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListRequest.Unmarshal(m, b)
}
func (m *ListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListRequest.Marshal(b, m, deterministic)
}
func (m *ListRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListRequest.Merge(m, src)
}
func (m *ListRequest) XXX_Size() int {
return xxx_messageInfo_ListRequest.Size(m)
}
func (m *ListRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ListRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ListRequest proto.InternalMessageInfo
type ListResponse struct {
Services []*Service `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListResponse) Reset() { *m = ListResponse{} }
func (m *ListResponse) String() string { return proto.CompactTextString(m) }
func (*ListResponse) ProtoMessage() {}
func (*ListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f287a6b809166ad2, []int{9}
}
func (m *ListResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListResponse.Unmarshal(m, b)
}
func (m *ListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListResponse.Marshal(b, m, deterministic)
}
func (m *ListResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListResponse.Merge(m, src)
}
func (m *ListResponse) XXX_Size() int {
return xxx_messageInfo_ListResponse.Size(m)
}
func (m *ListResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ListResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ListResponse proto.InternalMessageInfo
func (m *ListResponse) GetServices() []*Service {
if m != nil {
return m.Services
}
return nil
}
type WatchRequest struct {
// service is optional
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *WatchRequest) Reset() { *m = WatchRequest{} }
func (m *WatchRequest) String() string { return proto.CompactTextString(m) }
func (*WatchRequest) ProtoMessage() {}
func (*WatchRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_f287a6b809166ad2, []int{10}
}
func (m *WatchRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_WatchRequest.Unmarshal(m, b)
}
func (m *WatchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_WatchRequest.Marshal(b, m, deterministic)
}
func (m *WatchRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_WatchRequest.Merge(m, src)
}
func (m *WatchRequest) XXX_Size() int {
return xxx_messageInfo_WatchRequest.Size(m)
}
func (m *WatchRequest) XXX_DiscardUnknown() {
xxx_messageInfo_WatchRequest.DiscardUnknown(m)
}
var xxx_messageInfo_WatchRequest proto.InternalMessageInfo
func (m *WatchRequest) GetService() string {
if m != nil {
return m.Service
}
return ""
}
func init() {
proto.RegisterType((*Service)(nil), "go.micro.registry.Service")
proto.RegisterMapType((map[string]string)(nil), "go.micro.registry.Service.MetadataEntry")
proto.RegisterType((*Node)(nil), "go.micro.registry.Node")
proto.RegisterMapType((map[string]string)(nil), "go.micro.registry.Node.MetadataEntry")
proto.RegisterType((*Endpoint)(nil), "go.micro.registry.Endpoint")
proto.RegisterMapType((map[string]string)(nil), "go.micro.registry.Endpoint.MetadataEntry")
proto.RegisterType((*Value)(nil), "go.micro.registry.Value")
proto.RegisterType((*Result)(nil), "go.micro.registry.Result")
proto.RegisterType((*EmptyResponse)(nil), "go.micro.registry.EmptyResponse")
proto.RegisterType((*GetRequest)(nil), "go.micro.registry.GetRequest")
proto.RegisterType((*GetResponse)(nil), "go.micro.registry.GetResponse")
proto.RegisterType((*ListRequest)(nil), "go.micro.registry.ListRequest")
proto.RegisterType((*ListResponse)(nil), "go.micro.registry.ListResponse")
proto.RegisterType((*WatchRequest)(nil), "go.micro.registry.WatchRequest")
}
func init() {
proto.RegisterFile("micro/go-micro/registry/proto/registry.proto", fileDescriptor_f287a6b809166ad2)
}
var fileDescriptor_f287a6b809166ad2 = []byte{
// 577 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x6d, 0x8b, 0xd3, 0x4c,
0x14, 0x6d, 0x92, 0xbe, 0xde, 0x6e, 0x9f, 0x47, 0x2f, 0xa2, 0x31, 0xbe, 0x95, 0x80, 0x52, 0xc1,
0xcd, 0x2e, 0x75, 0x11, 0x5f, 0x3e, 0x09, 0x5b, 0x17, 0x64, 0x57, 0x70, 0x04, 0xfd, 0x1c, 0x9b,
0x4b, 0x0d, 0x6e, 0x5e, 0x9c, 0x99, 0x16, 0xfa, 0x1f, 0x04, 0xff, 0x84, 0x3f, 0xc5, 0x1f, 0x26,
0x99, 0xcc, 0x34, 0x5d, 0x36, 0xa9, 0x1f, 0x56, 0xbf, 0xcd, 0xcd, 0x9c, 0x73, 0xe6, 0x9e, 0x73,
0x67, 0x5a, 0x78, 0x92, 0xc4, 0x73, 0x9e, 0x1d, 0x2c, 0xb2, 0xfd, 0x72, 0xc1, 0x69, 0x11, 0x0b,
0xc9, 0xd7, 0x07, 0x39, 0xcf, 0x64, 0x55, 0x06, 0xaa, 0xc4, 0xeb, 0x8b, 0x2c, 0x50, 0xb8, 0xc0,
0x6c, 0xf8, 0x3f, 0x6d, 0xe8, 0x7d, 0x20, 0xbe, 0x8a, 0xe7, 0x84, 0x08, 0xed, 0x34, 0x4c, 0xc8,
0xb5, 0xc6, 0xd6, 0x64, 0xc0, 0xd4, 0x1a, 0x5d, 0xe8, 0xad, 0x88, 0x8b, 0x38, 0x4b, 0x5d, 0x5b,
0x7d, 0x36, 0x25, 0x1e, 0x43, 0x3f, 0x21, 0x19, 0x46, 0xa1, 0x0c, 0x5d, 0x67, 0xec, 0x4c, 0x86,
0xd3, 0x49, 0x70, 0x49, 0x3f, 0xd0, 0xda, 0xc1, 0x99, 0x86, 0xce, 0x52, 0xc9, 0xd7, 0x6c, 0xc3,
0xc4, 0x17, 0x30, 0xa0, 0x34, 0xca, 0xb3, 0x38, 0x95, 0xc2, 0x6d, 0x2b, 0x99, 0x3b, 0x35, 0x32,
0x33, 0x8d, 0x61, 0x15, 0x1a, 0xf7, 0xa1, 0x93, 0x66, 0x11, 0x09, 0xb7, 0xa3, 0x68, 0xb7, 0x6a,
0x68, 0xef, 0xb2, 0x88, 0x58, 0x89, 0xf2, 0x5e, 0xc1, 0xe8, 0x42, 0x13, 0x78, 0x0d, 0x9c, 0xaf,
0xb4, 0xd6, 0x6e, 0x8b, 0x25, 0xde, 0x80, 0xce, 0x2a, 0x3c, 0x5f, 0x92, 0xb6, 0x5a, 0x16, 0x2f,
0xed, 0xe7, 0x96, 0xff, 0xcb, 0x82, 0x76, 0x21, 0x86, 0xff, 0x81, 0x1d, 0x47, 0x9a, 0x63, 0xc7,
0x51, 0x91, 0x4f, 0x18, 0x45, 0x9c, 0x84, 0x30, 0xf9, 0xe8, 0xb2, 0x48, 0x33, 0xcf, 0xb8, 0x74,
0x9d, 0xb1, 0x35, 0x71, 0x98, 0x5a, 0xe3, 0xeb, 0xad, 0xcc, 0x4a, 0xb3, 0x0f, 0x1b, 0xba, 0x6e,
0x0a, 0xec, 0x6a, 0x36, 0xbe, 0xdb, 0xd0, 0x37, 0x51, 0xd6, 0x8e, 0x7b, 0x0a, 0x3d, 0x4e, 0xdf,
0x96, 0x24, 0xa4, 0x22, 0x0f, 0xa7, 0x6e, 0x4d, 0x7f, 0x1f, 0x0b, 0x3d, 0x66, 0x80, 0x78, 0x04,
0x7d, 0x4e, 0x22, 0xcf, 0x52, 0x41, 0xca, 0xec, 0x2e, 0xd2, 0x06, 0x89, 0xb3, 0x4b, 0x51, 0x3c,
0xde, 0x31, 0xf7, 0x7f, 0x13, 0x47, 0x08, 0x1d, 0xd5, 0x56, 0x6d, 0x14, 0x08, 0x6d, 0xb9, 0xce,
0x0d, 0x4b, 0xad, 0xf1, 0x10, 0xba, 0x8a, 0x2d, 0xf4, 0x8d, 0x6f, 0x36, 0xaa, 0x71, 0xbe, 0x84,
0x2e, 0x23, 0xb1, 0x3c, 0x97, 0x78, 0x13, 0xba, 0xe1, 0x5c, 0x16, 0x0f, 0xa9, 0x3c, 0x45, 0x57,
0x78, 0x04, 0x3d, 0x51, 0x3e, 0x12, 0x1d, 0xb9, 0xd7, 0xfc, 0x8c, 0x98, 0x81, 0xe2, 0x5d, 0x18,
0xc8, 0x38, 0x21, 0x21, 0xc3, 0x24, 0xd7, 0x57, 0xac, 0xfa, 0xe0, 0xff, 0x0f, 0xa3, 0x59, 0x92,
0xcb, 0x35, 0xd3, 0x69, 0xfb, 0x8f, 0x00, 0x4e, 0x48, 0x32, 0x3d, 0x31, 0xb7, 0x3a, 0xb2, 0xec,
0xc5, 0x94, 0xfe, 0x0c, 0x86, 0x0a, 0xa7, 0x87, 0xf4, 0x0c, 0xfa, 0x7a, 0x47, 0xb8, 0x96, 0x72,
0xbc, 0xab, 0xb9, 0x0d, 0xd6, 0x1f, 0xc1, 0xf0, 0x34, 0x16, 0xe6, 0x3c, 0xff, 0x0d, 0xec, 0x95,
0xe5, 0x15, 0x65, 0x27, 0xb0, 0xf7, 0x29, 0x94, 0xf3, 0x2f, 0x7f, 0xf4, 0x31, 0xfd, 0xe1, 0x40,
0x9f, 0x69, 0x21, 0x3c, 0x53, 0xe6, 0xcd, 0xaf, 0xdc, 0xbd, 0x9a, 0xa3, 0xaa, 0x6c, 0xbc, 0xfb,
0x4d, 0xdb, 0x3a, 0xc9, 0x16, 0xbe, 0x35, 0xd2, 0xc4, 0x71, 0x47, 0xdf, 0xde, 0xb8, 0xee, 0x3e,
0x5f, 0x98, 0x4a, 0x0b, 0x4f, 0x01, 0x8e, 0x89, 0xff, 0x2d, 0xb5, 0xf7, 0x65, 0xce, 0x9a, 0x22,
0xb0, 0xce, 0xcb, 0xd6, 0x5c, 0xbc, 0x07, 0x8d, 0xfb, 0x1b, 0xc9, 0x13, 0xe8, 0xa8, 0xc8, 0xb1,
0x0e, 0xbb, 0x3d, 0x0c, 0xef, 0x76, 0x0d, 0xa0, 0xbc, 0xfa, 0x7e, 0xeb, 0xd0, 0xfa, 0xdc, 0x55,
0x7f, 0x41, 0x4f, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x6b, 0x0b, 0x12, 0xd6, 0xb2, 0x06, 0x00,
0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// RegistryClient is the client API for Registry service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type RegistryClient interface {
GetService(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error)
Register(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error)
Deregister(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error)
ListServices(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Registry_WatchClient, error)
}
type registryClient struct {
cc *grpc.ClientConn
}
func NewRegistryClient(cc *grpc.ClientConn) RegistryClient {
return &registryClient{cc}
}
func (c *registryClient) GetService(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
out := new(GetResponse)
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/GetService", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryClient) Register(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) {
out := new(EmptyResponse)
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/Register", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryClient) Deregister(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) {
out := new(EmptyResponse)
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/Deregister", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryClient) ListServices(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
out := new(ListResponse)
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/ListServices", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryClient) Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Registry_WatchClient, error) {
stream, err := c.cc.NewStream(ctx, &_Registry_serviceDesc.Streams[0], "/go.micro.registry.Registry/Watch", opts...)
if err != nil {
return nil, err
}
x := &registryWatchClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Registry_WatchClient interface {
Recv() (*Result, error)
grpc.ClientStream
}
type registryWatchClient struct {
grpc.ClientStream
}
func (x *registryWatchClient) Recv() (*Result, error) {
m := new(Result)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// RegistryServer is the server API for Registry service.
type RegistryServer interface {
GetService(context.Context, *GetRequest) (*GetResponse, error)
Register(context.Context, *Service) (*EmptyResponse, error)
Deregister(context.Context, *Service) (*EmptyResponse, error)
ListServices(context.Context, *ListRequest) (*ListResponse, error)
Watch(*WatchRequest, Registry_WatchServer) error
}
func RegisterRegistryServer(s *grpc.Server, srv RegistryServer) {
s.RegisterService(&_Registry_serviceDesc, srv)
}
func _Registry_GetService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RegistryServer).GetService(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.registry.Registry/GetService",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RegistryServer).GetService(ctx, req.(*GetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Registry_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Service)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RegistryServer).Register(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.registry.Registry/Register",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RegistryServer).Register(ctx, req.(*Service))
}
return interceptor(ctx, in, info, handler)
}
func _Registry_Deregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Service)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RegistryServer).Deregister(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.registry.Registry/Deregister",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RegistryServer).Deregister(ctx, req.(*Service))
}
return interceptor(ctx, in, info, handler)
}
func _Registry_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RegistryServer).ListServices(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.registry.Registry/ListServices",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RegistryServer).ListServices(ctx, req.(*ListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Registry_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(WatchRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(RegistryServer).Watch(m, &registryWatchServer{stream})
}
type Registry_WatchServer interface {
Send(*Result) error
grpc.ServerStream
}
type registryWatchServer struct {
grpc.ServerStream
}
func (x *registryWatchServer) Send(m *Result) error {
return x.ServerStream.SendMsg(m)
}
var _Registry_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.registry.Registry",
HandlerType: (*RegistryServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetService",
Handler: _Registry_GetService_Handler,
},
{
MethodName: "Register",
Handler: _Registry_Register_Handler,
},
{
MethodName: "Deregister",
Handler: _Registry_Deregister_Handler,
},
{
MethodName: "ListServices",
Handler: _Registry_ListServices_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Watch",
Handler: _Registry_Watch_Handler,
ServerStreams: true,
},
},
Metadata: "micro/go-micro/registry/proto/registry.proto",
}

View File

@@ -0,0 +1,73 @@
syntax = "proto3";
package go.micro.registry;
service Registry {
rpc GetService(GetRequest) returns (GetResponse) {};
rpc Register(Service) returns (EmptyResponse) {};
rpc Deregister(Service) returns (EmptyResponse) {};
rpc ListServices(ListRequest) returns (ListResponse) {};
rpc Watch(WatchRequest) returns (stream Result) {};
}
// Service represents a go-micro service
message Service {
string name = 1;
string version = 2;
map<string,string> metadata = 3;
repeated Endpoint endpoints = 4;
repeated Node nodes = 5;
}
// Node represents the node the service is on
message Node {
string id = 1;
string address = 2;
int64 port = 3;
map<string,string> metadata = 4;
}
// Endpoint is a endpoint provided by a service
message Endpoint {
string name = 1;
Value request = 2;
Value response = 3;
map<string, string> metadata = 4;
}
// Value is an opaque value for a request or response
message Value {
string name = 1;
string type = 2;
repeated Value values = 3;
}
// Result is returns by the watcher
message Result {
string action = 1; // create, update, delete
Service service = 2;
int64 timestamp = 3; // unix timestamp
}
message EmptyResponse {}
message GetRequest {
string service = 1;
}
message GetResponse {
repeated Service services = 1;
}
message ListRequest {
// TODO: filtering
}
message ListResponse {
repeated Service services = 1;
}
message WatchRequest {
// service is optional
string service = 1;
}

155
registry/service/service.go Normal file
View File

@@ -0,0 +1,155 @@
// Package service uses the registry service
package service
import (
"context"
"time"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/registry"
pb "github.com/micro/go-micro/registry/proto"
)
var (
// The default service name
DefaultService = "go.micro.service"
)
type serviceRegistry struct {
opts registry.Options
// name of the registry
name string
// address
address []string
// client to call registry
client pb.RegistryService
}
func (s *serviceRegistry) callOpts() []client.CallOption {
var opts []client.CallOption
// set registry address
if len(s.address) > 0 {
opts = append(opts, client.WithAddress(s.address...))
}
// set timeout
if s.opts.Timeout > time.Duration(0) {
opts = append(opts, client.WithRequestTimeout(s.opts.Timeout))
}
return opts
}
func (s *serviceRegistry) Init(opts ...registry.Option) error {
for _, o := range opts {
o(&s.opts)
}
return nil
}
func (s *serviceRegistry) Options() registry.Options {
return s.opts
}
func (s *serviceRegistry) Register(srv *registry.Service, opts ...registry.RegisterOption) error {
var options registry.RegisterOptions
for _, o := range opts {
o(&options)
}
// register the service
_, err := s.client.Register(context.TODO(), ToProto(srv), s.callOpts()...)
if err != nil {
return err
}
return nil
}
func (s *serviceRegistry) Deregister(srv *registry.Service) error {
// deregister the service
_, err := s.client.Deregister(context.TODO(), ToProto(srv), s.callOpts()...)
if err != nil {
return err
}
return nil
}
func (s *serviceRegistry) GetService(name string) ([]*registry.Service, error) {
rsp, err := s.client.GetService(context.TODO(), &pb.GetRequest{
Service: name,
}, s.callOpts()...)
if err != nil {
return nil, err
}
var services []*registry.Service
for _, service := range rsp.Services {
services = append(services, ToService(service))
}
return services, nil
}
func (s *serviceRegistry) ListServices() ([]*registry.Service, error) {
rsp, err := s.client.ListServices(context.TODO(), &pb.ListRequest{}, s.callOpts()...)
if err != nil {
return nil, err
}
var services []*registry.Service
for _, service := range rsp.Services {
services = append(services, ToService(service))
}
return services, nil
}
func (s *serviceRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
var options registry.WatchOptions
for _, o := range opts {
o(&options)
}
stream, err := s.client.Watch(context.TODO(), &pb.WatchRequest{
Service: options.Service,
}, s.callOpts()...)
if err != nil {
return nil, err
}
return newWatcher(stream), nil
}
func (s *serviceRegistry) String() string {
return s.name
}
// NewRegistry returns a new registry service client
func NewRegistry(opts ...registry.Option) registry.Registry {
var options registry.Options
for _, o := range opts {
o(&options)
}
// use mdns to find the service registry
mReg := registry.NewRegistry()
// create new client with mdns
cli := client.NewClient(
client.Registry(mReg),
)
// service name
// TODO: accept option
name := DefaultService
return &serviceRegistry{
opts: options,
name: name,
address: options.Addrs,
client: pb.NewRegistryService(name, cli),
}
}

133
registry/service/util.go Normal file
View File

@@ -0,0 +1,133 @@
package service
import (
"github.com/micro/go-micro/registry"
pb "github.com/micro/go-micro/registry/proto"
)
func values(v []*registry.Value) []*pb.Value {
if len(v) == 0 {
return []*pb.Value{}
}
var vs []*pb.Value
for _, vi := range v {
vs = append(vs, &pb.Value{
Name: vi.Name,
Type: vi.Type,
Values: values(vi.Values),
})
}
return vs
}
func toValues(v []*pb.Value) []*registry.Value {
if len(v) == 0 {
return []*registry.Value{}
}
var vs []*registry.Value
for _, vi := range v {
vs = append(vs, &registry.Value{
Name: vi.Name,
Type: vi.Type,
Values: toValues(vi.Values),
})
}
return vs
}
func ToProto(s *registry.Service) *pb.Service {
var endpoints []*pb.Endpoint
for _, ep := range s.Endpoints {
var request, response *pb.Value
if ep.Request != nil {
request = &pb.Value{
Name: ep.Request.Name,
Type: ep.Request.Type,
Values: values(ep.Request.Values),
}
}
if ep.Response != nil {
response = &pb.Value{
Name: ep.Response.Name,
Type: ep.Response.Type,
Values: values(ep.Response.Values),
}
}
endpoints = append(endpoints, &pb.Endpoint{
Name: ep.Name,
Request: request,
Response: response,
Metadata: ep.Metadata,
})
}
var nodes []*pb.Node
for _, node := range s.Nodes {
nodes = append(nodes, &pb.Node{
Id: node.Id,
Address: node.Address,
Metadata: node.Metadata,
})
}
return &pb.Service{
Name: s.Name,
Version: s.Version,
Metadata: s.Metadata,
Endpoints: endpoints,
Nodes: nodes,
}
}
func ToService(s *pb.Service) *registry.Service {
var endpoints []*registry.Endpoint
for _, ep := range s.Endpoints {
var request, response *registry.Value
if ep.Request != nil {
request = &registry.Value{
Name: ep.Request.Name,
Type: ep.Request.Type,
Values: toValues(ep.Request.Values),
}
}
if ep.Response != nil {
response = &registry.Value{
Name: ep.Response.Name,
Type: ep.Response.Type,
Values: toValues(ep.Response.Values),
}
}
endpoints = append(endpoints, &registry.Endpoint{
Name: ep.Name,
Request: request,
Response: response,
Metadata: ep.Metadata,
})
}
var nodes []*registry.Node
for _, node := range s.Nodes {
nodes = append(nodes, &registry.Node{
Id: node.Id,
Address: node.Address,
Metadata: node.Metadata,
})
}
return &registry.Service{
Name: s.Name,
Version: s.Version,
Metadata: s.Metadata,
Endpoints: endpoints,
Nodes: nodes,
}
}

View File

@@ -0,0 +1,49 @@
package service
import (
"github.com/micro/go-micro/registry"
pb "github.com/micro/go-micro/registry/proto"
)
type serviceWatcher struct {
stream pb.Registry_WatchService
closed chan bool
}
func (s *serviceWatcher) Next() (*registry.Result, error) {
for {
// check if closed
select {
case <-s.closed:
return nil, registry.ErrWatcherStopped
default:
}
r, err := s.stream.Recv()
if err != nil {
return nil, err
}
return &registry.Result{
Action: r.Action,
Service: ToService(r.Service),
}, nil
}
}
func (s *serviceWatcher) Stop() {
select {
case <-s.closed:
return
default:
close(s.closed)
s.stream.Close()
}
}
func newWatcher(stream pb.Registry_WatchService) registry.Watcher {
return &serviceWatcher{
stream: stream,
closed: make(chan bool),
}
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/google/uuid"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/util/log"
)
const (
@@ -43,7 +44,7 @@ var (
// router implements default router
type router struct {
sync.RWMutex
opts Options
options Options
status Status
table *table
exit chan struct{}
@@ -70,7 +71,7 @@ func newRouter(opts ...Option) Router {
status := Status{Code: Stopped, Error: nil}
return &router{
opts: options,
options: options,
status: status,
table: newTable(),
advertWg: &sync.WaitGroup{},
@@ -85,7 +86,7 @@ func (r *router) Init(opts ...Option) error {
defer r.Unlock()
for _, o := range opts {
o(&r.opts)
o(&r.options)
}
return nil
@@ -94,10 +95,10 @@ func (r *router) Init(opts ...Option) error {
// Options returns router options
func (r *router) Options() Options {
r.Lock()
opts := r.opts
options := r.options
r.Unlock()
return opts
return options
}
// Table returns routing table
@@ -120,6 +121,9 @@ func (r *router) manageRoute(route Route, action string) error {
if err := r.table.Delete(route); err != nil && err != ErrRouteNotFound {
return fmt.Errorf("failed deleting route for service %s: %s", route.Service, err)
}
case "solicit":
// nothing to do here
return nil
default:
return fmt.Errorf("failed to manage route for service %s. Unknown action: %s", route.Service, action)
}
@@ -139,7 +143,8 @@ func (r *router) manageServiceRoutes(service *registry.Service, action string) e
Service: service.Name,
Address: node.Address,
Gateway: "",
Network: r.opts.Network,
Network: r.options.Network,
Router: r.options.Id,
Link: DefaultLink,
Metric: DefaultLocalMetric,
}
@@ -181,10 +186,6 @@ func (r *router) manageRegistryRoutes(reg registry.Registry, action string) erro
// watchRegistry watches registry and updates routing table based on the received events.
// It returns error if either the registry watcher fails with error or if the routing table update fails.
func (r *router) watchRegistry(w registry.Watcher) error {
// wait in the background for the router to stop
// when the router stops, stop the watcher and exit
r.wg.Add(1)
exit := make(chan bool)
defer func() {
@@ -193,6 +194,9 @@ func (r *router) watchRegistry(w registry.Watcher) error {
r.wg.Done()
}()
// wait in the background for the router to stop
// when the router stops, stop the watcher and exit
r.wg.Add(1)
go func() {
defer w.Stop()
@@ -226,9 +230,6 @@ func (r *router) watchRegistry(w registry.Watcher) error {
// watchTable watches routing table entries and either adds or deletes locally registered service to/from network registry
// It returns error if the locally registered services either fails to be added/deleted to/from network registry.
func (r *router) watchTable(w Watcher) error {
// wait in the background for the router to stop
// when the router stops, stop the watcher and exit
r.wg.Add(1)
exit := make(chan bool)
defer func() {
@@ -237,6 +238,9 @@ func (r *router) watchTable(w Watcher) error {
r.wg.Done()
}()
// wait in the background for the router to stop
// when the router stops, stop the watcher and exit
r.wg.Add(1)
go func() {
defer w.Stop()
@@ -279,13 +283,14 @@ func (r *router) publishAdvert(advType AdvertType, events []*Event) {
defer r.advertWg.Done()
a := &Advert{
Id: r.opts.Id,
Id: r.options.Id,
Type: advType,
TTL: DefaultAdvertTTL,
Timestamp: time.Now(),
Events: events,
}
log.Debugf("Router publishing advert; %+v", a)
r.RLock()
for _, sub := range r.subscribers {
// check the exit chan first
@@ -471,12 +476,38 @@ func (r *router) advertiseEvents() error {
}
}
// close closes exit channels
func (r *router) close() {
// notify all goroutines to finish
close(r.exit)
// drain the advertise channel only if advertising
if r.status.Code == Advertising {
// drain the event channel
for range r.eventChan {
}
// close advert subscribers
for id, sub := range r.subscribers {
// close the channel
close(sub)
// delete the subscriber
delete(r.subscribers, id)
}
}
// mark the router as Stopped and set its Error to nil
r.status = Status{Code: Stopped, Error: nil}
}
// watchErrors watches router errors and takes appropriate actions
func (r *router) watchErrors() {
var err error
select {
case <-r.exit:
return
case err = <-r.errChan:
}
@@ -484,22 +515,12 @@ func (r *router) watchErrors() {
defer r.Unlock()
// if the router is not stopped, stop it
if r.status.Code != Stopped {
// notify all goroutines to finish
close(r.exit)
// drain the advertise channel only if the router is advertising
if r.status.Code == Advertising {
// drain the event channel
for range r.eventChan {
}
// close all the channels
r.close()
// set the status error
if err != nil {
r.status.Error = err
}
// mark the router as Stopped and set its Error to nil
r.status = Status{Code: Stopped, Error: nil}
}
if err != nil {
r.status = Status{Code: Error, Error: err}
}
}
@@ -508,21 +529,28 @@ func (r *router) Start() error {
r.Lock()
defer r.Unlock()
// only start if we're stopped
if r.status.Code != Stopped {
return nil
}
// add all local service routes into the routing table
if err := r.manageRegistryRoutes(r.opts.Registry, "create"); err != nil {
if err := r.manageRegistryRoutes(r.options.Registry, "create"); err != nil {
e := fmt.Errorf("failed adding registry routes: %s", err)
r.status = Status{Code: Error, Error: e}
return e
}
// add default gateway into routing table
if r.opts.Gateway != "" {
if r.options.Gateway != "" {
// note, the only non-default value is the gateway
route := Route{
Service: "*",
Address: "*",
Gateway: r.opts.Gateway,
Gateway: r.options.Gateway,
Network: "*",
Router: r.options.Id,
Link: DefaultLink,
Metric: DefaultLocalMetric,
}
if err := r.table.Create(route); err != nil {
@@ -537,7 +565,7 @@ func (r *router) Start() error {
r.exit = make(chan struct{})
// registry watcher
regWatcher, err := r.opts.Registry.Watch()
regWatcher, err := r.options.Registry.Watch()
if err != nil {
e := fmt.Errorf("failed creating registry watcher: %v", err)
r.status = Status{Code: Error, Error: e}
@@ -575,25 +603,14 @@ func (r *router) Advertise() (<-chan *Advert, error) {
switch r.status.Code {
case Advertising:
advertChan := make(chan *Advert)
advertChan := make(chan *Advert, 128)
r.subscribers[uuid.New().String()] = advertChan
return advertChan, nil
case Running:
// list routing table routes to announce
routes, err := r.table.List()
// list all the routes and pack them into even slice to advertise
events, err := r.flushRouteEvents(Create)
if err != nil {
return nil, fmt.Errorf("failed listing routes: %s", err)
}
// collect all the added routes before we attempt to add default gateway
events := make([]*Event, len(routes))
for i, route := range routes {
event := &Event{
Type: Create,
Timestamp: time.Now(),
Route: route,
}
events[i] = event
return nil, fmt.Errorf("failed to flush routes: %s", err)
}
// create event channels
@@ -626,7 +643,7 @@ func (r *router) Advertise() (<-chan *Advert, error) {
r.status = Status{Code: Advertising, Error: nil}
// create advert channel
advertChan := make(chan *Advert)
advertChan := make(chan *Advert, 128)
r.subscribers[uuid.New().String()] = advertChan
return advertChan, nil
@@ -649,6 +666,10 @@ func (r *router) Process(a *Advert) error {
})
for _, event := range events {
// skip if the router is the origin of this route
if event.Route.Router == r.options.Id {
continue
}
// create a copy of the route
route := event.Route
action := event.Type
@@ -660,10 +681,49 @@ func (r *router) Process(a *Advert) error {
return nil
}
// flushRouteEvents returns a slice of events, one per each route in the routing table
func (r *router) flushRouteEvents(evType EventType) ([]*Event, error) {
// list all routes
routes, err := r.table.List()
if err != nil {
return nil, fmt.Errorf("failed listing routes: %s", err)
}
// build a list of events to advertise
events := make([]*Event, len(routes))
for i, route := range routes {
event := &Event{
Type: evType,
Timestamp: time.Now(),
Route: route,
}
events[i] = event
}
return events, nil
}
// Solicit advertises all of its routes to the network
// It returns error if the router fails to list the routes
func (r *router) Solicit() error {
events, err := r.flushRouteEvents(Update)
if err != nil {
return fmt.Errorf("failed solicit routes: %s", err)
}
// advertise the routes
r.advertWg.Add(1)
go r.publishAdvert(RouteUpdate, events)
return nil
}
// Lookup routes in the routing table
func (r *router) Lookup(q Query) ([]Route, error) {
return r.table.Query(q)
}
// Watch routes
func (r *router) Watch(opts ...WatchOption) (Watcher, error) {
return r.table.Watch(opts...)
}
@@ -682,31 +742,15 @@ func (r *router) Status() Status {
// Stop stops the router
func (r *router) Stop() error {
r.Lock()
// only close the channel if the router is running and/or advertising
if r.status.Code == Running || r.status.Code == Advertising {
// notify all goroutines to finish
close(r.exit)
defer r.Unlock()
// drain the advertise channel only if advertising
if r.status.Code == Advertising {
// drain the event channel
for range r.eventChan {
}
}
// close advert subscribers
for id, sub := range r.subscribers {
// close the channel
close(sub)
// delete the subscriber
delete(r.subscribers, id)
}
// mark the router as Stopped and set its Error to nil
r.status = Status{Code: Stopped, Error: nil}
switch r.status.Code {
case Stopped, Error:
return r.status.Error
case Running, Advertising:
// close all the channels
r.close()
}
r.Unlock()
// wait for all goroutines to finish
r.wg.Wait()
@@ -716,5 +760,5 @@ func (r *router) Stop() error {
// String prints debugging information about router
func (r *router) String() string {
return "default"
return "memory"
}

View File

@@ -33,6 +33,7 @@ func (r *Router) Lookup(ctx context.Context, req *pb.LookupRequest, resp *pb.Loo
Address: route.Address,
Gateway: route.Gateway,
Network: route.Network,
Router: route.Router,
Link: route.Link,
Metric: int64(route.Metric),
}
@@ -58,6 +59,7 @@ func (r *Router) Advertise(ctx context.Context, req *pb.Request, stream pb.Route
Address: event.Route.Address,
Gateway: event.Route.Gateway,
Network: event.Route.Network,
Router: event.Route.Router,
Link: event.Route.Link,
Metric: int64(event.Route.Metric),
}
@@ -97,6 +99,7 @@ func (r *Router) Process(ctx context.Context, req *pb.Advert, rsp *pb.ProcessRes
Address: event.Route.Address,
Gateway: event.Route.Gateway,
Network: event.Route.Network,
Router: event.Route.Router,
Link: event.Route.Link,
Metric: int(event.Route.Metric),
}
@@ -161,6 +164,7 @@ func (r *Router) Watch(ctx context.Context, req *pb.WatchRequest, stream pb.Rout
Address: event.Route.Address,
Gateway: event.Route.Gateway,
Network: event.Route.Network,
Router: event.Route.Router,
Link: event.Route.Link,
Metric: int64(event.Route.Metric),
}

View File

@@ -18,6 +18,7 @@ func (t *Table) Create(ctx context.Context, route *pb.Route, resp *pb.CreateResp
Address: route.Address,
Gateway: route.Gateway,
Network: route.Network,
Router: route.Router,
Link: route.Link,
Metric: int(route.Metric),
})
@@ -34,6 +35,7 @@ func (t *Table) Update(ctx context.Context, route *pb.Route, resp *pb.UpdateResp
Address: route.Address,
Gateway: route.Gateway,
Network: route.Network,
Router: route.Router,
Link: route.Link,
Metric: int(route.Metric),
})
@@ -50,6 +52,7 @@ func (t *Table) Delete(ctx context.Context, route *pb.Route, resp *pb.DeleteResp
Address: route.Address,
Gateway: route.Gateway,
Network: route.Network,
Router: route.Router,
Link: route.Link,
Metric: int(route.Metric),
})
@@ -74,6 +77,7 @@ func (t *Table) List(ctx context.Context, req *pb.Request, resp *pb.ListResponse
Address: route.Address,
Gateway: route.Gateway,
Network: route.Network,
Router: route.Router,
Link: route.Link,
Metric: int64(route.Metric),
}
@@ -102,6 +106,7 @@ func (t *Table) Query(ctx context.Context, req *pb.QueryRequest, resp *pb.QueryR
Address: route.Address,
Gateway: route.Gateway,
Network: route.Network,
Router: route.Router,
Link: route.Link,
Metric: int64(route.Metric),
}

View File

@@ -672,10 +672,12 @@ type Route struct {
Gateway string `protobuf:"bytes,3,opt,name=gateway,proto3" json:"gateway,omitempty"`
// the network for this destination
Network string `protobuf:"bytes,4,opt,name=network,proto3" json:"network,omitempty"`
// router if the router id
Router string `protobuf:"bytes,5,opt,name=router,proto3" json:"router,omitempty"`
// the network link
Link string `protobuf:"bytes,5,opt,name=link,proto3" json:"link,omitempty"`
Link string `protobuf:"bytes,6,opt,name=link,proto3" json:"link,omitempty"`
// the metric / score of this route
Metric int64 `protobuf:"varint,6,opt,name=metric,proto3" json:"metric,omitempty"`
Metric int64 `protobuf:"varint,7,opt,name=metric,proto3" json:"metric,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -734,6 +736,13 @@ func (m *Route) GetNetwork() string {
return ""
}
func (m *Route) GetRouter() string {
if m != nil {
return m.Router
}
return ""
}
func (m *Route) GetLink() string {
if m != nil {
return m.Link
@@ -861,51 +870,51 @@ func init() {
}
var fileDescriptor_6a36eee0b1adf739 = []byte{
// 689 bytes of a gzipped FileDescriptorProto
// 699 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x4e, 0xdb, 0x40,
0x10, 0xb6, 0x93, 0xd8, 0x28, 0xd3, 0x10, 0xdc, 0x51, 0x05, 0x56, 0x5a, 0x20, 0xf2, 0x29, 0x42,
0xd4, 0xa9, 0xd2, 0x6b, 0xff, 0x02, 0xa5, 0xaa, 0x54, 0x0e, 0xad, 0x0b, 0xea, 0xd9, 0xd8, 0x23,
0x6a, 0x91, 0xd8, 0x66, 0x77, 0x03, 0xca, 0xb9, 0x8f, 0xd1, 0x27, 0xe8, 0x73, 0xf5, 0xda, 0x87,
0xa8, 0xbc, 0xbb, 0x0e, 0x21, 0xc6, 0x48, 0x70, 0xf2, 0xce, 0xdf, 0x37, 0xff, 0x63, 0x18, 0x4c,
0x93, 0x88, 0x65, 0xc3, 0xf3, 0xec, 0xa5, 0x7a, 0xb0, 0x6c, 0x26, 0x88, 0x0d, 0x73, 0x96, 0x89,
0x92, 0xf0, 0x25, 0x81, 0x1b, 0xe7, 0x99, 0x2f, 0x75, 0x7c, 0xc5, 0xf6, 0xda, 0xb0, 0x16, 0xd0,
0xe5, 0x8c, 0xb8, 0xf0, 0xde, 0x41, 0xe7, 0x38, 0xe1, 0x22, 0x20, 0x9e, 0x67, 0x29, 0x27, 0xf4,
0xc1, 0x96, 0x4a, 0xdc, 0x35, 0xfb, 0xcd, 0xc1, 0x93, 0xd1, 0xa6, 0xbf, 0x62, 0xec, 0x07, 0xc5,
0x27, 0xd0, 0x5a, 0xde, 0x5b, 0x58, 0x3f, 0xce, 0xb2, 0x8b, 0x59, 0xae, 0x01, 0x71, 0x1f, 0xac,
0xcb, 0x19, 0xb1, 0xb9, 0x6b, 0xf6, 0xcd, 0x3b, 0xed, 0xbf, 0x15, 0xd2, 0x40, 0x29, 0x79, 0x1f,
0xa0, 0x5b, 0x9a, 0x3f, 0x32, 0x80, 0x37, 0xd0, 0x51, 0x88, 0x8f, 0xf2, 0xff, 0x1e, 0xd6, 0xb5,
0xf5, 0x23, 0xdd, 0x77, 0xa1, 0xf3, 0x23, 0x14, 0xd1, 0xcf, 0xb2, 0x9e, 0x7f, 0x4c, 0xb0, 0xc7,
0xf1, 0x15, 0x31, 0x81, 0x5d, 0x68, 0x24, 0xb1, 0x0c, 0xa3, 0x1d, 0x34, 0x92, 0x18, 0x87, 0xd0,
0x12, 0xf3, 0x9c, 0xdc, 0x46, 0xdf, 0x1c, 0x74, 0x47, 0xcf, 0x2b, 0xc0, 0xca, 0xec, 0x64, 0x9e,
0x53, 0x20, 0x15, 0xf1, 0x05, 0xb4, 0x45, 0x32, 0x25, 0x2e, 0xc2, 0x69, 0xee, 0x36, 0xfb, 0xe6,
0xa0, 0x19, 0xdc, 0x30, 0xd0, 0x81, 0xa6, 0x10, 0x13, 0xb7, 0x25, 0xf9, 0xc5, 0xb3, 0x88, 0x9d,
0xae, 0x28, 0x15, 0xdc, 0xb5, 0x6a, 0x62, 0x3f, 0x2a, 0xc4, 0x81, 0xd6, 0xf2, 0x9e, 0xc2, 0xc6,
0x57, 0x96, 0x45, 0xc4, 0x79, 0x99, 0xbe, 0xe7, 0x40, 0xf7, 0x90, 0x51, 0x28, 0x68, 0x99, 0xf3,
0x91, 0x26, 0x74, 0x9b, 0x73, 0x9a, 0xc7, 0xcb, 0x3a, 0xbf, 0x4c, 0xb0, 0x24, 0x34, 0xfa, 0x3a,
0x47, 0x53, 0xe6, 0xd8, 0xbb, 0x3b, 0x80, 0xba, 0x14, 0x1b, 0xab, 0x29, 0xee, 0x83, 0x25, 0xed,
0x64, 0xf2, 0xf5, 0xbd, 0x50, 0x4a, 0xde, 0x29, 0x58, 0xb2, 0x97, 0xe8, 0xc2, 0x1a, 0x27, 0x76,
0x95, 0x44, 0xa4, 0xab, 0x5f, 0x92, 0x85, 0xe4, 0x3c, 0x14, 0x74, 0x1d, 0xce, 0xa5, 0xb3, 0x76,
0x50, 0x92, 0x85, 0x24, 0x25, 0x71, 0x9d, 0xb1, 0x0b, 0xe9, 0xac, 0x1d, 0x94, 0xa4, 0xf7, 0xdb,
0x04, 0x4b, 0xfa, 0xb9, 0x1f, 0x37, 0x8c, 0x63, 0x46, 0x9c, 0x97, 0xb8, 0x9a, 0x5c, 0xf6, 0xd8,
0xac, 0xf5, 0xd8, 0xba, 0xe5, 0x11, 0x11, 0x5a, 0x93, 0x24, 0xbd, 0x70, 0x2d, 0xc9, 0x96, 0x6f,
0xdc, 0x04, 0x7b, 0x4a, 0x82, 0x25, 0x91, 0x6b, 0xcb, 0x2a, 0x69, 0xca, 0x1b, 0x81, 0xfd, 0x5d,
0x84, 0x62, 0xc6, 0x0b, 0xab, 0x28, 0x8b, 0xcb, 0xd0, 0xe4, 0x1b, 0x9f, 0x81, 0x45, 0x8c, 0x65,
0x4c, 0x47, 0xa5, 0x08, 0x6f, 0x0c, 0x5d, 0x65, 0xb3, 0x98, 0xfa, 0x21, 0xd8, 0x5c, 0x72, 0xf4,
0xd6, 0x6c, 0x55, 0x2a, 0xad, 0x0d, 0xb4, 0xda, 0xde, 0x08, 0xe0, 0x66, 0x5c, 0x11, 0xa1, 0xab,
0xa8, 0x71, 0x9a, 0x66, 0xb3, 0x34, 0x22, 0xc7, 0x40, 0x07, 0x3a, 0x8a, 0xa7, 0x66, 0xc5, 0x31,
0xf7, 0x86, 0xd0, 0x5e, 0xb4, 0x1f, 0x01, 0x6c, 0x35, 0x68, 0x8e, 0x51, 0xbc, 0xd5, 0x88, 0x39,
0x66, 0xf1, 0xd6, 0x06, 0x8d, 0xd1, 0xbf, 0x06, 0xd8, 0xb2, 0xf2, 0x0c, 0xbf, 0x80, 0xad, 0xee,
0x04, 0xee, 0x54, 0x42, 0xbb, 0x75, 0x7f, 0x7a, 0xbb, 0xb5, 0x72, 0x3d, 0xac, 0x06, 0x1e, 0x80,
0x25, 0x77, 0x16, 0xb7, 0x2b, 0xba, 0xcb, 0xbb, 0xdc, 0xab, 0xd9, 0x1f, 0xcf, 0x78, 0x65, 0xe2,
0x01, 0xb4, 0x55, 0x7a, 0x09, 0x27, 0x74, 0xab, 0x83, 0xa9, 0x21, 0xb6, 0x6a, 0xb6, 0x5c, 0x62,
0x7c, 0x82, 0x35, 0xbd, 0x7f, 0x58, 0xa7, 0xd7, 0xeb, 0x57, 0x04, 0xab, 0x2b, 0x6b, 0xe0, 0xd1,
0x62, 0x06, 0xea, 0x03, 0xd9, 0xad, 0xeb, 0xe8, 0x02, 0x66, 0xf4, 0xb7, 0x01, 0xd6, 0x49, 0x78,
0x36, 0x21, 0x3c, 0x2c, 0x9b, 0x83, 0x35, 0x2b, 0x77, 0x07, 0xdc, 0xca, 0xd9, 0x30, 0x0a, 0x10,
0xd5, 0xd5, 0x07, 0x80, 0xac, 0x5c, 0x1a, 0x09, 0xa2, 0xc6, 0xe1, 0x01, 0x20, 0x2b, 0xc7, 0xc9,
0xc0, 0x31, 0xb4, 0x8a, 0x7f, 0xdc, 0x3d, 0xd5, 0xa9, 0x0e, 0xc2, 0xf2, 0x4f, 0xd1, 0x33, 0xf0,
0x73, 0x79, 0x5b, 0xb6, 0x6b, 0xfe, 0x27, 0x1a, 0x68, 0xa7, 0x4e, 0x5c, 0x22, 0x9d, 0xd9, 0xf2,
0x9f, 0xfc, 0xfa, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8c, 0xd0, 0xc0, 0x27, 0xbf, 0x07, 0x00,
0x00,
0x10, 0xb6, 0x9d, 0xd8, 0x91, 0xa7, 0xc1, 0xb8, 0xa3, 0x0a, 0xac, 0xb4, 0x40, 0xe4, 0x53, 0x84,
0xa8, 0x53, 0xa5, 0xd7, 0xfe, 0x05, 0x4a, 0x55, 0xa9, 0x1c, 0x5a, 0x17, 0xd4, 0xb3, 0xb1, 0x57,
0xd4, 0x22, 0xb1, 0xcd, 0xee, 0x06, 0x94, 0x73, 0x9f, 0xa6, 0xe7, 0x3e, 0x52, 0xaf, 0x7d, 0x88,
0xca, 0xbb, 0xeb, 0x10, 0x62, 0x8c, 0x44, 0x4e, 0xde, 0x99, 0xf9, 0xe6, 0x9b, 0x99, 0xdd, 0x99,
0x31, 0x0c, 0xa6, 0x69, 0x4c, 0xf3, 0xe1, 0x45, 0xfe, 0x52, 0x1e, 0x68, 0x3e, 0xe3, 0x84, 0x0e,
0x0b, 0x9a, 0xf3, 0x4a, 0x08, 0x84, 0x80, 0x9b, 0x17, 0x79, 0x20, 0x30, 0x81, 0x54, 0xfb, 0x36,
0x74, 0x42, 0x72, 0x35, 0x23, 0x8c, 0xfb, 0xef, 0xa0, 0x7b, 0x92, 0x32, 0x1e, 0x12, 0x56, 0xe4,
0x19, 0x23, 0x18, 0x80, 0x25, 0x40, 0xcc, 0xd3, 0xfb, 0xad, 0xc1, 0x93, 0xd1, 0x56, 0xb0, 0xe2,
0x1c, 0x84, 0xe5, 0x27, 0x54, 0x28, 0xff, 0x2d, 0x6c, 0x9c, 0xe4, 0xf9, 0xe5, 0xac, 0x50, 0x84,
0x78, 0x00, 0xe6, 0xd5, 0x8c, 0xd0, 0xb9, 0xa7, 0xf7, 0xf5, 0x7b, 0xfd, 0xbf, 0x95, 0xd6, 0x50,
0x82, 0xfc, 0x0f, 0xe0, 0x54, 0xee, 0x6b, 0x26, 0xf0, 0x06, 0xba, 0x92, 0x71, 0xad, 0xf8, 0xef,
0x61, 0x43, 0x79, 0xaf, 0x19, 0xde, 0x81, 0xee, 0x8f, 0x88, 0xc7, 0x3f, 0xab, 0xfb, 0xfc, 0xad,
0x83, 0x35, 0x4e, 0xae, 0x09, 0xe5, 0xe8, 0x80, 0x91, 0x26, 0x22, 0x0d, 0x3b, 0x34, 0xd2, 0x04,
0x87, 0xd0, 0xe6, 0xf3, 0x82, 0x78, 0x46, 0x5f, 0x1f, 0x38, 0xa3, 0xe7, 0x35, 0x62, 0xe9, 0x76,
0x3a, 0x2f, 0x48, 0x28, 0x80, 0xf8, 0x02, 0x6c, 0x9e, 0x4e, 0x09, 0xe3, 0xd1, 0xb4, 0xf0, 0x5a,
0x7d, 0x7d, 0xd0, 0x0a, 0x6f, 0x15, 0xe8, 0x42, 0x8b, 0xf3, 0x89, 0xd7, 0x16, 0xfa, 0xf2, 0x58,
0xe6, 0x4e, 0xae, 0x49, 0xc6, 0x99, 0x67, 0x36, 0xe4, 0x7e, 0x5c, 0x9a, 0x43, 0x85, 0xf2, 0x9f,
0xc2, 0xe6, 0x57, 0x9a, 0xc7, 0x84, 0xb1, 0xaa, 0x7c, 0xdf, 0x05, 0xe7, 0x88, 0x92, 0x88, 0x93,
0x65, 0xcd, 0x47, 0x32, 0x21, 0x77, 0x35, 0x67, 0x45, 0xb2, 0x8c, 0xf9, 0xa5, 0x83, 0x29, 0xa8,
0x31, 0x50, 0x35, 0xea, 0xa2, 0xc6, 0xde, 0xfd, 0x09, 0x34, 0x95, 0x68, 0xac, 0x96, 0x78, 0x00,
0xa6, 0xf0, 0x13, 0xc5, 0x37, 0xbf, 0x85, 0x04, 0xf9, 0x67, 0x60, 0x8a, 0xb7, 0x44, 0x0f, 0x3a,
0x8c, 0xd0, 0xeb, 0x34, 0x26, 0xea, 0xf6, 0x2b, 0xb1, 0xb4, 0x5c, 0x44, 0x9c, 0xdc, 0x44, 0x73,
0x11, 0xcc, 0x0e, 0x2b, 0xb1, 0xb4, 0x64, 0x84, 0xdf, 0xe4, 0xf4, 0x52, 0x04, 0xb3, 0xc3, 0x4a,
0xf4, 0xff, 0xe8, 0x60, 0x8a, 0x38, 0x0f, 0xf3, 0x46, 0x49, 0x42, 0x09, 0x63, 0x15, 0xaf, 0x12,
0x97, 0x23, 0xb6, 0x1a, 0x23, 0xb6, 0xef, 0x44, 0xc4, 0x2d, 0xd5, 0x83, 0xd4, 0x33, 0x85, 0x41,
0x49, 0x88, 0xd0, 0x9e, 0xa4, 0xd9, 0xa5, 0x67, 0x09, 0xad, 0x38, 0x97, 0xd8, 0x29, 0xe1, 0x34,
0x8d, 0xbd, 0x8e, 0xb8, 0x3d, 0x25, 0xf9, 0x23, 0xb0, 0xbe, 0xf3, 0x88, 0xcf, 0x58, 0xe9, 0x15,
0xe7, 0x49, 0x95, 0xb2, 0x38, 0xe3, 0x33, 0x30, 0x09, 0xa5, 0x39, 0x55, 0xd9, 0x4a, 0xc1, 0x1f,
0x83, 0x23, 0x7d, 0x16, 0xd3, 0x30, 0x04, 0x8b, 0x09, 0x8d, 0x9a, 0xa6, 0xed, 0xda, 0x0b, 0x28,
0x07, 0x05, 0xdb, 0x1f, 0x01, 0xdc, 0xb6, 0x31, 0x22, 0x38, 0x52, 0x1a, 0x67, 0x59, 0x3e, 0xcb,
0x62, 0xe2, 0x6a, 0xe8, 0x42, 0x57, 0xea, 0x64, 0x0f, 0xb9, 0xfa, 0xfe, 0x10, 0xec, 0x45, 0x5b,
0x20, 0x80, 0x25, 0x1b, 0xd0, 0xd5, 0xca, 0xb3, 0x6c, 0x3d, 0x57, 0x2f, 0xcf, 0xca, 0xc1, 0x18,
0xfd, 0x33, 0xc0, 0x0a, 0xe5, 0x95, 0x7c, 0x01, 0x4b, 0xee, 0x0f, 0xdc, 0xad, 0xa5, 0x76, 0x67,
0x2f, 0xf5, 0xf6, 0x1a, 0xed, 0xaa, 0x89, 0x35, 0x3c, 0x04, 0x53, 0xcc, 0x32, 0xee, 0xd4, 0xb0,
0xcb, 0x33, 0xde, 0x6b, 0x98, 0x2b, 0x5f, 0x7b, 0xa5, 0xe3, 0x21, 0xd8, 0xb2, 0xbc, 0x94, 0x11,
0xf4, 0xea, 0x0d, 0xab, 0x28, 0xb6, 0x1b, 0xa6, 0x5f, 0x70, 0x7c, 0x82, 0x8e, 0x9a, 0x4b, 0x6c,
0xc2, 0xf5, 0xfa, 0x35, 0xc3, 0xea, 0x28, 0x6b, 0x78, 0xbc, 0xe8, 0x81, 0xe6, 0x44, 0xf6, 0x9a,
0x5e, 0x74, 0x41, 0x33, 0xfa, 0x6b, 0x80, 0x79, 0x1a, 0x9d, 0x4f, 0x08, 0x1e, 0x55, 0x8f, 0x83,
0x0d, 0xa3, 0x78, 0x0f, 0xdd, 0xca, 0x3a, 0xd1, 0x4a, 0x12, 0xf9, 0xaa, 0x8f, 0x20, 0x59, 0xd9,
0x40, 0x82, 0x44, 0xb6, 0xc3, 0x23, 0x48, 0x56, 0x96, 0x96, 0x86, 0x63, 0x68, 0x97, 0xff, 0xbe,
0x07, 0x6e, 0xa7, 0xde, 0x08, 0xcb, 0x3f, 0x4b, 0x5f, 0xc3, 0xcf, 0xd5, 0xce, 0xd9, 0x69, 0xf8,
0xcf, 0x28, 0xa2, 0xdd, 0x26, 0x73, 0xc5, 0x74, 0x6e, 0x89, 0x7f, 0xf5, 0xeb, 0xff, 0x01, 0x00,
0x00, 0xff, 0xff, 0xe2, 0xe9, 0xe2, 0x3b, 0xd7, 0x07, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.

View File

@@ -7,6 +7,7 @@ service Router {
rpc Lookup(LookupRequest) returns (LookupResponse) {};
rpc Watch(WatchRequest) returns (stream Event) {};
rpc Advertise(Request) returns (stream Advert) {};
rpc Solicit(Request) returns (Response) {};
rpc Process(Advert) returns (ProcessResponse) {};
rpc Status(Request) returns (StatusResponse) {};
}
@@ -22,6 +23,9 @@ service Table {
// Empty request
message Request {}
// Empty response
message Response {}
// ListResponse is returned by List
message ListResponse {
repeated Route routes = 1;
@@ -37,10 +41,12 @@ message LookupResponse {
repeated Route routes = 1;
}
// QueryRequest queries Table for Routes
message QueryRequest{
Query query = 1;
}
// QueryResponse is returned by Query
message QueryResponse {
repeated Route routes = 1;
}
@@ -117,10 +123,12 @@ message Route {
string gateway = 3;
// the network for this destination
string network = 4;
// router if the router id
string router = 5;
// the network link
string link = 5;
string link = 6;
// the metric / score of this route
int64 metric = 6;
int64 metric = 7;
}
message Status {

View File

@@ -11,29 +11,38 @@ type QueryOptions struct {
Gateway string
// Network is network address
Network string
// Router is router id
Router string
}
// QueryService sets destination address
// QueryService sets service to query
func QueryService(s string) QueryOption {
return func(o *QueryOptions) {
o.Service = s
}
}
// QueryGateway sets route gateway
// QueryGateway sets gateway address to query
func QueryGateway(g string) QueryOption {
return func(o *QueryOptions) {
o.Gateway = g
}
}
// QueryNetwork sets route network address
// QueryNetwork sets network name to query
func QueryNetwork(n string) QueryOption {
return func(o *QueryOptions) {
o.Network = n
}
}
// QueryRouter sets router id to query
func QueryRouter(r string) QueryOption {
return func(o *QueryOptions) {
o.Router = r
}
}
// Query is routing table query
type Query interface {
// Options returns query options
@@ -52,6 +61,7 @@ func NewQuery(opts ...QueryOption) Query {
Service: "*",
Gateway: "*",
Network: "*",
Router: "*",
}
for _, o := range opts {
@@ -67,8 +77,3 @@ func NewQuery(opts ...QueryOption) Query {
func (q *query) Options() QueryOptions {
return q.opts
}
// String prints routing table query in human readable form
func (q query) String() string {
return "query"
}

View File

@@ -7,9 +7,9 @@ import (
var (
// DefaultLink is default network link
DefaultLink = "local"
// DefaultLocalMetric is default route cost metric for the local network
// DefaultLocalMetric is default route cost for a local route
DefaultLocalMetric = 1
// DefaultNetworkMetric is default route cost metric for the micro network
// DefaultNetworkMetric is default route cost for a network route
DefaultNetworkMetric = 10
)
@@ -23,6 +23,8 @@ type Route struct {
Gateway string
// Network is network address
Network string
// Router is router id
Router string
// Link is network link
Link string
// Metric is the route cost metric
@@ -33,6 +35,6 @@ type Route struct {
func (r *Route) Hash() uint64 {
h := fnv.New64()
h.Reset()
h.Write([]byte(r.Service + r.Address + r.Gateway + r.Network + r.Link))
h.Write([]byte(r.Service + r.Address + r.Gateway + r.Network + r.Router + r.Link))
return h.Sum64()
}

View File

@@ -5,6 +5,17 @@ import (
"time"
)
var (
// DefaultAddress is default router address
DefaultAddress = ":9093"
// DefaultName is default router service name
DefaultName = "go.micro.router"
// DefaultNetwork is default micro network
DefaultNetwork = "go.micro"
// DefaultRouter is default network router
DefaultRouter = NewRouter()
)
// Router is an interface for a routing control plane
type Router interface {
// Init initializes the router with options
@@ -17,6 +28,8 @@ type Router interface {
Advertise() (<-chan *Advert, error)
// Process processes incoming adverts
Process(*Advert) error
// Solicit advertises the whole routing table to the network
Solicit() error
// Lookup queries routes in the routing table
Lookup(Query) ([]Route, error)
// Watch returns a watcher which tracks updates to the routing table
@@ -31,16 +44,17 @@ type Router interface {
String() string
}
// Table is an interface for routing table
type Table interface {
// Create new route in the routing table
Create(Route) error
// Delete deletes existing route from the routing table
// Delete existing route from the routing table
Delete(Route) error
// Update updates route in the routing table
// Update route in the routing table
Update(Route) error
// List returns the list of all routes in the table
// List all routes in the table
List() ([]Route, error)
// Query queries routes in the routing table
// Query routes in the routing table
Query(Query) ([]Route, error)
}
@@ -125,17 +139,6 @@ type Advert struct {
Events []*Event
}
var (
// DefaultAddress is default router address
DefaultAddress = ":9093"
// DefaultName is default router service name
DefaultName = "go.micro.router"
// DefaultNetwork is default micro network
DefaultNetwork = "go.micro"
// DefaultRouter is default network router
DefaultRouter = NewRouter()
)
// NewRouter creates new Router and returns it
func NewRouter(opts ...Option) Router {
return newRouter(opts...)

View File

@@ -220,6 +220,42 @@ func (s *svc) Process(advert *router.Advert) error {
return nil
}
// Solicit advertise all routes
func (s *svc) Solicit() error {
// list all the routes
routes, err := s.table.List()
if err != nil {
return err
}
// build events to advertise
events := make([]*router.Event, len(routes))
for i, _ := range events {
events[i] = &router.Event{
Type: router.Update,
Timestamp: time.Now(),
Route: routes[i],
}
}
advert := &router.Advert{
Id: s.opts.Id,
Type: router.RouteUpdate,
Timestamp: time.Now(),
TTL: time.Duration(router.DefaultAdvertTTL),
Events: events,
}
select {
case s.advertChan <- advert:
case <-s.exit:
close(s.advertChan)
return nil
}
return nil
}
// Status returns router status
func (s *svc) Status() router.Status {
s.Lock()

View File

@@ -8,7 +8,14 @@ import (
"github.com/google/uuid"
)
// table is an in memory routing table
var (
// ErrRouteNotFound is returned when no route was found in the routing table
ErrRouteNotFound = errors.New("route not found")
// ErrDuplicateRoute is returned when the route already exists
ErrDuplicateRoute = errors.New("duplicate route")
)
// table is an in-memory routing table
type table struct {
sync.RWMutex
// routes stores service routes
@@ -25,6 +32,19 @@ func newTable(opts ...Option) *table {
}
}
// sendEvent sends events to all subscribed watchers
func (t *table) sendEvent(e *Event) {
t.RLock()
defer t.RUnlock()
for _, w := range t.watchers {
select {
case w.resChan <- e:
case <-w.done:
}
}
}
// Create creates new route in the routing table
func (t *table) Create(r Route) error {
service := r.Service
@@ -63,8 +83,10 @@ func (t *table) Delete(r Route) error {
return ErrRouteNotFound
}
delete(t.routes[service], sum)
go t.sendEvent(&Event{Type: Delete, Timestamp: time.Now(), Route: r})
if _, ok := t.routes[service][sum]; ok {
delete(t.routes[service], sum)
go t.sendEvent(&Event{Type: Delete, Timestamp: time.Now(), Route: r})
}
return nil
}
@@ -85,8 +107,10 @@ func (t *table) Update(r Route) error {
return nil
}
t.routes[service][sum] = r
go t.sendEvent(&Event{Type: Update, Timestamp: time.Now(), Route: r})
if _, ok := t.routes[service][sum]; !ok {
t.routes[service][sum] = r
go t.sendEvent(&Event{Type: Update, Timestamp: time.Now(), Route: r})
}
return nil
}
@@ -106,21 +130,23 @@ func (t *table) List() ([]Route, error) {
return routes, nil
}
// isMatch checks if the route matches given network and router
func isMatch(route Route, network, router string) bool {
if network == "*" || network == route.Network {
if router == "*" || router == route.Gateway {
return true
// isMatch checks if the route matches given query options
func isMatch(route Route, gateway, network, router string) bool {
if gateway == "*" || gateway == route.Gateway {
if network == "*" || network == route.Network {
if router == "*" || router == route.Router {
return true
}
}
}
return false
}
// findRoutes finds all the routes for given network and router and returns them
func findRoutes(routes map[uint64]Route, network, router string) []Route {
func findRoutes(routes map[uint64]Route, gateway, network, router string) []Route {
var results []Route
for _, route := range routes {
if isMatch(route, network, router) {
if isMatch(route, gateway, network, router) {
results = append(results, route)
}
}
@@ -136,13 +162,13 @@ func (t *table) Query(q Query) ([]Route, error) {
if _, ok := t.routes[q.Options().Service]; !ok {
return nil, ErrRouteNotFound
}
return findRoutes(t.routes[q.Options().Service], q.Options().Network, q.Options().Gateway), nil
return findRoutes(t.routes[q.Options().Service], q.Options().Gateway, q.Options().Network, q.Options().Router), nil
}
var results []Route
// search through all destinations
for _, routes := range t.routes {
results = append(results, findRoutes(routes, q.Options().Network, q.Options().Gateway)...)
results = append(results, findRoutes(routes, q.Options().Gateway, q.Options().Network, q.Options().Router)...)
}
return results, nil
@@ -181,23 +207,3 @@ func (t *table) Watch(opts ...WatchOption) (Watcher, error) {
return w, nil
}
// sendEvent sends events to all subscribed watchers
func (t *table) sendEvent(e *Event) {
t.RLock()
defer t.RUnlock()
for _, w := range t.watchers {
select {
case w.resChan <- e:
case <-w.done:
}
}
}
var (
// ErrRouteNotFound is returned when no route was found in the routing table
ErrRouteNotFound = errors.New("route not found")
// ErrDuplicateRoute is returned when the route already exists
ErrDuplicateRoute = errors.New("duplicate route")
)

View File

@@ -9,6 +9,7 @@ func testSetup() (*table, Route) {
Service: "dest.svc",
Gateway: "dest.gw",
Network: "dest.network",
Router: "src.router",
Link: "det.link",
Metric: 10,
}
@@ -109,11 +110,13 @@ func TestQuery(t *testing.T) {
svc := []string{"svc1", "svc2", "svc3"}
net := []string{"net1", "net2", "net1"}
gw := []string{"gw1", "gw2", "gw3"}
rtr := []string{"rtr1", "rt2", "rt3"}
for i := 0; i < len(svc); i++ {
route.Service = svc[i]
route.Network = net[i]
route.Gateway = gw[i]
route.Router = rtr[i]
if err := table.Create(route); err != nil {
t.Errorf("error adding route: %s", err)
}
@@ -127,8 +130,9 @@ func TestQuery(t *testing.T) {
t.Errorf("error looking up routes: %s", err)
}
// query particular net
query = NewQuery(QueryNetwork("net1"))
// query routes particular network
network := "net1"
query = NewQuery(QueryNetwork(network))
routes, err = table.Query(query)
if err != nil {
@@ -139,7 +143,13 @@ func TestQuery(t *testing.T) {
t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 2, len(routes))
}
// query particular gateway
for _, route := range routes {
if route.Network != network {
t.Errorf("incorrect route returned. Expected network: %s, found: %s", network, route.Network)
}
}
// query routes for particular gateway
gateway := "gw1"
query = NewQuery(QueryGateway(gateway))
@@ -156,11 +166,28 @@ func TestQuery(t *testing.T) {
t.Errorf("incorrect route returned. Expected gateway: %s, found: %s", gateway, routes[0].Gateway)
}
// query particular route
network := "net1"
// query routes for particular router
router := "rtr1"
query = NewQuery(QueryRouter(router))
routes, err = table.Query(query)
if err != nil {
t.Errorf("error looking up routes: %s", err)
}
if len(routes) != 1 {
t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 1, len(routes))
}
if routes[0].Router != router {
t.Errorf("incorrect route returned. Expected router: %s, found: %s", router, routes[0].Router)
}
// query particular gateway and network
query = NewQuery(
QueryGateway(gateway),
QueryNetwork(network),
QueryRouter(router),
)
routes, err = table.Query(query)
@@ -180,7 +207,11 @@ func TestQuery(t *testing.T) {
t.Errorf("incorrect network returned. Expected network: %s, found: %s", network, routes[0].Network)
}
// bullshit route query
if routes[0].Router != router {
t.Errorf("incorrect route returned. Expected router: %s, found: %s", router, routes[0].Router)
}
// non-existen route query
query = NewQuery(QueryService("foobar"))
routes, err = table.Query(query)

View File

@@ -6,6 +6,11 @@ import (
"time"
)
var (
// ErrWatcherStopped is returned when routing table watcher has been stopped
ErrWatcherStopped = errors.New("watcher stopped")
)
// EventType defines routing table event
type EventType int
@@ -42,9 +47,6 @@ type Event struct {
Route Route
}
// WatchOption is used to define what routes to watch in the table
type WatchOption func(*WatchOptions)
// Watcher defines routing table watcher interface
// Watcher returns updates to the routing table
type Watcher interface {
@@ -56,7 +58,11 @@ type Watcher interface {
Stop()
}
// WatchOption is used to define what routes to watch in the table
type WatchOption func(*WatchOptions)
// WatchOptions are table watcher options
// TODO: expand the options to watch based on other criteria
type WatchOptions struct {
// Service allows to watch specific service routes
Service string
@@ -70,6 +76,7 @@ func WatchService(s string) WatchOption {
}
}
// tableWatcher implements routing table Watcher
type tableWatcher struct {
sync.RWMutex
id string
@@ -113,8 +120,3 @@ func (w *tableWatcher) Stop() {
close(w.done)
}
}
var (
// ErrWatcherStopped is returned when routing table watcher has been stopped
ErrWatcherStopped = errors.New("watcher stopped")
)

View File

@@ -53,6 +53,8 @@ type grpcServer struct {
opts server.Options
handlers map[string]server.Handler
subscribers map[*subscriber][]broker.Subscriber
// marks the serve as started
started bool
// used for first registration
registered bool
}
@@ -454,7 +456,10 @@ func (g *grpcServer) newCodec(contentType string) (codec.NewCodec, error) {
}
func (g *grpcServer) Options() server.Options {
g.RLock()
opts := g.opts
g.RUnlock()
return opts
}
@@ -700,7 +705,14 @@ func (g *grpcServer) Deregister() error {
}
func (g *grpcServer) Start() error {
config := g.opts
g.RLock()
if g.started {
g.RUnlock()
return nil
}
g.RUnlock()
config := g.Options()
// micro: config.Transport.Listen(config.Address)
ts, err := net.Listen("tcp", config.Address)
@@ -781,13 +793,34 @@ func (g *grpcServer) Start() error {
config.Broker.Disconnect()
}()
// mark the server as started
g.Lock()
g.started = true
g.Unlock()
return nil
}
func (g *grpcServer) Stop() error {
g.RLock()
if !g.started {
g.RUnlock()
return nil
}
g.RUnlock()
ch := make(chan error)
g.exit <- ch
return <-ch
var err error
select {
case err = <-ch:
g.Lock()
g.started = false
g.Unlock()
}
return err
}
func (g *grpcServer) String() string {

View File

@@ -15,9 +15,10 @@ import (
)
type rpcCodec struct {
socket transport.Socket
codec codec.Codec
first bool
socket transport.Socket
codec codec.Codec
first bool
protocol string
req *transport.Message
buf *readWriteCloser
@@ -157,12 +158,27 @@ func newRpcCodec(req *transport.Message, socket transport.Socket, c codec.NewCod
rbuf: bytes.NewBuffer(nil),
wbuf: bytes.NewBuffer(nil),
}
r := &rpcCodec{
buf: rwc,
codec: c(rwc),
req: req,
socket: socket,
buf: rwc,
codec: c(rwc),
req: req,
socket: socket,
protocol: "mucp",
}
// if grpc pre-load the buffer
// TODO: remove this terrible hack
switch r.codec.String() {
case "grpc":
// set as first
r.first = true
// write the body
rwc.rbuf.Write(req.Body)
// set the protocol
r.protocol = "grpc"
}
return r
}
@@ -173,27 +189,33 @@ func (c *rpcCodec) ReadHeader(r *codec.Message, t codec.MessageType) error {
Body: c.req.Body,
}
var tm transport.Message
// first message could be pre-loaded
if !c.first {
var tm transport.Message
// read off the socket
if err := c.socket.Recv(&tm); err != nil {
return err
}
// reset the read buffer
c.buf.rbuf.Reset()
// read off the socket
if err := c.socket.Recv(&tm); err != nil {
return err
}
// reset the read buffer
c.buf.rbuf.Reset()
// write the body to the buffer
if _, err := c.buf.rbuf.Write(tm.Body); err != nil {
return err
// write the body to the buffer
if _, err := c.buf.rbuf.Write(tm.Body); err != nil {
return err
}
// set the message header
m.Header = tm.Header
// set the message body
m.Body = tm.Body
// set req
c.req = &tm
}
// set the message header
m.Header = tm.Header
// set the message body
m.Body = tm.Body
// set req
c.req = &tm
// disable first
c.first = false
// set some internal things
getHeaders(&m)
@@ -293,5 +315,5 @@ func (c *rpcCodec) Close() error {
}
func (c *rpcCodec) String() string {
return "rpc"
return c.protocol
}

View File

@@ -30,6 +30,8 @@ type rpcServer struct {
opts Options
handlers map[string]Handler
subscribers map[*subscriber][]broker.Subscriber
// marks the serve as started
started bool
// used for first registration
registered bool
// graceful exit
@@ -61,20 +63,33 @@ func (r rpcRouter) ServeRequest(ctx context.Context, req Request, rsp Response)
// ServeConn serves a single connection
func (s *rpcServer) ServeConn(sock transport.Socket) {
var wg sync.WaitGroup
var mtx sync.RWMutex
// streams are multiplexed on Micro-Stream or Micro-Id header
sockets := make(map[string]*socket.Socket)
defer func() {
// close socket
// wait till done
wg.Wait()
// close underlying socket
sock.Close()
// close the sockets
mtx.Lock()
for id, psock := range sockets {
psock.Close()
delete(sockets, id)
}
mtx.Unlock()
// recover any panics
if r := recover(); r != nil {
log.Log("panic recovered: ", r)
log.Log(string(debug.Stack()))
}
}()
// multiplex the streams on a single socket by Micro-Stream
var mtx sync.RWMutex
sockets := make(map[string]*socket.Socket)
for {
var msg transport.Message
if err := sock.Recv(&msg); err != nil {
@@ -92,6 +107,9 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
id = msg.Header["Micro-Id"]
}
// we're starting processing
wg.Add(1)
// add to wait group if "wait" is opt-in
if s.wg != nil {
s.wg.Add(1)
@@ -117,6 +135,8 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
s.wg.Done()
}
wg.Done()
// continue to the next message
continue
}
@@ -134,28 +154,6 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
sockets[id] = psock
mtx.Unlock()
// process the outbound messages from the socket
go func(id string, psock *socket.Socket) {
defer psock.Close()
for {
// get the message from our internal handler/stream
m := new(transport.Message)
if err := psock.Process(m); err != nil {
// delete the socket
mtx.Lock()
delete(sockets, id)
mtx.Unlock()
return
}
// send the message back over the socket
if err := sock.Send(m); err != nil {
return
}
}
}(id, psock)
// now walk the usual path
// we use this Timeout header to set a server deadline
@@ -203,17 +201,23 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
},
Body: []byte(err.Error()),
})
if s.wg != nil {
s.wg.Done()
}
wg.Done()
return
}
}
rcodec := newRpcCodec(&msg, psock, cf)
protocol := rcodec.String()
// check stream id
var stream bool
if v := getHeader("Micro-Stream", msg.Header); len(v) > 0 {
stream = true
}
@@ -257,8 +261,44 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
r = rpcRouter{handler}
}
// wait for processing to exit
wg.Add(1)
// process the outbound messages from the socket
go func(id string, psock *socket.Socket) {
defer func() {
// TODO: don't hack this but if its grpc just break out of the stream
// We do this because the underlying connection is h2 and its a stream
switch protocol {
case "grpc":
sock.Close()
}
wg.Done()
}()
for {
// get the message from our internal handler/stream
m := new(transport.Message)
if err := psock.Process(m); err != nil {
// delete the socket
mtx.Lock()
delete(sockets, id)
mtx.Unlock()
return
}
// send the message back over the socket
if err := sock.Send(m); err != nil {
return
}
}
}(id, psock)
// serve the request in a go routine as this may be a stream
go func(id string, psock *socket.Socket) {
defer psock.Close()
// serve the actual request using the request router
if err := r.ServeRequest(ctx, request, response); err != nil {
// write an error response
@@ -283,8 +323,9 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
s.wg.Done()
}
// done with this socket
wg.Done()
}(id, psock)
}
}
@@ -584,6 +625,13 @@ func (s *rpcServer) Deregister() error {
}
func (s *rpcServer) Start() error {
s.RLock()
if s.started {
s.RUnlock()
return nil
}
s.RUnlock()
config := s.Options()
// start listening on the transport
@@ -708,13 +756,34 @@ func (s *rpcServer) Start() error {
s.Unlock()
}()
// mark the server as started
s.Lock()
s.started = true
s.Unlock()
return nil
}
func (s *rpcServer) Stop() error {
s.RLock()
if !s.started {
s.RUnlock()
return nil
}
s.RUnlock()
ch := make(chan error)
s.exit <- ch
return <-ch
var err error
select {
case err = <-ch:
s.Lock()
s.started = false
s.Unlock()
}
return err
}
func (s *rpcServer) String() string {

View File

@@ -3,9 +3,11 @@ package server
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/codec"
@@ -116,8 +118,8 @@ type Option func(*Options)
var (
DefaultAddress = ":0"
DefaultName = "server"
DefaultVersion = "latest"
DefaultName = "go.micro.server"
DefaultVersion = fmt.Sprintf("%d", time.Now().Unix())
DefaultId = uuid.New().String()
DefaultServer Server = newRpcServer()
DefaultRouter = newRpcRouter()

View File

@@ -3,6 +3,7 @@ package micro
import (
"os"
"os/signal"
"strings"
"sync"
"syscall"
@@ -10,7 +11,9 @@ import (
"github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/debug/handler"
"github.com/micro/go-micro/metadata"
"github.com/micro/go-micro/plugin"
"github.com/micro/go-micro/server"
"github.com/micro/go-micro/util/log"
)
type service struct {
@@ -44,6 +47,24 @@ func (s *service) Init(opts ...Option) {
}
s.once.Do(func() {
// setup the plugins
for _, p := range strings.Split(os.Getenv("MICRO_PLUGIN"), ",") {
if len(p) == 0 {
continue
}
// load the plugin
c, err := plugin.Load(p)
if err != nil {
log.Fatal(err)
}
// initialise the plugin
if err := plugin.Init(c); err != nil {
log.Fatal(err)
}
}
// Initialise the command flags, overriding new service
_ = s.opts.Cmd.Init(
cmd.Broker(&s.opts.Broker),

View File

@@ -17,5 +17,5 @@ func NewService(opts ...micro.Option) micro.Service {
options = append(options, opts...)
return micro.NewService(opts...)
return micro.NewService(options...)
}

View File

@@ -33,6 +33,8 @@ type httpTransportClient struct {
once sync.Once
sync.RWMutex
// request must be stored for response processing
r chan *http.Request
bl []*http.Request
buff *bufio.Reader
@@ -48,10 +50,18 @@ type httpTransportSocket struct {
r *http.Request
rw *bufio.ReadWriter
mtx sync.RWMutex
// the hijacked when using http 1
conn net.Conn
// for the first request
ch chan *http.Request
// h2 things
buf *bufio.Reader
// indicate if socket is closed
closed chan bool
// local/remote ip
local string
remote string
@@ -161,14 +171,13 @@ func (h *httpTransportClient) Recv(m *Message) error {
}
func (h *httpTransportClient) Close() error {
err := h.conn.Close()
h.once.Do(func() {
h.Lock()
h.buff.Reset(nil)
h.Unlock()
close(h.r)
})
return err
return h.conn.Close()
}
func (h *httpTransportSocket) Local() string {
@@ -232,14 +241,23 @@ func (h *httpTransportSocket) Recv(m *Message) error {
return nil
}
// only process if the socket is open
select {
case <-h.closed:
return io.EOF
default:
// no op
}
// processing http2 request
// read streaming body
// set max buffer size
buf := make([]byte, 4*1024)
// TODO: adjustable buffer size
buf := make([]byte, 4*1024*1024)
// read the request body
n, err := h.r.Body.Read(buf)
n, err := h.buf.Read(buf)
// not an eof error
if err != nil {
return err
@@ -290,7 +308,17 @@ func (h *httpTransportSocket) Send(m *Message) error {
return rsp.Write(h.conn)
}
// http2 request
// only process if the socket is open
select {
case <-h.closed:
return io.EOF
default:
// no op
}
// we need to lock to protect the write
h.mtx.RLock()
defer h.mtx.RUnlock()
// set headers
for k, v := range m.Header {
@@ -299,6 +327,10 @@ func (h *httpTransportSocket) Send(m *Message) error {
// write request
_, err := h.w.Write(m.Body)
// flush the trailers
h.w.(http.Flusher).Flush()
return err
}
@@ -321,13 +353,29 @@ func (h *httpTransportSocket) error(m *Message) error {
return rsp.Write(h.conn)
}
return nil
}
func (h *httpTransportSocket) Close() error {
if h.r.ProtoMajor == 1 {
return h.conn.Close()
h.mtx.Lock()
defer h.mtx.Unlock()
select {
case <-h.closed:
return nil
default:
// close the channel
close(h.closed)
// close the buffer
h.r.Body.Close()
// close the connection
if h.r.ProtoMajor == 1 {
return h.conn.Close()
}
}
return nil
}
@@ -374,20 +422,29 @@ func (h *httpTransportListener) Accept(fn func(Socket)) error {
con = conn
}
// buffered reader
bufr := bufio.NewReader(r.Body)
// save the request
ch := make(chan *http.Request, 1)
ch <- r
fn(&httpTransportSocket{
// create a new transport socket
sock := &httpTransportSocket{
ht: h.ht,
w: w,
r: r,
rw: buf,
buf: bufr,
ch: ch,
conn: con,
local: h.Addr(),
remote: r.RemoteAddr,
})
closed: make(chan bool),
}
// execute the socket
fn(sock)
})
// get optional handlers

213
tunnel/broker/broker.go Normal file
View File

@@ -0,0 +1,213 @@
// Package broker is a tunnel broker
package broker
import (
"context"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/tunnel"
)
type tunBroker struct {
opts broker.Options
tunnel tunnel.Tunnel
}
type tunSubscriber struct {
topic string
handler broker.Handler
opts broker.SubscribeOptions
closed chan bool
listener tunnel.Listener
}
type tunEvent struct {
topic string
message *broker.Message
}
// used to access tunnel from options context
type tunnelKey struct{}
type tunnelAddr struct{}
func (t *tunBroker) Init(opts ...broker.Option) error {
for _, o := range opts {
o(&t.opts)
}
return nil
}
func (t *tunBroker) Options() broker.Options {
return t.opts
}
func (t *tunBroker) Address() string {
return t.tunnel.Address()
}
func (t *tunBroker) Connect() error {
return t.tunnel.Connect()
}
func (t *tunBroker) Disconnect() error {
return t.tunnel.Close()
}
func (t *tunBroker) Publish(topic string, m *broker.Message, opts ...broker.PublishOption) error {
// TODO: this is probably inefficient, we might want to just maintain an open connection
// it may be easier to add broadcast to the tunnel
c, err := t.tunnel.Dial(topic, tunnel.DialMulticast())
if err != nil {
return err
}
defer c.Close()
return c.Send(&transport.Message{
Header: m.Header,
Body: m.Body,
})
}
func (t *tunBroker) Subscribe(topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
l, err := t.tunnel.Listen(topic)
if err != nil {
return nil, err
}
var options broker.SubscribeOptions
for _, o := range opts {
o(&options)
}
tunSub := &tunSubscriber{
topic: topic,
handler: h,
opts: options,
closed: make(chan bool),
listener: l,
}
// start processing
go tunSub.run()
return tunSub, nil
}
func (t *tunBroker) String() string {
return "tunnel"
}
func (t *tunSubscriber) run() {
for {
// accept a new connection
c, err := t.listener.Accept()
if err != nil {
select {
case <-t.closed:
return
default:
continue
}
}
// receive message
m := new(transport.Message)
if err := c.Recv(m); err != nil {
c.Close()
continue
}
// close the connection
c.Close()
// handle the message
go t.handler(&tunEvent{
topic: t.topic,
message: &broker.Message{
Header: m.Header,
Body: m.Body,
},
})
}
}
func (t *tunSubscriber) Options() broker.SubscribeOptions {
return t.opts
}
func (t *tunSubscriber) Topic() string {
return t.topic
}
func (t *tunSubscriber) Unsubscribe() error {
select {
case <-t.closed:
return nil
default:
close(t.closed)
return t.listener.Close()
}
}
func (t *tunEvent) Topic() string {
return t.topic
}
func (t *tunEvent) Message() *broker.Message {
return t.message
}
func (t *tunEvent) Ack() error {
return nil
}
func NewBroker(opts ...broker.Option) broker.Broker {
options := broker.Options{
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
t, ok := options.Context.Value(tunnelKey{}).(tunnel.Tunnel)
if !ok {
t = tunnel.NewTunnel()
}
a, ok := options.Context.Value(tunnelAddr{}).(string)
if ok {
// initialise address
t.Init(tunnel.Address(a))
}
if len(options.Addrs) > 0 {
// initialise nodes
t.Init(tunnel.Nodes(options.Addrs...))
}
return &tunBroker{
opts: options,
tunnel: t,
}
}
// WithAddress sets the tunnel address
func WithAddress(a string) broker.Option {
return func(o *broker.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, tunnelAddr{}, a)
}
}
// WithTunnel sets the internal tunnel
func WithTunnel(t tunnel.Tunnel) broker.Option {
return func(o *broker.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, tunnelKey{}, t)
}
}

File diff suppressed because it is too large Load Diff

101
tunnel/link.go Normal file
View File

@@ -0,0 +1,101 @@
package tunnel
import (
"sync"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/transport"
)
type link struct {
transport.Socket
sync.RWMutex
// unique id of this link e.g uuid
// which we define for ourselves
id string
// whether its a loopback connection
// this flag is used by the transport listener
// which accepts inbound quic connections
loopback bool
// whether its actually connected
// dialled side sets it to connected
// after sending the message. the
// listener waits for the connect
connected bool
// the last time we received a keepalive
// on this link from the remote side
lastKeepAlive time.Time
// channels keeps a mapping of channels and last seen
channels map[string]time.Time
// stop the link
closed chan bool
}
func newLink(s transport.Socket) *link {
l := &link{
Socket: s,
id: uuid.New().String(),
channels: make(map[string]time.Time),
closed: make(chan bool),
}
go l.run()
return l
}
func (l *link) run() {
t := time.NewTicker(time.Minute)
defer t.Stop()
for {
select {
case <-l.closed:
return
case <-t.C:
// drop any channel mappings older than 2 minutes
var kill []string
killTime := time.Minute * 2
l.RLock()
for ch, t := range l.channels {
if d := time.Since(t); d > killTime {
kill = append(kill, ch)
}
}
l.RUnlock()
// if nothing to kill don't bother with a wasted lock
if len(kill) == 0 {
continue
}
// kill the channels!
l.Lock()
for _, ch := range kill {
delete(l.channels, ch)
}
l.Unlock()
}
}
}
func (l *link) Id() string {
l.RLock()
defer l.RUnlock()
return l.id
}
func (l *link) Close() error {
select {
case <-l.closed:
return nil
default:
close(l.closed)
return nil
}
return nil
}

View File

@@ -8,8 +8,13 @@ import (
"github.com/micro/go-micro/transport"
)
var (
// ErrLinkClosed is returned when attempting i/o operation on the closed link
ErrLinkClosed = errors.New("link closed")
)
// Link is a layer on top of a transport socket with the
// buffering send and recv queue's with the ability to
// buffering send and recv queues with the ability to
// measure the actual transport link and reconnect if
// an address is specified.
type Link interface {
@@ -28,10 +33,6 @@ type Link interface {
Length() int
}
var (
ErrLinkClosed = errors.New("link closed")
)
// NewLink creates a new link on top of a socket
func NewLink(opts ...options.Option) Link {
return newLink(options.NewOptions(opts...))

View File

@@ -2,79 +2,148 @@ package tunnel
import (
"io"
"time"
"github.com/micro/go-micro/util/log"
)
type tunListener struct {
// address of the listener
addr string
channel string
// the accept channel
accept chan *socket
accept chan *session
// the channel to close
closed chan bool
// the tunnel closed channel
tunClosed chan bool
// the connection
conn Conn
// the listener socket
socket *socket
// the listener session
session *session
// del func to kill listener
delFunc func()
}
// periodically announce self
func (t *tunListener) announce() {
tick := time.NewTicker(time.Second * 30)
defer tick.Stop()
// first announcement
t.session.Announce()
for {
select {
case <-tick.C:
t.session.Announce()
case <-t.closed:
return
}
}
}
func (t *tunListener) process() {
// our connection map for session
conns := make(map[string]*socket)
conns := make(map[string]*session)
defer func() {
// close the sessions
for _, conn := range conns {
conn.Close()
}
}()
for {
select {
case <-t.closed:
return
case <-t.tunClosed:
t.Close()
return
// receive a new message
case m := <-t.socket.recv:
// get a socket
sock, ok := conns[m.session]
log.Debugf("Tunnel listener received id %s session %s exists: %t", m.id, m.session, ok)
case m := <-t.session.recv:
// get a session
sess, ok := conns[m.session]
log.Debugf("Tunnel listener received channel %s session %s exists: %t", m.channel, m.session, ok)
if !ok {
// create a new socket session
sock = &socket{
// our tunnel id
id: m.id,
switch m.typ {
case "open", "session":
default:
continue
}
// create a new session session
sess = &session{
// the id of the remote side
tunnel: m.tunnel,
// the channel
channel: m.channel,
// the session id
session: m.session,
// is loopback conn
loopback: m.loopback,
// the link the message was received on
link: m.link,
// set multicast
multicast: m.multicast,
// close chan
closed: make(chan bool),
// recv called by the acceptor
recv: make(chan *message, 128),
// use the internal send buffer
send: t.socket.send,
send: t.session.send,
// wait
wait: make(chan bool),
// error channel
errChan: make(chan error, 1),
}
// save the socket
conns[m.session] = sock
// save the session
conns[m.session] = sess
// send to accept chan
select {
case <-t.closed:
return
case t.accept <- sock:
// send to accept chan
case t.accept <- sess:
}
}
// an existing session was found
// received a close message
switch m.typ {
case "close":
select {
case <-sess.closed:
// no op
delete(conns, m.session)
default:
// close and delete session
close(sess.closed)
delete(conns, m.session)
}
// continue
continue
case "session":
// operate on this
default:
// non operational type
continue
}
// send this to the accept chan
select {
case <-sock.closed:
case <-sess.closed:
delete(conns, m.session)
case sock.recv <- m:
log.Debugf("Tunnel listener sent to recv chan id %s session %s", m.id, m.session)
case sess.recv <- m:
log.Debugf("Tunnel listener sent to recv chan channel %s session %s", m.channel, m.session)
}
}
}
}
func (t *tunListener) Addr() string {
return t.addr
func (t *tunListener) Channel() string {
return t.channel
}
// Close closes tunnel listener
@@ -83,26 +152,33 @@ func (t *tunListener) Close() error {
case <-t.closed:
return nil
default:
// close and delete
t.delFunc()
t.session.Close()
close(t.closed)
}
return nil
}
// Everytime accept is called we essentially block till we get a new connection
func (t *tunListener) Accept() (Conn, error) {
func (t *tunListener) Accept() (Session, error) {
select {
// if the socket is closed return
// if the session is closed return
case <-t.closed:
return nil, io.EOF
case <-t.tunClosed:
// close the listener when the tunnel closes
t.Close()
return nil, io.EOF
// wait for a new connection
case c, ok := <-t.accept:
// check if the accept chan is closed
if !ok {
return nil, io.EOF
}
// send back the accept
if err := c.Accept(); err != nil {
return nil, err
}
return c, nil
}
return nil, nil

View File

@@ -1,6 +1,8 @@
package tunnel
import (
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/transport/quic"
@@ -8,7 +10,9 @@ import (
var (
// DefaultAddress is default tunnel bind address
DefaultAddress = ":9096"
DefaultAddress = ":0"
// The shared default token
DefaultToken = "micro"
)
type Option func(*Options)
@@ -21,10 +25,21 @@ type Options struct {
Address string
// Nodes are remote nodes
Nodes []string
// The shared auth token
Token string
// Transport listens to incoming connections
Transport transport.Transport
}
type DialOption func(*DialOptions)
type DialOptions struct {
// specify a multicast connection
Multicast bool
// the dial timeout
Timeout time.Duration
}
// The tunnel id
func Id(id string) Option {
return func(o *Options) {
@@ -46,6 +61,13 @@ func Nodes(n ...string) Option {
}
}
// Token sets the shared token for auth
func Token(t string) Option {
return func(o *Options) {
o.Token = t
}
}
// Transport listens for incoming connections
func Transport(t transport.Transport) Option {
return func(o *Options) {
@@ -58,6 +80,22 @@ func DefaultOptions() Options {
return Options{
Id: uuid.New().String(),
Address: DefaultAddress,
Token: DefaultToken,
Transport: quic.NewTransport(),
}
}
// Dial options
// Dial multicast sets the multicast option to send only to those mapped
func DialMulticast() DialOption {
return func(o *DialOptions) {
o.Multicast = true
}
}
func DialTimeout(t time.Duration) DialOption {
return func(o *DialOptions) {
o.Timeout = t
}
}

286
tunnel/session.go Normal file
View File

@@ -0,0 +1,286 @@
package tunnel
import (
"errors"
"io"
"time"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/util/log"
)
// session is our pseudo session for transport.Socket
type session struct {
// the tunnel id
tunnel string
// the channel name
channel string
// the session id based on Micro.Tunnel-Session
session string
// closed
closed chan bool
// remote addr
remote string
// local addr
local string
// send chan
send chan *message
// recv chan
recv chan *message
// wait until we have a connection
wait chan bool
// if the discovery worked
discovered bool
// if the session was accepted
accepted bool
// outbound marks the session as outbound dialled connection
outbound bool
// lookback marks the session as a loopback on the inbound
loopback bool
// if the session is multicast
multicast bool
// if the session is broadcast
broadcast bool
// the timeout
timeout time.Duration
// the link on which this message was received
link string
// the error response
errChan chan error
}
// message is sent over the send channel
type message struct {
// type of message
typ string
// tunnel id
tunnel string
// channel name
channel string
// the session id
session string
// outbound marks the message as outbound
outbound bool
// loopback marks the message intended for loopback
loopback bool
// whether to send as multicast
multicast bool
// broadcast sets the broadcast type
broadcast bool
// the link to send the message on
link string
// transport data
data *transport.Message
// the error channel
errChan chan error
}
func (s *session) Remote() string {
return s.remote
}
func (s *session) Local() string {
return s.local
}
func (s *session) Id() string {
return s.session
}
func (s *session) Channel() string {
return s.channel
}
// newMessage creates a new message based on the session
func (s *session) newMessage(typ string) *message {
return &message{
typ: typ,
tunnel: s.tunnel,
channel: s.channel,
session: s.session,
outbound: s.outbound,
loopback: s.loopback,
multicast: s.multicast,
link: s.link,
errChan: s.errChan,
}
}
// Open will fire the open message for the session. This is called by the dialler.
func (s *session) Open() error {
// create a new message
msg := s.newMessage("open")
// send open message
s.send <- msg
// wait for an error response for send
select {
case err := <-msg.errChan:
if err != nil {
return err
}
case <-s.closed:
return io.EOF
}
// we don't wait on multicast
if s.multicast {
s.accepted = true
return nil
}
// now wait for the accept
select {
case msg = <-s.recv:
if msg.typ != "accept" {
log.Debugf("Received non accept message in Open %s", msg.typ)
return errors.New("failed to connect")
}
// set to accepted
s.accepted = true
// set link
s.link = msg.link
case <-time.After(s.timeout):
return ErrDialTimeout
case <-s.closed:
return io.EOF
}
return nil
}
// Accept sends the accept response to an open message from a dialled connection
func (s *session) Accept() error {
msg := s.newMessage("accept")
// send the accept message
select {
case <-s.closed:
return io.EOF
case s.send <- msg:
return nil
}
// wait for send response
select {
case err := <-s.errChan:
if err != nil {
return err
}
case <-s.closed:
return io.EOF
}
return nil
}
// Announce sends an announcement to notify that this session exists. This is primarily used by the listener.
func (s *session) Announce() error {
msg := s.newMessage("announce")
// we don't need an error back
msg.errChan = nil
// announce to all
msg.broadcast = true
// we don't need the link
msg.link = ""
select {
case s.send <- msg:
return nil
case <-s.closed:
return io.EOF
}
}
// Send is used to send a message
func (s *session) Send(m *transport.Message) error {
select {
case <-s.closed:
return io.EOF
default:
// no op
}
// make copy
data := &transport.Message{
Header: make(map[string]string),
Body: m.Body,
}
for k, v := range m.Header {
data.Header[k] = v
}
// create a new message
msg := s.newMessage("session")
// set the data
msg.data = data
// if multicast don't set the link
if s.multicast {
msg.link = ""
}
log.Debugf("Appending %+v to send backlog", msg)
// send the actual message
s.send <- msg
// wait for an error response
select {
case err := <-msg.errChan:
return err
case <-s.closed:
return io.EOF
}
return nil
}
// Recv is used to receive a message
func (s *session) Recv(m *transport.Message) error {
select {
case <-s.closed:
return errors.New("session is closed")
default:
// no op
}
// recv from backlog
msg := <-s.recv
// check the error if one exists
select {
case err := <-msg.errChan:
return err
default:
}
log.Debugf("Received %+v from recv backlog", msg)
// set message
*m = *msg.data
// return nil
return nil
}
// Close closes the session by sending a close message
func (s *session) Close() error {
select {
case <-s.closed:
// no op
default:
close(s.closed)
// append to backlog
msg := s.newMessage("close")
// no error response on close
msg.errChan = nil
// send the close message
select {
case s.send <- msg:
default:
}
}
return nil
}

View File

@@ -1,115 +0,0 @@
package tunnel
import (
"errors"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/util/log"
)
// socket is our pseudo socket for transport.Socket
type socket struct {
// socket id based on Micro-Tunnel
id string
// the session id based on Micro.Tunnel-Session
session string
// closed
closed chan bool
// remote addr
remote string
// local addr
local string
// send chan
send chan *message
// recv chan
recv chan *message
// wait until we have a connection
wait chan bool
// outbound marks the socket as outbound
outbound bool
}
// message is sent over the send channel
type message struct {
// tunnel id
id string
// the session id
session string
// outbound marks the message as outbound
outbound bool
// transport data
data *transport.Message
}
func (s *socket) Remote() string {
return s.remote
}
func (s *socket) Local() string {
return s.local
}
func (s *socket) Id() string {
return s.id
}
func (s *socket) Session() string {
return s.session
}
func (s *socket) Send(m *transport.Message) error {
select {
case <-s.closed:
return errors.New("socket is closed")
default:
// no op
}
// make copy
data := &transport.Message{
Header: make(map[string]string),
Body: m.Body,
}
for k, v := range m.Header {
data.Header[k] = v
}
// append to backlog
msg := &message{
id: s.id,
session: s.session,
outbound: s.outbound,
data: data,
}
log.Debugf("Appending %+v to send backlog", msg)
s.send <- msg
return nil
}
func (s *socket) Recv(m *transport.Message) error {
select {
case <-s.closed:
return errors.New("socket is closed")
default:
// no op
}
// recv from backlog
msg := <-s.recv
log.Debugf("Received %+v from recv backlog", msg)
// set message
*m = *msg.data
// return nil
return nil
}
// Close closes the socket
func (s *socket) Close() error {
select {
case <-s.closed:
// no op
default:
close(s.closed)
}
return nil
}

View File

@@ -10,7 +10,7 @@ type tunListener struct {
}
func (t *tunListener) Addr() string {
return t.l.Addr()
return t.l.Channel()
}
func (t *tunListener) Close() error {

View File

@@ -2,38 +2,64 @@
package tunnel
import (
"errors"
"time"
"github.com/micro/go-micro/transport"
)
// Tunnel creates a gre network tunnel on top of a link.
// It establishes multiple streams using the Micro-Tunnel-Id header
var (
// DefaultDialTimeout is the dial timeout if none is specified
DefaultDialTimeout = time.Second * 5
// ErrDialTimeout is returned by a call to Dial where the timeout occurs
ErrDialTimeout = errors.New("dial timeout")
// ErrDiscoverChan is returned when we failed to receive the "announce" back from a discovery
ErrDiscoverChan = errors.New("failed to discover channel")
)
// Tunnel creates a gre tunnel on top of the go-micro/transport.
// It establishes multiple streams using the Micro-Tunnel-Channel header
// and Micro-Tunnel-Session header. The tunnel id is a hash of
// the address being requested.
type Tunnel interface {
Init(opts ...Option) error
// Address the tunnel is listening on
Address() string
// Connect connects the tunnel
Connect() error
// Close closes the tunnel
Close() error
// Dial an endpoint
Dial(addr string) (Conn, error)
// Accept connections
Listen(addr string) (Listener, error)
// Connect to a channel
Dial(channel string, opts ...DialOption) (Session, error)
// Accept connections on a channel
Listen(channel string) (Listener, error)
// All the links the tunnel is connected to
Links() []Link
// Name of the tunnel implementation
String() string
}
// Link represents internal links to the tunnel
type Link interface {
// The id of the link
Id() string
// honours transport socket
transport.Socket
}
// The listener provides similar constructs to the transport.Listener
type Listener interface {
Addr() string
Accept() (Session, error)
Channel() string
Close() error
Accept() (Conn, error)
}
// Conn is a connection dialed or accepted which includes the tunnel id and session
type Conn interface {
// Specifies the tunnel id
// Session is a unique session created when dialling or accepting connections on the tunnel
type Session interface {
// The unique session id
Id() string
// The session
Session() string
// The channel name
Channel() string
// a transport socket
transport.Socket
}

View File

@@ -10,6 +10,8 @@ import (
// testAccept will accept connections on the transport, create a new link and tunnel on top
func testAccept(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) {
defer wg.Done()
// listen on some virtual address
tl, err := tun.Listen("test-tunnel")
if err != nil {
@@ -43,7 +45,8 @@ func testAccept(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) {
t.Fatal(err)
}
wg.Done()
wait <- true
return
}
}
@@ -79,6 +82,8 @@ func testSend(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) {
t.Fatal(err)
}
<-wait
if v := mr.Header["test"]; v != "accept" {
t.Fatalf("Message not received from accepted side. Received: %s", v)
}
@@ -140,6 +145,8 @@ func TestLoopbackTunnel(t *testing.T) {
}
defer tun.Close()
time.Sleep(500 * time.Millisecond)
wait := make(chan bool)
var wg sync.WaitGroup
@@ -180,30 +187,15 @@ func testBrokenTunAccept(t *testing.T, tun Tunnel, wait chan bool, wg *sync.Wait
if err := c.Recv(m); err != nil {
t.Fatal(err)
}
tun.Close()
// re-start tunnel
err = tun.Connect()
if err != nil {
t.Fatal(err)
}
defer tun.Close()
// listen on some virtual address
tl, err = tun.Listen("test-tunnel")
if err != nil {
t.Fatal(err)
// close all the links
for _, link := range tun.Links() {
link.Close()
}
// receiver ready; notify sender
wait <- true
// accept a connection
c, err = tl.Accept()
if err != nil {
t.Fatal(err)
}
// accept the message
m = new(transport.Message)
if err := c.Recv(m); err != nil {
@@ -272,6 +264,7 @@ func TestReconnectTunnel(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer tunB.Close()
// we manually override the tunnel.ReconnectTime value here
// this is so that we make the reconnects faster than the default 5s
@@ -282,6 +275,7 @@ func TestReconnectTunnel(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer tunA.Close()
wait := make(chan bool)

View File

@@ -40,14 +40,20 @@ func Extract(addr string) (string, error) {
}
var addrs []net.Addr
var loAddrs []net.Addr
for _, iface := range ifaces {
ifaceAddrs, err := iface.Addrs()
if err != nil {
// ignore error, interface can dissapear from system
continue
}
if iface.Flags&net.FlagLoopback != 0 {
loAddrs = append(loAddrs, ifaceAddrs...)
continue
}
addrs = append(addrs, ifaceAddrs...)
}
addrs = append(addrs, loAddrs...)
var ipAddr []byte
var publicIP []byte

View File

@@ -14,7 +14,7 @@ type Level int
const (
LevelFatal Level = iota
LevelInfo
LevelWarn
LevelError
LevelDebug
LevelTrace
)
@@ -29,16 +29,16 @@ var (
func init() {
switch os.Getenv("MICRO_LOG_LEVEL") {
case "trace":
level = LevelTrace
case "debug":
level = LevelDebug
case "info":
level = LevelInfo
case "trace":
level = LevelTrace
case "error":
level = LevelError
case "fatal":
level = LevelFatal
case "warn":
level = LevelWarn
}
}
@@ -98,14 +98,14 @@ func Infof(format string, v ...interface{}) {
WithLevelf(LevelInfo, format, v...)
}
// Warn provides warn level logging
func Warn(v ...interface{}) {
WithLevel(LevelWarn, v...)
// Error provides warn level logging
func Error(v ...interface{}) {
WithLevel(LevelError, v...)
}
// Warnf provides warn level logging
func Warnf(format string, v ...interface{}) {
WithLevelf(LevelWarn, format, v...)
// Errorf provides warn level logging
func Errorf(format string, v ...interface{}) {
WithLevelf(LevelError, format, v...)
}
// Fatal logs with Log and then exits with os.Exit(1)

View File

@@ -32,10 +32,10 @@ func (s *Socket) SetRemote(r string) {
// Accept passes a message to the socket which will be processed by the call to Recv
func (s *Socket) Accept(m *transport.Message) error {
select {
case <-s.closed:
return io.EOF
case s.recv <- m:
return nil
case <-s.closed:
return io.EOF
}
return nil
}
@@ -43,10 +43,17 @@ func (s *Socket) Accept(m *transport.Message) error {
// Process takes the next message off the send queue created by a call to Send
func (s *Socket) Process(m *transport.Message) error {
select {
case <-s.closed:
return io.EOF
case msg := <-s.send:
*m = *msg
case <-s.closed:
// see if we need to drain
select {
case msg := <-s.send:
*m = *msg
return nil
default:
return io.EOF
}
}
return nil
}
@@ -60,13 +67,6 @@ func (s *Socket) Local() string {
}
func (s *Socket) Send(m *transport.Message) error {
select {
case <-s.closed:
return io.EOF
default:
// no op
}
// make copy
msg := &transport.Message{
Header: make(map[string]string),
@@ -92,13 +92,6 @@ func (s *Socket) Send(m *transport.Message) error {
}
func (s *Socket) Recv(m *transport.Message) error {
select {
case <-s.closed:
return io.EOF
default:
// no op
}
// receive a message
select {
case msg := <-s.recv: