Compare commits
1012 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2b1e0a6fd6 | ||
|
0889b814bb | ||
|
a648c0d99c | ||
|
a291af59c4 | ||
|
d9f12731e1 | ||
|
4a838a8210 | ||
|
5969cc358e | ||
|
9f1a7e1139 | ||
|
dc257f5066 | ||
|
49b86c56e3 | ||
|
1be8258721 | ||
|
b2980aecb7 | ||
|
de3c3b27b2 | ||
|
a09eea8d4d | ||
|
62c067adcd | ||
|
59f6ca5564 | ||
|
895aa896bc | ||
|
74762edc42 | ||
|
101017a29c | ||
|
c725b4c797 | ||
|
1108cc5e91 | ||
|
6a9f5fac61 | ||
e6a34bcbe7 | |||
|
1d00f2f771 | ||
|
4a6570a772 | ||
|
a997a86e49 | ||
|
2d28ff72d7 | ||
|
2b1844971c | ||
|
49cc022ca2 | ||
|
8d2dc8a822 | ||
|
eeb6944ce5 | ||
|
add78f2967 | ||
|
bd6a5af2b5 | ||
|
ae08e9c106 | ||
|
77c2a021da | ||
|
1c19678d04 | ||
|
6d636b7ab3 | ||
|
009c598049 | ||
|
5cceb00df2 | ||
|
3e24276eb1 | ||
|
29c1076950 | ||
|
8dbacb34f8 | ||
|
8b306780ee | ||
|
9f7d374691 | ||
|
a18e53f028 | ||
|
2208839027 | ||
|
7a17a221ff | ||
|
8e1ff80b9e | ||
|
94bb0f4c08 | ||
|
dbe209ebe4 | ||
|
fa0d884cfe | ||
|
ed2bd68d28 | ||
|
97928e88f8 | ||
|
04cf86070c | ||
|
9df19e826e | ||
|
3f3c1919f4 | ||
|
d918694346 | ||
|
54fb61bba4 | ||
|
fac75866d9 | ||
|
a47ff65529 | ||
|
093bcedfe7 | ||
|
7b6d560bec | ||
|
43aa4e23bd | ||
|
bdd9ec560b | ||
|
0c03bf064b | ||
|
9a8c1b7ab8 | ||
|
e1ca40c1fc | ||
|
e75b99f89c | ||
|
ee7304a795 | ||
|
a82fd19209 | ||
|
1983d607f3 | ||
|
10093a0ea2 | ||
|
fc08a9146c | ||
|
cafd280718 | ||
|
11b104677a | ||
|
105596a0e5 | ||
|
0a37767127 | ||
|
31e195bac7 | ||
|
bdf1d20f4e | ||
|
8d6f82707a | ||
|
058fd8adbf | ||
|
13d1d2fa08 | ||
|
e666d0b807 | ||
|
39d7938405 | ||
|
65df711b01 | ||
|
fd6eb23307 | ||
|
fb3927fb8c | ||
|
891af703be | ||
|
474472eedd | ||
|
23d65145e6 | ||
|
9ea4919b9b | ||
|
f78e30770e | ||
|
bf9f319cdf | ||
|
ad28b72dd3 | ||
|
8425ae77f8 | ||
|
d7b9b2713b | ||
|
be788415ad | ||
|
a03791c581 | ||
|
ee922a3da6 | ||
|
624f1c1980 | ||
|
607a226e34 | ||
|
bac1bbfd97 | ||
|
7f9b3b5556 | ||
|
ba12513199 | ||
|
8fcfbc0d20 | ||
|
472186c1be | ||
|
60c05bd899 | ||
|
793e6013e5 | ||
|
071ab7aede | ||
|
eda8b00f84 | ||
|
eac2ab3c28 | ||
|
76fba34c2f | ||
|
491a42d352 | ||
|
5e85194a13 | ||
|
689ae7cfc7 | ||
|
19dbd77402 | ||
|
b194b3adc9 | ||
|
33a9b3bc17 | ||
|
6562154573 | ||
|
b32ebddf85 | ||
|
b3e3dac975 | ||
|
f20e4daa60 | ||
|
f67d87e99d | ||
|
36928b716c | ||
|
7c7b0ced5f | ||
|
c67ef7e017 | ||
|
dcd925f1e5 | ||
|
0ea56a5ffe | ||
|
821fda41ae | ||
|
1d311ab457 | ||
|
b699d969e4 | ||
|
994d371ff1 | ||
|
a91dad04ee | ||
|
b4261e8cf9 | ||
|
efcac3d009 | ||
|
770c7686ba | ||
|
11904e1137 | ||
|
1e009e52dd | ||
|
bf42c028fb | ||
|
0a4bd02503 | ||
|
63edfaa852 | ||
|
802cc8239a | ||
|
75b1a62af3 | ||
|
50b20413d3 | ||
|
fa5b3ee9d9 | ||
|
f50a50eeb3 | ||
|
e1e6199743 | ||
|
61dd2b8489 | ||
|
6ca298c61d | ||
f4fb923fb2 | |||
|
32a2005f6d | ||
|
37d1139a57 | ||
|
a90a74c9e2 | ||
|
78aed5beed | ||
|
59fccb82ec | ||
|
048065fe96 | ||
|
0b8ff3a8bb | ||
|
1892bd05a5 | ||
|
be6e8a7c78 | ||
|
df9055f69c | ||
|
649dd235c3 | ||
|
1af82df8b1 | ||
|
7098e59b5c | ||
|
31362bc331 | ||
|
4e2339749c | ||
|
9cecf2e097 | ||
|
fe9c68238f | ||
|
225b17559b | ||
|
e697912ee5 | ||
|
6358b9277d | ||
|
45c986c5f1 | ||
|
fa01ff6604 | ||
|
fe1e018e8e | ||
|
60ea537bbc | ||
|
488dc31743 | ||
|
b6915f0898 | ||
|
04dfe4e867 | ||
|
d8fe030a4b | ||
|
f40d4578d5 | ||
|
e0078bbcd5 | ||
|
c145f355dd | ||
943445270f | |||
|
61cde4a9f4 | ||
|
22aa7d14b3 | ||
|
a09b6729cc | ||
|
2fe64001c0 | ||
|
14c9c412cd | ||
|
361fdfba04 | ||
|
5c8d1ae2b9 | ||
|
81e20160f5 | ||
|
ef95b28e3d | ||
|
1781542964 | ||
|
45208992b5 | ||
|
847a01df82 | ||
|
ce33e3b072 | ||
|
ae12fd1021 | ||
|
95ae2688a2 | ||
|
2bcfb85613 | ||
|
d52a111735 | ||
|
d4bec24eb7 | ||
|
2338e7c9d2 | ||
|
f0e841595c | ||
|
a82af19d43 | ||
|
5d7254e79a | ||
|
7c21a1b92a | ||
|
587f64a87a | ||
|
cb9c4c3aef | ||
|
dda96cb87e | ||
|
ebae497a72 | ||
|
515014fbeb | ||
|
e9efcbe8dc | ||
|
5a52593e66 | ||
|
c61e12d5ee | ||
|
c2d59c1f4d | ||
|
812fe9e640 | ||
|
f95bccce84 | ||
|
81e7edd666 | ||
|
46fd205eda | ||
|
c2b307e5bb | ||
|
b7ac62f7d2 | ||
|
50d5c6402b | ||
|
d2a3fd0b04 | ||
|
51f4bc6d56 | ||
|
c3607c23e7 | ||
|
34b1c403bb | ||
|
91e057440d | ||
|
53ca742c66 | ||
|
b35dfb1086 | ||
|
d502e2f58a | ||
|
bc30efcf70 | ||
|
0415ead504 | ||
|
e95f44d3f8 | ||
|
0489ae91e9 | ||
|
4e02f444fd | ||
|
6027a81f06 | ||
|
01f0e70213 | ||
|
cb15fadcee | ||
|
1c8d15fe4b | ||
|
55f5937c8b | ||
|
56619f2745 | ||
|
0b59e2ce3d | ||
|
1ea6390eae | ||
|
303adca500 | ||
|
03700ae6c0 | ||
|
a1ddfa827e | ||
|
59751c02e6 | ||
|
e8e112144f | ||
|
59246e0412 | ||
|
0131e9468f | ||
|
d5951f1d7c | ||
|
f33b562c16 | ||
|
572fe58314 | ||
|
5602b93d7a | ||
|
64e438a8d4 | ||
|
b0b6b8fce2 | ||
|
417a05db60 | ||
|
11e42aac69 | ||
|
885ba8f905 | ||
|
caa74d1b5f | ||
|
f6b4a9da1c | ||
|
74c5102e41 | ||
|
7bd50cd251 | ||
|
df728aaddd | ||
|
ae934c19f1 | ||
|
e260cc4a24 | ||
|
631faff7b8 | ||
|
fd531349d7 | ||
|
27bab29e3c | ||
|
27af221fd2 | ||
|
6e28e7a86f | ||
|
ff69d46c98 | ||
|
e966944ae5 | ||
|
8da77a3ddc | ||
|
8289dbabc4 | ||
|
d50f30d743 | ||
|
f8533551a4 | ||
|
7e46ff5d92 | ||
|
6b1eef5354 | ||
|
b29da80539 | ||
|
a725ab2c94 | ||
|
1582aa1572 | ||
|
5352d53346 | ||
|
7d884eff9d | ||
|
c9af88184b | ||
|
f25ad35f0a | ||
|
55ab44c8be | ||
|
59c1680594 | ||
|
b804303aa0 | ||
|
55a15ecf12 | ||
|
caa1bcf9fe | ||
|
e2b2a30668 | ||
|
a9be1288d2 | ||
|
679214e598 | ||
|
6064e1426c | ||
|
d28a868e46 | ||
|
398acc67ca | ||
|
ce578800d0 | ||
|
6307d6ba51 | ||
|
283c85d256 | ||
|
9bd0fb9125 | ||
|
c445aed6b1 | ||
|
1d8c66780e | ||
|
219efd27e9 | ||
|
ac9001ac74 | ||
|
2896614595 | ||
|
bca12a7003 | ||
1c5a4c470f | |||
|
744b19d625 | ||
|
5865e89bed | ||
|
3a10b1cdde | ||
|
5db7514a91 | ||
a957e90ca8 | |||
|
18cf025056 | ||
|
b55b7d6b20 | ||
|
26b5d1994a | ||
|
30aec5b872 | ||
|
b0626089f3 | ||
|
af3d4e595f | ||
|
40c09eed1c | ||
|
fe46e7a9e9 | ||
|
78647c7027 | ||
|
26755f86b1 | ||
|
898848795b | ||
|
24efbe6a41 | ||
|
97433f716f | ||
|
5200febaea | ||
a1eaf9cc20 | |||
|
3f8af8c1e0 | ||
|
becaeefcba | ||
7d5bdcf993 | |||
|
81a7cf458c | ||
|
a4ea61334b | ||
|
3fbba9f83d | ||
|
4a11a4c546 | ||
|
265233517e | ||
c581ceb1dc | |||
|
bb1a1358b7 | ||
|
29fb58db39 | ||
|
b6f0164501 | ||
|
2c0801fc1c | ||
|
34c92f4964 | ||
|
f9b900b2ca | ||
|
b5d65305db | ||
b8e96f45d4 | |||
90f9b0d0c9 | |||
|
972c0989af | ||
|
5f04fd58ab | ||
|
da04aa8ae8 | ||
|
62bf0df864 | ||
|
91e9c0cb62 | ||
|
3356b83f24 | ||
|
795ec509fd | ||
|
93d66afe8c | ||
|
c840cee404 | ||
|
e48cce6485 | ||
|
b2ecd93404 | ||
|
2928c66624 | ||
|
4613a820ca | ||
|
ecdadef633 | ||
|
7f1dea72f2 | ||
|
612f872f76 | ||
|
13d2a9ec7a | ||
|
9fab47ecdb | ||
|
265e8ade05 | ||
|
60e0e81523 | ||
|
b01357058a | ||
|
7deafbc5ce | ||
|
9e177be560 | ||
|
ee9776e7b2 | ||
|
3f7f2afc7b | ||
|
4e965e4ce5 | ||
|
6f1c30aef5 | ||
|
00bbb3ac61 | ||
|
ce1942c578 | ||
|
df7169c9f2 | ||
|
5e59db4c6d | ||
|
25b3fda25b | ||
|
9678daeafa | ||
|
9ed257f151 | ||
|
107124e5dc | ||
|
ce4acfa892 | ||
|
6bf4828296 | ||
|
76b4e78a6a | ||
|
8b6475b8d4 | ||
|
c3ed83dfba | ||
|
114bc1e18b | ||
|
af94899b54 | ||
|
266b6dbc64 | ||
|
5932dd753c | ||
86a6328254 | |||
|
0d33c029a9 | ||
|
8ecd2381a4 | ||
|
7318807dce | ||
|
811275be26 | ||
|
3f3fd38601 | ||
|
44dd0b1302 | ||
|
6475c1f3ad | ||
|
deabf0b8c9 | ||
|
04ee4b04ad | ||
|
2892686c5f | ||
|
8ee31a63f1 | ||
|
4e363da91f | ||
|
8b63df7a98 | ||
|
b06854b0d5 | ||
|
39bf71376a | ||
|
12d9c5b187 | ||
|
d2eba3f8f9 | ||
|
19f2f8b161 | ||
|
d41185eb84 | ||
|
c420fa2dec | ||
|
db03a564fb | ||
9763820c75 | |||
|
9095b99f6b | ||
|
080363e8c4 | ||
|
252667398e | ||
|
f82c267d81 | ||
|
61fe552ac4 | ||
|
7013e7467f | ||
|
dbc537007d | ||
|
95045be83d | ||
|
52ccd900c7 | ||
|
64a251d69a | ||
|
cae4148594 | ||
|
38e29c5101 | ||
|
8dc3fb964e | ||
|
212144d658 | ||
|
11d81221cc | ||
|
55252cbc32 | ||
|
c87a58db0a | ||
49d73faa5f | |||
da6c1be607 | |||
|
94d409b180 | ||
|
d6e97c5970 | ||
|
64d5a528ca | ||
|
538d3752f9 | ||
|
fb5b358ae2 | ||
|
6a0082741c | ||
|
5744050943 | ||
|
168cc06827 | ||
|
fa01cadc35 | ||
|
342c29de7d | ||
|
eeed493766 | ||
|
90d7a87914 | ||
|
a1c6cdf193 | ||
|
bec13a45cd | ||
|
4107733453 | ||
|
97c1300f53 | ||
|
0af8be35bb | ||
|
a91b3f3e8b | ||
|
383658edf2 | ||
|
16754a7477 | ||
|
58b25d7241 | ||
|
1c7d44282e | ||
|
8e9eef794f | ||
|
920c026f14 | ||
|
946c76cb03 | ||
|
43d11a9b8d | ||
|
9f481542f3 | ||
|
cffa5b6b50 | ||
|
8867539d78 | ||
|
671408b3a5 | ||
|
bdb62e8ed1 | ||
|
72522a869a | ||
|
fd5c29addc | ||
|
65b1283459 | ||
|
5ffe367cae | ||
|
5ae3e179b9 | ||
c696a859be | |||
|
174b01ca29 | ||
|
c433de80cd | ||
|
f2b4c07a00 | ||
|
929ffdcc42 | ||
|
5aa28dfb0d | ||
|
a9e8fc6039 | ||
|
0b1e6d7eaf | ||
|
1ffa289d39 | ||
|
68419cc024 | ||
8227206208 | |||
|
6f28852e1b | ||
|
0e3550229b | ||
|
f9400ba713 | ||
|
ce080d76c6 | ||
|
254045e9f3 | ||
|
b84134581c | ||
|
f67c5e779f | ||
|
4a694c9d02 | ||
|
24b8d2a315 | ||
|
2f3c251b00 | ||
|
c1b0a968ae | ||
|
81e9298be6 | ||
|
45cd14c4b7 | ||
|
8579c8b321 | ||
|
bd37e67839 | ||
|
d3151f1f0f | ||
|
c45ea62ea8 | ||
|
c14bf5dc4e | ||
|
292da40886 | ||
|
6f7702a093 | ||
|
a94a95ab55 | ||
|
e8d2f207d8 | ||
|
bd1918900e | ||
|
cf3af68e31 | ||
|
15e3b9b4c0 | ||
|
107a7ab07f | ||
|
e9dfccc616 | ||
|
f88518d994 | ||
|
ee35fe61af | ||
|
dee63b2b2c | ||
|
0aa01b2ebf | ||
|
f089a89e8a | ||
|
174fbde049 | ||
|
967d7ecda7 | ||
|
fb76755684 | ||
|
cf593e7c50 | ||
|
74286c2939 | ||
|
f9c639af4e | ||
|
dab0f3223f | ||
|
d89256d8d5 | ||
|
99b410c81b | ||
|
92b7d2db3b | ||
|
20c6c36bc4 | ||
|
1f626a55ed | ||
|
b42d242ec1 | ||
|
51922c1763 | ||
|
1c6b85e05d | ||
|
e85863d6cc | ||
|
5d7bf53f78 | ||
|
44c0f1946d | ||
|
1c9ada6413 | ||
|
c170189efb | ||
|
3831199600 | ||
|
1f658cfbff | ||
|
f26d470db1 | ||
|
f5b8a12106 | ||
|
494eb13534 | ||
|
4db1e09798 | ||
|
232c8ac7a1 | ||
|
68d0efbeaa | ||
|
70aaca9876 | ||
|
3ce71e12ff | ||
|
fb3d729681 | ||
|
d65658c890 | ||
|
3fc04f4dff | ||
|
82f94c7861 | ||
|
ecac392dbe | ||
|
4e5a568063 | ||
|
87de2ecaa0 | ||
|
4f1dd3f965 | ||
|
71122836b8 | ||
|
b67be88952 | ||
|
83b232ae26 | ||
|
776284b187 | ||
|
53ee4ee482 | ||
|
35729092e0 | ||
|
4f5db08238 | ||
|
68789af4ea | ||
|
b3d4a7f740 | ||
|
f4f178c130 | ||
|
1ff65e140a | ||
|
326156671d | ||
|
6353b2b894 | ||
|
caca93f65b | ||
|
bf4a73d5c0 | ||
|
fe180148a1 | ||
|
842fc01568 | ||
|
d4832e8f34 | ||
|
5ac5865154 | ||
|
f07a6ac29b | ||
|
d64f8c665e | ||
|
407694232a | ||
|
418b8648bb | ||
|
85e273afa5 | ||
|
ab9fa20a50 | ||
|
4fddd69229 | ||
|
317cf76566 | ||
|
f792fac1cc | ||
|
a89d1edc41 | ||
|
d3140c0fc2 | ||
|
3d5d9be02a | ||
|
5c38f38dd9 | ||
|
63fd8b9d1b | ||
|
3aedea4c56 | ||
|
0da9dff077 | ||
|
05774f2c76 | ||
|
4885bba2ac | ||
|
9d559848c2 | ||
|
2ae583ce94 | ||
|
7c1e22b607 | ||
|
7d2afa34a0 | ||
|
a6e95d389f | ||
|
b1d5dc20fa | ||
|
be5093798b | ||
|
3759c9c091 | ||
|
4936a2e1a5 | ||
|
ca934951ad | ||
|
ca18089382 | ||
|
7b1f5584ab | ||
|
fed5af68e6 | ||
|
fdfeb437f9 | ||
|
a46133f059 | ||
|
9bd0a8f3b5 | ||
|
44b794722e | ||
|
247249050b | ||
|
b1fed01752 | ||
|
df1e680256 | ||
|
854b01c20c | ||
|
745299bce5 | ||
|
607fdb3fcb | ||
|
a1342c23fb | ||
|
1cea2f5bba | ||
|
a1b4786682 | ||
|
b701da6d69 | ||
|
f77df51f60 | ||
|
d6c6e7815e | ||
|
01492997ea | ||
|
174f1b857c | ||
|
5029d80e68 | ||
|
b59c5a4488 | ||
|
f7f65b82e6 | ||
|
2e47fdc6f5 | ||
|
18ea19a122 | ||
|
4d75b936f8 | ||
|
62aaa72715 | ||
|
8c344ed55b | ||
|
db843c8d87 | ||
|
dd7677e6cc | ||
|
a4f0dd8939 | ||
|
591e87448b | ||
|
09a202ccf0 | ||
|
723c17fdd7 | ||
|
9bd96d4cc1 | ||
|
9bfe4d9bf7 | ||
|
76eee089e3 | ||
|
cfa2b668e2 | ||
|
a96f6adf07 | ||
|
49fe5d9fd5 | ||
|
21469a0427 | ||
|
e351e9518f | ||
|
fc89c9831e | ||
|
5e5d57d954 | ||
|
98e1f2c2d3 | ||
|
59a3e7d4f4 | ||
|
1be6ec9b3c | ||
|
f6931f3da7 | ||
|
b2f99a27b7 | ||
|
1f5ebf330d | ||
|
0dee11e006 | ||
|
b55018eaa1 | ||
|
77108771db | ||
|
5a6e73d4a8 | ||
|
7a4bff4e9f | ||
|
d5ce96da24 | ||
|
837597fe6f | ||
|
96e564e402 | ||
|
fe94237448 | ||
|
107b7419b7 | ||
|
226d55d752 | ||
|
88ef785127 | ||
|
44473f954f | ||
|
fe5846603a | ||
|
61800fb7d7 | ||
|
ec2fbde979 | ||
|
b886dd4b8f | ||
|
94adeebed4 | ||
|
d043ca15c1 | ||
|
ad823d5177 | ||
|
89d71417f5 | ||
|
9d9683b6f9 | ||
|
0edcd5c8dc | ||
|
2e1432d5dc | ||
|
e4f8b5de70 | ||
|
e9dcff49e0 | ||
|
fa6590f999 | ||
|
fd8a0fb2f5 | ||
|
b594547408 | ||
|
2c00e726b6 | ||
|
68a3fc7996 | ||
|
2fb2d7145e | ||
|
6fe9f2a958 | ||
|
86984a8a8a | ||
|
cfb846ee7e | ||
|
e36960612a | ||
|
04320d69ff | ||
|
c4b6d0f3a8 | ||
|
3c6b6553fb | ||
|
d5658ab0b0 | ||
|
2244eb8597 | ||
|
05eacd74c8 | ||
|
b80654bf7e | ||
|
0941a0f031 | ||
|
4de346920f | ||
|
b8815dff14 | ||
|
b1163b9dee | ||
|
af5d7a3420 | ||
|
b5f33b2aaa | ||
|
a9c85eda68 | ||
|
b5ca40a91a | ||
|
b81bb07afc | ||
|
8d2b12258f | ||
|
31026da2a1 | ||
|
1129803bcb | ||
|
25148af44c | ||
|
36675aff1e | ||
|
b6db0d2663 | ||
|
2370fb1209 | ||
|
519e8a7213 | ||
|
308424488b | ||
|
5d77ce9e9b | ||
|
9eb6262168 | ||
b722798caa | |||
|
0cf7b70423 | ||
|
03b8ceab5c | ||
|
e8a53610f1 | ||
|
e48155118f | ||
|
6477c3afff | ||
|
57647772c8 | ||
|
4b73ac9dc5 | ||
|
3f3f1272b3 | ||
|
859ecb1872 | ||
|
204c7d1fcf | ||
|
8417361bce | ||
|
d85ca7abd2 | ||
|
e973bfaa25 | ||
|
27bd9581bf | ||
|
16c7b3a390 | ||
|
f933457cc1 | ||
|
219d759f1d | ||
|
b90871c241 | ||
|
1322fb0d9d | ||
|
0eb69e4f9a | ||
|
1ed73d0f91 | ||
|
866631df1d | ||
|
d5e962c4a8 | ||
|
9ec27392de | ||
|
de1d9122ea | ||
|
87a5e85062 | ||
|
da572041ca | ||
|
a725998c0a | ||
|
f3b723ca44 | ||
|
e1bb4d7379 | ||
|
2d7975a7ce | ||
|
8b77d62ed4 | ||
|
ef7bb46884 | ||
|
06975f64b7 | ||
|
a4c04d8f50 | ||
|
b2577e6022 | ||
|
77f3e7ef48 | ||
|
6f2a8298ef | ||
|
9e33637213 | ||
|
99dbed0b67 | ||
|
2b8210a106 | ||
|
dfcedbab1e | ||
|
140e3d576c | ||
|
afa1f50435 | ||
|
cb22136a35 | ||
|
ae40553bad | ||
|
855cd5ecf4 | ||
|
f23c6d91ba | ||
|
c3b430af53 | ||
|
3d2bf7d4f6 | ||
|
6c2b9d7636 | ||
|
be5799b09f | ||
|
7fe64192a7 | ||
|
624d37cf13 | ||
|
1f23c8a85a | ||
|
96e79c4498 | ||
|
1b08036a0b | ||
|
c52651c4d0 | ||
|
9f880a1215 | ||
|
39755721d0 | ||
|
ccda1d3559 | ||
|
61ee436cc4 | ||
|
04a5d884da | ||
|
0ec1b840fd | ||
|
71ab35e055 | ||
|
fa0d020556 | ||
|
d8dc713e2d | ||
|
d38c8b23f2 | ||
|
4260913b45 | ||
|
ac5eb5da47 | ||
|
2434c7b2a7 | ||
|
9fbc88a60f | ||
|
444cc59250 | ||
|
95e4ed8ee9 | ||
|
4eb1aaae85 | ||
|
46450ba507 | ||
|
f13887f604 | ||
|
66769e671f | ||
|
7e05d2c440 | ||
|
0abeb3f660 | ||
|
a38482ffcb | ||
|
ee74e26582 | ||
|
6222bc2a1e | ||
|
05e62a2b95 | ||
|
cdbab3df66 | ||
|
38d6ffdf9a | ||
|
e586763301 | ||
|
3201b4cb36 | ||
|
837cb4fc11 | ||
|
21dc7bcccf | ||
|
a811b4be3d | ||
|
9147d378bc | ||
|
b7b968ad74 | ||
|
8e8a4c1a9d | ||
|
bc29164f77 | ||
|
e161b2fa84 | ||
|
a72a2f717d | ||
|
2599ee8591 | ||
|
364c5a4861 | ||
|
c8a675249d | ||
|
0cdfc7b9ea | ||
|
0fc4c180ee | ||
|
e5f6480f8a | ||
|
ccb6778f7f | ||
|
ef86c9625b | ||
|
b23ee58865 | ||
|
323a72be34 | ||
|
d72e91fb38 | ||
|
b91c3147e7 | ||
|
ef91d836eb | ||
|
77c6c9781b | ||
|
fa4ff8921e | ||
|
d6be91e8af | ||
|
588484c3bf | ||
|
d58eb51976 | ||
|
35cf2a5739 | ||
|
2dfbe93d65 | ||
|
16fcf1fbda | ||
|
cbce5490d7 | ||
|
4c709f7ac1 | ||
|
baf4c05663 | ||
|
195c6a8c90 | ||
|
f91d0408ab | ||
|
eec780aaa7 | ||
|
f0a1031e97 | ||
|
a6668ae057 | ||
|
af5421c2cf | ||
|
2406ef9999 | ||
|
af585d3a57 | ||
|
97cf478f71 | ||
|
ec6a30be37 | ||
|
634c55e2d7 | ||
|
cb0de43dba | ||
|
63d535aea9 | ||
|
6819386e05 | ||
|
988603f87e | ||
|
9ca7d90f11 | ||
|
6ec32805d0 | ||
|
c1c173dfe5 | ||
|
ce18de2647 | ||
|
3e3bbe3fd0 | ||
|
b5eea02f7a | ||
|
08c6f60b0f | ||
|
065c7d5616 | ||
|
a5ce3e32da | ||
|
3bfbcd5e6a | ||
|
b6c6b13277 | ||
|
04b31d374c | ||
|
e828a099c5 | ||
|
2c16c7e62f | ||
|
1f44d7a4a1 | ||
|
b076ef906a | ||
|
c669a2b155 | ||
|
48a3e51aca | ||
|
e8aaca27d3 | ||
|
5596407144 | ||
|
7971b1b7f9 | ||
|
dafbacbdcb | ||
|
df5657dcd1 | ||
|
bb595c85b2 | ||
|
bc6187ea89 | ||
|
ed1faa7a5c | ||
|
1d9298ae2b | ||
|
dddfb6f878 | ||
|
ec354934e3 | ||
|
b01c8e06e0 | ||
|
97b1071f7e | ||
|
1527a84297 | ||
|
5ddfd911ba | ||
|
2310ee424c | ||
|
2522d8cb96 | ||
|
d198765c6c | ||
|
1840b5bd74 | ||
|
9161b20d6b | ||
|
a1ba1482c5 | ||
|
d0761e0a1b | ||
|
4b1a7abb42 | ||
|
e33bd17894 | ||
|
cc5d811a83 | ||
|
e15389febb | ||
|
6d63c3777f | ||
|
d8a1b47954 | ||
|
b9a2f719a0 | ||
|
46a9767648 | ||
|
dd9f42e3b9 | ||
|
f2c8492c77 | ||
|
407381912b | ||
|
d559ce9da2 | ||
|
7ab3934eb7 | ||
|
0075477df0 | ||
|
d5be2136ad | ||
|
c718b8bf93 | ||
|
a24818ee54 | ||
|
66db0ac52c | ||
|
b9c437fbfe | ||
|
147899283c | ||
|
5b991cd2c2 | ||
|
bb64f94313 | ||
|
4f4b3d3bae | ||
|
eb4a709195 | ||
|
6c21b31226 | ||
|
6eb6d050ed | ||
|
6c7582a6be | ||
|
3ea4490d6c | ||
|
b50c44a758 | ||
|
ec6318befc | ||
|
5440325a18 | ||
|
fb13877904 | ||
|
2f5e3c66b9 | ||
|
a8d4299df9 | ||
|
90745c14f2 | ||
|
86665454e7 | ||
|
4f5a849211 | ||
|
bf53c16e4b | ||
|
6c3631728b | ||
|
2cdfed359f | ||
|
956be5c59d | ||
|
52d9d75dfa | ||
|
0d94784e72 | ||
|
65c2de5a79 | ||
|
6fa9d7270f | ||
|
140c830af1 | ||
|
b37837ad92 | ||
|
10b64af0b3 | ||
|
5d01284574 | ||
|
ff81e4b246 | ||
|
e955e3f798 | ||
|
a17a8b3372 | ||
|
e1d56fbf58 | ||
|
e7d8cdda44 | ||
|
690640eeeb | ||
|
4f788c6fc7 | ||
|
f50bd400f8 | ||
|
b457ec1990 | ||
|
ffa6b551f4 | ||
|
3d03fe4076 | ||
|
6eecb199e9 | ||
|
7479515099 | ||
|
6e3d53e1ee | ||
|
721c5e6857 | ||
|
7d033818cf | ||
|
00ab58f61b | ||
|
b3aef71fdb | ||
|
8606f1e143 | ||
|
927fac2cec | ||
|
6ab86c9e57 | ||
|
db8e2620cb | ||
|
d09b7dbbef | ||
|
a4f5772555 | ||
|
731f6f74dd | ||
|
5e7208119e | ||
|
470304ef87 | ||
|
a6ab4d7b4b | ||
|
87b56d46ac | ||
|
371b23d055 | ||
|
f97565ef0a | ||
|
0888d2fbbc | ||
|
75e20b5bf7 | ||
|
443fc0ebde | ||
|
35e7b9551f | ||
|
6daf4fda72 | ||
|
36623bfe50 | ||
|
6128d18ee0 | ||
|
abadb2211e | ||
|
ca267f73de | ||
|
d8608b2343 | ||
|
ed8d28c9ab | ||
|
88e47b9b06 | ||
|
1b0295de0d | ||
|
9448d7c164 | ||
|
8c3eec9f2a | ||
|
e53484302c | ||
|
db89fc4efe | ||
|
e1599b0f17 | ||
|
a09d5d2e9a | ||
|
6c1f1d66f7 | ||
|
a6e1287b27 | ||
|
fcec6e8eae | ||
|
30dd3f54f0 | ||
|
6beae23afd | ||
|
718780367e | ||
|
ba99f037fb | ||
|
80dc0b97a9 | ||
|
1a32e3a11d | ||
|
955dc2a23d | ||
|
934b8eb86d | ||
|
b7f510ff64 | ||
|
353eade6c3 | ||
|
a133e61c2d | ||
|
99d39e743b | ||
|
0cdac2aa36 | ||
|
75871287a1 | ||
|
fb750a0bb1 | ||
|
c6e15ef2d1 | ||
|
f787cc0ee0 | ||
|
c2d85a6e1f | ||
|
86f0c06fac | ||
|
0aea8e3163 | ||
|
4ea27517b5 | ||
|
f8e68ae101 | ||
|
f848041c49 | ||
|
dfbd730b8c |
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
issuehunt: micro/development
|
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: For reporting bugs in go-micro
|
||||
title: "[BUG]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
1. What are you trying to do?
|
||||
2. What did you expect to happen?
|
||||
3. What happens instead?
|
||||
|
||||
**How to reproduce the bug:**
|
||||
|
||||
If possible, please include a minimal code snippet here.
|
||||
|
||||
**Environment:**
|
||||
Go Version: please paste `go version` output here
|
||||
```
|
||||
please paste `go env` output here
|
||||
```
|
17
.github/ISSUE_TEMPLATE/feature-request---enhancement.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature-request---enhancement.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request / Enhancement
|
||||
about: If you have a need not served by go-micro
|
||||
title: "[FEATURE]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
14
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a question about go-micro
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Before asking, please check if your question has already been answered:
|
||||
|
||||
1. Check the documentation - https://micro.mu/docs/
|
||||
2. Check the examples and plugins - https://github.com/micro/examples & https://github.com/micro/go-plugins
|
||||
3. Search existing issues
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,7 @@
|
||||
# Develop tools
|
||||
/.vscode/
|
||||
/.idea/
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
|
26
.golangci.yml
Normal file
26
.golangci.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
run:
|
||||
deadline: 10m
|
||||
linters:
|
||||
disable-all: false
|
||||
enable-all: false
|
||||
enable:
|
||||
- megacheck
|
||||
- staticcheck
|
||||
- deadcode
|
||||
- varcheck
|
||||
- gosimple
|
||||
- unused
|
||||
- prealloc
|
||||
- scopelint
|
||||
- gocritic
|
||||
- goimports
|
||||
- unconvert
|
||||
- govet
|
||||
- nakedret
|
||||
- structcheck
|
||||
- gosec
|
||||
disable:
|
||||
- maligned
|
||||
- interfacer
|
||||
- typecheck
|
||||
- dupl
|
10
.travis.yml
10
.travis.yml
@@ -1,8 +1,14 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- GO111MODULE=on IN_TRAVIS_CI=yes
|
||||
before_script:
|
||||
- go install github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
script:
|
||||
- golangci-lint run || true
|
||||
- go test -v -race ./... || true
|
||||
- go test -v ./...
|
||||
notifications:
|
||||
slack:
|
||||
secure: aEvhLbhujaGaKSrOokiG3//PaVHTIrc3fBpoRbCRqfZpyq6WREoapJJhF+tIpWWOwaC9GmChbD6aHo/jMUgwKXVyPSaNjiEL87YzUUpL8B2zslNp1rgfTg/LrzthOx3Q1TYwpaAl3to0fuHUVFX4yMeC2vuThq7WSXgMMxFCtbc=
|
||||
|
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM golang:1.13-alpine
|
||||
|
||||
RUN mkdir /user && \
|
||||
echo 'nobody:x:65534:65534:nobody:/:' > /user/passwd && \
|
||||
echo 'nobody:x:65534:' > /user/group
|
||||
|
||||
ENV GO111MODULE=on
|
||||
RUN apk --no-cache add make git gcc libtool musl-dev ca-certificates && \
|
||||
rm -rf /var/cache/apk/* /tmp/*
|
||||
|
||||
WORKDIR /
|
||||
COPY ./go.mod ./go.sum ./
|
||||
RUN go mod download && rm go.mod go.sum
|
13
README.md
13
README.md
@@ -12,7 +12,7 @@ but everything can be easily swapped out.
|
||||
|
||||
Plugins are available at [github.com/micro/go-plugins](https://github.com/micro/go-plugins).
|
||||
|
||||
Follow us on [Twitter](https://twitter.com/microhq) or join the [Slack](http://slack.micro.mu/) community.
|
||||
Follow us on [Twitter](https://twitter.com/microhq) or join the [Community](https://micro.mu/slack).
|
||||
|
||||
## Features
|
||||
|
||||
@@ -20,8 +20,7 @@ Go Micro abstracts away the details of distributed systems. Here are the main fe
|
||||
|
||||
- **Service Discovery** - Automatic service registration and name resolution. Service discovery is at the core of micro service
|
||||
development. When service A needs to speak to service B it needs the location of that service. The default discovery mechanism is
|
||||
multicast DNS (mdns), a zeroconf system. You can optionally set gossip using the SWIM protocol for p2p networks or consul for a
|
||||
resilient cloud-native setup.
|
||||
multicast DNS (mdns), a zeroconf system.
|
||||
|
||||
- **Load Balancing** - Client side load balancing built on service discovery. Once we have the addresses of any number of instances
|
||||
of a service we now need a way to decide which node to route to. We use random hashed load balancing to provide even distribution
|
||||
@@ -33,11 +32,11 @@ and server handle this by default. This includes protobuf and json by default.
|
||||
|
||||
- **Request/Response** - RPC based request/response with support for bidirectional streaming. We provide an abstraction for synchronous
|
||||
communication. A request made to a service will be automatically resolved, load balanced, dialled and streamed. The default
|
||||
transport is http/1.1 or http2 when tls is enabled.
|
||||
transport is [gRPC](https://grpc.io/).
|
||||
|
||||
- **Async Messaging** - PubSub is built in as a first class citizen for asynchronous communication and event driven architectures.
|
||||
Event notifications are a core pattern in micro service development. The default messaging is point-to-point http/1.1 or http2 when tls
|
||||
is enabled.
|
||||
Event notifications are a core pattern in micro service development. The default messaging system is an embedded [NATS](https://nats.io/)
|
||||
server.
|
||||
|
||||
- **Pluggable Interfaces** - Go Micro makes use of Go interfaces for each distributed system abstraction. Because of this these interfaces
|
||||
are pluggable and allows Go Micro to be runtime agnostic. You can plugin any underlying technology. Find plugins in
|
||||
@@ -45,5 +44,5 @@ are pluggable and allows Go Micro to be runtime agnostic. You can plugin any und
|
||||
|
||||
## Getting Started
|
||||
|
||||
See the [docs](https://micro.mu/docs/go-micro.html) for detailed information on the architecture, installation and use of go-micro.
|
||||
See the [docs](https://micro.mu/docs/framework.html) for detailed information on the architecture, installation and use of go-micro.
|
||||
|
||||
|
@@ -32,5 +32,5 @@ Go Micro把分布式系统的各种细节抽象出来。下面是它的主要特
|
||||
|
||||
## 快速上手
|
||||
|
||||
更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/go-micro_cn.html)。
|
||||
更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/cn/)。
|
||||
|
||||
|
1
_config.yml
Normal file
1
_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-architect
|
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/micro/cli"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/agent/input"
|
||||
)
|
||||
|
||||
@@ -36,21 +36,21 @@ type discordInput struct {
|
||||
|
||||
func (d *discordInput) Flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "discord_token",
|
||||
EnvVar: "MICRO_DISCORD_TOKEN",
|
||||
Usage: "Discord token (prefix with Bot if it's for bot account)",
|
||||
&cli.StringFlag{
|
||||
Name: "discord_token",
|
||||
EnvVars: []string{"MICRO_DISCORD_TOKEN"},
|
||||
Usage: "Discord token (prefix with Bot if it's for bot account)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "discord_whitelist",
|
||||
EnvVar: "MICRO_DISCORD_WHITELIST",
|
||||
Usage: "Discord Whitelist (seperated by ,)",
|
||||
&cli.StringFlag{
|
||||
Name: "discord_whitelist",
|
||||
EnvVars: []string{"MICRO_DISCORD_WHITELIST"},
|
||||
Usage: "Discord Whitelist (seperated by ,)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "discord_prefix",
|
||||
Usage: "Discord Prefix",
|
||||
EnvVar: "MICRO_DISCORD_PREFIX",
|
||||
Value: "Micro ",
|
||||
&cli.StringFlag{
|
||||
Name: "discord_prefix",
|
||||
Usage: "Discord Prefix",
|
||||
EnvVars: []string{"MICRO_DISCORD_PREFIX"},
|
||||
Value: "Micro ",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"github.com/micro/cli"
|
||||
"github.com/micro/cli/v2"
|
||||
)
|
||||
|
||||
type EventType string
|
||||
|
@@ -4,7 +4,7 @@ import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/micro/cli"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/agent/input"
|
||||
"github.com/nlopes/slack"
|
||||
)
|
||||
@@ -26,15 +26,15 @@ func init() {
|
||||
|
||||
func (p *slackInput) Flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "slack_debug",
|
||||
Usage: "Slack debug output",
|
||||
EnvVar: "MICRO_SLACK_DEBUG",
|
||||
&cli.BoolFlag{
|
||||
Name: "slack_debug",
|
||||
Usage: "Slack debug output",
|
||||
EnvVars: []string{"MICRO_SLACK_DEBUG"},
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "slack_token",
|
||||
Usage: "Slack token",
|
||||
EnvVar: "MICRO_SLACK_TOKEN",
|
||||
&cli.StringFlag{
|
||||
Name: "slack_token",
|
||||
Usage: "Slack token",
|
||||
EnvVars: []string{"MICRO_SLACK_TOKEN"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/forestgiant/sliceutil"
|
||||
"github.com/micro/go-micro/agent/input"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
"gopkg.in/telegram-bot-api.v4"
|
||||
tgbotapi "gopkg.in/telegram-bot-api.v4"
|
||||
)
|
||||
|
||||
type telegramConn struct {
|
||||
@@ -44,11 +44,9 @@ func (tc *telegramConn) run() {
|
||||
tc.recv = updates
|
||||
tc.syncCond.Signal()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tc.exit:
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-tc.exit:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,9 +5,9 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/micro/cli"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/agent/input"
|
||||
"gopkg.in/telegram-bot-api.v4"
|
||||
tgbotapi "gopkg.in/telegram-bot-api.v4"
|
||||
)
|
||||
|
||||
type telegramInput struct {
|
||||
@@ -34,20 +34,20 @@ func init() {
|
||||
|
||||
func (ti *telegramInput) Flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "telegram_debug",
|
||||
EnvVar: "MICRO_TELEGRAM_DEBUG",
|
||||
Usage: "Telegram debug output",
|
||||
&cli.BoolFlag{
|
||||
Name: "telegram_debug",
|
||||
EnvVars: []string{"MICRO_TELEGRAM_DEBUG"},
|
||||
Usage: "Telegram debug output",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "telegram_token",
|
||||
EnvVar: "MICRO_TELEGRAM_TOKEN",
|
||||
Usage: "Telegram token",
|
||||
&cli.StringFlag{
|
||||
Name: "telegram_token",
|
||||
EnvVars: []string{"MICRO_TELEGRAM_TOKEN"},
|
||||
Usage: "Telegram token",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "telegram_whitelist",
|
||||
EnvVar: "MICRO_TELEGRAM_WHITELIST",
|
||||
Usage: "Telegram bot's users (comma-separated values)",
|
||||
&cli.StringFlag{
|
||||
Name: "telegram_whitelist",
|
||||
EnvVars: []string{"MICRO_TELEGRAM_WHITELIST"},
|
||||
Usage: "Telegram bot's users (comma-separated values)",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,13 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/micro/go-bot/proto/bot.proto
|
||||
// source: github.com/micro/go-micro/agent/proto/bot.proto
|
||||
|
||||
package go_micro_bot
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -16,7 +18,7 @@ var _ = math.Inf
|
||||
// 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.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type HelpRequest struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
@@ -28,16 +30,17 @@ func (m *HelpRequest) Reset() { *m = HelpRequest{} }
|
||||
func (m *HelpRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*HelpRequest) ProtoMessage() {}
|
||||
func (*HelpRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_bot_654832eab83ed4b5, []int{0}
|
||||
return fileDescriptor_018e8d5b14a89d12, []int{0}
|
||||
}
|
||||
|
||||
func (m *HelpRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HelpRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HelpRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HelpRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *HelpRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HelpRequest.Merge(dst, src)
|
||||
func (m *HelpRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HelpRequest.Merge(m, src)
|
||||
}
|
||||
func (m *HelpRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_HelpRequest.Size(m)
|
||||
@@ -60,16 +63,17 @@ func (m *HelpResponse) Reset() { *m = HelpResponse{} }
|
||||
func (m *HelpResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*HelpResponse) ProtoMessage() {}
|
||||
func (*HelpResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_bot_654832eab83ed4b5, []int{1}
|
||||
return fileDescriptor_018e8d5b14a89d12, []int{1}
|
||||
}
|
||||
|
||||
func (m *HelpResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HelpResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HelpResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HelpResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *HelpResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HelpResponse.Merge(dst, src)
|
||||
func (m *HelpResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HelpResponse.Merge(m, src)
|
||||
}
|
||||
func (m *HelpResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_HelpResponse.Size(m)
|
||||
@@ -105,16 +109,17 @@ func (m *ExecRequest) Reset() { *m = ExecRequest{} }
|
||||
func (m *ExecRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExecRequest) ProtoMessage() {}
|
||||
func (*ExecRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_bot_654832eab83ed4b5, []int{2}
|
||||
return fileDescriptor_018e8d5b14a89d12, []int{2}
|
||||
}
|
||||
|
||||
func (m *ExecRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ExecRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ExecRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ExecRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ExecRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExecRequest.Merge(dst, src)
|
||||
func (m *ExecRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExecRequest.Merge(m, src)
|
||||
}
|
||||
func (m *ExecRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_ExecRequest.Size(m)
|
||||
@@ -144,16 +149,17 @@ func (m *ExecResponse) Reset() { *m = ExecResponse{} }
|
||||
func (m *ExecResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExecResponse) ProtoMessage() {}
|
||||
func (*ExecResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_bot_654832eab83ed4b5, []int{3}
|
||||
return fileDescriptor_018e8d5b14a89d12, []int{3}
|
||||
}
|
||||
|
||||
func (m *ExecResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ExecResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ExecResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ExecResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ExecResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExecResponse.Merge(dst, src)
|
||||
func (m *ExecResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExecResponse.Merge(m, src)
|
||||
}
|
||||
func (m *ExecResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_ExecResponse.Size(m)
|
||||
@@ -186,25 +192,25 @@ func init() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/micro/go-bot/proto/bot.proto", fileDescriptor_bot_654832eab83ed4b5)
|
||||
proto.RegisterFile("github.com/micro/go-micro/agent/proto/bot.proto", fileDescriptor_018e8d5b14a89d12)
|
||||
}
|
||||
|
||||
var fileDescriptor_bot_654832eab83ed4b5 = []byte{
|
||||
var fileDescriptor_018e8d5b14a89d12 = []byte{
|
||||
// 246 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x41, 0x4b, 0xc4, 0x30,
|
||||
0x10, 0x85, 0xb7, 0xba, 0xae, 0xec, 0xb4, 0x5e, 0x82, 0x48, 0xdd, 0x53, 0xcd, 0xc5, 0xbd, 0x98,
|
||||
0x82, 0x5e, 0x05, 0x0f, 0xa2, 0x78, 0xee, 0x3f, 0x68, 0xba, 0x43, 0x2c, 0x6c, 0x3b, 0x35, 0x99,
|
||||
0x82, 0xff, 0xc1, 0x3f, 0x2d, 0x4d, 0x72, 0x08, 0xcb, 0xde, 0xe6, 0x65, 0x86, 0xf7, 0xbe, 0x17,
|
||||
0x78, 0x34, 0x3d, 0x7f, 0xcf, 0x5a, 0x75, 0x34, 0xd4, 0x43, 0xdf, 0x59, 0xaa, 0x0d, 0x3d, 0x69,
|
||||
0xe2, 0x7a, 0xb2, 0xc4, 0x54, 0x6b, 0x62, 0xe5, 0x27, 0x51, 0x18, 0x52, 0xfe, 0x40, 0x69, 0x62,
|
||||
0x79, 0x03, 0xf9, 0x17, 0x1e, 0xa7, 0x06, 0x7f, 0x66, 0x74, 0x2c, 0x3f, 0xa1, 0x08, 0xd2, 0x4d,
|
||||
0x34, 0x3a, 0x14, 0xb7, 0x70, 0x35, 0xbb, 0xd6, 0x60, 0x99, 0x55, 0xd9, 0x7e, 0xdb, 0x04, 0x21,
|
||||
0x2a, 0xc8, 0x0f, 0xe8, 0x3a, 0xdb, 0x4f, 0xdc, 0xd3, 0x58, 0x5e, 0xf8, 0x5d, 0xfa, 0x24, 0x1f,
|
||||
0x20, 0xff, 0xf8, 0xc5, 0x2e, 0xda, 0x0a, 0x01, 0xeb, 0xd6, 0x1a, 0x57, 0x66, 0xd5, 0xe5, 0x7e,
|
||||
0xdb, 0xf8, 0x59, 0xbe, 0x42, 0x11, 0x4e, 0x62, 0xd4, 0x1d, 0x6c, 0x2c, 0xba, 0xf9, 0xc8, 0x3e,
|
||||
0xab, 0x68, 0xa2, 0x5a, 0x10, 0xd0, 0x5a, 0xb2, 0x31, 0x26, 0x88, 0xe7, 0xbf, 0x0c, 0xae, 0xdf,
|
||||
0x69, 0x18, 0xda, 0xf1, 0x20, 0xde, 0x60, 0xbd, 0x40, 0x8b, 0x7b, 0x95, 0x56, 0x53, 0x49, 0xaf,
|
||||
0xdd, 0xee, 0xdc, 0x2a, 0x04, 0xcb, 0xd5, 0x62, 0xb0, 0xa0, 0x9c, 0x1a, 0x24, 0x0d, 0x4e, 0x0d,
|
||||
0x52, 0x72, 0xb9, 0xd2, 0x1b, 0xff, 0xb5, 0x2f, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcb, 0x77,
|
||||
0xdf, 0x28, 0x85, 0x01, 0x00, 0x00,
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x50, 0x4d, 0x4b, 0xc4, 0x30,
|
||||
0x10, 0xdd, 0xea, 0xba, 0xb2, 0xd3, 0x7a, 0x09, 0x22, 0x75, 0x4f, 0x35, 0xa7, 0xbd, 0x98, 0x80,
|
||||
0x5e, 0x05, 0x0f, 0xa2, 0x78, 0xee, 0x3f, 0x68, 0xbb, 0x43, 0x2c, 0x6c, 0x3b, 0x35, 0x99, 0x82,
|
||||
0xff, 0xc1, 0x3f, 0x2d, 0x4d, 0x72, 0x08, 0xc5, 0xdb, 0x7b, 0x79, 0xe1, 0x7d, 0x0c, 0x68, 0xd3,
|
||||
0xf3, 0xd7, 0xdc, 0xaa, 0x8e, 0x06, 0x3d, 0xf4, 0x9d, 0x25, 0x6d, 0xe8, 0x31, 0x80, 0xc6, 0xe0,
|
||||
0xc8, 0x7a, 0xb2, 0xc4, 0xa4, 0x5b, 0x62, 0xe5, 0x91, 0x28, 0x0c, 0x29, 0xaf, 0xab, 0x96, 0x58,
|
||||
0xde, 0x40, 0xfe, 0x89, 0xe7, 0xa9, 0xc6, 0xef, 0x19, 0x1d, 0xcb, 0x0f, 0x28, 0x02, 0x75, 0x13,
|
||||
0x8d, 0x0e, 0xc5, 0x2d, 0x5c, 0xcd, 0xae, 0x31, 0x58, 0x66, 0x55, 0x76, 0xdc, 0xd7, 0x81, 0x88,
|
||||
0x0a, 0xf2, 0x13, 0xba, 0xce, 0xf6, 0x13, 0xf7, 0x34, 0x96, 0x17, 0x5e, 0x4b, 0x9f, 0xe4, 0x03,
|
||||
0xe4, 0xef, 0x3f, 0xd8, 0x45, 0x5b, 0x21, 0x60, 0xdb, 0x58, 0xe3, 0xca, 0xac, 0xba, 0x3c, 0xee,
|
||||
0x6b, 0x8f, 0xe5, 0x0b, 0x14, 0xe1, 0x4b, 0x8c, 0xba, 0x83, 0x9d, 0x45, 0x37, 0x9f, 0xd9, 0x67,
|
||||
0x15, 0x75, 0x64, 0x4b, 0x05, 0xb4, 0x96, 0x6c, 0x8c, 0x09, 0xe4, 0xe9, 0x37, 0x83, 0xeb, 0x37,
|
||||
0x1a, 0x86, 0x66, 0x3c, 0x89, 0x57, 0xd8, 0x2e, 0xa5, 0xc5, 0xbd, 0x4a, 0xa7, 0xa9, 0x64, 0xd7,
|
||||
0xe1, 0xf0, 0x9f, 0x14, 0x82, 0xe5, 0x66, 0x31, 0x58, 0xaa, 0xac, 0x0d, 0x92, 0x05, 0x6b, 0x83,
|
||||
0xb4, 0xb9, 0xdc, 0xb4, 0x3b, 0x7f, 0xda, 0xe7, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0xbd,
|
||||
0x39, 0x29, 0x8d, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
@@ -1,23 +1,13 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: github.com/micro/go-bot/proto/bot.proto
|
||||
// source: github.com/micro/go-micro/agent/proto/bot.proto
|
||||
|
||||
/*
|
||||
Package go_micro_bot is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
github.com/micro/go-bot/proto/bot.proto
|
||||
|
||||
It has these top-level messages:
|
||||
HelpRequest
|
||||
HelpResponse
|
||||
ExecRequest
|
||||
ExecResponse
|
||||
*/
|
||||
package go_micro_bot
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
@@ -34,7 +24,7 @@ var _ = math.Inf
|
||||
// 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.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
@@ -94,12 +84,12 @@ type CommandHandler interface {
|
||||
}
|
||||
|
||||
func RegisterCommandHandler(s server.Server, hdlr CommandHandler, opts ...server.HandlerOption) error {
|
||||
type _command interface {
|
||||
type command interface {
|
||||
Help(ctx context.Context, in *HelpRequest, out *HelpResponse) error
|
||||
Exec(ctx context.Context, in *ExecRequest, out *ExecResponse) error
|
||||
}
|
||||
type Command struct {
|
||||
_command
|
||||
command
|
||||
}
|
||||
h := &commandHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Command{h}, opts...))
|
25
api/api.go
25
api/api.go
@@ -57,14 +57,25 @@ func Encode(e *Endpoint) map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
"endpoint": e.Name,
|
||||
"description": e.Description,
|
||||
"method": strings.Join(e.Method, ","),
|
||||
"path": strings.Join(e.Path, ","),
|
||||
"host": strings.Join(e.Host, ","),
|
||||
"handler": e.Handler,
|
||||
// endpoint map
|
||||
ep := make(map[string]string)
|
||||
|
||||
// set vals only if they exist
|
||||
set := func(k, v string) {
|
||||
if len(v) == 0 {
|
||||
return
|
||||
}
|
||||
ep[k] = v
|
||||
}
|
||||
|
||||
set("endpoint", e.Name)
|
||||
set("description", e.Description)
|
||||
set("handler", e.Handler)
|
||||
set("method", strings.Join(e.Method, ","))
|
||||
set("path", strings.Join(e.Path, ","))
|
||||
set("host", strings.Join(e.Host, ","))
|
||||
|
||||
return ep
|
||||
}
|
||||
|
||||
// Decode decodes endpoint metadata into an endpoint
|
||||
|
@@ -29,16 +29,20 @@ func requestToProto(r *http.Request) (*api.Request, error) {
|
||||
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
ct = "application/x-www-form-urlencoded"
|
||||
ct = "text/plain; charset=UTF-8" //default CT is text/plain
|
||||
r.Header.Set("Content-Type", ct)
|
||||
}
|
||||
|
||||
switch ct {
|
||||
case "application/x-www-form-urlencoded":
|
||||
// expect form vals
|
||||
default:
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
req.Body = string(data)
|
||||
//set the body:
|
||||
if r.Body != nil {
|
||||
switch ct {
|
||||
case "application/x-www-form-urlencoded":
|
||||
// expect form vals in Post data
|
||||
default:
|
||||
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
req.Body = string(data)
|
||||
}
|
||||
}
|
||||
|
||||
// Set X-Forwarded-For if it does not exist
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
func TestRequestToProto(t *testing.T) {
|
||||
testData := []*http.Request{
|
||||
&http.Request{
|
||||
{
|
||||
Method: "GET",
|
||||
Header: http.Header{
|
||||
"Header": []string{"test"},
|
||||
|
@@ -32,7 +32,7 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
validator "gopkg.in/go-playground/validator.v9"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@@ -2,6 +2,7 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -91,12 +92,17 @@ func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// set body
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
if r.Method == "GET" {
|
||||
bytes, _ := json.Marshal(r.URL.Query())
|
||||
ev.Data = string(bytes)
|
||||
} else {
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
ev.Data = string(b)
|
||||
}
|
||||
ev.Data = string(b)
|
||||
|
||||
// get client
|
||||
c := e.options.Service.Client()
|
||||
|
@@ -9,14 +9,12 @@ import (
|
||||
"github.com/micro/go-micro/api/handler"
|
||||
"github.com/micro/go-micro/api/router"
|
||||
regRouter "github.com/micro/go-micro/api/router/registry"
|
||||
"github.com/micro/go-micro/config/cmd"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/registry/memory"
|
||||
)
|
||||
|
||||
func testHttp(t *testing.T, path, service, ns string) {
|
||||
r := memory.NewRegistry()
|
||||
cmd.DefaultCmd = cmd.NewCmd(cmd.Registry(&r))
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
@@ -27,7 +25,7 @@ func testHttp(t *testing.T, path, service, ns string) {
|
||||
s := ®istry.Service{
|
||||
Name: service,
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: service + "-1",
|
||||
Address: l.Addr().String(),
|
||||
},
|
||||
@@ -57,6 +55,7 @@ func testHttp(t *testing.T, path, service, ns string) {
|
||||
rt := regRouter.NewRouter(
|
||||
router.WithHandler("http"),
|
||||
router.WithNamespace(ns),
|
||||
router.WithRegistry(r),
|
||||
)
|
||||
|
||||
p := NewHandler(handler.WithRouter(rt))
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/micro/go-micro/api/proto"
|
||||
go_api "github.com/micro/go-micro/api/proto"
|
||||
)
|
||||
|
||||
func TestRequestPayloadFromRequest(t *testing.T) {
|
||||
|
@@ -3,9 +3,11 @@
|
||||
|
||||
package go_api
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -16,7 +18,7 @@ var _ = math.Inf
|
||||
// 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.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Pair struct {
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
@@ -30,16 +32,17 @@ func (m *Pair) Reset() { *m = Pair{} }
|
||||
func (m *Pair) String() string { return proto.CompactTextString(m) }
|
||||
func (*Pair) ProtoMessage() {}
|
||||
func (*Pair) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_api_17a7876430d97ebd, []int{0}
|
||||
return fileDescriptor_7b6696ef87ec1943, []int{0}
|
||||
}
|
||||
|
||||
func (m *Pair) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Pair.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Pair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Pair.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Pair) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Pair.Merge(dst, src)
|
||||
func (m *Pair) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Pair.Merge(m, src)
|
||||
}
|
||||
func (m *Pair) XXX_Size() int {
|
||||
return xxx_messageInfo_Pair.Size(m)
|
||||
@@ -83,16 +86,17 @@ func (m *Request) Reset() { *m = Request{} }
|
||||
func (m *Request) String() string { return proto.CompactTextString(m) }
|
||||
func (*Request) ProtoMessage() {}
|
||||
func (*Request) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_api_17a7876430d97ebd, []int{1}
|
||||
return fileDescriptor_7b6696ef87ec1943, []int{1}
|
||||
}
|
||||
|
||||
func (m *Request) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Request.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(dst, src)
|
||||
func (m *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(m, src)
|
||||
}
|
||||
func (m *Request) XXX_Size() int {
|
||||
return xxx_messageInfo_Request.Size(m)
|
||||
@@ -167,16 +171,17 @@ func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return proto.CompactTextString(m) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
func (*Response) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_api_17a7876430d97ebd, []int{2}
|
||||
return fileDescriptor_7b6696ef87ec1943, []int{2}
|
||||
}
|
||||
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Response.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(dst, src)
|
||||
func (m *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(m, src)
|
||||
}
|
||||
func (m *Response) XXX_Size() int {
|
||||
return xxx_messageInfo_Response.Size(m)
|
||||
@@ -230,16 +235,17 @@ func (m *Event) Reset() { *m = Event{} }
|
||||
func (m *Event) String() string { return proto.CompactTextString(m) }
|
||||
func (*Event) ProtoMessage() {}
|
||||
func (*Event) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_api_17a7876430d97ebd, []int{3}
|
||||
return fileDescriptor_7b6696ef87ec1943, []int{3}
|
||||
}
|
||||
|
||||
func (m *Event) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Event.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Event.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Event) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Event.Merge(dst, src)
|
||||
func (m *Event) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Event.Merge(m, src)
|
||||
}
|
||||
func (m *Event) XXX_Size() int {
|
||||
return xxx_messageInfo_Event.Size(m)
|
||||
@@ -298,35 +304,35 @@ func init() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/micro/go-micro/api/proto/api.proto", fileDescriptor_api_17a7876430d97ebd)
|
||||
proto.RegisterFile("github.com/micro/go-micro/api/proto/api.proto", fileDescriptor_7b6696ef87ec1943)
|
||||
}
|
||||
|
||||
var fileDescriptor_api_17a7876430d97ebd = []byte{
|
||||
// 410 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0xc1, 0x6e, 0xd4, 0x30,
|
||||
0x10, 0x55, 0xe2, 0x24, 0x6d, 0x66, 0x11, 0x42, 0x3e, 0x20, 0x53, 0x2a, 0xb4, 0xca, 0x85, 0x15,
|
||||
0x52, 0x13, 0x68, 0x39, 0x20, 0xae, 0xb0, 0x2a, 0xc7, 0xca, 0x7f, 0xe0, 0x6d, 0xac, 0xc4, 0x62,
|
||||
0x13, 0x9b, 0xd8, 0xa9, 0xb4, 0x1f, 0xc7, 0x81, 0xcf, 0xe0, 0x6f, 0x90, 0x27, 0xde, 0xdd, 0xb2,
|
||||
0x5a, 0x2e, 0x74, 0x6f, 0x2f, 0xf6, 0x9b, 0x37, 0x6f, 0xde, 0x38, 0xf0, 0xb6, 0x51, 0xae, 0x1d,
|
||||
0x57, 0xe5, 0xbd, 0xee, 0xaa, 0x4e, 0xdd, 0x0f, 0xba, 0x6a, 0xf4, 0x95, 0x30, 0xaa, 0x32, 0x83,
|
||||
0x76, 0xba, 0x12, 0x46, 0x95, 0x88, 0x68, 0xd6, 0xe8, 0x52, 0x18, 0x55, 0xbc, 0x87, 0xe4, 0x4e,
|
||||
0xa8, 0x81, 0xbe, 0x00, 0xf2, 0x5d, 0x6e, 0x58, 0x34, 0x8f, 0x16, 0x39, 0xf7, 0x90, 0xbe, 0x84,
|
||||
0xec, 0x41, 0xac, 0x47, 0x69, 0x59, 0x3c, 0x27, 0x8b, 0x9c, 0x87, 0xaf, 0xe2, 0x17, 0x81, 0x33,
|
||||
0x2e, 0x7f, 0x8c, 0xd2, 0x3a, 0xcf, 0xe9, 0xa4, 0x6b, 0x75, 0x1d, 0x0a, 0xc3, 0x17, 0xa5, 0x90,
|
||||
0x18, 0xe1, 0x5a, 0x16, 0xe3, 0x29, 0x62, 0x7a, 0x03, 0x59, 0x2b, 0x45, 0x2d, 0x07, 0x46, 0xe6,
|
||||
0x64, 0x31, 0xbb, 0x7e, 0x5d, 0x4e, 0x16, 0xca, 0x20, 0x56, 0x7e, 0xc3, 0xdb, 0x65, 0xef, 0x86,
|
||||
0x0d, 0x0f, 0x54, 0xfa, 0x0e, 0x48, 0x23, 0x1d, 0x4b, 0xb0, 0x82, 0x1d, 0x56, 0xdc, 0x4a, 0x37,
|
||||
0xd1, 0x3d, 0x89, 0x5e, 0x41, 0x62, 0xb4, 0x75, 0x2c, 0x45, 0xf2, 0xab, 0x43, 0xf2, 0x9d, 0xb6,
|
||||
0x81, 0x8d, 0x34, 0xef, 0x71, 0xa5, 0xeb, 0x0d, 0xcb, 0x26, 0x8f, 0x1e, 0xfb, 0x14, 0xc6, 0x61,
|
||||
0xcd, 0xce, 0xa6, 0x14, 0xc6, 0x61, 0x7d, 0x71, 0x0b, 0xb3, 0x47, 0xbe, 0x8e, 0xc4, 0x54, 0x40,
|
||||
0x8a, 0xc1, 0xe0, 0xac, 0xb3, 0xeb, 0x67, 0xdb, 0xb6, 0x3e, 0x55, 0x3e, 0x5d, 0x7d, 0x8e, 0x3f,
|
||||
0x45, 0x17, 0x5f, 0xe1, 0x7c, 0x6b, 0xf7, 0x09, 0x2a, 0x4b, 0xc8, 0x77, 0x73, 0xfc, 0xbf, 0x4c,
|
||||
0xf1, 0x33, 0x82, 0x73, 0x2e, 0xad, 0xd1, 0xbd, 0x95, 0xf4, 0x0d, 0x80, 0x75, 0xc2, 0x8d, 0xf6,
|
||||
0x8b, 0xae, 0x25, 0xaa, 0xa5, 0xfc, 0xd1, 0x09, 0xfd, 0xb8, 0x5b, 0x5c, 0x8c, 0xc9, 0x5e, 0xee,
|
||||
0x93, 0x9d, 0x14, 0x8e, 0x6e, 0x6e, 0x1b, 0x2f, 0xd9, 0xc7, 0x7b, 0xb2, 0x30, 0x8b, 0xdf, 0x11,
|
||||
0xa4, 0xcb, 0x07, 0xd9, 0xe3, 0x16, 0x7b, 0xd1, 0xc9, 0x20, 0x82, 0x98, 0x3e, 0x87, 0x58, 0xd5,
|
||||
0xe1, 0xed, 0xc5, 0xaa, 0xa6, 0x97, 0x90, 0x3b, 0xd5, 0x49, 0xeb, 0x44, 0x67, 0xd0, 0x0f, 0xe1,
|
||||
0xfb, 0x03, 0xfa, 0x61, 0x37, 0x5e, 0xf2, 0xf7, 0xc3, 0xc1, 0x06, 0xff, 0x9a, 0xad, 0x16, 0x4e,
|
||||
0xb0, 0x74, 0x6a, 0xea, 0xf1, 0xc9, 0x66, 0x5b, 0x65, 0xf8, 0x83, 0xde, 0xfc, 0x09, 0x00, 0x00,
|
||||
0xff, 0xff, 0x7a, 0xb4, 0xd4, 0x8f, 0xcb, 0x03, 0x00, 0x00,
|
||||
var fileDescriptor_7b6696ef87ec1943 = []byte{
|
||||
// 408 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0x4d, 0x8f, 0xd3, 0x30,
|
||||
0x10, 0x55, 0xe2, 0x24, 0xbb, 0x99, 0x22, 0x84, 0x7c, 0x40, 0x66, 0x59, 0xa1, 0x2a, 0xa7, 0x0a,
|
||||
0xa9, 0x29, 0xec, 0x72, 0x40, 0x5c, 0xa1, 0x5a, 0x8e, 0x2b, 0xff, 0x03, 0x77, 0x63, 0x25, 0x16,
|
||||
0x4d, 0x1c, 0x62, 0xa7, 0x52, 0x7f, 0x1c, 0x07, 0x7e, 0x06, 0xff, 0x06, 0x79, 0xec, 0x7e, 0x50,
|
||||
0x95, 0x0b, 0xf4, 0xf6, 0x62, 0xbf, 0x79, 0xf3, 0xe6, 0x8d, 0x03, 0xf3, 0x5a, 0xd9, 0x66, 0x5c,
|
||||
0x95, 0x4f, 0xba, 0x5d, 0xb4, 0xea, 0x69, 0xd0, 0x8b, 0x5a, 0xcf, 0x3d, 0x10, 0xbd, 0x5a, 0xf4,
|
||||
0x83, 0xb6, 0x88, 0x4a, 0x44, 0x34, 0xab, 0x75, 0x29, 0x7a, 0x55, 0xbc, 0x83, 0xe4, 0x51, 0xa8,
|
||||
0x81, 0xbe, 0x00, 0xf2, 0x4d, 0x6e, 0x59, 0x34, 0x8d, 0x66, 0x39, 0x77, 0x90, 0xbe, 0x84, 0x6c,
|
||||
0x23, 0xd6, 0xa3, 0x34, 0x2c, 0x9e, 0x92, 0x59, 0xce, 0xc3, 0x57, 0xf1, 0x93, 0xc0, 0x15, 0x97,
|
||||
0xdf, 0x47, 0x69, 0xac, 0xe3, 0xb4, 0xd2, 0x36, 0xba, 0x0a, 0x85, 0xe1, 0x8b, 0x52, 0x48, 0x7a,
|
||||
0x61, 0x1b, 0x16, 0xe3, 0x29, 0x62, 0x7a, 0x0f, 0x59, 0x23, 0x45, 0x25, 0x07, 0x46, 0xa6, 0x64,
|
||||
0x36, 0xb9, 0x7b, 0x5d, 0x7a, 0x0b, 0x65, 0x10, 0x2b, 0xbf, 0xe2, 0xed, 0xb2, 0xb3, 0xc3, 0x96,
|
||||
0x07, 0x2a, 0x7d, 0x0b, 0xa4, 0x96, 0x96, 0x25, 0x58, 0xc1, 0x4e, 0x2b, 0x1e, 0xa4, 0xf5, 0x74,
|
||||
0x47, 0xa2, 0x73, 0x48, 0x7a, 0x6d, 0x2c, 0x4b, 0x91, 0xfc, 0xea, 0x94, 0xfc, 0xa8, 0x4d, 0x60,
|
||||
0x23, 0xcd, 0x79, 0x5c, 0xe9, 0x6a, 0xcb, 0x32, 0xef, 0xd1, 0x61, 0x97, 0xc2, 0x38, 0xac, 0xd9,
|
||||
0x95, 0x4f, 0x61, 0x1c, 0xd6, 0x37, 0x0f, 0x30, 0x39, 0xf2, 0x75, 0x26, 0xa6, 0x02, 0x52, 0x0c,
|
||||
0x06, 0x67, 0x9d, 0xdc, 0x3d, 0xdb, 0xb5, 0x75, 0xa9, 0x72, 0x7f, 0xf5, 0x29, 0xfe, 0x18, 0xdd,
|
||||
0x7c, 0x81, 0xeb, 0x9d, 0xdd, 0xff, 0x50, 0x59, 0x42, 0xbe, 0x9f, 0xe3, 0xdf, 0x65, 0x8a, 0x1f,
|
||||
0x11, 0x5c, 0x73, 0x69, 0x7a, 0xdd, 0x19, 0x49, 0xdf, 0x00, 0x18, 0x2b, 0xec, 0x68, 0x3e, 0xeb,
|
||||
0x4a, 0xa2, 0x5a, 0xca, 0x8f, 0x4e, 0xe8, 0x87, 0xfd, 0xe2, 0x62, 0x4c, 0xf6, 0xf6, 0x90, 0xac,
|
||||
0x57, 0x38, 0xbb, 0xb9, 0x5d, 0xbc, 0xe4, 0x10, 0xef, 0xc5, 0xc2, 0x2c, 0x7e, 0x45, 0x90, 0x2e,
|
||||
0x37, 0xb2, 0xc3, 0x2d, 0x76, 0xa2, 0x95, 0x41, 0x04, 0x31, 0x7d, 0x0e, 0xb1, 0xaa, 0xc2, 0xdb,
|
||||
0x8b, 0x55, 0x45, 0x6f, 0x21, 0xb7, 0xaa, 0x95, 0xc6, 0x8a, 0xb6, 0x47, 0x3f, 0x84, 0x1f, 0x0e,
|
||||
0xe8, 0xfb, 0xfd, 0x78, 0xc9, 0x9f, 0x0f, 0x07, 0x1b, 0xfc, 0x6d, 0xb6, 0x4a, 0x58, 0xc1, 0x52,
|
||||
0xdf, 0xd4, 0xe1, 0x8b, 0xcd, 0xb6, 0xca, 0xf0, 0x07, 0xbd, 0xff, 0x1d, 0x00, 0x00, 0xff, 0xff,
|
||||
0x97, 0xf3, 0x59, 0x6e, 0xd1, 0x03, 0x00, 0x00,
|
||||
}
|
||||
|
@@ -1,23 +1,13 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: github.com/micro/go-micro/api/proto/api.proto
|
||||
|
||||
/*
|
||||
Package go_api is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
github.com/micro/go-micro/api/proto/api.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Pair
|
||||
Request
|
||||
Response
|
||||
Event
|
||||
*/
|
||||
package go_api
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -28,4 +18,4 @@ var _ = math.Inf
|
||||
// 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.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
@@ -3,7 +3,6 @@ package router
|
||||
import (
|
||||
"github.com/micro/go-micro/api/resolver"
|
||||
"github.com/micro/go-micro/api/resolver/micro"
|
||||
"github.com/micro/go-micro/config/cmd"
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
@@ -19,7 +18,7 @@ type Option func(o *Options)
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Handler: "meta",
|
||||
Registry: *cmd.DefaultOptions().Registry,
|
||||
Registry: registry.DefaultRegistry,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
|
24
api/server/acme/acme.go
Normal file
24
api/server/acme/acme.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Package acme abstracts away various ACME libraries
|
||||
package acme
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrProviderNotImplemented can be returned when attempting to
|
||||
// instantiate an unimplemented provider
|
||||
ErrProviderNotImplemented = errors.New("Provider not implemented")
|
||||
)
|
||||
|
||||
// Provider is a ACME provider interface
|
||||
type Provider interface {
|
||||
NewListener(...string) (net.Listener, error)
|
||||
}
|
||||
|
||||
// The Let's Encrypt ACME endpoints
|
||||
const (
|
||||
LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
)
|
23
api/server/acme/autocert/autocert.go
Normal file
23
api/server/acme/autocert/autocert.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Package autocert is the ACME provider from golang.org/x/crypto/acme/autocert
|
||||
// This provider does not take any config.
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/micro/go-micro/api/server/acme"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
// autoCertACME is the ACME provider from golang.org/x/crypto/acme/autocert
|
||||
type autocertProvider struct{}
|
||||
|
||||
// NewListener implements acme.Provider
|
||||
func (a *autocertProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
|
||||
return autocert.NewListener(ACMEHosts...), nil
|
||||
}
|
||||
|
||||
// New returns an autocert acme.Provider
|
||||
func New() acme.Provider {
|
||||
return &autocertProvider{}
|
||||
}
|
16
api/server/acme/autocert/autocert_test.go
Normal file
16
api/server/acme/autocert/autocert_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAutocert(t *testing.T) {
|
||||
l := New()
|
||||
if _, ok := l.(*autocertProvider); !ok {
|
||||
t.Error("New() didn't return an autocertProvider")
|
||||
}
|
||||
// TODO: Travis CI doesn't let us bind :443
|
||||
// if _, err := l.NewListener(); err != nil {
|
||||
// t.Error(err.Error())
|
||||
// }
|
||||
}
|
58
api/server/acme/certmagic/certmagic.go
Normal file
58
api/server/acme/certmagic/certmagic.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Package certmagic is the ACME provider from github.com/mholt/certmagic
|
||||
package certmagic
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/mholt/certmagic"
|
||||
|
||||
"github.com/micro/go-micro/api/server/acme"
|
||||
)
|
||||
|
||||
type certmagicProvider struct {
|
||||
opts acme.Options
|
||||
}
|
||||
|
||||
func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
|
||||
certmagic.Default.CA = c.opts.CA
|
||||
if c.opts.ChallengeProvider != nil {
|
||||
// Enabling DNS Challenge disables the other challenges
|
||||
certmagic.Default.DNSProvider = c.opts.ChallengeProvider
|
||||
}
|
||||
if c.opts.OnDemand {
|
||||
certmagic.Default.OnDemand = new(certmagic.OnDemandConfig)
|
||||
}
|
||||
if c.opts.Cache != nil {
|
||||
// already validated by new()
|
||||
certmagic.Default.Storage = c.opts.Cache.(certmagic.Storage)
|
||||
}
|
||||
// If multiple instances of the provider are running, inject some
|
||||
// randomness so they don't collide
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
randomDuration := (7 * 24 * time.Hour) + (time.Duration(rand.Intn(504)) * time.Hour)
|
||||
certmagic.Default.RenewDurationBefore = randomDuration
|
||||
|
||||
return certmagic.Listen(ACMEHosts)
|
||||
}
|
||||
|
||||
// New returns a certmagic provider
|
||||
func New(options ...acme.Option) acme.Provider {
|
||||
opts := acme.DefaultOptions()
|
||||
|
||||
for _, o := range options {
|
||||
o(&opts)
|
||||
}
|
||||
|
||||
if opts.Cache != nil {
|
||||
if _, ok := opts.Cache.(certmagic.Storage); !ok {
|
||||
log.Fatal("ACME: cache provided doesn't implement certmagic's Storage interface")
|
||||
}
|
||||
}
|
||||
|
||||
return &certmagicProvider{
|
||||
opts: opts,
|
||||
}
|
||||
}
|
224
api/server/acme/certmagic/certmagic_test.go
Normal file
224
api/server/acme/certmagic/certmagic_test.go
Normal file
@@ -0,0 +1,224 @@
|
||||
package certmagic
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v3/providers/dns/cloudflare"
|
||||
"github.com/mholt/certmagic"
|
||||
"github.com/micro/go-micro/api/server/acme"
|
||||
cfstore "github.com/micro/go-micro/store/cloudflare"
|
||||
"github.com/micro/go-micro/sync/lock/memory"
|
||||
)
|
||||
|
||||
func TestCertMagic(t *testing.T) {
|
||||
if len(os.Getenv("IN_TRAVIS_CI")) != 0 {
|
||||
t.Skip("Travis doesn't let us bind :443")
|
||||
}
|
||||
l, err := New().NewListener()
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
l.Close()
|
||||
|
||||
c := cloudflare.NewDefaultConfig()
|
||||
c.AuthEmail = ""
|
||||
c.AuthKey = ""
|
||||
c.AuthToken = "test"
|
||||
c.ZoneToken = "test"
|
||||
|
||||
p, err := cloudflare.NewDNSProviderConfig(c)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
l, err = New(acme.AcceptToS(true),
|
||||
acme.CA(acme.LetsEncryptStagingCA),
|
||||
acme.ChallengeProvider(p),
|
||||
).NewListener()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
l.Close()
|
||||
}
|
||||
|
||||
func TestStorageImplementation(t *testing.T) {
|
||||
apiToken, accountID := os.Getenv("CF_API_TOKEN"), os.Getenv("CF_ACCOUNT_ID")
|
||||
kvID := os.Getenv("KV_NAMESPACE_ID")
|
||||
if len(apiToken) == 0 || len(accountID) == 0 || len(kvID) == 0 {
|
||||
t.Skip("No Cloudflare API keys available, skipping test")
|
||||
}
|
||||
|
||||
var s certmagic.Storage
|
||||
st := cfstore.NewStore(
|
||||
cfstore.Token(apiToken),
|
||||
cfstore.Account(accountID),
|
||||
cfstore.Namespace(kvID),
|
||||
)
|
||||
s = &storage{
|
||||
lock: memory.NewLock(),
|
||||
store: st,
|
||||
}
|
||||
|
||||
// Test Lock
|
||||
if err := s.Lock("test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test Unlock
|
||||
if err := s.Unlock("test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test data
|
||||
testdata := []struct {
|
||||
key string
|
||||
value []byte
|
||||
}{
|
||||
{key: "/foo/a", value: []byte("lorem")},
|
||||
{key: "/foo/b", value: []byte("ipsum")},
|
||||
{key: "/foo/c", value: []byte("dolor")},
|
||||
{key: "/foo/d", value: []byte("sit")},
|
||||
{key: "/bar/a", value: []byte("amet")},
|
||||
{key: "/bar/b", value: []byte("consectetur")},
|
||||
{key: "/bar/c", value: []byte("adipiscing")},
|
||||
{key: "/bar/d", value: []byte("elit")},
|
||||
{key: "/foo/bar/a", value: []byte("sed")},
|
||||
{key: "/foo/bar/b", value: []byte("do")},
|
||||
{key: "/foo/bar/c", value: []byte("eiusmod")},
|
||||
{key: "/foo/bar/d", value: []byte("tempor")},
|
||||
{key: "/foo/bar/baz/a", value: []byte("incididunt")},
|
||||
{key: "/foo/bar/baz/b", value: []byte("ut")},
|
||||
{key: "/foo/bar/baz/c", value: []byte("labore")},
|
||||
{key: "/foo/bar/baz/d", value: []byte("et")},
|
||||
// a duplicate just in case there's any edge cases
|
||||
{key: "/foo/a", value: []byte("lorem")},
|
||||
}
|
||||
|
||||
// Test Store
|
||||
for _, d := range testdata {
|
||||
if err := s.Store(d.key, d.value); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Test Load
|
||||
for _, d := range testdata {
|
||||
if value, err := s.Load(d.key); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(value, d.value) {
|
||||
t.Fatalf("Load %s: expected %v, got %v", d.key, d.value, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test Exists
|
||||
for _, d := range testdata {
|
||||
if !s.Exists(d.key) {
|
||||
t.Fatalf("%s should exist, but doesn't\n", d.key)
|
||||
}
|
||||
}
|
||||
|
||||
// Test List
|
||||
if list, err := s.List("/", true); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else {
|
||||
var expected []string
|
||||
for i, d := range testdata {
|
||||
if i != len(testdata)-1 {
|
||||
// Don't store the intentionally duplicated key
|
||||
expected = append(expected, d.key)
|
||||
}
|
||||
}
|
||||
sort.Strings(expected)
|
||||
sort.Strings(list)
|
||||
if !reflect.DeepEqual(expected, list) {
|
||||
t.Fatalf("List: Expected %v, got %v\n", expected, list)
|
||||
}
|
||||
}
|
||||
if list, err := s.List("/foo", false); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else {
|
||||
sort.Strings(list)
|
||||
expected := []string{"/foo/a", "/foo/b", "/foo/bar", "/foo/c", "/foo/d"}
|
||||
if !reflect.DeepEqual(expected, list) {
|
||||
t.Fatalf("List: expected %s, got %s\n", expected, list)
|
||||
}
|
||||
}
|
||||
|
||||
// Test Stat
|
||||
for _, d := range testdata {
|
||||
info, err := s.Stat(d.key)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else {
|
||||
if info.Key != d.key {
|
||||
t.Fatalf("Stat().Key: expected %s, got %s\n", d.key, info.Key)
|
||||
}
|
||||
if info.Size != int64(len(d.value)) {
|
||||
t.Fatalf("Stat().Size: expected %d, got %d\n", len(d.value), info.Size)
|
||||
}
|
||||
if time.Since(info.Modified) > time.Minute {
|
||||
t.Fatalf("Stat().Modified: expected time since last modified to be < 1 minute, got %v\n", time.Since(info.Modified))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Test Delete
|
||||
for _, d := range testdata {
|
||||
if err := s.Delete(d.key); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// New interface doesn't return an error, so call it in case any log.Fatal
|
||||
// happens
|
||||
New(acme.Cache(s))
|
||||
}
|
||||
|
||||
// Full test with a real zone, with against LE staging
|
||||
func TestE2e(t *testing.T) {
|
||||
apiToken, accountID := os.Getenv("CF_API_TOKEN"), os.Getenv("CF_ACCOUNT_ID")
|
||||
kvID := os.Getenv("KV_NAMESPACE_ID")
|
||||
if len(apiToken) == 0 || len(accountID) == 0 || len(kvID) == 0 {
|
||||
t.Skip("No Cloudflare API keys available, skipping test")
|
||||
}
|
||||
|
||||
testLock := memory.NewLock()
|
||||
testStore := cfstore.NewStore(
|
||||
cfstore.Token(apiToken),
|
||||
cfstore.Account(accountID),
|
||||
cfstore.Namespace(kvID),
|
||||
)
|
||||
testStorage := NewStorage(testLock, testStore)
|
||||
|
||||
conf := cloudflare.NewDefaultConfig()
|
||||
conf.AuthToken = apiToken
|
||||
conf.ZoneToken = apiToken
|
||||
testChallengeProvider, err := cloudflare.NewDNSProviderConfig(conf)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
testProvider := New(
|
||||
acme.AcceptToS(true),
|
||||
acme.Cache(testStorage),
|
||||
acme.CA(acme.LetsEncryptStagingCA),
|
||||
acme.ChallengeProvider(testChallengeProvider),
|
||||
acme.OnDemand(false),
|
||||
)
|
||||
|
||||
listener, err := testProvider.NewListener("*.micro.mu", "micro.mu")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
go http.Serve(listener, http.NotFoundHandler())
|
||||
time.Sleep(10 * time.Minute)
|
||||
}
|
147
api/server/acme/certmagic/storage.go
Normal file
147
api/server/acme/certmagic/storage.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package certmagic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mholt/certmagic"
|
||||
"github.com/micro/go-micro/store"
|
||||
"github.com/micro/go-micro/sync/lock"
|
||||
)
|
||||
|
||||
// File represents a "File" that will be stored in store.Store - the contents and last modified time
|
||||
type File struct {
|
||||
// last modified time
|
||||
LastModified time.Time
|
||||
// Contents
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
// storage is an implementation of certmagic.Storage using micro's sync.Map and store.Store interfaces.
|
||||
// As certmagic storage expects a filesystem (with stat() abilities) we have to implement
|
||||
// the bare minimum of metadata.
|
||||
type storage struct {
|
||||
lock lock.Lock
|
||||
store store.Store
|
||||
}
|
||||
|
||||
func (s *storage) Lock(key string) error {
|
||||
return s.lock.Acquire(key, lock.TTL(10*time.Minute))
|
||||
}
|
||||
|
||||
func (s *storage) Unlock(key string) error {
|
||||
return s.lock.Release(key)
|
||||
}
|
||||
|
||||
func (s *storage) Store(key string, value []byte) error {
|
||||
f := File{
|
||||
LastModified: time.Now(),
|
||||
Contents: value,
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
e := gob.NewEncoder(buf)
|
||||
if err := e.Encode(f); err != nil {
|
||||
return err
|
||||
}
|
||||
r := &store.Record{
|
||||
Key: key,
|
||||
Value: buf.Bytes(),
|
||||
}
|
||||
return s.store.Write(r)
|
||||
}
|
||||
|
||||
func (s *storage) Load(key string) ([]byte, error) {
|
||||
if !s.Exists(key) {
|
||||
return nil, certmagic.ErrNotExist(errors.New(key + " doesn't exist"))
|
||||
}
|
||||
records, err := s.store.Read(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(records) != 1 {
|
||||
return nil, fmt.Errorf("ACME Storage: multiple records matched key %s", key)
|
||||
}
|
||||
b := bytes.NewBuffer(records[0].Value)
|
||||
d := gob.NewDecoder(b)
|
||||
var f File
|
||||
err = d.Decode(&f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.Contents, nil
|
||||
}
|
||||
|
||||
func (s *storage) Delete(key string) error {
|
||||
return s.store.Delete(key)
|
||||
}
|
||||
|
||||
func (s *storage) Exists(key string) bool {
|
||||
if _, err := s.store.Read(key); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *storage) List(prefix string, recursive bool) ([]string, error) {
|
||||
records, err := s.store.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//nolint:prealloc
|
||||
var results []string
|
||||
for _, r := range records {
|
||||
if strings.HasPrefix(r.Key, prefix) {
|
||||
results = append(results, r.Key)
|
||||
}
|
||||
}
|
||||
if recursive {
|
||||
return results, nil
|
||||
}
|
||||
keysMap := make(map[string]bool)
|
||||
for _, key := range results {
|
||||
dir := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/")
|
||||
keysMap[dir[0]] = true
|
||||
}
|
||||
results = make([]string, 0)
|
||||
for k := range keysMap {
|
||||
results = append(results, path.Join(prefix, k))
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (s *storage) Stat(key string) (certmagic.KeyInfo, error) {
|
||||
records, err := s.store.Read(key)
|
||||
if err != nil {
|
||||
return certmagic.KeyInfo{}, err
|
||||
}
|
||||
if len(records) != 1 {
|
||||
return certmagic.KeyInfo{}, fmt.Errorf("ACME Storage: multiple records matched key %s", key)
|
||||
}
|
||||
b := bytes.NewBuffer(records[0].Value)
|
||||
d := gob.NewDecoder(b)
|
||||
var f File
|
||||
err = d.Decode(&f)
|
||||
if err != nil {
|
||||
return certmagic.KeyInfo{}, err
|
||||
}
|
||||
return certmagic.KeyInfo{
|
||||
Key: key,
|
||||
Modified: f.LastModified,
|
||||
Size: int64(len(f.Contents)),
|
||||
IsTerminal: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewStorage returns a certmagic.Storage backed by a go-micro/lock and go-micro/store
|
||||
func NewStorage(lock lock.Lock, store store.Store) certmagic.Storage {
|
||||
return &storage{
|
||||
lock: lock,
|
||||
store: store,
|
||||
}
|
||||
}
|
73
api/server/acme/options.go
Normal file
73
api/server/acme/options.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package acme
|
||||
|
||||
import "github.com/go-acme/lego/v3/challenge"
|
||||
|
||||
// Option (or Options) are passed to New() to configure providers
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options represents various options you can present to ACME providers
|
||||
type Options struct {
|
||||
// AcceptTLS must be set to true to indicate that you have read your
|
||||
// provider's terms of service.
|
||||
AcceptToS bool
|
||||
// CA is the CA to use
|
||||
CA string
|
||||
// ChallengeProvider is a go-acme/lego challenge provider. Set this if you
|
||||
// want to use DNS Challenges. Otherwise, tls-alpn-01 will be used
|
||||
ChallengeProvider challenge.Provider
|
||||
// Issue certificates for domains on demand. Otherwise, certs will be
|
||||
// retrieved / issued on start-up.
|
||||
OnDemand bool
|
||||
// Cache is a storage interface. Most ACME libraries have an cache, but
|
||||
// there's no defined interface, so if you consume this option
|
||||
// sanity check it before using.
|
||||
Cache interface{}
|
||||
}
|
||||
|
||||
// AcceptToS indicates whether you accept your CA's terms of service
|
||||
func AcceptToS(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.AcceptToS = b
|
||||
}
|
||||
}
|
||||
|
||||
// CA sets the CA of an acme.Options
|
||||
func CA(CA string) Option {
|
||||
return func(o *Options) {
|
||||
o.CA = CA
|
||||
}
|
||||
}
|
||||
|
||||
// ChallengeProvider sets the Challenge provider of an acme.Options
|
||||
// if set, it enables the DNS challenge, otherwise tls-alpn-01 will be used.
|
||||
func ChallengeProvider(p challenge.Provider) Option {
|
||||
return func(o *Options) {
|
||||
o.ChallengeProvider = p
|
||||
}
|
||||
}
|
||||
|
||||
// OnDemand enables on-demand certificate issuance. Not recommended for use
|
||||
// with the DNS challenge, as the first connection may be very slow.
|
||||
func OnDemand(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.OnDemand = b
|
||||
}
|
||||
}
|
||||
|
||||
// Cache provides a cache / storage interface to the underlying ACME library
|
||||
// as there is no standard, this needs to be validated by the underlying
|
||||
// implentation.
|
||||
func Cache(c interface{}) Option {
|
||||
return func(o *Options) {
|
||||
o.Cache = c
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultOptions uses the Let's Encrypt Production CA, with DNS Challenge disabled.
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
AcceptToS: true,
|
||||
CA: LetsEncryptProductionCA,
|
||||
OnDemand: true,
|
||||
}
|
||||
}
|
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/micro/go-micro/api/server"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
type httpServer struct {
|
||||
@@ -53,9 +52,9 @@ func (s *httpServer) Start() error {
|
||||
var l net.Listener
|
||||
var err error
|
||||
|
||||
if s.opts.EnableACME {
|
||||
if s.opts.EnableACME && s.opts.ACMEProvider != nil {
|
||||
// should we check the address to make sure its using :443?
|
||||
l = autocert.NewListener(s.opts.ACMEHosts...)
|
||||
l, err = s.opts.ACMEProvider.NewListener(s.opts.ACMEHosts...)
|
||||
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
|
||||
l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig)
|
||||
} else {
|
||||
|
@@ -2,15 +2,24 @@ package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/micro/go-micro/api/server/acme"
|
||||
)
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
type Options struct {
|
||||
EnableACME bool
|
||||
EnableTLS bool
|
||||
ACMEHosts []string
|
||||
TLSConfig *tls.Config
|
||||
EnableACME bool
|
||||
ACMEProvider acme.Provider
|
||||
EnableTLS bool
|
||||
ACMEHosts []string
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
func EnableACME(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.EnableACME = b
|
||||
}
|
||||
}
|
||||
|
||||
func ACMEHosts(hosts ...string) Option {
|
||||
@@ -19,9 +28,9 @@ func ACMEHosts(hosts ...string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func EnableACME(b bool) Option {
|
||||
func ACMEProvider(p acme.Provider) Option {
|
||||
return func(o *Options) {
|
||||
o.EnableACME = b
|
||||
o.ACMEProvider = p
|
||||
}
|
||||
}
|
||||
|
||||
|
40
auth/auth.go
Normal file
40
auth/auth.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Package auth provides authentication and authorization capability
|
||||
package auth
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Auth providers authentication and authorization
|
||||
type Auth interface {
|
||||
// Generate a new auth token
|
||||
Generate(string) (*Token, error)
|
||||
// Revoke an authorization token
|
||||
Revoke(*Token) error
|
||||
// Grant access to a resource
|
||||
Grant(*Token, *Service) error
|
||||
// Verify a token can access a resource
|
||||
Verify(*Token, *Service) error
|
||||
}
|
||||
|
||||
// Service is some thing to provide access to
|
||||
type Service struct {
|
||||
// Name of the resource
|
||||
Name string
|
||||
// Endpoint is the specific endpoint
|
||||
Endpoint string
|
||||
}
|
||||
|
||||
// Token providers by an auth provider
|
||||
type Token struct {
|
||||
// Unique token id
|
||||
Id string `json: "id"`
|
||||
// Time of token creation
|
||||
Created time.Time `json:"created"`
|
||||
// Time of token expiry
|
||||
Expiry time.Time `json:"expiry"`
|
||||
// Roles associated with the token
|
||||
Roles []string `json:"roles"`
|
||||
// Any other associated metadata
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
@@ -38,13 +38,9 @@ type Subscriber interface {
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultBroker Broker = newHttpBroker()
|
||||
DefaultBroker Broker = NewBroker()
|
||||
)
|
||||
|
||||
func NewBroker(opts ...Option) Broker {
|
||||
return newHttpBroker(opts...)
|
||||
}
|
||||
|
||||
func Init(opts ...Option) error {
|
||||
return DefaultBroker.Init(opts...)
|
||||
}
|
||||
|
@@ -1,47 +0,0 @@
|
||||
package broker
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
var (
|
||||
// mock data
|
||||
testData = map[string][]*registry.Service{
|
||||
"foo": []*registry.Service{
|
||||
{
|
||||
Name: "foo",
|
||||
Version: "1.0.0",
|
||||
Nodes: []*registry.Node{
|
||||
{
|
||||
Id: "foo-1.0.0-123",
|
||||
Address: "localhost:9999",
|
||||
},
|
||||
{
|
||||
Id: "foo-1.0.0-321",
|
||||
Address: "localhost:9999",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "foo",
|
||||
Version: "1.0.1",
|
||||
Nodes: []*registry.Node{
|
||||
{
|
||||
Id: "foo-1.0.1-321",
|
||||
Address: "localhost:6666",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "foo",
|
||||
Version: "1.0.3",
|
||||
Nodes: []*registry.Node{
|
||||
{
|
||||
Id: "foo-1.0.3-345",
|
||||
Address: "localhost:8888",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
459
broker/default.go
Normal file
459
broker/default.go
Normal file
@@ -0,0 +1,459 @@
|
||||
package broker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/codec/json"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/util/addr"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
"github.com/nats-io/nats-server/v2/server"
|
||||
nats "github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
type natsBroker struct {
|
||||
sync.Once
|
||||
sync.RWMutex
|
||||
|
||||
// indicate if we're connected
|
||||
connected bool
|
||||
|
||||
// address to bind routes to
|
||||
addrs []string
|
||||
// servers for the client
|
||||
servers []string
|
||||
|
||||
// client connection and nats opts
|
||||
conn *nats.Conn
|
||||
opts Options
|
||||
nopts nats.Options
|
||||
|
||||
// should we drain the connection
|
||||
drain bool
|
||||
closeCh chan (error)
|
||||
|
||||
// embedded server
|
||||
server *server.Server
|
||||
// configure to use local server
|
||||
local bool
|
||||
// server exit channel
|
||||
exit chan bool
|
||||
}
|
||||
|
||||
type subscriber struct {
|
||||
s *nats.Subscription
|
||||
opts SubscribeOptions
|
||||
}
|
||||
|
||||
type publication struct {
|
||||
t string
|
||||
m *Message
|
||||
}
|
||||
|
||||
func (p *publication) Topic() string {
|
||||
return p.t
|
||||
}
|
||||
|
||||
func (p *publication) Message() *Message {
|
||||
return p.m
|
||||
}
|
||||
|
||||
func (p *publication) Ack() error {
|
||||
// nats does not support acking
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *subscriber) Options() SubscribeOptions {
|
||||
return s.opts
|
||||
}
|
||||
|
||||
func (s *subscriber) Topic() string {
|
||||
return s.s.Subject
|
||||
}
|
||||
|
||||
func (s *subscriber) Unsubscribe() error {
|
||||
return s.s.Unsubscribe()
|
||||
}
|
||||
|
||||
func (n *natsBroker) Address() string {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
|
||||
if n.server != nil {
|
||||
return n.server.ClusterAddr().String()
|
||||
}
|
||||
|
||||
if n.conn != nil && n.conn.IsConnected() {
|
||||
return n.conn.ConnectedUrl()
|
||||
}
|
||||
|
||||
if len(n.addrs) > 0 {
|
||||
return n.addrs[0]
|
||||
}
|
||||
|
||||
return "127.0.0.1:-1"
|
||||
}
|
||||
|
||||
func (n *natsBroker) setAddrs(addrs []string) []string {
|
||||
//nolint:prealloc
|
||||
var cAddrs []string
|
||||
for _, addr := range addrs {
|
||||
if len(addr) == 0 {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(addr, "nats://") {
|
||||
addr = "nats://" + addr
|
||||
}
|
||||
cAddrs = append(cAddrs, addr)
|
||||
}
|
||||
// if there's no address and we weren't told to
|
||||
// embed a local server then use the default url
|
||||
if len(cAddrs) == 0 && !n.local {
|
||||
cAddrs = []string{nats.DefaultURL}
|
||||
}
|
||||
return cAddrs
|
||||
}
|
||||
|
||||
// serve stats a local nats server if needed
|
||||
func (n *natsBroker) serve(exit chan bool) error {
|
||||
// local server address
|
||||
host := "127.0.0.1"
|
||||
port := -1
|
||||
|
||||
// cluster address
|
||||
caddr := "0.0.0.0"
|
||||
cport := -1
|
||||
|
||||
// with no address we just default it
|
||||
// this is a local client address
|
||||
if len(n.addrs) > 0 {
|
||||
address := n.addrs[0]
|
||||
if strings.HasPrefix(address, "nats://") {
|
||||
address = strings.TrimPrefix(address, "nats://")
|
||||
}
|
||||
|
||||
// parse out the address
|
||||
h, p, err := net.SplitHostPort(address)
|
||||
if err == nil {
|
||||
caddr = h
|
||||
cport, _ = strconv.Atoi(p)
|
||||
}
|
||||
}
|
||||
|
||||
// 1. create new server
|
||||
// 2. register the server
|
||||
// 3. connect to other servers
|
||||
|
||||
// set cluster opts
|
||||
cOpts := server.ClusterOpts{
|
||||
Host: caddr,
|
||||
Port: cport,
|
||||
}
|
||||
|
||||
// get the routes for other nodes
|
||||
var routes []*url.URL
|
||||
|
||||
// get existing nats servers to connect to
|
||||
services, err := n.opts.Registry.GetService("go.micro.nats.broker")
|
||||
if err == nil {
|
||||
for _, service := range services {
|
||||
for _, node := range service.Nodes {
|
||||
u, err := url.Parse("nats://" + node.Address)
|
||||
if err != nil {
|
||||
log.Log(err)
|
||||
continue
|
||||
}
|
||||
// append to the cluster routes
|
||||
routes = append(routes, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try get existing server
|
||||
s := n.server
|
||||
|
||||
if s != nil {
|
||||
// stop the existing server
|
||||
s.Shutdown()
|
||||
}
|
||||
|
||||
s, err = server.NewServer(&server.Options{
|
||||
// Specify the host
|
||||
Host: host,
|
||||
// Use a random port
|
||||
Port: port,
|
||||
// Set the cluster ops
|
||||
Cluster: cOpts,
|
||||
// Set the routes
|
||||
Routes: routes,
|
||||
NoLog: true,
|
||||
NoSigs: true,
|
||||
MaxControlLine: 2048,
|
||||
TLSConfig: n.opts.TLSConfig,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save the server
|
||||
n.server = s
|
||||
|
||||
// start the server
|
||||
go s.Start()
|
||||
|
||||
var ready bool
|
||||
|
||||
// wait till its ready for connections
|
||||
for i := 0; i < 3; i++ {
|
||||
if s.ReadyForConnections(time.Second) {
|
||||
ready = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ready {
|
||||
return errors.New("server not ready")
|
||||
}
|
||||
|
||||
// set the client address
|
||||
n.servers = []string{s.ClientURL()}
|
||||
|
||||
go func() {
|
||||
var advertise string
|
||||
|
||||
// parse out the address
|
||||
_, port, err := net.SplitHostPort(s.ClusterAddr().String())
|
||||
if err == nil {
|
||||
addr, _ := addr.Extract("")
|
||||
advertise = net.JoinHostPort(addr, port)
|
||||
} else {
|
||||
s.ClusterAddr().String()
|
||||
}
|
||||
|
||||
// register the cluster address
|
||||
for {
|
||||
select {
|
||||
case <-exit:
|
||||
// deregister on exit
|
||||
n.opts.Registry.Deregister(®istry.Service{
|
||||
Name: "go.micro.nats.broker",
|
||||
Version: "v2",
|
||||
Nodes: []*registry.Node{
|
||||
{Id: s.ID(), Address: advertise},
|
||||
},
|
||||
})
|
||||
s.Shutdown()
|
||||
return
|
||||
default:
|
||||
// register the broker
|
||||
n.opts.Registry.Register(®istry.Service{
|
||||
Name: "go.micro.nats.broker",
|
||||
Version: "v2",
|
||||
Nodes: []*registry.Node{
|
||||
{Id: s.ID(), Address: advertise},
|
||||
},
|
||||
}, registry.RegisterTTL(time.Minute))
|
||||
time.Sleep(time.Minute)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) Connect() error {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
if !n.connected {
|
||||
// create exit chan
|
||||
n.exit = make(chan bool)
|
||||
|
||||
// start the local server
|
||||
if err := n.serve(n.exit); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set to connected
|
||||
n.connected = true
|
||||
}
|
||||
|
||||
status := nats.CLOSED
|
||||
if n.conn != nil {
|
||||
status = n.conn.Status()
|
||||
}
|
||||
|
||||
switch status {
|
||||
case nats.CONNECTED, nats.RECONNECTING, nats.CONNECTING:
|
||||
return nil
|
||||
default: // DISCONNECTED or CLOSED or DRAINING
|
||||
opts := n.nopts
|
||||
opts.Servers = n.servers
|
||||
opts.Secure = n.opts.Secure
|
||||
opts.TLSConfig = n.opts.TLSConfig
|
||||
|
||||
// secure might not be set
|
||||
if n.opts.TLSConfig != nil {
|
||||
opts.Secure = true
|
||||
}
|
||||
|
||||
c, err := opts.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.conn = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *natsBroker) Disconnect() error {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
|
||||
if !n.connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
// drain the connection if specified
|
||||
if n.drain {
|
||||
n.conn.Drain()
|
||||
return <-n.closeCh
|
||||
}
|
||||
|
||||
// close the client connection
|
||||
n.conn.Close()
|
||||
|
||||
// shutdown the local server
|
||||
// and deregister
|
||||
select {
|
||||
case <-n.exit:
|
||||
default:
|
||||
close(n.exit)
|
||||
}
|
||||
|
||||
// set not connected
|
||||
n.connected = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) Init(opts ...Option) error {
|
||||
n.setOption(opts...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) Options() Options {
|
||||
return n.opts
|
||||
}
|
||||
|
||||
func (n *natsBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
|
||||
b, err := n.opts.Codec.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
return n.conn.Publish(topic, b)
|
||||
}
|
||||
|
||||
func (n *natsBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
|
||||
if n.conn == nil {
|
||||
return nil, errors.New("not connected")
|
||||
}
|
||||
|
||||
opt := SubscribeOptions{
|
||||
AutoAck: true,
|
||||
Context: context.Background(),
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
fn := func(msg *nats.Msg) {
|
||||
var m Message
|
||||
if err := n.opts.Codec.Unmarshal(msg.Data, &m); err != nil {
|
||||
return
|
||||
}
|
||||
handler(&publication{m: &m, t: msg.Subject})
|
||||
}
|
||||
|
||||
var sub *nats.Subscription
|
||||
var err error
|
||||
|
||||
n.RLock()
|
||||
if len(opt.Queue) > 0 {
|
||||
sub, err = n.conn.QueueSubscribe(topic, opt.Queue, fn)
|
||||
} else {
|
||||
sub, err = n.conn.Subscribe(topic, fn)
|
||||
}
|
||||
n.RUnlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &subscriber{s: sub, opts: opt}, nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) String() string {
|
||||
return "eats"
|
||||
}
|
||||
|
||||
func (n *natsBroker) setOption(opts ...Option) {
|
||||
for _, o := range opts {
|
||||
o(&n.opts)
|
||||
}
|
||||
|
||||
n.Once.Do(func() {
|
||||
n.nopts = nats.GetDefaultOptions()
|
||||
})
|
||||
|
||||
// local embedded server
|
||||
n.local = true
|
||||
// set to drain
|
||||
n.drain = true
|
||||
|
||||
if !n.opts.Secure {
|
||||
n.opts.Secure = n.nopts.Secure
|
||||
}
|
||||
|
||||
if n.opts.TLSConfig == nil {
|
||||
n.opts.TLSConfig = n.nopts.TLSConfig
|
||||
}
|
||||
|
||||
n.addrs = n.setAddrs(n.opts.Addrs)
|
||||
}
|
||||
|
||||
func (n *natsBroker) onClose(conn *nats.Conn) {
|
||||
n.closeCh <- nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err error) {
|
||||
// There are kinds of different async error nats might callback, but we are interested
|
||||
// in ErrDrainTimeout only here.
|
||||
if err == nats.ErrDrainTimeout {
|
||||
n.closeCh <- err
|
||||
}
|
||||
}
|
||||
|
||||
func NewBroker(opts ...Option) Broker {
|
||||
options := Options{
|
||||
// Default codec
|
||||
Codec: json.Marshaler{},
|
||||
Context: context.Background(),
|
||||
Registry: registry.DefaultRegistry,
|
||||
}
|
||||
|
||||
n := &natsBroker{
|
||||
opts: options,
|
||||
}
|
||||
n.setOption(opts...)
|
||||
|
||||
return n
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
// Package http provides a http based message broker
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/broker"
|
||||
)
|
||||
|
||||
// NewBroker returns a new http broker
|
||||
func NewBroker(opts ...broker.Option) broker.Broker {
|
||||
return broker.NewBroker(opts...)
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/micro/go-micro/broker"
|
||||
)
|
||||
|
||||
// Handle registers the handler for the given pattern.
|
||||
func Handle(pattern string, handler http.Handler) broker.Option {
|
||||
return func(o *broker.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
handlers, ok := o.Context.Value("http_handlers").(map[string]http.Handler)
|
||||
if !ok {
|
||||
handlers = make(map[string]http.Handler)
|
||||
}
|
||||
handlers[pattern] = handler
|
||||
o.Context = context.WithValue(o.Context, "http_handlers", handlers)
|
||||
}
|
||||
}
|
@@ -1,681 +0,0 @@
|
||||
package broker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/go-micro/codec/json"
|
||||
merr "github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/registry/cache"
|
||||
maddr "github.com/micro/go-micro/util/addr"
|
||||
mnet "github.com/micro/go-micro/util/net"
|
||||
mls "github.com/micro/go-micro/util/tls"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
// HTTP Broker is a point to point async broker
|
||||
type httpBroker struct {
|
||||
id string
|
||||
address string
|
||||
opts Options
|
||||
|
||||
mux *http.ServeMux
|
||||
|
||||
c *http.Client
|
||||
r registry.Registry
|
||||
|
||||
sync.RWMutex
|
||||
subscribers map[string][]*httpSubscriber
|
||||
running bool
|
||||
exit chan chan error
|
||||
|
||||
// offline message inbox
|
||||
mtx sync.RWMutex
|
||||
inbox map[string][][]byte
|
||||
}
|
||||
|
||||
type httpSubscriber struct {
|
||||
opts SubscribeOptions
|
||||
id string
|
||||
topic string
|
||||
fn Handler
|
||||
svc *registry.Service
|
||||
hb *httpBroker
|
||||
}
|
||||
|
||||
type httpEvent struct {
|
||||
m *Message
|
||||
t string
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultSubPath = "/_sub"
|
||||
broadcastVersion = "ff.http.broadcast"
|
||||
registerTTL = time.Minute
|
||||
registerInterval = time.Second * 30
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
}
|
||||
|
||||
func newTransport(config *tls.Config) *http.Transport {
|
||||
if config == nil {
|
||||
config = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
}
|
||||
|
||||
dialTLS := func(network string, addr string) (net.Conn, error) {
|
||||
return tls.Dial(network, addr, config)
|
||||
}
|
||||
|
||||
t := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
DialTLS: dialTLS,
|
||||
}
|
||||
runtime.SetFinalizer(&t, func(tr **http.Transport) {
|
||||
(*tr).CloseIdleConnections()
|
||||
})
|
||||
|
||||
// setup http2
|
||||
http2.ConfigureTransport(t)
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func newHttpBroker(opts ...Option) Broker {
|
||||
options := Options{
|
||||
Codec: json.Marshaler{},
|
||||
Context: context.TODO(),
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
// set address
|
||||
addr := ":0"
|
||||
if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
|
||||
addr = options.Addrs[0]
|
||||
}
|
||||
|
||||
// get registry
|
||||
reg, ok := options.Context.Value(registryKey).(registry.Registry)
|
||||
if !ok {
|
||||
reg = registry.DefaultRegistry
|
||||
}
|
||||
|
||||
h := &httpBroker{
|
||||
id: "broker-" + uuid.New().String(),
|
||||
address: addr,
|
||||
opts: options,
|
||||
r: reg,
|
||||
c: &http.Client{Transport: newTransport(options.TLSConfig)},
|
||||
subscribers: make(map[string][]*httpSubscriber),
|
||||
exit: make(chan chan error),
|
||||
mux: http.NewServeMux(),
|
||||
inbox: make(map[string][][]byte),
|
||||
}
|
||||
|
||||
// specify the message handler
|
||||
h.mux.Handle(DefaultSubPath, h)
|
||||
|
||||
// get optional handlers
|
||||
if h.opts.Context != nil {
|
||||
handlers, ok := h.opts.Context.Value("http_handlers").(map[string]http.Handler)
|
||||
if ok {
|
||||
for pattern, handler := range handlers {
|
||||
h.mux.Handle(pattern, handler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *httpEvent) Ack() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpEvent) Message() *Message {
|
||||
return h.m
|
||||
}
|
||||
|
||||
func (h *httpEvent) Topic() string {
|
||||
return h.t
|
||||
}
|
||||
|
||||
func (h *httpSubscriber) Options() SubscribeOptions {
|
||||
return h.opts
|
||||
}
|
||||
|
||||
func (h *httpSubscriber) Topic() string {
|
||||
return h.topic
|
||||
}
|
||||
|
||||
func (h *httpSubscriber) Unsubscribe() error {
|
||||
return h.hb.unsubscribe(h)
|
||||
}
|
||||
|
||||
func (h *httpBroker) saveMessage(topic string, msg []byte) {
|
||||
h.mtx.Lock()
|
||||
defer h.mtx.Unlock()
|
||||
|
||||
// get messages
|
||||
c := h.inbox[topic]
|
||||
|
||||
// save message
|
||||
c = append(c, msg)
|
||||
|
||||
// max length 64
|
||||
if len(c) > 64 {
|
||||
c = c[:64]
|
||||
}
|
||||
|
||||
// save inbox
|
||||
h.inbox[topic] = c
|
||||
}
|
||||
|
||||
func (h *httpBroker) getMessage(topic string, num int) [][]byte {
|
||||
h.mtx.Lock()
|
||||
defer h.mtx.Unlock()
|
||||
|
||||
// get messages
|
||||
c, ok := h.inbox[topic]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// more message than requests
|
||||
if len(c) >= num {
|
||||
msg := c[:num]
|
||||
h.inbox[topic] = c[num:]
|
||||
return msg
|
||||
}
|
||||
|
||||
// reset inbox
|
||||
h.inbox[topic] = nil
|
||||
|
||||
// return all messages
|
||||
return c
|
||||
}
|
||||
|
||||
func (h *httpBroker) subscribe(s *httpSubscriber) error {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
if err := h.r.Register(s.svc, registry.RegisterTTL(registerTTL)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.subscribers[s.topic] = append(h.subscribers[s.topic], s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpBroker) unsubscribe(s *httpSubscriber) error {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
var subscribers []*httpSubscriber
|
||||
|
||||
// look for subscriber
|
||||
for _, sub := range h.subscribers[s.topic] {
|
||||
// deregister and skip forward
|
||||
if sub.id == s.id {
|
||||
_ = h.r.Deregister(sub.svc)
|
||||
continue
|
||||
}
|
||||
// keep subscriber
|
||||
subscribers = append(subscribers, sub)
|
||||
}
|
||||
|
||||
// set subscribers
|
||||
h.subscribers[s.topic] = subscribers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpBroker) run(l net.Listener) {
|
||||
t := time.NewTicker(registerInterval)
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
// heartbeat for each subscriber
|
||||
case <-t.C:
|
||||
h.RLock()
|
||||
for _, subs := range h.subscribers {
|
||||
for _, sub := range subs {
|
||||
_ = h.r.Register(sub.svc, registry.RegisterTTL(registerTTL))
|
||||
}
|
||||
}
|
||||
h.RUnlock()
|
||||
// received exit signal
|
||||
case ch := <-h.exit:
|
||||
ch <- l.Close()
|
||||
h.RLock()
|
||||
for _, subs := range h.subscribers {
|
||||
for _, sub := range subs {
|
||||
_ = h.r.Deregister(sub.svc)
|
||||
}
|
||||
}
|
||||
h.RUnlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != "POST" {
|
||||
err := merr.BadRequest("go.micro.broker", "Method not allowed")
|
||||
http.Error(w, err.Error(), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
defer req.Body.Close()
|
||||
|
||||
req.ParseForm()
|
||||
|
||||
b, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
errr := merr.InternalServerError("go.micro.broker", "Error reading request body: %v", err)
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(errr.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
var m *Message
|
||||
if err = h.opts.Codec.Unmarshal(b, &m); err != nil {
|
||||
errr := merr.InternalServerError("go.micro.broker", "Error parsing request body: %v", err)
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(errr.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
topic := m.Header[":topic"]
|
||||
delete(m.Header, ":topic")
|
||||
|
||||
if len(topic) == 0 {
|
||||
errr := merr.InternalServerError("go.micro.broker", "Topic not found")
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(errr.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
p := &httpEvent{m: m, t: topic}
|
||||
id := req.Form.Get("id")
|
||||
|
||||
h.RLock()
|
||||
for _, subscriber := range h.subscribers[topic] {
|
||||
if id == subscriber.id {
|
||||
// sub is sync; crufty rate limiting
|
||||
// so we don't hose the cpu
|
||||
subscriber.fn(p)
|
||||
}
|
||||
}
|
||||
h.RUnlock()
|
||||
}
|
||||
|
||||
func (h *httpBroker) Address() string {
|
||||
h.RLock()
|
||||
defer h.RUnlock()
|
||||
return h.address
|
||||
}
|
||||
|
||||
func (h *httpBroker) Connect() error {
|
||||
h.RLock()
|
||||
if h.running {
|
||||
h.RUnlock()
|
||||
return nil
|
||||
}
|
||||
h.RUnlock()
|
||||
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
var l net.Listener
|
||||
var err error
|
||||
|
||||
if h.opts.Secure || h.opts.TLSConfig != nil {
|
||||
config := h.opts.TLSConfig
|
||||
|
||||
fn := func(addr string) (net.Listener, error) {
|
||||
if config == nil {
|
||||
hosts := []string{addr}
|
||||
|
||||
// check if its a valid host:port
|
||||
if host, _, err := net.SplitHostPort(addr); err == nil {
|
||||
if len(host) == 0 {
|
||||
hosts = maddr.IPs()
|
||||
} else {
|
||||
hosts = []string{host}
|
||||
}
|
||||
}
|
||||
|
||||
// generate a certificate
|
||||
cert, err := mls.Certificate(hosts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
}
|
||||
return tls.Listen("tcp", addr, config)
|
||||
}
|
||||
|
||||
l, err = mnet.Listen(h.address, fn)
|
||||
} else {
|
||||
fn := func(addr string) (net.Listener, error) {
|
||||
return net.Listen("tcp", addr)
|
||||
}
|
||||
|
||||
l, err = mnet.Listen(h.address, fn)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addr := h.address
|
||||
h.address = l.Addr().String()
|
||||
|
||||
go http.Serve(l, h.mux)
|
||||
go func() {
|
||||
h.run(l)
|
||||
h.Lock()
|
||||
h.opts.Addrs = []string{addr}
|
||||
h.address = addr
|
||||
h.Unlock()
|
||||
}()
|
||||
|
||||
// get registry
|
||||
reg, ok := h.opts.Context.Value(registryKey).(registry.Registry)
|
||||
if !ok {
|
||||
reg = registry.DefaultRegistry
|
||||
}
|
||||
// set cache
|
||||
h.r = cache.New(reg)
|
||||
|
||||
// set running
|
||||
h.running = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpBroker) Disconnect() error {
|
||||
|
||||
h.RLock()
|
||||
if !h.running {
|
||||
h.RUnlock()
|
||||
return nil
|
||||
}
|
||||
h.RUnlock()
|
||||
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
// stop cache
|
||||
rc, ok := h.r.(cache.Cache)
|
||||
if ok {
|
||||
rc.Stop()
|
||||
}
|
||||
|
||||
// exit and return err
|
||||
ch := make(chan error)
|
||||
h.exit <- ch
|
||||
err := <-ch
|
||||
|
||||
// set not running
|
||||
h.running = false
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *httpBroker) Init(opts ...Option) error {
|
||||
h.RLock()
|
||||
if h.running {
|
||||
h.RUnlock()
|
||||
return errors.New("cannot init while connected")
|
||||
}
|
||||
h.RUnlock()
|
||||
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
for _, o := range opts {
|
||||
o(&h.opts)
|
||||
}
|
||||
|
||||
if len(h.opts.Addrs) > 0 && len(h.opts.Addrs[0]) > 0 {
|
||||
h.address = h.opts.Addrs[0]
|
||||
}
|
||||
|
||||
if len(h.id) == 0 {
|
||||
h.id = "broker-" + uuid.New().String()
|
||||
}
|
||||
|
||||
// get registry
|
||||
reg, ok := h.opts.Context.Value(registryKey).(registry.Registry)
|
||||
if !ok {
|
||||
reg = registry.DefaultRegistry
|
||||
}
|
||||
|
||||
// get cache
|
||||
if rc, ok := h.r.(cache.Cache); ok {
|
||||
rc.Stop()
|
||||
}
|
||||
|
||||
// set registry
|
||||
h.r = cache.New(reg)
|
||||
|
||||
// reconfigure tls config
|
||||
if c := h.opts.TLSConfig; c != nil {
|
||||
h.c = &http.Client{
|
||||
Transport: newTransport(c),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpBroker) Options() Options {
|
||||
return h.opts
|
||||
}
|
||||
|
||||
func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
|
||||
// create the message first
|
||||
m := &Message{
|
||||
Header: make(map[string]string),
|
||||
Body: msg.Body,
|
||||
}
|
||||
|
||||
for k, v := range msg.Header {
|
||||
m.Header[k] = v
|
||||
}
|
||||
|
||||
m.Header[":topic"] = topic
|
||||
|
||||
// encode the message
|
||||
b, err := h.opts.Codec.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save the message
|
||||
h.saveMessage(topic, b)
|
||||
|
||||
// now attempt to get the service
|
||||
h.RLock()
|
||||
s, err := h.r.GetService("topic:" + topic)
|
||||
if err != nil {
|
||||
h.RUnlock()
|
||||
// ignore error
|
||||
return nil
|
||||
}
|
||||
h.RUnlock()
|
||||
|
||||
pub := func(node *registry.Node, t string, b []byte) error {
|
||||
scheme := "http"
|
||||
|
||||
// check if secure is added in metadata
|
||||
if node.Metadata["secure"] == "true" {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
vals := url.Values{}
|
||||
vals.Add("id", node.Id)
|
||||
|
||||
uri := fmt.Sprintf("%s://%s%s?%s", scheme, node.Address, DefaultSubPath, vals.Encode())
|
||||
r, err := h.c.Post(uri, "application/json", bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// discard response body
|
||||
io.Copy(ioutil.Discard, r.Body)
|
||||
r.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
srv := func(s []*registry.Service, b []byte) {
|
||||
for _, service := range s {
|
||||
// only process if we have nodes
|
||||
if len(service.Nodes) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
switch service.Version {
|
||||
// broadcast version means broadcast to all nodes
|
||||
case broadcastVersion:
|
||||
var success bool
|
||||
|
||||
// publish to all nodes
|
||||
for _, node := range service.Nodes {
|
||||
// publish async
|
||||
if err := pub(node, topic, b); err == nil {
|
||||
success = true
|
||||
}
|
||||
}
|
||||
|
||||
// save if it failed to publish at least once
|
||||
if !success {
|
||||
h.saveMessage(topic, b)
|
||||
}
|
||||
default:
|
||||
// select node to publish to
|
||||
node := service.Nodes[rand.Int()%len(service.Nodes)]
|
||||
|
||||
// publish async to one node
|
||||
if err := pub(node, topic, b); err != nil {
|
||||
// if failed save it
|
||||
h.saveMessage(topic, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do the rest async
|
||||
go func() {
|
||||
// get a third of the backlog
|
||||
messages := h.getMessage(topic, 8)
|
||||
delay := (len(messages) > 1)
|
||||
|
||||
// publish all the messages
|
||||
for _, msg := range messages {
|
||||
// serialize here
|
||||
srv(s, msg)
|
||||
|
||||
// sending a backlog of messages
|
||||
if delay {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
|
||||
var err error
|
||||
var host, port string
|
||||
options := NewSubscribeOptions(opts...)
|
||||
|
||||
// parse address for host, port
|
||||
host, port, err = net.SplitHostPort(h.Address())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr, err := maddr.Extract(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create unique id
|
||||
id := h.id + "." + uuid.New().String()
|
||||
|
||||
var secure bool
|
||||
|
||||
if h.opts.Secure || h.opts.TLSConfig != nil {
|
||||
secure = true
|
||||
}
|
||||
|
||||
// register service
|
||||
node := ®istry.Node{
|
||||
Id: id,
|
||||
Address: mnet.HostPort(addr, port),
|
||||
Metadata: map[string]string{
|
||||
"secure": fmt.Sprintf("%t", secure),
|
||||
},
|
||||
}
|
||||
|
||||
// check for queue group or broadcast queue
|
||||
version := options.Queue
|
||||
if len(version) == 0 {
|
||||
version = broadcastVersion
|
||||
}
|
||||
|
||||
service := ®istry.Service{
|
||||
Name: "topic:" + topic,
|
||||
Version: version,
|
||||
Nodes: []*registry.Node{node},
|
||||
}
|
||||
|
||||
// generate subscriber
|
||||
subscriber := &httpSubscriber{
|
||||
opts: options,
|
||||
hb: h,
|
||||
id: id,
|
||||
topic: topic,
|
||||
fn: handler,
|
||||
svc: service,
|
||||
}
|
||||
|
||||
// subscribe now
|
||||
if err := h.subscribe(subscriber); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// return the subscriber
|
||||
return subscriber, nil
|
||||
}
|
||||
|
||||
func (h *httpBroker) String() string {
|
||||
return "http"
|
||||
}
|
@@ -1,351 +0,0 @@
|
||||
package broker
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
glog "github.com/go-log/log"
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/go-micro/registry/memory"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
func newTestRegistry() *memory.Registry {
|
||||
r := memory.NewRegistry()
|
||||
m := r.(*memory.Registry)
|
||||
m.Services = testData
|
||||
return m
|
||||
}
|
||||
|
||||
func sub(be *testing.B, c int) {
|
||||
// set no op logger
|
||||
log.SetLogger(glog.DefaultLogger)
|
||||
|
||||
be.StopTimer()
|
||||
m := newTestRegistry()
|
||||
|
||||
b := NewBroker(Registry(m))
|
||||
topic := uuid.New().String()
|
||||
|
||||
if err := b.Init(); err != nil {
|
||||
be.Fatalf("Unexpected init error: %v", err)
|
||||
}
|
||||
|
||||
if err := b.Connect(); err != nil {
|
||||
be.Fatalf("Unexpected connect error: %v", err)
|
||||
}
|
||||
|
||||
msg := &Message{
|
||||
Header: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
Body: []byte(`{"message": "Hello World"}`),
|
||||
}
|
||||
|
||||
var subs []Subscriber
|
||||
done := make(chan bool, c)
|
||||
|
||||
for i := 0; i < c; i++ {
|
||||
sub, err := b.Subscribe(topic, func(p Event) error {
|
||||
done <- true
|
||||
m := p.Message()
|
||||
|
||||
if string(m.Body) != string(msg.Body) {
|
||||
be.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
|
||||
}
|
||||
|
||||
return nil
|
||||
}, Queue("shared"))
|
||||
if err != nil {
|
||||
be.Fatalf("Unexpected subscribe error: %v", err)
|
||||
}
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
|
||||
for i := 0; i < be.N; i++ {
|
||||
be.StartTimer()
|
||||
if err := b.Publish(topic, msg); err != nil {
|
||||
be.Fatalf("Unexpected publish error: %v", err)
|
||||
}
|
||||
<-done
|
||||
be.StopTimer()
|
||||
}
|
||||
|
||||
for _, sub := range subs {
|
||||
sub.Unsubscribe()
|
||||
}
|
||||
|
||||
if err := b.Disconnect(); err != nil {
|
||||
be.Fatalf("Unexpected disconnect error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func pub(be *testing.B, c int) {
|
||||
// set no op logger
|
||||
log.SetLogger(glog.DefaultLogger)
|
||||
|
||||
be.StopTimer()
|
||||
m := newTestRegistry()
|
||||
b := NewBroker(Registry(m))
|
||||
topic := uuid.New().String()
|
||||
|
||||
if err := b.Init(); err != nil {
|
||||
be.Fatalf("Unexpected init error: %v", err)
|
||||
}
|
||||
|
||||
if err := b.Connect(); err != nil {
|
||||
be.Fatalf("Unexpected connect error: %v", err)
|
||||
}
|
||||
|
||||
msg := &Message{
|
||||
Header: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
Body: []byte(`{"message": "Hello World"}`),
|
||||
}
|
||||
|
||||
done := make(chan bool, c*4)
|
||||
|
||||
sub, err := b.Subscribe(topic, func(p Event) error {
|
||||
done <- true
|
||||
m := p.Message()
|
||||
if string(m.Body) != string(msg.Body) {
|
||||
be.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
|
||||
}
|
||||
return nil
|
||||
}, Queue("shared"))
|
||||
if err != nil {
|
||||
be.Fatalf("Unexpected subscribe error: %v", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
ch := make(chan int, c*4)
|
||||
be.StartTimer()
|
||||
|
||||
for i := 0; i < c; i++ {
|
||||
go func() {
|
||||
for _ = range ch {
|
||||
if err := b.Publish(topic, msg); err != nil {
|
||||
be.Fatalf("Unexpected publish error: %v", err)
|
||||
}
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
wg.Done()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < be.N; i++ {
|
||||
wg.Add(1)
|
||||
ch <- i
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
be.StopTimer()
|
||||
sub.Unsubscribe()
|
||||
close(ch)
|
||||
close(done)
|
||||
|
||||
if err := b.Disconnect(); err != nil {
|
||||
be.Fatalf("Unexpected disconnect error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroker(t *testing.T) {
|
||||
m := newTestRegistry()
|
||||
b := NewBroker(Registry(m))
|
||||
|
||||
if err := b.Init(); err != nil {
|
||||
t.Fatalf("Unexpected init error: %v", err)
|
||||
}
|
||||
|
||||
if err := b.Connect(); err != nil {
|
||||
t.Fatalf("Unexpected connect error: %v", err)
|
||||
}
|
||||
|
||||
msg := &Message{
|
||||
Header: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
Body: []byte(`{"message": "Hello World"}`),
|
||||
}
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
sub, err := b.Subscribe("test", func(p Event) error {
|
||||
m := p.Message()
|
||||
|
||||
if string(m.Body) != string(msg.Body) {
|
||||
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
|
||||
}
|
||||
|
||||
close(done)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected subscribe error: %v", err)
|
||||
}
|
||||
|
||||
if err := b.Publish("test", msg); err != nil {
|
||||
t.Fatalf("Unexpected publish error: %v", err)
|
||||
}
|
||||
|
||||
<-done
|
||||
sub.Unsubscribe()
|
||||
|
||||
if err := b.Disconnect(); err != nil {
|
||||
t.Fatalf("Unexpected disconnect error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrentSubBroker(t *testing.T) {
|
||||
m := newTestRegistry()
|
||||
b := NewBroker(Registry(m))
|
||||
|
||||
if err := b.Init(); err != nil {
|
||||
t.Fatalf("Unexpected init error: %v", err)
|
||||
}
|
||||
|
||||
if err := b.Connect(); err != nil {
|
||||
t.Fatalf("Unexpected connect error: %v", err)
|
||||
}
|
||||
|
||||
msg := &Message{
|
||||
Header: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
Body: []byte(`{"message": "Hello World"}`),
|
||||
}
|
||||
|
||||
var subs []Subscriber
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
sub, err := b.Subscribe("test", func(p Event) error {
|
||||
defer wg.Done()
|
||||
|
||||
m := p.Message()
|
||||
|
||||
if string(m.Body) != string(msg.Body) {
|
||||
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected subscribe error: %v", err)
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
|
||||
if err := b.Publish("test", msg); err != nil {
|
||||
t.Fatalf("Unexpected publish error: %v", err)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
for _, sub := range subs {
|
||||
sub.Unsubscribe()
|
||||
}
|
||||
|
||||
if err := b.Disconnect(); err != nil {
|
||||
t.Fatalf("Unexpected disconnect error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrentPubBroker(t *testing.T) {
|
||||
m := newTestRegistry()
|
||||
b := NewBroker(Registry(m))
|
||||
|
||||
if err := b.Init(); err != nil {
|
||||
t.Fatalf("Unexpected init error: %v", err)
|
||||
}
|
||||
|
||||
if err := b.Connect(); err != nil {
|
||||
t.Fatalf("Unexpected connect error: %v", err)
|
||||
}
|
||||
|
||||
msg := &Message{
|
||||
Header: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
Body: []byte(`{"message": "Hello World"}`),
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
sub, err := b.Subscribe("test", func(p Event) error {
|
||||
defer wg.Done()
|
||||
|
||||
m := p.Message()
|
||||
|
||||
if string(m.Body) != string(msg.Body) {
|
||||
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected subscribe error: %v", err)
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
|
||||
if err := b.Publish("test", msg); err != nil {
|
||||
t.Fatalf("Unexpected publish error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
sub.Unsubscribe()
|
||||
|
||||
if err := b.Disconnect(); err != nil {
|
||||
t.Fatalf("Unexpected disconnect error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSub1(b *testing.B) {
|
||||
sub(b, 1)
|
||||
}
|
||||
func BenchmarkSub8(b *testing.B) {
|
||||
sub(b, 8)
|
||||
}
|
||||
|
||||
func BenchmarkSub32(b *testing.B) {
|
||||
sub(b, 32)
|
||||
}
|
||||
|
||||
func BenchmarkSub64(b *testing.B) {
|
||||
sub(b, 64)
|
||||
}
|
||||
|
||||
func BenchmarkSub128(b *testing.B) {
|
||||
sub(b, 128)
|
||||
}
|
||||
|
||||
func BenchmarkPub1(b *testing.B) {
|
||||
pub(b, 1)
|
||||
}
|
||||
|
||||
func BenchmarkPub8(b *testing.B) {
|
||||
pub(b, 8)
|
||||
}
|
||||
|
||||
func BenchmarkPub32(b *testing.B) {
|
||||
pub(b, 32)
|
||||
}
|
||||
|
||||
func BenchmarkPub64(b *testing.B) {
|
||||
pub(b, 64)
|
||||
}
|
||||
|
||||
func BenchmarkPub128(b *testing.B) {
|
||||
pub(b, 128)
|
||||
}
|
@@ -4,23 +4,44 @@ package nats
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/broker"
|
||||
"github.com/micro/go-micro/codec/json"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/util/addr"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
"github.com/nats-io/nats-server/v2/server"
|
||||
nats "github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
type natsBroker struct {
|
||||
sync.Once
|
||||
sync.RWMutex
|
||||
addrs []string
|
||||
conn *nats.Conn
|
||||
opts broker.Options
|
||||
nopts nats.Options
|
||||
|
||||
// indicate if we're connected
|
||||
connected bool
|
||||
|
||||
addrs []string
|
||||
conn *nats.Conn
|
||||
opts broker.Options
|
||||
nopts nats.Options
|
||||
|
||||
// should we drain the connection
|
||||
drain bool
|
||||
closeCh chan (error)
|
||||
|
||||
// embedded server
|
||||
server *server.Server
|
||||
// configure to use local server
|
||||
local bool
|
||||
// server exit channel
|
||||
exit chan bool
|
||||
}
|
||||
|
||||
type subscriber struct {
|
||||
@@ -62,6 +83,7 @@ func (n *natsBroker) Address() string {
|
||||
if n.conn != nil && n.conn.IsConnected() {
|
||||
return n.conn.ConnectedUrl()
|
||||
}
|
||||
|
||||
if len(n.addrs) > 0 {
|
||||
return n.addrs[0]
|
||||
}
|
||||
@@ -69,7 +91,8 @@ func (n *natsBroker) Address() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func setAddrs(addrs []string) []string {
|
||||
func (n *natsBroker) setAddrs(addrs []string) []string {
|
||||
//nolint:prealloc
|
||||
var cAddrs []string
|
||||
for _, addr := range addrs {
|
||||
if len(addr) == 0 {
|
||||
@@ -80,16 +103,186 @@ func setAddrs(addrs []string) []string {
|
||||
}
|
||||
cAddrs = append(cAddrs, addr)
|
||||
}
|
||||
if len(cAddrs) == 0 {
|
||||
// if there's no address and we weren't told to
|
||||
// embed a local server then use the default url
|
||||
if len(cAddrs) == 0 && !n.local {
|
||||
cAddrs = []string{nats.DefaultURL}
|
||||
}
|
||||
return cAddrs
|
||||
}
|
||||
|
||||
// serve stats a local nats server if needed
|
||||
func (n *natsBroker) serve(exit chan bool) error {
|
||||
var host string
|
||||
var port int
|
||||
var local bool
|
||||
|
||||
// with no address we just default it
|
||||
// this is a local client address
|
||||
if len(n.addrs) == 0 {
|
||||
// find an advertiseable ip
|
||||
if h, err := addr.Extract(""); err != nil {
|
||||
host = "127.0.0.1"
|
||||
} else {
|
||||
host = h
|
||||
}
|
||||
|
||||
port = -1
|
||||
local = true
|
||||
} else {
|
||||
address := n.addrs[0]
|
||||
if strings.HasPrefix(address, "nats://") {
|
||||
address = strings.TrimPrefix(address, "nats://")
|
||||
}
|
||||
|
||||
// check if its a local address and only then embed
|
||||
if addr.IsLocal(address) {
|
||||
h, p, err := net.SplitHostPort(address)
|
||||
if err == nil {
|
||||
host = h
|
||||
port, _ = strconv.Atoi(p)
|
||||
local = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we only setup a server for local things
|
||||
if !local {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 1. create new server
|
||||
// 2. register the server
|
||||
// 3. connect to other servers
|
||||
var cOpts server.ClusterOpts
|
||||
var routes []*url.URL
|
||||
|
||||
// get existing nats servers to connect to
|
||||
services, err := n.opts.Registry.GetService("go.micro.nats.broker")
|
||||
if err == nil {
|
||||
for _, service := range services {
|
||||
for _, node := range service.Nodes {
|
||||
u, err := url.Parse("nats://" + node.Address)
|
||||
if err != nil {
|
||||
log.Log(err)
|
||||
continue
|
||||
}
|
||||
// append to the cluster routes
|
||||
routes = append(routes, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try get existing server
|
||||
s := n.server
|
||||
|
||||
// get a host address
|
||||
caddr, err := addr.Extract("")
|
||||
if err != nil {
|
||||
caddr = "0.0.0.0"
|
||||
}
|
||||
|
||||
// set cluster opts
|
||||
cOpts = server.ClusterOpts{
|
||||
Host: caddr,
|
||||
Port: -1,
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
var err error
|
||||
s, err = server.NewServer(&server.Options{
|
||||
// Specify the host
|
||||
Host: host,
|
||||
// Use a random port
|
||||
Port: port,
|
||||
// Set the cluster ops
|
||||
Cluster: cOpts,
|
||||
// Set the routes
|
||||
Routes: routes,
|
||||
NoLog: true,
|
||||
NoSigs: true,
|
||||
MaxControlLine: 2048,
|
||||
TLSConfig: n.opts.TLSConfig,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save the server
|
||||
n.server = s
|
||||
}
|
||||
|
||||
// start the server
|
||||
go s.Start()
|
||||
|
||||
var ready bool
|
||||
|
||||
// wait till its ready for connections
|
||||
for i := 0; i < 3; i++ {
|
||||
if s.ReadyForConnections(time.Second) {
|
||||
ready = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ready {
|
||||
return errors.New("server not ready")
|
||||
}
|
||||
|
||||
// set the client address
|
||||
n.addrs = []string{s.ClientURL()}
|
||||
|
||||
go func() {
|
||||
// register the cluster address
|
||||
for {
|
||||
select {
|
||||
case <-exit:
|
||||
// deregister on exit
|
||||
n.opts.Registry.Deregister(®istry.Service{
|
||||
Name: "go.micro.nats.broker",
|
||||
Version: "v2",
|
||||
Nodes: []*registry.Node{
|
||||
{Id: s.ID(), Address: s.ClusterAddr().String()},
|
||||
},
|
||||
})
|
||||
s.Shutdown()
|
||||
return
|
||||
default:
|
||||
// register the broker
|
||||
n.opts.Registry.Register(®istry.Service{
|
||||
Name: "go.micro.nats.broker",
|
||||
Version: "v2",
|
||||
Nodes: []*registry.Node{
|
||||
{Id: s.ID(), Address: s.ClusterAddr().String()},
|
||||
},
|
||||
}, registry.RegisterTTL(time.Minute))
|
||||
time.Sleep(time.Minute)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) Connect() error {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
if !n.connected {
|
||||
// create exit chan
|
||||
n.exit = make(chan bool)
|
||||
|
||||
// start embedded server if asked to
|
||||
if n.local {
|
||||
if err := n.serve(n.exit); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// set to connected
|
||||
n.connected = true
|
||||
}
|
||||
|
||||
status := nats.CLOSED
|
||||
if n.conn != nil {
|
||||
status = n.conn.Status()
|
||||
@@ -121,11 +314,29 @@ func (n *natsBroker) Connect() error {
|
||||
func (n *natsBroker) Disconnect() error {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
|
||||
// drain the connection if specified
|
||||
if n.drain {
|
||||
n.conn.Drain()
|
||||
return <-n.closeCh
|
||||
}
|
||||
|
||||
// close the client connection
|
||||
n.conn.Close()
|
||||
|
||||
// shutdown the local server
|
||||
// and deregister
|
||||
if n.server != nil {
|
||||
select {
|
||||
case <-n.exit:
|
||||
default:
|
||||
close(n.exit)
|
||||
}
|
||||
}
|
||||
|
||||
// set not connected
|
||||
n.connected = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -190,21 +401,6 @@ func (n *natsBroker) String() string {
|
||||
return "nats"
|
||||
}
|
||||
|
||||
func NewBroker(opts ...broker.Option) broker.Broker {
|
||||
options := broker.Options{
|
||||
// Default codec
|
||||
Codec: json.Marshaler{},
|
||||
Context: context.Background(),
|
||||
}
|
||||
|
||||
n := &natsBroker{
|
||||
opts: options,
|
||||
}
|
||||
n.setOption(opts...)
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *natsBroker) setOption(opts ...broker.Option) {
|
||||
for _, o := range opts {
|
||||
o(&n.opts)
|
||||
@@ -218,10 +414,15 @@ func (n *natsBroker) setOption(opts ...broker.Option) {
|
||||
n.nopts = nopts
|
||||
}
|
||||
|
||||
local, ok := n.opts.Context.Value(localServerKey{}).(bool)
|
||||
if ok {
|
||||
n.local = local
|
||||
}
|
||||
|
||||
// broker.Options have higher priority than nats.Options
|
||||
// only if Addrs, Secure or TLSConfig were not set through a broker.Option
|
||||
// we read them from nats.Option
|
||||
if len(n.opts.Addrs) == 0 {
|
||||
if len(n.opts.Addrs) == 0 && !n.local {
|
||||
n.opts.Addrs = n.nopts.Servers
|
||||
}
|
||||
|
||||
@@ -232,7 +433,7 @@ func (n *natsBroker) setOption(opts ...broker.Option) {
|
||||
if n.opts.TLSConfig == nil {
|
||||
n.opts.TLSConfig = n.nopts.TLSConfig
|
||||
}
|
||||
n.addrs = setAddrs(n.opts.Addrs)
|
||||
n.addrs = n.setAddrs(n.opts.Addrs)
|
||||
|
||||
if n.opts.Context.Value(drainConnectionKey{}) != nil {
|
||||
n.drain = true
|
||||
@@ -253,3 +454,19 @@ func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err e
|
||||
n.closeCh <- err
|
||||
}
|
||||
}
|
||||
|
||||
func NewBroker(opts ...broker.Option) broker.Broker {
|
||||
options := broker.Options{
|
||||
// Default codec
|
||||
Codec: json.Marshaler{},
|
||||
Context: context.Background(),
|
||||
Registry: registry.DefaultRegistry,
|
||||
}
|
||||
|
||||
n := &natsBroker{
|
||||
opts: options,
|
||||
}
|
||||
n.setOption(opts...)
|
||||
|
||||
return n
|
||||
}
|
||||
|
@@ -94,6 +94,5 @@ func TestInitAddrs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -7,12 +7,18 @@ import (
|
||||
|
||||
type optionsKey struct{}
|
||||
type drainConnectionKey struct{}
|
||||
type localServerKey struct{}
|
||||
|
||||
// Options accepts nats.Options
|
||||
func Options(opts nats.Options) broker.Option {
|
||||
return setBrokerOption(optionsKey{}, opts)
|
||||
}
|
||||
|
||||
// LocalServer embeds a local server rather than connecting to one
|
||||
func LocalServer() broker.Option {
|
||||
return setBrokerOption(localServerKey{}, true)
|
||||
}
|
||||
|
||||
// DrainConnection will drain subscription on close
|
||||
func DrainConnection() broker.Option {
|
||||
return setBrokerOption(drainConnectionKey{}, struct{}{})
|
||||
|
@@ -13,6 +13,8 @@ type Options struct {
|
||||
Secure bool
|
||||
Codec codec.Marshaler
|
||||
TLSConfig *tls.Config
|
||||
// Registry used for clustering
|
||||
Registry registry.Registry
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
@@ -44,10 +46,6 @@ type PublishOption func(*PublishOptions)
|
||||
|
||||
type SubscribeOption func(*SubscribeOptions)
|
||||
|
||||
var (
|
||||
registryKey = "github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
|
||||
opt := SubscribeOptions{
|
||||
AutoAck: true,
|
||||
@@ -92,7 +90,7 @@ func Queue(name string) SubscribeOption {
|
||||
|
||||
func Registry(r registry.Registry) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = context.WithValue(o.Context, registryKey, r)
|
||||
o.Registry = r
|
||||
}
|
||||
}
|
||||
|
||||
|
229
broker/service/proto/broker.pb.go
Normal file
229
broker/service/proto/broker.pb.go
Normal file
@@ -0,0 +1,229 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: micro/go-micro/broker/service/proto/broker.proto
|
||||
|
||||
package go_micro_broker
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Empty struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Empty) Reset() { *m = Empty{} }
|
||||
func (m *Empty) String() string { return proto.CompactTextString(m) }
|
||||
func (*Empty) ProtoMessage() {}
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_178fdc60944ff5e5, []int{0}
|
||||
}
|
||||
|
||||
func (m *Empty) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Empty.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Empty) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Empty.Merge(m, src)
|
||||
}
|
||||
func (m *Empty) XXX_Size() int {
|
||||
return xxx_messageInfo_Empty.Size(m)
|
||||
}
|
||||
func (m *Empty) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Empty.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Empty proto.InternalMessageInfo
|
||||
|
||||
type PublishRequest struct {
|
||||
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
|
||||
Message *Message `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PublishRequest) Reset() { *m = PublishRequest{} }
|
||||
func (m *PublishRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*PublishRequest) ProtoMessage() {}
|
||||
func (*PublishRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_178fdc60944ff5e5, []int{1}
|
||||
}
|
||||
|
||||
func (m *PublishRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PublishRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PublishRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PublishRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PublishRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PublishRequest.Merge(m, src)
|
||||
}
|
||||
func (m *PublishRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_PublishRequest.Size(m)
|
||||
}
|
||||
func (m *PublishRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PublishRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PublishRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *PublishRequest) GetTopic() string {
|
||||
if m != nil {
|
||||
return m.Topic
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PublishRequest) GetMessage() *Message {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SubscribeRequest struct {
|
||||
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
|
||||
Queue string `protobuf:"bytes,2,opt,name=queue,proto3" json:"queue,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} }
|
||||
func (m *SubscribeRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*SubscribeRequest) ProtoMessage() {}
|
||||
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_178fdc60944ff5e5, []int{2}
|
||||
}
|
||||
|
||||
func (m *SubscribeRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SubscribeRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *SubscribeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_SubscribeRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *SubscribeRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_SubscribeRequest.Merge(m, src)
|
||||
}
|
||||
func (m *SubscribeRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_SubscribeRequest.Size(m)
|
||||
}
|
||||
func (m *SubscribeRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_SubscribeRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_SubscribeRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *SubscribeRequest) GetTopic() string {
|
||||
if m != nil {
|
||||
return m.Topic
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SubscribeRequest) GetQueue() string {
|
||||
if m != nil {
|
||||
return m.Queue
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Header map[string]string `protobuf:"bytes,1,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Message) Reset() { *m = Message{} }
|
||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||
func (*Message) ProtoMessage() {}
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_178fdc60944ff5e5, []int{3}
|
||||
}
|
||||
|
||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Message.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Message) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Message.Merge(m, src)
|
||||
}
|
||||
func (m *Message) XXX_Size() int {
|
||||
return xxx_messageInfo_Message.Size(m)
|
||||
}
|
||||
func (m *Message) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Message.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Message proto.InternalMessageInfo
|
||||
|
||||
func (m *Message) GetHeader() map[string]string {
|
||||
if m != nil {
|
||||
return m.Header
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Empty)(nil), "go.micro.broker.Empty")
|
||||
proto.RegisterType((*PublishRequest)(nil), "go.micro.broker.PublishRequest")
|
||||
proto.RegisterType((*SubscribeRequest)(nil), "go.micro.broker.SubscribeRequest")
|
||||
proto.RegisterType((*Message)(nil), "go.micro.broker.Message")
|
||||
proto.RegisterMapType((map[string]string)(nil), "go.micro.broker.Message.HeaderEntry")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("micro/go-micro/broker/service/proto/broker.proto", fileDescriptor_178fdc60944ff5e5)
|
||||
}
|
||||
|
||||
var fileDescriptor_178fdc60944ff5e5 = []byte{
|
||||
// 305 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0x4d, 0x4f, 0xc2, 0x40,
|
||||
0x14, 0x64, 0x41, 0x68, 0x78, 0x18, 0x25, 0x1b, 0x62, 0x1a, 0x2e, 0x62, 0xe3, 0x81, 0x8b, 0x5b,
|
||||
0x52, 0x2f, 0x6a, 0x8c, 0x07, 0x23, 0x89, 0x07, 0x4d, 0xcc, 0x7a, 0xf3, 0xd6, 0x2d, 0x2f, 0xa5,
|
||||
0x81, 0xba, 0x65, 0xb7, 0x25, 0xe9, 0x1f, 0xf1, 0xe4, 0x8f, 0x35, 0xec, 0x16, 0x3f, 0x68, 0xf0,
|
||||
0x36, 0xf3, 0x76, 0x76, 0xde, 0x64, 0x1e, 0x4c, 0xd2, 0x24, 0x52, 0xd2, 0x8f, 0xe5, 0x85, 0x05,
|
||||
0x42, 0xc9, 0x05, 0x2a, 0x5f, 0xa3, 0x5a, 0x27, 0x11, 0xfa, 0x99, 0x92, 0xf9, 0x76, 0xc8, 0x0c,
|
||||
0xa1, 0xc7, 0xb1, 0x64, 0x46, 0xcb, 0xec, 0xd8, 0x73, 0xa0, 0x3d, 0x4d, 0xb3, 0xbc, 0xf4, 0xde,
|
||||
0xe0, 0xe8, 0xa5, 0x10, 0xcb, 0x44, 0xcf, 0x39, 0xae, 0x0a, 0xd4, 0x39, 0x1d, 0x40, 0x3b, 0x97,
|
||||
0x59, 0x12, 0xb9, 0x64, 0x44, 0xc6, 0x5d, 0x6e, 0x09, 0x0d, 0xc0, 0x49, 0x51, 0xeb, 0x30, 0x46,
|
||||
0xb7, 0x39, 0x22, 0xe3, 0x5e, 0xe0, 0xb2, 0x1d, 0x4f, 0xf6, 0x6c, 0xdf, 0xf9, 0x56, 0xe8, 0xdd,
|
||||
0x41, 0xff, 0xb5, 0x10, 0x3a, 0x52, 0x89, 0xc0, 0xff, 0xdd, 0x07, 0xd0, 0x5e, 0x15, 0x58, 0x58,
|
||||
0xef, 0x2e, 0xb7, 0xc4, 0xfb, 0x20, 0xe0, 0x54, 0xa6, 0xf4, 0x16, 0x3a, 0x73, 0x0c, 0x67, 0xa8,
|
||||
0x5c, 0x32, 0x6a, 0x8d, 0x7b, 0xc1, 0xf9, 0xbe, 0xf5, 0xec, 0xd1, 0xc8, 0xa6, 0xef, 0xb9, 0x2a,
|
||||
0x79, 0xf5, 0x87, 0x52, 0x38, 0x10, 0x72, 0x56, 0x1a, 0xfb, 0x43, 0x6e, 0xf0, 0xf0, 0x1a, 0x7a,
|
||||
0xbf, 0xa4, 0xb4, 0x0f, 0xad, 0x05, 0x96, 0x55, 0xac, 0x0d, 0xdc, 0x84, 0x5a, 0x87, 0xcb, 0x9f,
|
||||
0x50, 0x86, 0xdc, 0x34, 0xaf, 0x48, 0xf0, 0x49, 0xa0, 0x73, 0x6f, 0xb6, 0xd2, 0x07, 0x70, 0xaa,
|
||||
0xfe, 0xe8, 0x69, 0x2d, 0xd2, 0xdf, 0x66, 0x87, 0x27, 0x35, 0x81, 0xbd, 0x41, 0x83, 0x3e, 0x41,
|
||||
0xf7, 0xbb, 0x29, 0x7a, 0x56, 0x93, 0xed, 0xb6, 0x38, 0xdc, 0x5b, 0xbe, 0xd7, 0x98, 0x10, 0xd1,
|
||||
0x31, 0x47, 0xbf, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x60, 0x8c, 0x40, 0xd5, 0x28, 0x02, 0x00,
|
||||
0x00,
|
||||
}
|
173
broker/service/proto/broker.pb.micro.go
Normal file
173
broker/service/proto/broker.pb.micro.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: micro/go-micro/broker/service/proto/broker.proto
|
||||
|
||||
package go_micro_broker
|
||||
|
||||
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 Broker service
|
||||
|
||||
type BrokerService interface {
|
||||
Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*Empty, error)
|
||||
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (Broker_SubscribeService, error)
|
||||
}
|
||||
|
||||
type brokerService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewBrokerService(name string, c client.Client) BrokerService {
|
||||
if c == nil {
|
||||
c = client.NewClient()
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = "go.micro.broker"
|
||||
}
|
||||
return &brokerService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *brokerService) Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*Empty, error) {
|
||||
req := c.c.NewRequest(c.name, "Broker.Publish", in)
|
||||
out := new(Empty)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *brokerService) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (Broker_SubscribeService, error) {
|
||||
req := c.c.NewRequest(c.name, "Broker.Subscribe", &SubscribeRequest{})
|
||||
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 &brokerServiceSubscribe{stream}, nil
|
||||
}
|
||||
|
||||
type Broker_SubscribeService interface {
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Recv() (*Message, error)
|
||||
}
|
||||
|
||||
type brokerServiceSubscribe struct {
|
||||
stream client.Stream
|
||||
}
|
||||
|
||||
func (x *brokerServiceSubscribe) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *brokerServiceSubscribe) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *brokerServiceSubscribe) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *brokerServiceSubscribe) Recv() (*Message, error) {
|
||||
m := new(Message)
|
||||
err := x.stream.Recv(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Server API for Broker service
|
||||
|
||||
type BrokerHandler interface {
|
||||
Publish(context.Context, *PublishRequest, *Empty) error
|
||||
Subscribe(context.Context, *SubscribeRequest, Broker_SubscribeStream) error
|
||||
}
|
||||
|
||||
func RegisterBrokerHandler(s server.Server, hdlr BrokerHandler, opts ...server.HandlerOption) error {
|
||||
type broker interface {
|
||||
Publish(ctx context.Context, in *PublishRequest, out *Empty) error
|
||||
Subscribe(ctx context.Context, stream server.Stream) error
|
||||
}
|
||||
type Broker struct {
|
||||
broker
|
||||
}
|
||||
h := &brokerHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Broker{h}, opts...))
|
||||
}
|
||||
|
||||
type brokerHandler struct {
|
||||
BrokerHandler
|
||||
}
|
||||
|
||||
func (h *brokerHandler) Publish(ctx context.Context, in *PublishRequest, out *Empty) error {
|
||||
return h.BrokerHandler.Publish(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *brokerHandler) Subscribe(ctx context.Context, stream server.Stream) error {
|
||||
m := new(SubscribeRequest)
|
||||
if err := stream.Recv(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return h.BrokerHandler.Subscribe(ctx, m, &brokerSubscribeStream{stream})
|
||||
}
|
||||
|
||||
type Broker_SubscribeStream interface {
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Send(*Message) error
|
||||
}
|
||||
|
||||
type brokerSubscribeStream struct {
|
||||
stream server.Stream
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeStream) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeStream) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeStream) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeStream) Send(m *Message) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
25
broker/service/proto/broker.proto
Normal file
25
broker/service/proto/broker.proto
Normal file
@@ -0,0 +1,25 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package go.micro.broker;
|
||||
|
||||
service Broker {
|
||||
rpc Publish(PublishRequest) returns (Empty) {};
|
||||
rpc Subscribe(SubscribeRequest) returns (stream Message) {};
|
||||
}
|
||||
|
||||
message Empty {}
|
||||
|
||||
message PublishRequest {
|
||||
string topic = 1;
|
||||
Message message = 2;
|
||||
}
|
||||
|
||||
message SubscribeRequest {
|
||||
string topic = 1;
|
||||
string queue = 2;
|
||||
}
|
||||
|
||||
message Message {
|
||||
map<string,string> header = 1;
|
||||
bytes body = 2;
|
||||
}
|
134
broker/service/service.go
Normal file
134
broker/service/service.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Package service provides the broker service client
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/broker"
|
||||
pb "github.com/micro/go-micro/broker/service/proto"
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
type serviceBroker struct {
|
||||
Addrs []string
|
||||
Client pb.BrokerService
|
||||
options broker.Options
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultName = "go.micro.broker"
|
||||
)
|
||||
|
||||
func (b *serviceBroker) Address() string {
|
||||
return b.Addrs[0]
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Disconnect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Init(opts ...broker.Option) error {
|
||||
for _, o := range opts {
|
||||
o(&b.options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Options() broker.Options {
|
||||
return b.options
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
|
||||
log.Debugf("Publishing to topic %s broker %v", topic, b.Addrs)
|
||||
_, err := b.Client.Publish(context.TODO(), &pb.PublishRequest{
|
||||
Topic: topic,
|
||||
Message: &pb.Message{
|
||||
Header: msg.Header,
|
||||
Body: msg.Body,
|
||||
},
|
||||
}, client.WithAddress(b.Addrs...))
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
||||
var options broker.SubscribeOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
log.Debugf("Subscribing to topic %s queue %s broker %v", topic, options.Queue, b.Addrs)
|
||||
stream, err := b.Client.Subscribe(context.TODO(), &pb.SubscribeRequest{
|
||||
Topic: topic,
|
||||
Queue: options.Queue,
|
||||
}, client.WithAddress(b.Addrs...), client.WithRequestTimeout(time.Hour))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sub := &serviceSub{
|
||||
topic: topic,
|
||||
queue: options.Queue,
|
||||
handler: handler,
|
||||
stream: stream,
|
||||
closed: make(chan bool),
|
||||
options: options,
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-sub.closed:
|
||||
log.Debugf("Unsubscribed from topic %s", topic)
|
||||
return
|
||||
default:
|
||||
// run the subscriber
|
||||
log.Debugf("Streaming from broker %v to topic [%s] queue [%s]", b.Addrs, topic, options.Queue)
|
||||
if err := sub.run(); err != nil {
|
||||
log.Debugf("Resubscribing to topic %s broker %v", topic, b.Addrs)
|
||||
stream, err := b.Client.Subscribe(context.TODO(), &pb.SubscribeRequest{
|
||||
Topic: topic,
|
||||
Queue: options.Queue,
|
||||
}, client.WithAddress(b.Addrs...), client.WithRequestTimeout(time.Hour))
|
||||
if err != nil {
|
||||
log.Debugf("Failed to resubscribe to topic %s: %v", topic, err)
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
// new stream
|
||||
sub.stream = stream
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return sub, nil
|
||||
}
|
||||
|
||||
func (b *serviceBroker) String() string {
|
||||
return "service"
|
||||
}
|
||||
|
||||
func NewBroker(opts ...broker.Option) broker.Broker {
|
||||
var options broker.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
addrs := options.Addrs
|
||||
if len(addrs) == 0 {
|
||||
addrs = []string{"127.0.0.1:8001"}
|
||||
}
|
||||
|
||||
cli := client.DefaultClient
|
||||
|
||||
return &serviceBroker{
|
||||
Addrs: addrs,
|
||||
Client: pb.NewBrokerService(DefaultName, cli),
|
||||
options: options,
|
||||
}
|
||||
}
|
101
broker/service/subscriber.go
Normal file
101
broker/service/subscriber.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/broker"
|
||||
pb "github.com/micro/go-micro/broker/service/proto"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
type serviceSub struct {
|
||||
topic string
|
||||
queue string
|
||||
handler broker.Handler
|
||||
stream pb.Broker_SubscribeService
|
||||
closed chan bool
|
||||
options broker.SubscribeOptions
|
||||
}
|
||||
|
||||
type serviceEvent struct {
|
||||
topic string
|
||||
message *broker.Message
|
||||
}
|
||||
|
||||
func (s *serviceEvent) Topic() string {
|
||||
return s.topic
|
||||
}
|
||||
|
||||
func (s *serviceEvent) Message() *broker.Message {
|
||||
return s.message
|
||||
}
|
||||
|
||||
func (s *serviceEvent) Ack() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *serviceSub) isClosed() bool {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceSub) run() error {
|
||||
exit := make(chan bool)
|
||||
go func() {
|
||||
select {
|
||||
case <-exit:
|
||||
case <-s.closed:
|
||||
}
|
||||
|
||||
// close the stream
|
||||
s.stream.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
// TODO: do not fail silently
|
||||
msg, err := s.stream.Recv()
|
||||
if err != nil {
|
||||
log.Debugf("Streaming error for subcription to topic %s: %v", s.Topic(), err)
|
||||
|
||||
// close the exit channel
|
||||
close(exit)
|
||||
|
||||
// don't return an error if we unsubscribed
|
||||
if s.isClosed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// return stream error
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: handle error
|
||||
s.handler(&serviceEvent{
|
||||
topic: s.topic,
|
||||
message: &broker.Message{
|
||||
Header: msg.Header,
|
||||
Body: msg.Body,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceSub) Options() broker.SubscribeOptions {
|
||||
return s.options
|
||||
}
|
||||
|
||||
func (s *serviceSub) Topic() string {
|
||||
return s.topic
|
||||
}
|
||||
|
||||
func (s *serviceSub) Unsubscribe() error {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return nil
|
||||
default:
|
||||
close(s.closed)
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -110,6 +110,9 @@ var (
|
||||
DefaultPoolSize = 100
|
||||
// DefaultPoolTTL sets the connection pool ttl
|
||||
DefaultPoolTTL = time.Minute
|
||||
|
||||
// NewClient returns a new client
|
||||
NewClient func(...Option) Client = newRpcClient
|
||||
)
|
||||
|
||||
// Makes a synchronous call to a service using the default client
|
||||
@@ -128,11 +131,6 @@ func NewMessage(topic string, payload interface{}, opts ...MessageOption) Messag
|
||||
return DefaultClient.NewMessage(topic, payload, opts...)
|
||||
}
|
||||
|
||||
// Creates a new client with the options passed in
|
||||
func NewClient(opt ...Option) Client {
|
||||
return newRpcClient(opt...)
|
||||
}
|
||||
|
||||
// Creates a new request using the default client. Content Type will
|
||||
// be set to the default within options and use the appropriate codec
|
||||
func NewRequest(service, endpoint string, request interface{}, reqOpts ...RequestOption) Request {
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
var (
|
||||
// mock data
|
||||
testData = map[string][]*registry.Service{
|
||||
"foo": []*registry.Service{
|
||||
"foo": {
|
||||
{
|
||||
Name: "foo",
|
||||
Version: "1.0.0",
|
||||
@@ -15,10 +15,16 @@ var (
|
||||
{
|
||||
Id: "foo-1.0.0-123",
|
||||
Address: "localhost:9999",
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "foo-1.0.0-321",
|
||||
Address: "localhost:9999",
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -29,6 +35,9 @@ var (
|
||||
{
|
||||
Id: "foo-1.0.1-321",
|
||||
Address: "localhost:6666",
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -39,6 +48,9 @@ var (
|
||||
{
|
||||
Id: "foo-1.0.3-345",
|
||||
Address: "localhost:8888",
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -11,8 +11,6 @@ import (
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/micro/go-micro/codec"
|
||||
"github.com/micro/go-micro/codec/bytes"
|
||||
"github.com/micro/go-micro/codec/jsonrpc"
|
||||
"github.com/micro/go-micro/codec/protorpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/encoding"
|
||||
)
|
||||
@@ -30,19 +28,12 @@ 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{},
|
||||
}
|
||||
|
||||
defaultRPCCodecs = map[string]codec.NewCodec{
|
||||
"application/json": jsonrpc.NewCodec,
|
||||
"application/json-rpc": jsonrpc.NewCodec,
|
||||
"application/protobuf": protorpc.NewCodec,
|
||||
"application/proto-rpc": protorpc.NewCodec,
|
||||
"application/octet-stream": protorpc.NewCodec,
|
||||
}
|
||||
|
||||
json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
)
|
||||
|
||||
@@ -125,10 +116,12 @@ func (jsonCodec) Marshal(v interface{}) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (jsonCodec) Unmarshal(data []byte, v interface{}) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
if pb, ok := v.(proto.Message); ok {
|
||||
return jsonpb.Unmarshal(b.NewReader(data), pb)
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
@@ -181,7 +174,7 @@ func (g *grpcCodec) Write(m *codec.Message, v interface{}) error {
|
||||
return g.s.SendMsg(v)
|
||||
}
|
||||
// write the body using the framing codec
|
||||
return g.s.SendMsg(&bytes.Frame{m.Body})
|
||||
return g.s.SendMsg(&bytes.Frame{Data: m.Body})
|
||||
}
|
||||
|
||||
func (g *grpcCodec) Close() error {
|
||||
|
@@ -12,11 +12,10 @@ import (
|
||||
"github.com/micro/go-micro/broker"
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/client/selector"
|
||||
"github.com/micro/go-micro/codec"
|
||||
raw "github.com/micro/go-micro/codec/bytes"
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/metadata"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/transport"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
@@ -110,12 +109,21 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
|
||||
|
||||
var grr error
|
||||
|
||||
cc, err := g.pool.getConn(address, grpc.WithDefaultCallOptions(grpc.ForceCodec(cf)),
|
||||
grpc.WithTimeout(opts.DialTimeout), g.secure(),
|
||||
grpcDialOptions := []grpc.DialOption{
|
||||
grpc.WithDefaultCallOptions(grpc.ForceCodec(cf)),
|
||||
grpc.WithTimeout(opts.DialTimeout),
|
||||
g.secure(),
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(maxRecvMsgSize),
|
||||
grpc.MaxCallSendMsgSize(maxSendMsgSize),
|
||||
))
|
||||
),
|
||||
}
|
||||
|
||||
if opts := g.getGrpcDialOptions(); opts != nil {
|
||||
grpcDialOptions = append(grpcDialOptions, opts...)
|
||||
}
|
||||
|
||||
cc, err := g.pool.getConn(address, grpcDialOptions...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
|
||||
}
|
||||
@@ -127,7 +135,11 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
|
||||
ch := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpc.CallContentSubtype(cf.Name()))
|
||||
grpcCallOptions := []grpc.CallOption{grpc.CallContentSubtype(cf.Name())}
|
||||
if opts := g.getGrpcCallOptions(); opts != nil {
|
||||
grpcCallOptions = append(grpcCallOptions, opts...)
|
||||
}
|
||||
err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpcCallOptions...)
|
||||
ch <- microError(err)
|
||||
}()
|
||||
|
||||
@@ -175,7 +187,16 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
|
||||
|
||||
wc := wrapCodec{cf}
|
||||
|
||||
cc, err := grpc.DialContext(dialCtx, address, grpc.WithDefaultCallOptions(grpc.ForceCodec(wc)), g.secure())
|
||||
grpcDialOptions := []grpc.DialOption{
|
||||
grpc.WithDefaultCallOptions(grpc.ForceCodec(wc)),
|
||||
g.secure(),
|
||||
}
|
||||
|
||||
if opts := g.getGrpcDialOptions(); opts != nil {
|
||||
grpcDialOptions = append(grpcDialOptions, opts...)
|
||||
}
|
||||
|
||||
cc, err := grpc.DialContext(dialCtx, address, grpcDialOptions...)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
|
||||
}
|
||||
@@ -186,7 +207,15 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
|
||||
ServerStreams: true,
|
||||
}
|
||||
|
||||
st, err := cc.NewStream(ctx, desc, methodToGRPC(req.Service(), req.Endpoint()))
|
||||
grpcCallOptions := []grpc.CallOption{grpc.CallContentSubtype(cf.Name())}
|
||||
if opts := g.getGrpcCallOptions(); opts != nil {
|
||||
grpcCallOptions = append(grpcCallOptions, opts...)
|
||||
}
|
||||
|
||||
// create a new cancelling context
|
||||
newCtx, cancel := context.WithCancel(ctx)
|
||||
|
||||
st, err := cc.NewStream(newCtx, desc, methodToGRPC(req.Service(), req.Endpoint()), grpcCallOptions...)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err))
|
||||
}
|
||||
@@ -214,9 +243,32 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
|
||||
response: rsp,
|
||||
stream: st,
|
||||
conn: cc,
|
||||
cancel: cancel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *grpcClient) poolMaxStreams() int {
|
||||
if g.opts.Context == nil {
|
||||
return DefaultPoolMaxStreams
|
||||
}
|
||||
v := g.opts.Context.Value(poolMaxStreams{})
|
||||
if v == nil {
|
||||
return DefaultPoolMaxStreams
|
||||
}
|
||||
return v.(int)
|
||||
}
|
||||
|
||||
func (g *grpcClient) poolMaxIdle() int {
|
||||
if g.opts.Context == nil {
|
||||
return DefaultPoolMaxIdle
|
||||
}
|
||||
v := g.opts.Context.Value(poolMaxIdle{})
|
||||
if v == nil {
|
||||
return DefaultPoolMaxIdle
|
||||
}
|
||||
return v.(int)
|
||||
}
|
||||
|
||||
func (g *grpcClient) maxRecvMsgSizeValue() int {
|
||||
if g.opts.Context == nil {
|
||||
return DefaultMaxRecvMsgSize
|
||||
@@ -255,16 +307,6 @@ func (g *grpcClient) newGRPCCodec(contentType string) (encoding.Codec, error) {
|
||||
return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
|
||||
}
|
||||
|
||||
func (g *grpcClient) newCodec(contentType string) (codec.NewCodec, error) {
|
||||
if c, ok := g.opts.Codecs[contentType]; ok {
|
||||
return c, nil
|
||||
}
|
||||
if cf, ok := defaultRPCCodecs[contentType]; ok {
|
||||
return cf, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
|
||||
}
|
||||
|
||||
func (g *grpcClient) Init(opts ...client.Option) error {
|
||||
size := g.opts.PoolSize
|
||||
ttl := g.opts.PoolTTL
|
||||
@@ -312,7 +354,9 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
|
||||
d, ok := ctx.Deadline()
|
||||
if !ok {
|
||||
// no deadline so we create a new one
|
||||
ctx, _ = context.WithTimeout(ctx, callOpts.RequestTimeout)
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout)
|
||||
defer cancel()
|
||||
} else {
|
||||
// got a deadline so no need to setup context
|
||||
// but we need to set the timeout we pass along
|
||||
@@ -484,29 +528,56 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
|
||||
}
|
||||
|
||||
func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
|
||||
var options client.PublishOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
if !ok {
|
||||
md = make(map[string]string)
|
||||
}
|
||||
md["Content-Type"] = p.ContentType()
|
||||
md["Micro-Topic"] = p.Topic()
|
||||
|
||||
cf, err := g.newGRPCCodec(p.ContentType())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
b, err := cf.Marshal(p.Payload())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
var body []byte
|
||||
|
||||
// passed in raw data
|
||||
if d, ok := p.Payload().(*raw.Frame); ok {
|
||||
body = d.Data
|
||||
} else {
|
||||
// set the body
|
||||
b, err := cf.Marshal(p.Payload())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
body = b
|
||||
}
|
||||
|
||||
g.once.Do(func() {
|
||||
g.opts.Broker.Connect()
|
||||
})
|
||||
|
||||
return g.opts.Broker.Publish(p.Topic(), &broker.Message{
|
||||
topic := p.Topic()
|
||||
|
||||
// get proxy topic
|
||||
if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 {
|
||||
options.Exchange = prx
|
||||
}
|
||||
|
||||
// get the exchange
|
||||
if len(options.Exchange) > 0 {
|
||||
topic = options.Exchange
|
||||
}
|
||||
|
||||
return g.opts.Broker.Publish(topic, &broker.Message{
|
||||
Header: md,
|
||||
Body: b,
|
||||
Body: body,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -514,48 +585,62 @@ func (g *grpcClient) String() string {
|
||||
return "grpc"
|
||||
}
|
||||
|
||||
func newClient(opts ...client.Option) client.Client {
|
||||
options := client.Options{
|
||||
Codecs: make(map[string]codec.NewCodec),
|
||||
CallOptions: client.CallOptions{
|
||||
Backoff: client.DefaultBackoff,
|
||||
Retry: client.DefaultRetry,
|
||||
Retries: client.DefaultRetries,
|
||||
RequestTimeout: client.DefaultRequestTimeout,
|
||||
DialTimeout: transport.DefaultDialTimeout,
|
||||
},
|
||||
PoolSize: client.DefaultPoolSize,
|
||||
PoolTTL: client.DefaultPoolTTL,
|
||||
func (g *grpcClient) getGrpcDialOptions() []grpc.DialOption {
|
||||
if g.opts.CallOptions.Context == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := g.opts.CallOptions.Context.Value(grpcDialOptions{})
|
||||
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
opts, ok := v.([]grpc.DialOption)
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func (g *grpcClient) getGrpcCallOptions() []grpc.CallOption {
|
||||
if g.opts.CallOptions.Context == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := g.opts.CallOptions.Context.Value(grpcCallOptions{})
|
||||
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
opts, ok := v.([]grpc.CallOption)
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func newClient(opts ...client.Option) client.Client {
|
||||
options := client.NewOptions()
|
||||
// default content type for grpc
|
||||
options.ContentType = "application/grpc+proto"
|
||||
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
if len(options.ContentType) == 0 {
|
||||
options.ContentType = "application/grpc+proto"
|
||||
}
|
||||
|
||||
if options.Broker == nil {
|
||||
options.Broker = broker.DefaultBroker
|
||||
}
|
||||
|
||||
if options.Registry == nil {
|
||||
options.Registry = registry.DefaultRegistry
|
||||
}
|
||||
|
||||
if options.Selector == nil {
|
||||
options.Selector = selector.NewSelector(
|
||||
selector.Registry(options.Registry),
|
||||
)
|
||||
}
|
||||
|
||||
rc := &grpcClient{
|
||||
once: sync.Once{},
|
||||
opts: options,
|
||||
pool: newPool(options.PoolSize, options.PoolTTL),
|
||||
}
|
||||
|
||||
rc.pool = newPool(options.PoolSize, options.PoolTTL, rc.poolMaxIdle(), rc.poolMaxStreams())
|
||||
|
||||
c := client.Client(rc)
|
||||
|
||||
// wrap in reverse
|
||||
|
@@ -11,73 +11,181 @@ type pool struct {
|
||||
size int
|
||||
ttl int64
|
||||
|
||||
// max streams on a *poolConn
|
||||
maxStreams int
|
||||
// max idle conns
|
||||
maxIdle int
|
||||
|
||||
sync.Mutex
|
||||
conns map[string][]*poolConn
|
||||
conns map[string]*streamsPool
|
||||
}
|
||||
|
||||
type streamsPool struct {
|
||||
// head of list
|
||||
head *poolConn
|
||||
// busy conns list
|
||||
busy *poolConn
|
||||
// the siza of list
|
||||
count int
|
||||
// idle conn
|
||||
idle int
|
||||
}
|
||||
|
||||
type poolConn struct {
|
||||
// grpc conn
|
||||
*grpc.ClientConn
|
||||
err error
|
||||
addr string
|
||||
|
||||
// pool and streams pool
|
||||
pool *pool
|
||||
sp *streamsPool
|
||||
streams int
|
||||
created int64
|
||||
|
||||
// list
|
||||
pre *poolConn
|
||||
next *poolConn
|
||||
in bool
|
||||
}
|
||||
|
||||
func newPool(size int, ttl time.Duration) *pool {
|
||||
func newPool(size int, ttl time.Duration, idle int, ms int) *pool {
|
||||
if ms <= 0 {
|
||||
ms = 1
|
||||
}
|
||||
if idle < 0 {
|
||||
idle = 0
|
||||
}
|
||||
return &pool{
|
||||
size: size,
|
||||
ttl: int64(ttl.Seconds()),
|
||||
conns: make(map[string][]*poolConn),
|
||||
size: size,
|
||||
ttl: int64(ttl.Seconds()),
|
||||
maxStreams: ms,
|
||||
maxIdle: idle,
|
||||
conns: make(map[string]*streamsPool),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pool) getConn(addr string, opts ...grpc.DialOption) (*poolConn, error) {
|
||||
p.Lock()
|
||||
conns := p.conns[addr]
|
||||
now := time.Now().Unix()
|
||||
|
||||
// while we have conns check age and then return one
|
||||
// otherwise we'll create a new conn
|
||||
for len(conns) > 0 {
|
||||
conn := conns[len(conns)-1]
|
||||
conns = conns[:len(conns)-1]
|
||||
p.conns[addr] = conns
|
||||
|
||||
// if conn is old kill it and move on
|
||||
if d := now - conn.created; d > p.ttl {
|
||||
conn.ClientConn.Close()
|
||||
p.Lock()
|
||||
sp, ok := p.conns[addr]
|
||||
if !ok {
|
||||
sp = &streamsPool{head: &poolConn{}, busy: &poolConn{}, count: 0, idle: 0}
|
||||
p.conns[addr] = sp
|
||||
}
|
||||
// while we have conns check streams and then return one
|
||||
// otherwise we'll create a new conn
|
||||
conn := sp.head.next
|
||||
for conn != nil {
|
||||
// a old conn
|
||||
if now-conn.created > p.ttl {
|
||||
next := conn.next
|
||||
if conn.streams == 0 {
|
||||
removeConn(conn)
|
||||
conn.ClientConn.Close()
|
||||
sp.idle--
|
||||
}
|
||||
conn = next
|
||||
continue
|
||||
}
|
||||
|
||||
// we got a good conn, lets unlock and return it
|
||||
// a busy conn
|
||||
if conn.streams >= p.maxStreams {
|
||||
next := conn.next
|
||||
removeConn(conn)
|
||||
addConnAfter(conn, sp.busy)
|
||||
conn = next
|
||||
continue
|
||||
}
|
||||
// a idle conn
|
||||
if conn.streams == 0 {
|
||||
sp.idle--
|
||||
}
|
||||
// a good conn
|
||||
conn.streams++
|
||||
p.Unlock()
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
p.Unlock()
|
||||
|
||||
// create new conn
|
||||
// create new conn
|
||||
cc, err := grpc.Dial(addr, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn = &poolConn{cc, nil, addr, p, sp, 1, time.Now().Unix(), nil, nil, false}
|
||||
|
||||
return &poolConn{cc, time.Now().Unix()}, nil
|
||||
// add conn to streams pool
|
||||
p.Lock()
|
||||
if sp.count < p.size {
|
||||
addConnAfter(conn, sp.head)
|
||||
}
|
||||
p.Unlock()
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (p *pool) release(addr string, conn *poolConn, err error) {
|
||||
// don't store the conn if it has errored
|
||||
if err != nil {
|
||||
conn.ClientConn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise put it back for reuse
|
||||
p.Lock()
|
||||
conns := p.conns[addr]
|
||||
if len(conns) >= p.size {
|
||||
p, sp, created := conn.pool, conn.sp, conn.created
|
||||
// try to add conn
|
||||
if !conn.in && sp.count < p.size {
|
||||
addConnAfter(conn, sp.head)
|
||||
}
|
||||
if !conn.in {
|
||||
p.Unlock()
|
||||
conn.ClientConn.Close()
|
||||
return
|
||||
}
|
||||
p.conns[addr] = append(conns, conn)
|
||||
// a busy conn
|
||||
if conn.streams >= p.maxStreams {
|
||||
removeConn(conn)
|
||||
addConnAfter(conn, sp.head)
|
||||
}
|
||||
conn.streams--
|
||||
// if streams == 0, we can do something
|
||||
if conn.streams == 0 {
|
||||
// 1. it has errored
|
||||
// 2. too many idle conn or
|
||||
// 3. conn is too old
|
||||
now := time.Now().Unix()
|
||||
if err != nil || sp.idle >= p.maxIdle || now-created > p.ttl {
|
||||
removeConn(conn)
|
||||
p.Unlock()
|
||||
conn.ClientConn.Close()
|
||||
return
|
||||
}
|
||||
sp.idle++
|
||||
}
|
||||
p.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *poolConn) Close() {
|
||||
conn.pool.release(conn.addr, conn, conn.err)
|
||||
}
|
||||
|
||||
func removeConn(conn *poolConn) {
|
||||
if conn.pre != nil {
|
||||
conn.pre.next = conn.next
|
||||
}
|
||||
if conn.next != nil {
|
||||
conn.next.pre = conn.pre
|
||||
}
|
||||
conn.pre = nil
|
||||
conn.next = nil
|
||||
conn.in = false
|
||||
conn.sp.count--
|
||||
return
|
||||
}
|
||||
|
||||
func addConnAfter(conn *poolConn, after *poolConn) {
|
||||
conn.next = after.next
|
||||
conn.pre = after
|
||||
if after.next != nil {
|
||||
after.next.pre = conn
|
||||
}
|
||||
after.next = conn
|
||||
conn.in = true
|
||||
conn.sp.count++
|
||||
return
|
||||
}
|
||||
|
@@ -1,17 +1,17 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
"google.golang.org/grpc"
|
||||
pgrpc "google.golang.org/grpc"
|
||||
pb "google.golang.org/grpc/examples/helloworld/helloworld"
|
||||
)
|
||||
|
||||
func testPool(t *testing.T, size int, ttl time.Duration) {
|
||||
func testPool(t *testing.T, size int, ttl time.Duration, idle int, ms int) {
|
||||
// setup server
|
||||
l, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
@@ -26,7 +26,7 @@ func testPool(t *testing.T, size int, ttl time.Duration) {
|
||||
defer s.Stop()
|
||||
|
||||
// zero pool
|
||||
p := newPool(size, ttl)
|
||||
p := newPool(size, ttl, idle, ms)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
// get a conn
|
||||
@@ -50,7 +50,7 @@ func testPool(t *testing.T, size int, ttl time.Duration) {
|
||||
p.release(l.Addr().String(), cc, nil)
|
||||
|
||||
p.Lock()
|
||||
if i := len(p.conns[l.Addr().String()]); i > size {
|
||||
if i := p.conns[l.Addr().String()].count; i > size {
|
||||
p.Unlock()
|
||||
t.Fatalf("pool size %d is greater than expected %d", i, size)
|
||||
}
|
||||
@@ -59,6 +59,6 @@ func testPool(t *testing.T, size int, ttl time.Duration) {
|
||||
}
|
||||
|
||||
func TestGRPCPool(t *testing.T) {
|
||||
testPool(t, 0, time.Minute)
|
||||
testPool(t, 2, time.Minute)
|
||||
testPool(t, 0, time.Minute, 10, 2)
|
||||
testPool(t, 2, time.Minute, 10, 1)
|
||||
}
|
||||
|
@@ -42,9 +42,12 @@ func TestGRPCClient(t *testing.T) {
|
||||
Name: "helloworld",
|
||||
Version: "test",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test-1",
|
||||
Address: l.Addr().String(),
|
||||
Metadata: map[string]string{
|
||||
"protocol": "grpc",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@@ -6,10 +6,19 @@ import (
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/micro/go-micro/client"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/encoding"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultPoolMaxStreams maximum streams on a connectioin
|
||||
// (20)
|
||||
DefaultPoolMaxStreams = 20
|
||||
|
||||
// DefaultPoolMaxIdle maximum idle conns of a pool
|
||||
// (50)
|
||||
DefaultPoolMaxIdle = 50
|
||||
|
||||
// DefaultMaxRecvMsgSize maximum message that client can receive
|
||||
// (4 MB).
|
||||
DefaultMaxRecvMsgSize = 1024 * 1024 * 4
|
||||
@@ -19,10 +28,34 @@ var (
|
||||
DefaultMaxSendMsgSize = 1024 * 1024 * 4
|
||||
)
|
||||
|
||||
type poolMaxStreams struct{}
|
||||
type poolMaxIdle struct{}
|
||||
type codecsKey struct{}
|
||||
type tlsAuth struct{}
|
||||
type maxRecvMsgSizeKey struct{}
|
||||
type maxSendMsgSizeKey struct{}
|
||||
type grpcDialOptions struct{}
|
||||
type grpcCallOptions struct{}
|
||||
|
||||
// maximum streams on a connectioin
|
||||
func PoolMaxStreams(n int) client.Option {
|
||||
return func(o *client.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, poolMaxStreams{}, n)
|
||||
}
|
||||
}
|
||||
|
||||
// maximum idle conns of a pool
|
||||
func PoolMaxIdle(d int) client.Option {
|
||||
return func(o *client.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, poolMaxIdle{}, d)
|
||||
}
|
||||
}
|
||||
|
||||
// gRPC Codec to be used to encode/decode requests for a given content type
|
||||
func Codec(contentType string, c encoding.Codec) client.Option {
|
||||
@@ -72,3 +105,27 @@ func MaxSendMsgSize(s int) client.Option {
|
||||
o.Context = context.WithValue(o.Context, maxSendMsgSizeKey{}, s)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// DialOptions to be used to configure gRPC dial options
|
||||
//
|
||||
func DialOptions(opts ...grpc.DialOption) client.CallOption {
|
||||
return func(o *client.CallOptions) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, grpcDialOptions{}, opts)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CallOptions to be used to configure gRPC call options
|
||||
//
|
||||
func CallOptions(opts ...grpc.CallOption) client.CallOption {
|
||||
return func(o *client.CallOptions) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, grpcCallOptions{}, opts)
|
||||
}
|
||||
}
|
||||
|
@@ -12,12 +12,14 @@ import (
|
||||
// Implements the streamer interface
|
||||
type grpcStream struct {
|
||||
sync.RWMutex
|
||||
closed bool
|
||||
err error
|
||||
conn *grpc.ClientConn
|
||||
stream grpc.ClientStream
|
||||
request client.Request
|
||||
response client.Response
|
||||
context context.Context
|
||||
cancel func()
|
||||
}
|
||||
|
||||
func (g *grpcStream) Context() context.Context {
|
||||
@@ -43,14 +45,12 @@ func (g *grpcStream) Send(msg interface{}) error {
|
||||
func (g *grpcStream) Recv(msg interface{}) (err error) {
|
||||
defer g.setError(err)
|
||||
if err = g.stream.RecvMsg(msg); err != nil {
|
||||
if err == io.EOF {
|
||||
// #202 - inconsistent gRPC stream behavior
|
||||
// the only way to tell if the stream is done is when we get a EOF on the Recv
|
||||
// here we should close the underlying gRPC ClientConn
|
||||
closeErr := g.conn.Close()
|
||||
if closeErr != nil {
|
||||
err = closeErr
|
||||
}
|
||||
// #202 - inconsistent gRPC stream behavior
|
||||
// the only way to tell if the stream is done is when we get a EOF on the Recv
|
||||
// here we should close the underlying gRPC ClientConn
|
||||
closeErr := g.Close()
|
||||
if err == io.EOF && closeErr != nil {
|
||||
err = closeErr
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -74,5 +74,15 @@ func (g *grpcStream) setError(e error) {
|
||||
// stream should still be able to receive after this function call
|
||||
// TODO: should the conn be closed in another way?
|
||||
func (g *grpcStream) Close() error {
|
||||
return g.stream.CloseSend()
|
||||
g.Lock()
|
||||
defer g.Unlock()
|
||||
|
||||
if g.closed {
|
||||
return nil
|
||||
}
|
||||
// cancel the context
|
||||
g.cancel()
|
||||
g.closed = true
|
||||
g.stream.CloseSend()
|
||||
return g.conn.Close()
|
||||
}
|
||||
|
@@ -1,16 +0,0 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type responseKey struct{}
|
||||
|
||||
func fromContext(ctx context.Context) (map[string][]MockResponse, bool) {
|
||||
r, ok := ctx.Value(responseKey{}).(map[string][]MockResponse)
|
||||
return r, ok
|
||||
}
|
||||
|
||||
func newContext(ctx context.Context, r map[string][]MockResponse) context.Context {
|
||||
return context.WithValue(ctx, responseKey{}, r)
|
||||
}
|
@@ -1,136 +0,0 @@
|
||||
// Package mock provides a mock client for testing
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
_ client.Client = NewClient()
|
||||
)
|
||||
|
||||
type MockResponse struct {
|
||||
Endpoint string
|
||||
Response interface{}
|
||||
Error error
|
||||
}
|
||||
|
||||
type MockClient struct {
|
||||
Client client.Client
|
||||
Opts client.Options
|
||||
|
||||
sync.Mutex
|
||||
Response map[string][]MockResponse
|
||||
}
|
||||
|
||||
func (m *MockClient) Init(opts ...client.Option) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&m.Opts)
|
||||
}
|
||||
|
||||
r, ok := fromContext(m.Opts.Context)
|
||||
if !ok {
|
||||
r = make(map[string][]MockResponse)
|
||||
}
|
||||
m.Response = r
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockClient) Options() client.Options {
|
||||
return m.Opts
|
||||
}
|
||||
|
||||
func (m *MockClient) NewMessage(topic string, msg interface{}, opts ...client.MessageOption) client.Message {
|
||||
return m.Client.NewMessage(topic, msg, opts...)
|
||||
}
|
||||
|
||||
func (m *MockClient) NewRequest(service, endpoint string, req interface{}, reqOpts ...client.RequestOption) client.Request {
|
||||
return m.Client.NewRequest(service, endpoint, req, reqOpts...)
|
||||
}
|
||||
|
||||
func (m *MockClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
response, ok := m.Response[req.Service()]
|
||||
if !ok {
|
||||
return errors.NotFound("go.micro.client.mock", "service not found")
|
||||
}
|
||||
|
||||
for _, r := range response {
|
||||
if r.Endpoint != req.Endpoint() {
|
||||
continue
|
||||
}
|
||||
|
||||
if r.Error != nil {
|
||||
return r.Error
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(rsp)
|
||||
|
||||
if t := reflect.TypeOf(rsp); t.Kind() == reflect.Ptr {
|
||||
v = reflect.Indirect(v)
|
||||
}
|
||||
response := r.Response
|
||||
if t := reflect.TypeOf(r.Response); t.Kind() == reflect.Func {
|
||||
var request []reflect.Value
|
||||
if t.NumIn() == 1 {
|
||||
request = append(request, reflect.ValueOf(req.Body()))
|
||||
}
|
||||
response = reflect.ValueOf(r.Response).Call(request)[0].Interface()
|
||||
}
|
||||
|
||||
v.Set(reflect.ValueOf(response))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("rpc: can't find service %s", req.Endpoint())
|
||||
}
|
||||
|
||||
func (m *MockClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
// TODO: mock stream
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockClient) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockClient) String() string {
|
||||
return "mock"
|
||||
}
|
||||
|
||||
func NewClient(opts ...client.Option) *MockClient {
|
||||
options := client.Options{
|
||||
Context: context.TODO(),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
r, ok := fromContext(options.Context)
|
||||
if !ok {
|
||||
r = make(map[string][]MockResponse)
|
||||
}
|
||||
|
||||
return &MockClient{
|
||||
Client: client.DefaultClient,
|
||||
Opts: options,
|
||||
Response: r,
|
||||
}
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/micro/go-micro/errors"
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
type TestResponse struct {
|
||||
Param string
|
||||
}
|
||||
|
||||
response := []MockResponse{
|
||||
{Endpoint: "Foo.Bar", Response: map[string]interface{}{"foo": "bar"}},
|
||||
{Endpoint: "Foo.Struct", Response: &TestResponse{Param: "aparam"}},
|
||||
{Endpoint: "Foo.Fail", Error: errors.InternalServerError("go.mock", "failed")},
|
||||
{Endpoint: "Foo.Func", Response: func() string { return "string" }},
|
||||
{Endpoint: "Foo.FuncStruct", Response: func() *TestResponse { return &TestResponse{Param: "aparam"} }},
|
||||
{Endpoint: "Foo.FuncWithReqBody", Response: func(req interface{}) string {
|
||||
if req.(map[string]string)["foo"] == "bar" {
|
||||
return "string"
|
||||
}
|
||||
return "wrong"
|
||||
}},
|
||||
}
|
||||
|
||||
c := NewClient(Response("go.mock", response))
|
||||
|
||||
for _, r := range response {
|
||||
req := c.NewRequest("go.mock", r.Endpoint, map[string]string{"foo": "bar"})
|
||||
var rsp interface{}
|
||||
|
||||
err := c.Call(context.TODO(), req, &rsp)
|
||||
|
||||
if err != r.Error {
|
||||
t.Fatalf("Expecter error %v got %v", r.Error, err)
|
||||
}
|
||||
|
||||
t.Log(rsp)
|
||||
if r.Endpoint == "Foo.FuncWithReqBody" {
|
||||
req := c.NewRequest("go.mock", r.Endpoint, map[string]string{"foo": "wrong"})
|
||||
var rsp interface{}
|
||||
|
||||
err := c.Call(context.TODO(), req, &rsp)
|
||||
|
||||
if err != r.Error {
|
||||
t.Fatalf("Expecter error %v got %v", r.Error, err)
|
||||
}
|
||||
if rsp.(string) != "wrong" {
|
||||
t.Fatalf("Expecter response 'wrong' got %v", rsp)
|
||||
}
|
||||
t.Log(rsp)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/client"
|
||||
)
|
||||
|
||||
// Response sets the response methods for a service
|
||||
func Response(service string, response []MockResponse) client.Option {
|
||||
return func(o *client.Options) {
|
||||
r, ok := fromContext(o.Context)
|
||||
if !ok {
|
||||
r = make(map[string][]MockResponse)
|
||||
}
|
||||
r[service] = response
|
||||
o.Context = newContext(o.Context, r)
|
||||
}
|
||||
}
|
@@ -85,9 +85,11 @@ type RequestOptions struct {
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
func newOptions(options ...Option) Options {
|
||||
func NewOptions(options ...Option) Options {
|
||||
opts := Options{
|
||||
Codecs: make(map[string]codec.NewCodec),
|
||||
Context: context.Background(),
|
||||
ContentType: DefaultContentType,
|
||||
Codecs: make(map[string]codec.NewCodec),
|
||||
CallOptions: CallOptions{
|
||||
Backoff: DefaultBackoff,
|
||||
Retry: DefaultRetry,
|
||||
@@ -95,36 +97,18 @@ func newOptions(options ...Option) Options {
|
||||
RequestTimeout: DefaultRequestTimeout,
|
||||
DialTimeout: transport.DefaultDialTimeout,
|
||||
},
|
||||
PoolSize: DefaultPoolSize,
|
||||
PoolTTL: DefaultPoolTTL,
|
||||
PoolSize: DefaultPoolSize,
|
||||
PoolTTL: DefaultPoolTTL,
|
||||
Broker: broker.DefaultBroker,
|
||||
Selector: selector.DefaultSelector,
|
||||
Registry: registry.DefaultRegistry,
|
||||
Transport: transport.DefaultTransport,
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
o(&opts)
|
||||
}
|
||||
|
||||
if len(opts.ContentType) == 0 {
|
||||
opts.ContentType = DefaultContentType
|
||||
}
|
||||
|
||||
if opts.Broker == nil {
|
||||
opts.Broker = broker.DefaultBroker
|
||||
}
|
||||
|
||||
if opts.Registry == nil {
|
||||
opts.Registry = registry.DefaultRegistry
|
||||
}
|
||||
|
||||
if opts.Selector == nil {
|
||||
opts.Selector = selector.NewSelector(
|
||||
selector.Registry(opts.Registry),
|
||||
)
|
||||
}
|
||||
|
||||
if opts.Transport == nil {
|
||||
opts.Transport = transport.DefaultTransport
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
@@ -156,7 +140,7 @@ func PoolSize(d int) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// PoolSize sets the connection pool size
|
||||
// PoolTTL sets the connection pool ttl
|
||||
func PoolTTL(d time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.PoolTTL = d
|
||||
@@ -167,6 +151,8 @@ func PoolTTL(d time.Duration) Option {
|
||||
func Registry(r registry.Registry) Option {
|
||||
return func(o *Options) {
|
||||
o.Registry = r
|
||||
// set in the selector
|
||||
o.Selector.Init(selector.Registry(r))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@ func TestCallOptions(t *testing.T) {
|
||||
var cl Client
|
||||
|
||||
if d.set {
|
||||
opts = newOptions(
|
||||
opts = NewOptions(
|
||||
Retries(d.retries),
|
||||
RequestTimeout(d.rtimeout),
|
||||
DialTimeout(d.dtimeout),
|
||||
@@ -35,7 +35,7 @@ func TestCallOptions(t *testing.T) {
|
||||
DialTimeout(d.dtimeout),
|
||||
)
|
||||
} else {
|
||||
opts = newOptions()
|
||||
opts = NewOptions()
|
||||
cl = NewClient()
|
||||
}
|
||||
|
||||
|
@@ -1,388 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: micro/go-micro/client/proto/client.proto
|
||||
|
||||
package go_micro_client
|
||||
|
||||
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
|
||||
|
||||
type Request struct {
|
||||
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
|
||||
Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
|
||||
ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
|
||||
Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Request) Reset() { *m = Request{} }
|
||||
func (m *Request) String() string { return proto.CompactTextString(m) }
|
||||
func (*Request) ProtoMessage() {}
|
||||
func (*Request) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_7d733ae29171347b, []int{0}
|
||||
}
|
||||
|
||||
func (m *Request) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Request.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(m, src)
|
||||
}
|
||||
func (m *Request) XXX_Size() int {
|
||||
return xxx_messageInfo_Request.Size(m)
|
||||
}
|
||||
func (m *Request) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Request.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Request proto.InternalMessageInfo
|
||||
|
||||
func (m *Request) GetService() string {
|
||||
if m != nil {
|
||||
return m.Service
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Request) GetEndpoint() string {
|
||||
if m != nil {
|
||||
return m.Endpoint
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Request) GetContentType() string {
|
||||
if m != nil {
|
||||
return m.ContentType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Request) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Body []byte `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return proto.CompactTextString(m) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
func (*Response) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_7d733ae29171347b, []int{1}
|
||||
}
|
||||
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Response.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(m, src)
|
||||
}
|
||||
func (m *Response) XXX_Size() int {
|
||||
return xxx_messageInfo_Response.Size(m)
|
||||
}
|
||||
func (m *Response) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Response.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Response proto.InternalMessageInfo
|
||||
|
||||
func (m *Response) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
|
||||
ContentType string `protobuf:"bytes,2,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
|
||||
Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Message) Reset() { *m = Message{} }
|
||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||
func (*Message) ProtoMessage() {}
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_7d733ae29171347b, []int{2}
|
||||
}
|
||||
|
||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Message.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Message) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Message.Merge(m, src)
|
||||
}
|
||||
func (m *Message) XXX_Size() int {
|
||||
return xxx_messageInfo_Message.Size(m)
|
||||
}
|
||||
func (m *Message) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Message.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Message proto.InternalMessageInfo
|
||||
|
||||
func (m *Message) GetTopic() string {
|
||||
if m != nil {
|
||||
return m.Topic
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Message) GetContentType() string {
|
||||
if m != nil {
|
||||
return m.ContentType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Message) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Request)(nil), "go.micro.client.Request")
|
||||
proto.RegisterType((*Response)(nil), "go.micro.client.Response")
|
||||
proto.RegisterType((*Message)(nil), "go.micro.client.Message")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("micro/go-micro/client/proto/client.proto", fileDescriptor_7d733ae29171347b)
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// MicroClient is the client API for Micro 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 {
|
||||
// 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)
|
||||
// Publish publishes a message and returns an empty Message
|
||||
Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error)
|
||||
}
|
||||
|
||||
type microClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewMicroClient(cc *grpc.ClientConn) MicroClient {
|
||||
return µClient{cc}
|
||||
}
|
||||
|
||||
func (c *microClient) 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...)
|
||||
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...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := µStreamClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Micro_StreamClient interface {
|
||||
Send(*Request) error
|
||||
Recv() (*Response, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type microStreamClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *microStreamClient) Send(m *Request) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *microStreamClient) Recv() (*Response, error) {
|
||||
m := new(Response)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *microClient) 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...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// MicroServer is the server API for Micro service.
|
||||
type MicroServer interface {
|
||||
// Call allows a single request to be made
|
||||
Call(context.Context, *Request) (*Response, error)
|
||||
// Stream is a bidirectional stream
|
||||
Stream(Micro_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 _Micro_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)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.client.Micro/Call",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MicroServer).Call(ctx, req.(*Request))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Micro_Stream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(MicroServer).Stream(µStreamServer{stream})
|
||||
}
|
||||
|
||||
type Micro_StreamServer interface {
|
||||
Send(*Response) error
|
||||
Recv() (*Request, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type microStreamServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *microStreamServer) Send(m *Response) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *microStreamServer) Recv() (*Request, error) {
|
||||
m := new(Request)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func _Micro_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)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.client.Micro/Publish",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MicroServer).Publish(ctx, req.(*Message))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Micro_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.client.Micro",
|
||||
HandlerType: (*MicroServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Call",
|
||||
Handler: _Micro_Call_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Publish",
|
||||
Handler: _Micro_Publish_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Stream",
|
||||
Handler: _Micro_Stream_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "micro/go-micro/client/proto/client.proto",
|
||||
}
|
@@ -10,14 +10,15 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/go-micro/broker"
|
||||
"github.com/micro/go-micro/client/pool"
|
||||
"github.com/micro/go-micro/client/selector"
|
||||
"github.com/micro/go-micro/codec"
|
||||
raw "github.com/micro/go-micro/codec/bytes"
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/metadata"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/transport"
|
||||
"github.com/micro/go-micro/util/buf"
|
||||
"github.com/micro/go-micro/util/pool"
|
||||
)
|
||||
|
||||
type rpcClient struct {
|
||||
@@ -28,7 +29,7 @@ type rpcClient struct {
|
||||
}
|
||||
|
||||
func newRpcClient(opt ...Option) Client {
|
||||
opts := newOptions(opt...)
|
||||
opts := NewOptions(opt...)
|
||||
|
||||
p := pool.NewPool(
|
||||
pool.Size(opts.PoolSize),
|
||||
@@ -96,7 +97,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)
|
||||
}
|
||||
@@ -153,7 +162,6 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
|
||||
|
||||
select {
|
||||
case err := <-ch:
|
||||
grr = err
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
@@ -212,7 +220,7 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
|
||||
}
|
||||
|
||||
c, err := r.pool.Get(address, dOpts...)
|
||||
c, err := r.opts.Transport.Dial(address, dOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "connection error: %v", err)
|
||||
}
|
||||
@@ -246,7 +254,7 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
// signal the end of stream,
|
||||
sendEOS: true,
|
||||
// release func
|
||||
release: func(err error) { r.pool.Release(c, err) },
|
||||
release: func(err error) { c.Close() },
|
||||
}
|
||||
|
||||
// wait for error response
|
||||
@@ -308,6 +316,22 @@ func (r *rpcClient) Options() Options {
|
||||
return r.opts
|
||||
}
|
||||
|
||||
// hasProxy checks if we have proxy set in the environment
|
||||
func (r *rpcClient) hasProxy() bool {
|
||||
// get proxy
|
||||
if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// get proxy address
|
||||
if prx := os.Getenv("MICRO_PROXY_ADDRESS"); len(prx) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// next returns an iterator for the next nodes to call
|
||||
func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, error) {
|
||||
service := request.Service()
|
||||
|
||||
@@ -369,7 +393,9 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
d, ok := ctx.Deadline()
|
||||
if !ok {
|
||||
// no deadline so we create a new one
|
||||
ctx, _ = context.WithTimeout(ctx, callOpts.RequestTimeout)
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout)
|
||||
defer cancel()
|
||||
} else {
|
||||
// got a deadline so no need to setup context
|
||||
// but we need to set the timeout we pass along
|
||||
@@ -421,10 +447,18 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
return err
|
||||
}
|
||||
|
||||
ch := make(chan error, callOpts.Retries+1)
|
||||
// get the retries
|
||||
retries := callOpts.Retries
|
||||
|
||||
// disable retries when using a proxy
|
||||
if r.hasProxy() {
|
||||
retries = 0
|
||||
}
|
||||
|
||||
ch := make(chan error, retries+1)
|
||||
var gerr error
|
||||
|
||||
for i := 0; i <= callOpts.Retries; i++ {
|
||||
for i := 0; i <= retries; i++ {
|
||||
go func(i int) {
|
||||
ch <- call(i)
|
||||
}(i)
|
||||
@@ -504,10 +538,18 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
|
||||
err error
|
||||
}
|
||||
|
||||
ch := make(chan response, callOpts.Retries+1)
|
||||
// get the retries
|
||||
retries := callOpts.Retries
|
||||
|
||||
// disable retries when using a proxy
|
||||
if r.hasProxy() {
|
||||
retries = 0
|
||||
}
|
||||
|
||||
ch := make(chan response, retries+1)
|
||||
var grr error
|
||||
|
||||
for i := 0; i <= callOpts.Retries; i++ {
|
||||
for i := 0; i <= retries; i++ {
|
||||
go func(i int) {
|
||||
s, err := call(i)
|
||||
ch <- response{s, err}
|
||||
@@ -575,26 +617,37 @@ func (r *rpcClient) Publish(ctx context.Context, msg Message, opts ...PublishOpt
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// new buffer
|
||||
b := buf.New(nil)
|
||||
var body []byte
|
||||
|
||||
if err := cf(b).Write(&codec.Message{
|
||||
Target: topic,
|
||||
Type: codec.Event,
|
||||
Header: map[string]string{
|
||||
"Micro-Id": id,
|
||||
"Micro-Topic": msg.Topic(),
|
||||
},
|
||||
}, msg.Payload()); err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
// passed in raw data
|
||||
if d, ok := msg.Payload().(*raw.Frame); ok {
|
||||
body = d.Data
|
||||
} else {
|
||||
// new buffer
|
||||
b := buf.New(nil)
|
||||
|
||||
if err := cf(b).Write(&codec.Message{
|
||||
Target: topic,
|
||||
Type: codec.Event,
|
||||
Header: map[string]string{
|
||||
"Micro-Id": id,
|
||||
"Micro-Topic": msg.Topic(),
|
||||
},
|
||||
}, msg.Payload()); err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// set the body
|
||||
body = b.Bytes()
|
||||
}
|
||||
|
||||
r.once.Do(func() {
|
||||
r.opts.Broker.Connect()
|
||||
})
|
||||
|
||||
return r.opts.Broker.Publish(topic, &broker.Message{
|
||||
Header: md,
|
||||
Body: b.Bytes(),
|
||||
Body: body,
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -12,10 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func newTestRegistry() registry.Registry {
|
||||
r := memory.NewRegistry()
|
||||
reg := r.(*memory.Registry)
|
||||
reg.Services = testData
|
||||
return reg
|
||||
return memory.NewRegistry(memory.Services(testData))
|
||||
}
|
||||
|
||||
func TestCallAddress(t *testing.T) {
|
||||
@@ -143,9 +140,12 @@ func TestCallWrapper(t *testing.T) {
|
||||
Name: service,
|
||||
Version: "latest",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: id,
|
||||
Address: address,
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@@ -88,32 +88,24 @@ func (rwc *readWriteCloser) Close() error {
|
||||
}
|
||||
|
||||
func getHeaders(m *codec.Message) {
|
||||
get := func(hdr string) string {
|
||||
if hd := m.Header[hdr]; len(hd) > 0 {
|
||||
return hd
|
||||
set := func(v, hdr string) string {
|
||||
if len(v) > 0 {
|
||||
return v
|
||||
}
|
||||
// old
|
||||
return m.Header["X-"+hdr]
|
||||
return m.Header[hdr]
|
||||
}
|
||||
|
||||
// check error in header
|
||||
if len(m.Error) == 0 {
|
||||
m.Error = get("Micro-Error")
|
||||
}
|
||||
m.Error = set(m.Error, "Micro-Error")
|
||||
|
||||
// check endpoint in header
|
||||
if len(m.Endpoint) == 0 {
|
||||
m.Endpoint = get("Micro-Endpoint")
|
||||
}
|
||||
m.Endpoint = set(m.Endpoint, "Micro-Endpoint")
|
||||
|
||||
// check method in header
|
||||
if len(m.Method) == 0 {
|
||||
m.Method = get("Micro-Method")
|
||||
}
|
||||
m.Method = set(m.Method, "Micro-Method")
|
||||
|
||||
if len(m.Id) == 0 {
|
||||
m.Id = get("Micro-Id")
|
||||
}
|
||||
// set the request id
|
||||
m.Id = set(m.Id, "Micro-Id")
|
||||
}
|
||||
|
||||
func setHeaders(m *codec.Message, stream string) {
|
||||
@@ -122,7 +114,6 @@ func setHeaders(m *codec.Message, stream string) {
|
||||
return
|
||||
}
|
||||
m.Header[hdr] = v
|
||||
m.Header["X-"+hdr] = v
|
||||
}
|
||||
|
||||
set("Micro-Id", m.Id)
|
||||
@@ -145,6 +136,11 @@ func setupProtocol(msg *transport.Message, node *registry.Node) codec.NewCodec {
|
||||
return nil
|
||||
}
|
||||
|
||||
// processing topic publishing
|
||||
if len(msg.Header["Micro-Topic"]) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// no protocol use old codecs
|
||||
switch msg.Header["Content-Type"] {
|
||||
case "application/json":
|
||||
@@ -190,32 +186,30 @@ func (c *rpcCodec) Write(m *codec.Message, body interface{}) error {
|
||||
|
||||
// if body is bytes Frame don't encode
|
||||
if body != nil {
|
||||
b, ok := body.(*raw.Frame)
|
||||
if ok {
|
||||
if b, ok := body.(*raw.Frame); ok {
|
||||
// set body
|
||||
m.Body = b.Data
|
||||
body = nil
|
||||
} else {
|
||||
// write to codec
|
||||
if err := c.codec.Write(m, body); err != nil {
|
||||
return errors.InternalServerError("go.micro.client.codec", err.Error())
|
||||
}
|
||||
// set body
|
||||
m.Body = c.buf.wbuf.Bytes()
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.Body) == 0 {
|
||||
// write to codec
|
||||
if err := c.codec.Write(m, body); err != nil {
|
||||
return errors.InternalServerError("go.micro.client.codec", err.Error())
|
||||
}
|
||||
// set body
|
||||
m.Body = c.buf.wbuf.Bytes()
|
||||
}
|
||||
|
||||
// create new transport message
|
||||
msg := transport.Message{
|
||||
Header: m.Header,
|
||||
Body: m.Body,
|
||||
}
|
||||
|
||||
// send the request
|
||||
if err := c.client.Send(&msg); err != nil {
|
||||
return errors.InternalServerError("go.micro.client.transport", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -83,7 +83,10 @@ func (r *rpcStream) Recv(msg interface{}) error {
|
||||
|
||||
var resp codec.Message
|
||||
|
||||
if err := r.codec.ReadHeader(&resp, codec.Response); err != nil {
|
||||
r.Unlock()
|
||||
err := r.codec.ReadHeader(&resp, codec.Response)
|
||||
r.Lock()
|
||||
if err != nil {
|
||||
if err == io.EOF && !r.isClosed() {
|
||||
r.err = io.ErrUnexpectedEOF
|
||||
return io.ErrUnexpectedEOF
|
||||
@@ -102,11 +105,17 @@ func (r *rpcStream) Recv(msg interface{}) error {
|
||||
} else {
|
||||
r.err = io.EOF
|
||||
}
|
||||
if err := r.codec.ReadBody(nil); err != nil {
|
||||
r.Unlock()
|
||||
err = r.codec.ReadBody(nil)
|
||||
r.Lock()
|
||||
if err != nil {
|
||||
r.err = err
|
||||
}
|
||||
default:
|
||||
if err := r.codec.ReadBody(msg); err != nil {
|
||||
r.Unlock()
|
||||
err = r.codec.ReadBody(msg)
|
||||
r.Lock()
|
||||
if err != nil {
|
||||
r.err = err
|
||||
}
|
||||
}
|
||||
@@ -121,11 +130,15 @@ func (r *rpcStream) Error() error {
|
||||
}
|
||||
|
||||
func (r *rpcStream) Close() error {
|
||||
r.RLock()
|
||||
|
||||
select {
|
||||
case <-r.closed:
|
||||
r.RUnlock()
|
||||
return nil
|
||||
default:
|
||||
close(r.closed)
|
||||
r.RUnlock()
|
||||
|
||||
// send the end of stream message
|
||||
if r.sendEOS {
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
var (
|
||||
// mock data
|
||||
testData = map[string][]*registry.Service{
|
||||
"foo": []*registry.Service{
|
||||
"foo": {
|
||||
{
|
||||
Name: "foo",
|
||||
Version: "1.0.0",
|
||||
|
@@ -9,9 +9,7 @@ import (
|
||||
func TestRegistrySelector(t *testing.T) {
|
||||
counts := map[string]int{}
|
||||
|
||||
r := memory.NewRegistry()
|
||||
rg := r.(*memory.Registry)
|
||||
rg.Services = testData
|
||||
r := memory.NewRegistry(memory.Services(testData))
|
||||
cache := NewSelector(Registry(r))
|
||||
|
||||
next, err := cache.Select("foo")
|
||||
|
@@ -63,7 +63,7 @@ func (d *dnsSelector) Select(service string, opts ...selector.SelectOption) (sel
|
||||
}
|
||||
}
|
||||
|
||||
var nodes []*registry.Node
|
||||
nodes := make([]*registry.Node, 0, len(srv))
|
||||
for _, node := range srv {
|
||||
nodes = append(nodes, ®istry.Node{
|
||||
Id: node.Target,
|
||||
@@ -72,7 +72,7 @@ func (d *dnsSelector) Select(service string, opts ...selector.SelectOption) (sel
|
||||
}
|
||||
|
||||
services := []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: service,
|
||||
Nodes: nodes,
|
||||
},
|
||||
@@ -99,13 +99,9 @@ func (d *dnsSelector) Select(service string, opts ...selector.SelectOption) (sel
|
||||
return sopts.Strategy(services), nil
|
||||
}
|
||||
|
||||
func (d *dnsSelector) Mark(service string, node *registry.Node, err error) {
|
||||
return
|
||||
}
|
||||
func (d *dnsSelector) Mark(service string, node *registry.Node, err error) {}
|
||||
|
||||
func (d *dnsSelector) Reset(service string) {
|
||||
return
|
||||
}
|
||||
func (d *dnsSelector) Reset(service string) {}
|
||||
|
||||
func (d *dnsSelector) Close() error {
|
||||
return nil
|
||||
|
@@ -14,20 +14,20 @@ func TestFilterEndpoint(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Endpoints: []*registry.Endpoint{
|
||||
®istry.Endpoint{
|
||||
{
|
||||
Name: "Foo.Bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
Endpoints: []*registry.Endpoint{
|
||||
®istry.Endpoint{
|
||||
{
|
||||
Name: "Baz.Bar",
|
||||
},
|
||||
},
|
||||
@@ -38,20 +38,20 @@ func TestFilterEndpoint(t *testing.T) {
|
||||
},
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Endpoints: []*registry.Endpoint{
|
||||
®istry.Endpoint{
|
||||
{
|
||||
Name: "Foo.Bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
Endpoints: []*registry.Endpoint{
|
||||
®istry.Endpoint{
|
||||
{
|
||||
Name: "Foo.Bar",
|
||||
},
|
||||
},
|
||||
@@ -95,11 +95,11 @@ func TestFilterLabel(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test-1",
|
||||
Address: "localhost",
|
||||
Metadata: map[string]string{
|
||||
@@ -108,11 +108,11 @@ func TestFilterLabel(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test-2",
|
||||
Address: "localhost",
|
||||
Metadata: map[string]string{
|
||||
@@ -127,21 +127,21 @@ func TestFilterLabel(t *testing.T) {
|
||||
},
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test-1",
|
||||
Address: "localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test-2",
|
||||
Address: "localhost",
|
||||
},
|
||||
@@ -187,11 +187,11 @@ func TestFilterVersion(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
},
|
||||
@@ -201,11 +201,11 @@ func TestFilterVersion(t *testing.T) {
|
||||
},
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
},
|
||||
|
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/micro/go-micro/client/selector"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/router"
|
||||
pb "github.com/micro/go-micro/router/proto"
|
||||
pb "github.com/micro/go-micro/router/service/proto"
|
||||
)
|
||||
|
||||
type routerSelector struct {
|
||||
@@ -43,9 +43,9 @@ type routerKey struct{}
|
||||
func (r *routerSelector) getRoutes(service string) ([]router.Route, error) {
|
||||
if !r.remote {
|
||||
// lookup router for routes for the service
|
||||
return r.r.Lookup(router.NewQuery(
|
||||
return r.r.Lookup(
|
||||
router.QueryService(service),
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
// lookup the remote router
|
||||
@@ -101,7 +101,7 @@ func (r *routerSelector) getRoutes(service string) ([]router.Route, error) {
|
||||
return nil, selector.ErrNoneAvailable
|
||||
}
|
||||
|
||||
var routes []router.Route
|
||||
routes := make([]router.Route, 0, len(pbRoutes.Routes))
|
||||
|
||||
// convert from pb to []*router.Route
|
||||
for _, r := range pbRoutes.Routes {
|
||||
@@ -111,7 +111,7 @@ func (r *routerSelector) getRoutes(service string) ([]router.Route, error) {
|
||||
Gateway: r.Gateway,
|
||||
Network: r.Network,
|
||||
Link: r.Link,
|
||||
Metric: int(r.Metric),
|
||||
Metric: r.Metric,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -176,12 +176,10 @@ func (r *routerSelector) Select(service string, opts ...selector.SelectOption) (
|
||||
|
||||
func (r *routerSelector) Mark(service string, node *registry.Node, err error) {
|
||||
// TODO: pass back metrics or information to the router
|
||||
return
|
||||
}
|
||||
|
||||
func (r *routerSelector) Reset(service string) {
|
||||
// TODO: reset the metrics or information at the router
|
||||
return
|
||||
}
|
||||
|
||||
func (r *routerSelector) Close() error {
|
||||
|
@@ -3,6 +3,7 @@ package selector
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
|
@@ -14,7 +14,7 @@ func init() {
|
||||
|
||||
// Random is a random strategy algorithm for node selection
|
||||
func Random(services []*registry.Service) Next {
|
||||
var nodes []*registry.Node
|
||||
nodes := make([]*registry.Node, 0, len(services))
|
||||
|
||||
for _, service := range services {
|
||||
nodes = append(nodes, service.Nodes...)
|
||||
@@ -32,7 +32,7 @@ func Random(services []*registry.Service) Next {
|
||||
|
||||
// RoundRobin is a roundrobin strategy algorithm for node selection
|
||||
func RoundRobin(services []*registry.Service) Next {
|
||||
var nodes []*registry.Node
|
||||
nodes := make([]*registry.Node, 0, len(services))
|
||||
|
||||
for _, service := range services {
|
||||
nodes = append(nodes, service.Nodes...)
|
||||
|
@@ -8,29 +8,29 @@ import (
|
||||
|
||||
func TestStrategies(t *testing.T) {
|
||||
testData := []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test1",
|
||||
Version: "latest",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test1-1",
|
||||
Address: "10.0.0.1:1001",
|
||||
},
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test1-2",
|
||||
Address: "10.0.0.2:1002",
|
||||
},
|
||||
},
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test1",
|
||||
Version: "default",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test1-3",
|
||||
Address: "10.0.0.3:1003",
|
||||
},
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test1-4",
|
||||
Address: "10.0.0.4:1004",
|
||||
},
|
||||
|
210
client/service/proto/client.pb.go
Normal file
210
client/service/proto/client.pb.go
Normal file
@@ -0,0 +1,210 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/micro/go-micro/client/proto/client.proto
|
||||
|
||||
package go_micro_client
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Request struct {
|
||||
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
|
||||
Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
|
||||
ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
|
||||
Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Request) Reset() { *m = Request{} }
|
||||
func (m *Request) String() string { return proto.CompactTextString(m) }
|
||||
func (*Request) ProtoMessage() {}
|
||||
func (*Request) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_d418333f021a3308, []int{0}
|
||||
}
|
||||
|
||||
func (m *Request) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Request.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(m, src)
|
||||
}
|
||||
func (m *Request) XXX_Size() int {
|
||||
return xxx_messageInfo_Request.Size(m)
|
||||
}
|
||||
func (m *Request) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Request.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Request proto.InternalMessageInfo
|
||||
|
||||
func (m *Request) GetService() string {
|
||||
if m != nil {
|
||||
return m.Service
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Request) GetEndpoint() string {
|
||||
if m != nil {
|
||||
return m.Endpoint
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Request) GetContentType() string {
|
||||
if m != nil {
|
||||
return m.ContentType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Request) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Body []byte `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return proto.CompactTextString(m) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
func (*Response) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_d418333f021a3308, []int{1}
|
||||
}
|
||||
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Response.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(m, src)
|
||||
}
|
||||
func (m *Response) XXX_Size() int {
|
||||
return xxx_messageInfo_Response.Size(m)
|
||||
}
|
||||
func (m *Response) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Response.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Response proto.InternalMessageInfo
|
||||
|
||||
func (m *Response) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
|
||||
ContentType string `protobuf:"bytes,2,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
|
||||
Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Message) Reset() { *m = Message{} }
|
||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||
func (*Message) ProtoMessage() {}
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_d418333f021a3308, []int{2}
|
||||
}
|
||||
|
||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Message.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Message) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Message.Merge(m, src)
|
||||
}
|
||||
func (m *Message) XXX_Size() int {
|
||||
return xxx_messageInfo_Message.Size(m)
|
||||
}
|
||||
func (m *Message) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Message.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Message proto.InternalMessageInfo
|
||||
|
||||
func (m *Message) GetTopic() string {
|
||||
if m != nil {
|
||||
return m.Topic
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Message) GetContentType() string {
|
||||
if m != nil {
|
||||
return m.ContentType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Message) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Request)(nil), "go.micro.client.Request")
|
||||
proto.RegisterType((*Response)(nil), "go.micro.client.Response")
|
||||
proto.RegisterType((*Message)(nil), "go.micro.client.Message")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/micro/go-micro/client/proto/client.proto", fileDescriptor_d418333f021a3308)
|
||||
}
|
||||
|
||||
var fileDescriptor_d418333f021a3308 = []byte{
|
||||
// 279 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x31, 0x4f, 0xfb, 0x30,
|
||||
0x10, 0xc5, 0xeb, 0xb6, 0xff, 0xa4, 0xff, 0xa3, 0x12, 0x92, 0xc5, 0x60, 0x32, 0xa0, 0x92, 0x29,
|
||||
0x0b, 0x0e, 0xa2, 0x33, 0x62, 0xc8, 0x8c, 0x84, 0x02, 0x62, 0x45, 0x89, 0x7b, 0x4a, 0x2d, 0x25,
|
||||
0xb6, 0x89, 0x9d, 0x4a, 0xf9, 0x90, 0x7c, 0x27, 0x24, 0x27, 0xa5, 0x08, 0xda, 0x85, 0xed, 0xde,
|
||||
0xfd, 0xac, 0x77, 0xe7, 0x77, 0xb0, 0xae, 0xa4, 0xdb, 0x76, 0x25, 0x17, 0xba, 0x49, 0x1b, 0x29,
|
||||
0x5a, 0x9d, 0x56, 0xfa, 0x66, 0x28, 0x44, 0x2d, 0x51, 0xb9, 0xd4, 0xb4, 0xda, 0xed, 0x05, 0xf7,
|
||||
0x82, 0x9e, 0x57, 0x9a, 0xfb, 0x37, 0x7c, 0x68, 0xc7, 0x3b, 0x08, 0x73, 0x7c, 0xef, 0xd0, 0x3a,
|
||||
0xca, 0x20, 0xb4, 0xd8, 0xee, 0xa4, 0x40, 0x46, 0x56, 0x24, 0xf9, 0x9f, 0xef, 0x25, 0x8d, 0x60,
|
||||
0x81, 0x6a, 0x63, 0xb4, 0x54, 0x8e, 0x4d, 0x3d, 0xfa, 0xd2, 0xf4, 0x1a, 0x96, 0x42, 0x2b, 0x87,
|
||||
0xca, 0xbd, 0xb9, 0xde, 0x20, 0x9b, 0x79, 0x7e, 0x36, 0xf6, 0x5e, 0x7a, 0x83, 0x94, 0xc2, 0xbc,
|
||||
0xd4, 0x9b, 0x9e, 0xcd, 0x57, 0x24, 0x59, 0xe6, 0xbe, 0x8e, 0xaf, 0x60, 0x91, 0xa3, 0x35, 0x5a,
|
||||
0xd9, 0x03, 0x27, 0xdf, 0xf8, 0x2b, 0x84, 0x8f, 0x68, 0x6d, 0x51, 0x21, 0xbd, 0x80, 0x7f, 0x4e,
|
||||
0x1b, 0x29, 0xc6, 0xad, 0x06, 0xf1, 0x6b, 0xee, 0xf4, 0xf4, 0xdc, 0xd9, 0xc1, 0xf7, 0xee, 0x83,
|
||||
0x40, 0x90, 0xf9, 0xaf, 0xd3, 0x7b, 0x98, 0x67, 0x45, 0x5d, 0x53, 0xc6, 0x7f, 0x84, 0xc2, 0xc7,
|
||||
0x44, 0xa2, 0xcb, 0x23, 0x64, 0xd8, 0x39, 0x9e, 0xd0, 0x0c, 0x82, 0x67, 0xd7, 0x62, 0xd1, 0xfc,
|
||||
0xd1, 0x20, 0x21, 0xb7, 0x84, 0x3e, 0x40, 0xf8, 0xd4, 0x95, 0xb5, 0xb4, 0xdb, 0x23, 0x2e, 0x63,
|
||||
0x00, 0xd1, 0x49, 0x12, 0x4f, 0xca, 0xc0, 0xdf, 0x75, 0xfd, 0x19, 0x00, 0x00, 0xff, 0xff, 0xb6,
|
||||
0x4d, 0x6e, 0xd5, 0x0e, 0x02, 0x00, 0x00,
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: micro/go-micro/client/proto/client.proto
|
||||
// source: github.com/micro/go-micro/client/proto/client.proto
|
||||
|
||||
package go_micro_client
|
||||
|
||||
@@ -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 µService{
|
||||
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 µServiceStream{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 := µHandler{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, µStreamStream{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)
|
||||
}
|
@@ -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
|
@@ -29,12 +29,10 @@ func (c *Codec) ReadBody(b interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
switch b.(type) {
|
||||
switch v := b.(type) {
|
||||
case *[]byte:
|
||||
v := b.(*[]byte)
|
||||
*v = buf
|
||||
case *Frame:
|
||||
v := b.(*Frame)
|
||||
v.Data = buf
|
||||
default:
|
||||
return fmt.Errorf("failed to read body: %v is not type of *[]byte", b)
|
||||
@@ -45,14 +43,13 @@ func (c *Codec) ReadBody(b interface{}) error {
|
||||
|
||||
func (c *Codec) Write(m *codec.Message, b interface{}) error {
|
||||
var v []byte
|
||||
switch b.(type) {
|
||||
switch vb := b.(type) {
|
||||
case *Frame:
|
||||
v = b.(*Frame).Data
|
||||
v = vb.Data
|
||||
case *[]byte:
|
||||
ve := b.(*[]byte)
|
||||
v = *ve
|
||||
v = *vb
|
||||
case []byte:
|
||||
v = b.([]byte)
|
||||
v = vb
|
||||
default:
|
||||
return fmt.Errorf("failed to write: %v is not type of *[]byte or []byte", b)
|
||||
}
|
||||
|
@@ -12,25 +12,22 @@ type Message struct {
|
||||
}
|
||||
|
||||
func (n Marshaler) Marshal(v interface{}) ([]byte, error) {
|
||||
switch v.(type) {
|
||||
switch ve := v.(type) {
|
||||
case *[]byte:
|
||||
ve := v.(*[]byte)
|
||||
return *ve, nil
|
||||
case []byte:
|
||||
return v.([]byte), nil
|
||||
return ve, nil
|
||||
case *Message:
|
||||
return v.(*Message).Body, nil
|
||||
return ve.Body, nil
|
||||
}
|
||||
return nil, errors.New("invalid message")
|
||||
}
|
||||
|
||||
func (n Marshaler) Unmarshal(d []byte, v interface{}) error {
|
||||
switch v.(type) {
|
||||
switch ve := v.(type) {
|
||||
case *[]byte:
|
||||
ve := v.(*[]byte)
|
||||
*ve = d
|
||||
case *Message:
|
||||
ve := v.(*Message)
|
||||
ve.Body = d
|
||||
}
|
||||
return errors.New("invalid message")
|
||||
|
@@ -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
|
||||
@@ -113,6 +126,10 @@ func (c *Codec) Write(m *codec.Message, b interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(buf) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return encode(0, buf, c.Conn)
|
||||
}
|
||||
|
||||
|
@@ -60,7 +60,6 @@ func (j *jsonCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error {
|
||||
default:
|
||||
return fmt.Errorf("Unrecognised message type: %v", mt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *jsonCodec) ReadBody(b interface{}) error {
|
||||
|
@@ -29,15 +29,12 @@ func (c *Codec) ReadBody(b interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
switch b.(type) {
|
||||
switch v := b.(type) {
|
||||
case *string:
|
||||
v := b.(*string)
|
||||
*v = string(buf)
|
||||
case *[]byte:
|
||||
v := b.(*[]byte)
|
||||
*v = buf
|
||||
case *Frame:
|
||||
v := b.(*Frame)
|
||||
v.Data = buf
|
||||
default:
|
||||
return fmt.Errorf("failed to read body: %v is not type of *[]byte", b)
|
||||
@@ -48,20 +45,17 @@ func (c *Codec) ReadBody(b interface{}) error {
|
||||
|
||||
func (c *Codec) Write(m *codec.Message, b interface{}) error {
|
||||
var v []byte
|
||||
switch b.(type) {
|
||||
switch ve := b.(type) {
|
||||
case *Frame:
|
||||
v = b.(*Frame).Data
|
||||
v = ve.Data
|
||||
case *[]byte:
|
||||
ve := b.(*[]byte)
|
||||
v = *ve
|
||||
case *string:
|
||||
ve := b.(*string)
|
||||
v = []byte(*ve)
|
||||
case string:
|
||||
ve := b.(string)
|
||||
v = []byte(ve)
|
||||
case []byte:
|
||||
v = b.([]byte)
|
||||
v = ve
|
||||
default:
|
||||
return fmt.Errorf("failed to write: %v is not type of *[]byte or []byte", b)
|
||||
}
|
||||
|
@@ -3,46 +3,58 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/micro/cli"
|
||||
"github.com/micro/go-micro/broker"
|
||||
"github.com/micro/go-micro/client"
|
||||
cgrpc "github.com/micro/go-micro/client/grpc"
|
||||
cmucp "github.com/micro/go-micro/client/mucp"
|
||||
"github.com/micro/go-micro/client/selector"
|
||||
"github.com/micro/go-micro/debug/trace"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/runtime"
|
||||
"github.com/micro/go-micro/server"
|
||||
sgrpc "github.com/micro/go-micro/server/grpc"
|
||||
smucp "github.com/micro/go-micro/server/mucp"
|
||||
"github.com/micro/go-micro/store"
|
||||
"github.com/micro/go-micro/transport"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
|
||||
// clients
|
||||
cgrpc "github.com/micro/go-micro/client/grpc"
|
||||
cmucp "github.com/micro/go-micro/client/mucp"
|
||||
|
||||
// servers
|
||||
"github.com/micro/cli/v2"
|
||||
sgrpc "github.com/micro/go-micro/server/grpc"
|
||||
smucp "github.com/micro/go-micro/server/mucp"
|
||||
|
||||
// brokers
|
||||
"github.com/micro/go-micro/broker"
|
||||
"github.com/micro/go-micro/broker/http"
|
||||
"github.com/micro/go-micro/broker/memory"
|
||||
"github.com/micro/go-micro/broker/nats"
|
||||
brokerSrv "github.com/micro/go-micro/broker/service"
|
||||
|
||||
// registries
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/registry/consul"
|
||||
"github.com/micro/go-micro/registry/gossip"
|
||||
"github.com/micro/go-micro/registry/etcd"
|
||||
kreg "github.com/micro/go-micro/registry/kubernetes"
|
||||
"github.com/micro/go-micro/registry/mdns"
|
||||
rmem "github.com/micro/go-micro/registry/memory"
|
||||
regSrv "github.com/micro/go-micro/registry/service"
|
||||
|
||||
// selectors
|
||||
"github.com/micro/go-micro/client/selector"
|
||||
"github.com/micro/go-micro/client/selector/dns"
|
||||
"github.com/micro/go-micro/client/selector/router"
|
||||
"github.com/micro/go-micro/client/selector/static"
|
||||
|
||||
// transports
|
||||
"github.com/micro/go-micro/transport"
|
||||
tgrpc "github.com/micro/go-micro/transport/grpc"
|
||||
thttp "github.com/micro/go-micro/transport/http"
|
||||
tmem "github.com/micro/go-micro/transport/memory"
|
||||
"github.com/micro/go-micro/transport/quic"
|
||||
|
||||
// stores
|
||||
memStore "github.com/micro/go-micro/store/memory"
|
||||
svcStore "github.com/micro/go-micro/store/service"
|
||||
|
||||
// tracers
|
||||
// jTracer "github.com/micro/go-micro/debug/trace/jaeger"
|
||||
memTracer "github.com/micro/go-micro/debug/trace/memory"
|
||||
)
|
||||
|
||||
type Cmd interface {
|
||||
@@ -66,144 +78,180 @@ var (
|
||||
DefaultCmd = newCmd()
|
||||
|
||||
DefaultFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "client",
|
||||
EnvVar: "MICRO_CLIENT",
|
||||
Usage: "Client for go-micro; rpc",
|
||||
&cli.StringFlag{
|
||||
Name: "client",
|
||||
EnvVars: []string{"MICRO_CLIENT"},
|
||||
Usage: "Client for go-micro; rpc",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "client_request_timeout",
|
||||
EnvVar: "MICRO_CLIENT_REQUEST_TIMEOUT",
|
||||
Usage: "Sets the client request timeout. e.g 500ms, 5s, 1m. Default: 5s",
|
||||
&cli.StringFlag{
|
||||
Name: "client_request_timeout",
|
||||
EnvVars: []string{"MICRO_CLIENT_REQUEST_TIMEOUT"},
|
||||
Usage: "Sets the client request timeout. e.g 500ms, 5s, 1m. Default: 5s",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "client_retries",
|
||||
EnvVar: "MICRO_CLIENT_RETRIES",
|
||||
Value: client.DefaultRetries,
|
||||
Usage: "Sets the client retries. Default: 1",
|
||||
&cli.IntFlag{
|
||||
Name: "client_retries",
|
||||
EnvVars: []string{"MICRO_CLIENT_RETRIES"},
|
||||
Value: client.DefaultRetries,
|
||||
Usage: "Sets the client retries. Default: 1",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "client_pool_size",
|
||||
EnvVar: "MICRO_CLIENT_POOL_SIZE",
|
||||
Usage: "Sets the client connection pool size. Default: 1",
|
||||
&cli.IntFlag{
|
||||
Name: "client_pool_size",
|
||||
EnvVars: []string{"MICRO_CLIENT_POOL_SIZE"},
|
||||
Usage: "Sets the client connection pool size. Default: 1",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "client_pool_ttl",
|
||||
EnvVar: "MICRO_CLIENT_POOL_TTL",
|
||||
Usage: "Sets the client connection pool ttl. e.g 500ms, 5s, 1m. Default: 1m",
|
||||
&cli.StringFlag{
|
||||
Name: "client_pool_ttl",
|
||||
EnvVars: []string{"MICRO_CLIENT_POOL_TTL"},
|
||||
Usage: "Sets the client connection pool ttl. e.g 500ms, 5s, 1m. Default: 1m",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "register_ttl",
|
||||
EnvVar: "MICRO_REGISTER_TTL",
|
||||
Usage: "Register TTL in seconds",
|
||||
&cli.IntFlag{
|
||||
Name: "register_ttl",
|
||||
EnvVars: []string{"MICRO_REGISTER_TTL"},
|
||||
Value: 60,
|
||||
Usage: "Register TTL in seconds",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "register_interval",
|
||||
EnvVar: "MICRO_REGISTER_INTERVAL",
|
||||
Usage: "Register interval in seconds",
|
||||
&cli.IntFlag{
|
||||
Name: "register_interval",
|
||||
EnvVars: []string{"MICRO_REGISTER_INTERVAL"},
|
||||
Value: 30,
|
||||
Usage: "Register interval in seconds",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "server",
|
||||
EnvVar: "MICRO_SERVER",
|
||||
Usage: "Server for go-micro; rpc",
|
||||
&cli.StringFlag{
|
||||
Name: "server",
|
||||
EnvVars: []string{"MICRO_SERVER"},
|
||||
Usage: "Server for go-micro; rpc",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "server_name",
|
||||
EnvVar: "MICRO_SERVER_NAME",
|
||||
Usage: "Name of the server. go.micro.srv.example",
|
||||
&cli.StringFlag{
|
||||
Name: "server_name",
|
||||
EnvVars: []string{"MICRO_SERVER_NAME"},
|
||||
Usage: "Name of the server. go.micro.srv.example",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "server_version",
|
||||
EnvVar: "MICRO_SERVER_VERSION",
|
||||
Usage: "Version of the server. 1.1.0",
|
||||
&cli.StringFlag{
|
||||
Name: "server_version",
|
||||
EnvVars: []string{"MICRO_SERVER_VERSION"},
|
||||
Usage: "Version of the server. 1.1.0",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "server_id",
|
||||
EnvVar: "MICRO_SERVER_ID",
|
||||
Usage: "Id of the server. Auto-generated if not specified",
|
||||
&cli.StringFlag{
|
||||
Name: "server_id",
|
||||
EnvVars: []string{"MICRO_SERVER_ID"},
|
||||
Usage: "Id of the server. Auto-generated if not specified",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "server_address",
|
||||
EnvVar: "MICRO_SERVER_ADDRESS",
|
||||
Usage: "Bind address for the server. 127.0.0.1:8080",
|
||||
&cli.StringFlag{
|
||||
Name: "server_address",
|
||||
EnvVars: []string{"MICRO_SERVER_ADDRESS"},
|
||||
Usage: "Bind address for the server. 127.0.0.1:8080",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "server_advertise",
|
||||
EnvVar: "MICRO_SERVER_ADVERTISE",
|
||||
Usage: "Used instead of the server_address when registering with discovery. 127.0.0.1:8080",
|
||||
&cli.StringFlag{
|
||||
Name: "server_advertise",
|
||||
EnvVars: []string{"MICRO_SERVER_ADVERTISE"},
|
||||
Usage: "Used instead of the server_address when registering with discovery. 127.0.0.1:8080",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "server_metadata",
|
||||
EnvVar: "MICRO_SERVER_METADATA",
|
||||
Value: &cli.StringSlice{},
|
||||
Usage: "A list of key-value pairs defining metadata. version=1.0.0",
|
||||
&cli.StringSliceFlag{
|
||||
Name: "server_metadata",
|
||||
EnvVars: []string{"MICRO_SERVER_METADATA"},
|
||||
Value: &cli.StringSlice{},
|
||||
Usage: "A list of key-value pairs defining metadata. version=1.0.0",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "broker",
|
||||
EnvVar: "MICRO_BROKER",
|
||||
Usage: "Broker for pub/sub. http, nats, rabbitmq",
|
||||
&cli.StringFlag{
|
||||
Name: "broker",
|
||||
EnvVars: []string{"MICRO_BROKER"},
|
||||
Usage: "Broker for pub/sub. http, nats, rabbitmq",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "broker_address",
|
||||
EnvVar: "MICRO_BROKER_ADDRESS",
|
||||
Usage: "Comma-separated list of broker addresses",
|
||||
&cli.StringFlag{
|
||||
Name: "broker_address",
|
||||
EnvVars: []string{"MICRO_BROKER_ADDRESS"},
|
||||
Usage: "Comma-separated list of broker addresses",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "registry",
|
||||
EnvVar: "MICRO_REGISTRY",
|
||||
Usage: "Registry for discovery. consul, mdns",
|
||||
&cli.StringFlag{
|
||||
Name: "profile",
|
||||
Usage: "Debug profiler for cpu and memory stats",
|
||||
EnvVars: []string{"MICRO_DEBUG_PROFILE"},
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "registry_address",
|
||||
EnvVar: "MICRO_REGISTRY_ADDRESS",
|
||||
Usage: "Comma-separated list of registry addresses",
|
||||
&cli.StringFlag{
|
||||
Name: "registry",
|
||||
EnvVars: []string{"MICRO_REGISTRY"},
|
||||
Usage: "Registry for discovery. etcd, mdns",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "selector",
|
||||
EnvVar: "MICRO_SELECTOR",
|
||||
Usage: "Selector used to pick nodes for querying",
|
||||
&cli.StringFlag{
|
||||
Name: "registry_address",
|
||||
EnvVars: []string{"MICRO_REGISTRY_ADDRESS"},
|
||||
Usage: "Comma-separated list of registry addresses",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "transport",
|
||||
EnvVar: "MICRO_TRANSPORT",
|
||||
Usage: "Transport mechanism used; http",
|
||||
&cli.StringFlag{
|
||||
Name: "runtime",
|
||||
Usage: "Runtime for building and running services e.g local, kubernetes",
|
||||
EnvVars: []string{"MICRO_RUNTIME"},
|
||||
Value: "local",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "transport_address",
|
||||
EnvVar: "MICRO_TRANSPORT_ADDRESS",
|
||||
Usage: "Comma-separated list of transport addresses",
|
||||
&cli.StringFlag{
|
||||
Name: "selector",
|
||||
EnvVars: []string{"MICRO_SELECTOR"},
|
||||
Usage: "Selector used to pick nodes for querying",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "store",
|
||||
EnvVars: []string{"MICRO_STORE"},
|
||||
Usage: "Store used for key-value storage",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "store_address",
|
||||
EnvVars: []string{"MICRO_STORE_ADDRESS"},
|
||||
Usage: "Comma-separated list of store addresses",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "store_namespace",
|
||||
EnvVars: []string{"MICRO_STORE_NAMESPACE"},
|
||||
Usage: "Namespace for store data",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "transport",
|
||||
EnvVars: []string{"MICRO_TRANSPORT"},
|
||||
Usage: "Transport mechanism used; http",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "transport_address",
|
||||
EnvVars: []string{"MICRO_TRANSPORT_ADDRESS"},
|
||||
Usage: "Comma-separated list of transport addresses",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracer",
|
||||
EnvVars: []string{"MICRO_TRACER"},
|
||||
Usage: "Tracer for distributed tracing, e.g. memory, jaeger",
|
||||
Value: "memory",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracer_address",
|
||||
EnvVars: []string{"MICRO_TRACER_ADDRESS"},
|
||||
Usage: "Comma-separated list of tracer addresses",
|
||||
},
|
||||
}
|
||||
|
||||
DefaultBrokers = map[string]func(...broker.Option) broker.Broker{
|
||||
"http": http.NewBroker,
|
||||
"memory": memory.NewBroker,
|
||||
"nats": nats.NewBroker,
|
||||
"service": brokerSrv.NewBroker,
|
||||
"memory": memory.NewBroker,
|
||||
"nats": nats.NewBroker,
|
||||
}
|
||||
|
||||
DefaultClients = map[string]func(...client.Option) client.Client{
|
||||
"rpc": client.NewClient,
|
||||
"mucp": cmucp.NewClient,
|
||||
"grpc": cgrpc.NewClient,
|
||||
}
|
||||
|
||||
DefaultRegistries = map[string]func(...registry.Option) registry.Registry{
|
||||
"consul": consul.NewRegistry,
|
||||
"gossip": gossip.NewRegistry,
|
||||
"mdns": mdns.NewRegistry,
|
||||
"memory": rmem.NewRegistry,
|
||||
"service": regSrv.NewRegistry,
|
||||
"etcd": etcd.NewRegistry,
|
||||
"mdns": mdns.NewRegistry,
|
||||
"memory": rmem.NewRegistry,
|
||||
"kubernetes": kreg.NewRegistry,
|
||||
}
|
||||
|
||||
DefaultSelectors = map[string]func(...selector.Option) selector.Selector{
|
||||
"default": selector.NewSelector,
|
||||
"dns": dns.NewSelector,
|
||||
"cache": selector.NewSelector,
|
||||
"router": router.NewSelector,
|
||||
"static": static.NewSelector,
|
||||
"dns": dns.NewSelector,
|
||||
"router": router.NewSelector,
|
||||
"static": static.NewSelector,
|
||||
}
|
||||
|
||||
DefaultServers = map[string]func(...server.Option) server.Server{
|
||||
"rpc": server.NewServer,
|
||||
"mucp": smucp.NewServer,
|
||||
"grpc": sgrpc.NewServer,
|
||||
}
|
||||
@@ -211,26 +259,35 @@ var (
|
||||
DefaultTransports = map[string]func(...transport.Option) transport.Transport{
|
||||
"memory": tmem.NewTransport,
|
||||
"http": thttp.NewTransport,
|
||||
"grpc": tgrpc.NewTransport,
|
||||
"quic": quic.NewTransport,
|
||||
}
|
||||
|
||||
DefaultRuntimes = map[string]func(...runtime.Option) runtime.Runtime{
|
||||
"local": runtime.NewRuntime,
|
||||
}
|
||||
|
||||
DefaultStores = map[string]func(...store.Option) store.Store{
|
||||
"memory": memStore.NewStore,
|
||||
"service": svcStore.NewStore,
|
||||
}
|
||||
|
||||
DefaultTracers = map[string]func(...trace.Option) trace.Tracer{
|
||||
"memory": memTracer.NewTracer,
|
||||
// "jaeger": jTracer.NewTracer,
|
||||
}
|
||||
|
||||
// used for default selection as the fall back
|
||||
defaultClient = "rpc"
|
||||
defaultServer = "rpc"
|
||||
defaultBroker = "http"
|
||||
defaultClient = "grpc"
|
||||
defaultServer = "grpc"
|
||||
defaultBroker = "eats"
|
||||
defaultRegistry = "mdns"
|
||||
defaultSelector = "registry"
|
||||
defaultTransport = "http"
|
||||
defaultRuntime = "local"
|
||||
defaultStore = "memory"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
help := cli.HelpPrinter
|
||||
cli.HelpPrinter = func(writer io.Writer, templ string, data interface{}) {
|
||||
help(writer, templ, data)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func newCmd(opts ...Option) Cmd {
|
||||
@@ -241,6 +298,9 @@ func newCmd(opts ...Option) Cmd {
|
||||
Server: &server.DefaultServer,
|
||||
Selector: &selector.DefaultSelector,
|
||||
Transport: &transport.DefaultTransport,
|
||||
Runtime: &runtime.DefaultRuntime,
|
||||
Store: &store.DefaultStore,
|
||||
Tracer: &trace.DefaultTracer,
|
||||
|
||||
Brokers: DefaultBrokers,
|
||||
Clients: DefaultClients,
|
||||
@@ -248,6 +308,9 @@ func newCmd(opts ...Option) Cmd {
|
||||
Selectors: DefaultSelectors,
|
||||
Servers: DefaultServers,
|
||||
Transports: DefaultTransports,
|
||||
Runtimes: DefaultRuntimes,
|
||||
Stores: DefaultStores,
|
||||
Tracers: DefaultTracers,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
@@ -266,7 +329,9 @@ func newCmd(opts ...Option) Cmd {
|
||||
cmd.app.Usage = cmd.opts.Description
|
||||
cmd.app.Before = cmd.Before
|
||||
cmd.app.Flags = DefaultFlags
|
||||
cmd.app.Action = func(c *cli.Context) {}
|
||||
cmd.app.Action = func(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(options.Version) == 0 {
|
||||
cmd.app.HideVersion = true
|
||||
@@ -288,6 +353,36 @@ func (c *cmd) Before(ctx *cli.Context) error {
|
||||
var serverOpts []server.Option
|
||||
var clientOpts []client.Option
|
||||
|
||||
// Set the store
|
||||
if name := ctx.String("store"); len(name) > 0 {
|
||||
s, ok := c.opts.Stores[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("Unsupported store: %s", name)
|
||||
}
|
||||
|
||||
*c.opts.Store = s()
|
||||
}
|
||||
|
||||
// Set the runtime
|
||||
if name := ctx.String("runtime"); len(name) > 0 {
|
||||
r, ok := c.opts.Runtimes[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("Unsupported runtime: %s", name)
|
||||
}
|
||||
|
||||
*c.opts.Runtime = r()
|
||||
}
|
||||
|
||||
// Set the tracer
|
||||
if name := ctx.String("tracer"); len(name) > 0 {
|
||||
r, ok := c.opts.Tracers[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("Unsupported tracer: %s", name)
|
||||
}
|
||||
|
||||
*c.opts.Tracer = r()
|
||||
}
|
||||
|
||||
// Set the client
|
||||
if name := ctx.String("client"); len(name) > 0 {
|
||||
// only change if we have the client and type differs
|
||||
@@ -397,6 +492,18 @@ func (c *cmd) Before(ctx *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
if len(ctx.String("store_address")) > 0 {
|
||||
if err := (*c.opts.Store).Init(store.Nodes(strings.Split(ctx.String("store_address"), ",")...)); err != nil {
|
||||
log.Fatalf("Error configuring store: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ctx.String("store_namespace")) > 0 {
|
||||
if err := (*c.opts.Store).Init(store.Namespace(ctx.String("store_address"))); err != nil {
|
||||
log.Fatalf("Error configuring store: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ctx.String("server_name")) > 0 {
|
||||
serverOpts = append(serverOpts, server.Name(ctx.String("server_name")))
|
||||
}
|
||||
@@ -417,11 +524,11 @@ func (c *cmd) Before(ctx *cli.Context) error {
|
||||
serverOpts = append(serverOpts, server.Advertise(ctx.String("server_advertise")))
|
||||
}
|
||||
|
||||
if ttl := time.Duration(ctx.GlobalInt("register_ttl")); ttl > 0 {
|
||||
if ttl := time.Duration(ctx.Int("register_ttl")); ttl >= 0 {
|
||||
serverOpts = append(serverOpts, server.RegisterTTL(ttl*time.Second))
|
||||
}
|
||||
|
||||
if val := time.Duration(ctx.GlobalInt("register_interval")); val > 0 {
|
||||
if val := time.Duration(ctx.Int("register_interval")); val >= 0 {
|
||||
serverOpts = append(serverOpts, server.RegisterInterval(val*time.Second))
|
||||
}
|
||||
|
||||
@@ -472,8 +579,12 @@ func (c *cmd) Init(opts ...Option) error {
|
||||
for _, o := range opts {
|
||||
o(&c.opts)
|
||||
}
|
||||
c.app.Name = c.opts.Name
|
||||
c.app.Version = c.opts.Version
|
||||
if len(c.opts.Name) > 0 {
|
||||
c.app.Name = c.opts.Name
|
||||
}
|
||||
if len(c.opts.Version) > 0 {
|
||||
c.app.Version = c.opts.Version
|
||||
}
|
||||
c.app.HideVersion = len(c.opts.Version) == 0
|
||||
c.app.Usage = c.opts.Description
|
||||
c.app.RunAndExitOnError()
|
||||
|
@@ -6,8 +6,11 @@ import (
|
||||
"github.com/micro/go-micro/broker"
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/client/selector"
|
||||
"github.com/micro/go-micro/debug/trace"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/runtime"
|
||||
"github.com/micro/go-micro/server"
|
||||
"github.com/micro/go-micro/store"
|
||||
"github.com/micro/go-micro/transport"
|
||||
)
|
||||
|
||||
@@ -24,6 +27,9 @@ type Options struct {
|
||||
Transport *transport.Transport
|
||||
Client *client.Client
|
||||
Server *server.Server
|
||||
Runtime *runtime.Runtime
|
||||
Store *store.Store
|
||||
Tracer *trace.Tracer
|
||||
|
||||
Brokers map[string]func(...broker.Option) broker.Broker
|
||||
Clients map[string]func(...client.Option) client.Client
|
||||
@@ -31,6 +37,9 @@ type Options struct {
|
||||
Selectors map[string]func(...selector.Option) selector.Selector
|
||||
Servers map[string]func(...server.Option) server.Server
|
||||
Transports map[string]func(...transport.Option) transport.Transport
|
||||
Runtimes map[string]func(...runtime.Option) runtime.Runtime
|
||||
Stores map[string]func(...store.Option) store.Store
|
||||
Tracers map[string]func(...trace.Option) trace.Tracer
|
||||
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
@@ -94,6 +103,12 @@ func Server(s *server.Server) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func Tracer(t *trace.Tracer) Option {
|
||||
return func(o *Options) {
|
||||
o.Tracer = t
|
||||
}
|
||||
}
|
||||
|
||||
// New broker func
|
||||
func NewBroker(name string, b func(...broker.Option) broker.Broker) Option {
|
||||
return func(o *Options) {
|
||||
@@ -135,3 +150,17 @@ func NewTransport(name string, t func(...transport.Option) transport.Transport)
|
||||
o.Transports[name] = t
|
||||
}
|
||||
}
|
||||
|
||||
// New runtime func
|
||||
func NewRuntime(name string, r func(...runtime.Option) runtime.Runtime) Option {
|
||||
return func(o *Options) {
|
||||
o.Runtimes[name] = r
|
||||
}
|
||||
}
|
||||
|
||||
// New tracer func
|
||||
func NewTracer(name string, t func(...trace.Option) trace.Tracer) Option {
|
||||
return func(o *Options) {
|
||||
o.Tracers[name] = t
|
||||
}
|
||||
}
|
||||
|
@@ -43,11 +43,11 @@ type Option func(o *Options)
|
||||
|
||||
var (
|
||||
// Default Config Manager
|
||||
DefaultConfig = NewConfig()
|
||||
DefaultConfig, _ = NewConfig()
|
||||
)
|
||||
|
||||
// NewConfig returns new config
|
||||
func NewConfig(opts ...Option) Config {
|
||||
func NewConfig(opts ...Option) (Config, error) {
|
||||
return newConfig(opts...)
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,7 @@ type watcher struct {
|
||||
value reader.Value
|
||||
}
|
||||
|
||||
func newConfig(opts ...Option) Config {
|
||||
func newConfig(opts ...Option) (Config, error) {
|
||||
options := Options{
|
||||
Loader: memory.NewLoader(),
|
||||
Reader: json.NewReader(),
|
||||
@@ -40,9 +40,18 @@ func newConfig(opts ...Option) Config {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
options.Loader.Load(options.Source...)
|
||||
snap, _ := options.Loader.Snapshot()
|
||||
vals, _ := options.Reader.Values(snap.ChangeSet)
|
||||
if err := options.Loader.Load(options.Source...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
snap, err := options.Loader.Snapshot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vals, err := options.Reader.Values(snap.ChangeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &config{
|
||||
exit: make(chan bool),
|
||||
@@ -53,7 +62,7 @@ func newConfig(opts ...Option) Config {
|
||||
|
||||
go c.run()
|
||||
|
||||
return c
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *config) run() {
|
||||
@@ -172,6 +181,28 @@ func (c *config) Get(path ...string) reader.Value {
|
||||
return newValue()
|
||||
}
|
||||
|
||||
func (c *config) Set(val interface{}, path ...string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.vals != nil {
|
||||
c.vals.Set(val, path...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *config) Del(path ...string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.vals != nil {
|
||||
c.vals.Del(path...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *config) Bytes() []byte {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
@@ -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"
|
||||
)
|
||||
@@ -57,7 +55,10 @@ func TestConfigLoadWithGoodFile(t *testing.T) {
|
||||
}()
|
||||
|
||||
// Create new config
|
||||
conf := NewConfig()
|
||||
conf, err := NewConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error but got %v", err)
|
||||
}
|
||||
// Load file source
|
||||
if err := conf.Load(file.NewSource(
|
||||
file.WithPath(path),
|
||||
@@ -75,9 +76,12 @@ func TestConfigLoadWithInvalidFile(t *testing.T) {
|
||||
}()
|
||||
|
||||
// Create new config
|
||||
conf := NewConfig()
|
||||
conf, err := NewConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error but got %v", err)
|
||||
}
|
||||
// Load file source
|
||||
err := conf.Load(file.NewSource(
|
||||
err = conf.Load(file.NewSource(
|
||||
file.WithPath(path),
|
||||
file.WithPath("/i/do/not/exists.json"),
|
||||
))
|
||||
@@ -107,13 +111,18 @@ func TestConfigMerge(t *testing.T) {
|
||||
}()
|
||||
os.Setenv("AMQP_HOST", "rabbit.testing.com")
|
||||
|
||||
conf := NewConfig()
|
||||
conf.Load(
|
||||
conf, err := NewConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error but got %v", err)
|
||||
}
|
||||
if err := conf.Load(
|
||||
file.NewSource(
|
||||
file.WithPath(path),
|
||||
),
|
||||
env.NewSource(),
|
||||
)
|
||||
); err != nil {
|
||||
t.Fatalf("Expected no error but got %v", err)
|
||||
}
|
||||
|
||||
actualHost := conf.Get("amqp", "host").String("backup")
|
||||
if actualHost != "rabbit.testing.com" {
|
||||
@@ -122,57 +131,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))
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package memory
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -28,8 +29,7 @@ type memory struct {
|
||||
// all the sources
|
||||
sources []source.Source
|
||||
|
||||
idx int
|
||||
watchers map[int]*watcher
|
||||
watchers *list.List
|
||||
}
|
||||
|
||||
type watcher struct {
|
||||
@@ -153,11 +153,11 @@ func (m *memory) reload() error {
|
||||
}
|
||||
|
||||
func (m *memory) update() {
|
||||
var watchers []*watcher
|
||||
watchers := make([]*watcher, 0, m.watchers.Len())
|
||||
|
||||
m.RLock()
|
||||
for _, w := range m.watchers {
|
||||
watchers = append(watchers, w)
|
||||
for e := m.watchers.Front(); e != nil; e = e.Next() {
|
||||
watchers = append(watchers, e.Value.(*watcher))
|
||||
}
|
||||
m.RUnlock()
|
||||
|
||||
@@ -193,6 +193,7 @@ func (m *memory) Snapshot() (*loader.Snapshot, error) {
|
||||
|
||||
// Sync loads all the sources, calls the parser and updates the config
|
||||
func (m *memory) Sync() error {
|
||||
//nolint:prealloc
|
||||
var sets []*source.ChangeSet
|
||||
|
||||
m.Lock()
|
||||
@@ -335,16 +336,14 @@ func (m *memory) Watch(path ...string) (loader.Watcher, error) {
|
||||
updates: make(chan reader.Value, 1),
|
||||
}
|
||||
|
||||
id := m.idx
|
||||
m.watchers[id] = w
|
||||
m.idx++
|
||||
e := m.watchers.PushBack(w)
|
||||
|
||||
m.Unlock()
|
||||
|
||||
go func() {
|
||||
<-w.exit
|
||||
m.Lock()
|
||||
delete(m.watchers, id)
|
||||
m.watchers.Remove(e)
|
||||
m.Unlock()
|
||||
}()
|
||||
|
||||
@@ -403,7 +402,7 @@ func NewLoader(opts ...loader.Option) loader.Loader {
|
||||
m := &memory{
|
||||
exit: make(chan bool),
|
||||
opts: options,
|
||||
watchers: make(map[int]*watcher),
|
||||
watchers: list.New(),
|
||||
sources: options.Source,
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -68,6 +69,8 @@ func WithString(s string) Option {
|
||||
// NewOptions returns a new initialiser
|
||||
func NewOptions(opts ...Option) Options {
|
||||
o := new(defaultOptions)
|
||||
o.Init(opts...)
|
||||
if err := o.Init(opts...); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
@@ -30,13 +30,6 @@ func newValues(ch *source.ChangeSet) (reader.Values, error) {
|
||||
return &jsonValues{ch, sj}, nil
|
||||
}
|
||||
|
||||
func newValue(s *simple.Json) reader.Value {
|
||||
if s == nil {
|
||||
s = simple.New()
|
||||
}
|
||||
return &jsonValue{s}
|
||||
}
|
||||
|
||||
func (j *jsonValues) Get(path ...string) reader.Value {
|
||||
return &jsonValue{j.sj.GetPath(path...)}
|
||||
}
|
||||
|
@@ -62,7 +62,7 @@ func TestStructArray(t *testing.T) {
|
||||
{
|
||||
[]byte(`[{"foo": "bar"}]`),
|
||||
emptyTSlice,
|
||||
[]T{T{Foo: "bar"}},
|
||||
[]T{{Foo: "bar"}},
|
||||
},
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user