317 Commits

Author SHA1 Message Date
Redkale
75698d9390 2020-08-31 13:35:52 +08:00
Redkale
9d343694cc <tt>替换成<code> 2020-08-31 09:31:38 +08:00
Redkale
fddb2a3be0 2020-08-28 22:35:43 +08:00
Redkale
3b44eddbb9 2020-08-28 19:37:34 +08:00
Redkale
ba4100e22d 增加 ThreadHashExecutor 2020-08-28 15:15:26 +08:00
Redkale
72155e31dd 2020-08-19 23:03:35 +08:00
Redkale
60903c844e 2020-08-19 22:56:26 +08:00
Redkale
5c810b5ff6 2020-08-11 19:57:17 +08:00
Redkale
e3a8c2d392 2020-08-06 16:33:49 +08:00
Redkale
1f27d5f3e3 完善Rest的泛型识别 2020-08-06 16:06:46 +08:00
Redkale
abb790da54 2020-07-31 12:00:27 +08:00
Redkale
f4a2f2af94 2020-07-31 10:26:26 +08:00
Redkale
29a21d060b 2020-07-31 10:22:19 +08:00
Redkale
de19861974 2020-07-31 10:13:53 +08:00
Redkale
e5766e31dc 2020-07-31 09:50:59 +08:00
Redkale
fca727ecaf 2020-07-30 20:54:35 +08:00
Redkale
d009c9b2ed 2020-07-30 19:14:39 +08:00
Redkale
fd2e84f781 2020-07-30 18:02:29 +08:00
Redkale
2b7676449d 2020-07-30 17:57:31 +08:00
Redkale
4b2c6eba63 2020-07-30 17:35:59 +08:00
Redkale
84147280cf 2020-07-30 09:44:07 +08:00
Redkale
e62d9f8201 2020-07-30 09:11:49 +08:00
Redkale
4cec4e64f9 2020-07-30 09:10:08 +08:00
Redkale
9eb647428c 2020-07-29 21:20:12 +08:00
Redkale
b68808b325 加入spop系列方法 2020-07-29 20:08:47 +08:00
Redkale
a9dce0efbf 2020-07-27 16:43:56 +08:00
Redkale
de63f81238 CacheSource remove系列方法返回值由void改成int 2020-07-25 09:39:43 +08:00
Redkale
a05afe45c0 2020-07-24 09:41:43 +08:00
Redkale
236996caa1 2020-07-22 13:41:54 +08:00
Redkale
b808c93885 2020-07-22 11:53:16 +08:00
Redkale
7865b74359 2020-07-22 11:27:10 +08:00
Redkale
143e1880d4 2020-07-22 11:24:39 +08:00
Redkale
1be467cfe9 2020-07-22 11:09:49 +08:00
Redkale
7f8f157616 2020-07-21 19:49:32 +08:00
Redkale
c140ad6828 2020-07-20 11:45:51 +08:00
Redkale
50e41dba25 2020-07-19 21:36:19 +08:00
Redkale
c73fc5f266 2020-07-19 15:25:59 +08:00
Redkale
a5f3adc477 2020-07-19 13:42:26 +08:00
Redkale
b77764c1cf 2020-07-17 20:18:10 +08:00
Redkale
714b188560 2020-07-17 18:21:09 +08:00
Redkale
47315f188a 2020-07-17 17:37:02 +08:00
Redkale
384a8a3991 2020-07-17 16:57:41 +08:00
Redkale
7a80c00439 2020-07-17 12:13:45 +08:00
Redkale
43486ce1e2 2020-07-17 11:43:51 +08:00
Redkale
627c1e953e CacheSource增加hxxx方法 2020-07-17 10:41:41 +08:00
Redkale
da141cc6bd 2020-07-16 23:43:35 +08:00
Redkale
8f3a9a6297 2020-07-16 17:50:04 +08:00
Redkale
e8d496b0ad 2020-07-16 17:30:27 +08:00
Redkale
f5accfbe8f 2020-07-15 23:07:55 +08:00
Redkale
16e291036e 2020-07-15 21:02:00 +08:00
Redkale
a79b8b77b5 2020-07-15 20:58:03 +08:00
Redkale
8e2d6acdf8 2020-07-15 19:34:31 +08:00
Redkale
70c5123f7a 增加强大的HttpMessageClusterClient功能 2020-07-15 18:03:33 +08:00
Redkale
7cfbaee199 2020-07-14 23:23:34 +08:00
Redkale
fa8d78e18f 2020-07-13 22:57:21 +08:00
Redkale
4f5e72a31a 2020-07-13 22:56:42 +08:00
Redkale
7c5822fb7c 2020-07-12 17:56:58 +08:00
Redkale
60df140bfd 2020-07-11 20:22:44 +08:00
Redkale
f41e905842 RetResult增加CompletableFuture相关方法 2020-07-11 11:02:18 +08:00
Redkale
9824fe6da3 2020-07-11 10:45:12 +08:00
Redkale
1fb37f4b67 2020-07-10 17:50:48 +08:00
Redkale
a8a6861a21 2020-07-09 19:50:06 +08:00
Redkale
614222bdc3 2020-07-09 16:58:05 +08:00
Redkale
8e98dda795 2020-07-09 00:22:19 +08:00
Redkale
3686306505 2020-07-08 23:18:28 +08:00
Redkale
d29aa78022 2020-07-04 23:52:27 +08:00
Redkale
85fa406e1d 2020-07-03 09:21:49 +08:00
Redkale
d501e2016f 2020-07-01 18:08:39 +08:00
Redkale
cf0cd66ac9 2020-07-01 15:02:26 +08:00
Redkale
c921f657b1 2020-06-30 20:33:07 +08:00
Redkale
c93c1d84c5 2020-06-30 19:30:53 +08:00
Redkale
2e66b1a546 2020-06-29 20:12:37 +08:00
Redkale
48629146ed 2020-06-28 23:18:59 +08:00
Redkale
a6c8502416 2020-06-28 23:16:53 +08:00
Redkale
fc74dc33c1 2020-06-28 22:49:14 +08:00
Redkale
73de9dfc33 2020-06-28 22:22:53 +08:00
Redkale
68d78a4aa4 2020-06-28 19:28:56 +08:00
Redkale
b618e0e884 2020-06-28 09:45:53 +08:00
Redkale
2d1a6e0edc 2020-06-28 07:23:07 +08:00
Redkale
76a7e92787 2020-06-26 22:06:22 +08:00
Redkale
a2178b9a5f 2020-06-26 00:45:29 +08:00
Redkale
dcf0fecdb0 2020-06-26 00:38:24 +08:00
Redkale
f88db8abf9 2020-06-25 23:33:57 +08:00
Redkale
0c8b0f5e19 2020-06-25 20:55:17 +08:00
Redkale
b794872147 2020-06-25 17:34:04 +08:00
Redkale
86614f035b 2020-06-25 16:14:57 +08:00
Redkale
5f3a472c5e 2020-06-24 23:08:17 +08:00
Redkale
7eb2a405d3 2020-06-24 18:04:42 +08:00
Redkale
1d030183bb 2020-06-24 16:34:58 +08:00
Redkale
d211692306 2020-06-24 10:43:46 +08:00
Redkale
86c7d95c80 2020-06-24 10:05:02 +08:00
Redkale
e99d43b25c 2020-06-24 09:25:23 +08:00
Redkale
730fc0a911 2020-06-23 10:28:25 +08:00
Redkale
1ea92d7165 2020-06-23 10:13:12 +08:00
Redkale
0dfc0ca853 2020-06-22 11:07:26 +08:00
Redkale
474f82b3cf 2020-06-21 09:41:14 +08:00
Redkale
ae80109c77 2020-06-21 09:15:17 +08:00
Redkale
807fe63cbe HttpSimpleRequest 增加 rpc 功能 2020-06-21 08:57:59 +08:00
Redkale
44ed3259f9 2020-06-19 13:56:46 +08:00
Redkale
93b2b5fab8 2020-06-19 00:46:05 +08:00
Redkale
58e807439c 2020-06-19 00:20:57 +08:00
Redkale
ca2f34bfac 2020-06-19 00:18:38 +08:00
Redkale
b8719d7f76 去掉Service.stop 增加 @Command 功能 2020-06-18 15:35:40 +08:00
Redkale
be0fdd9045 2020-06-18 15:00:14 +08:00
Redkale
1d2b583c0c 2020-06-17 22:50:04 +08:00
Redkale
9def35e2e1 2020-06-17 22:49:27 +08:00
Redkale
a11127ea58 2020-06-17 13:39:08 +08:00
Redkale
af80864e08 2020-06-16 21:03:22 +08:00
Redkale
f6a4fbdf87 2020-06-16 20:57:09 +08:00
Redkale
61f48be2a6 2020-06-16 19:58:08 +08:00
Redkale
8516925537 Service 增加 stop 方法 2020-06-16 17:48:19 +08:00
Redkale
f828ec3d08 2020-06-16 15:33:49 +08:00
Redkale
1eb1706166 2020-06-16 11:12:51 +08:00
Redkale
ffd3b6daf6 2020-06-15 23:35:26 +08:00
Redkale
76d538264a 2020-06-15 23:16:17 +08:00
Redkale
02d0a06195 2020-06-15 20:57:59 +08:00
Redkale
c43325e402 2020-06-15 18:01:49 +08:00
Redkale
63bc4b2d00 CacheSource 增加get多个keys值得系列方法 2020-06-14 21:52:08 +08:00
Redkale
6e5da92263 2020-06-14 14:25:13 +08:00
Redkale
6358719255 2020-06-14 12:04:59 +08:00
Redkale
bdfd92299a 2020-06-14 12:00:03 +08:00
Redkale
aa4c9896ba 2020-06-14 01:21:26 +08:00
Redkale
1549f1feed 2020-06-14 01:13:08 +08:00
Redkale
2a9a713f3e 2020-06-14 01:12:26 +08:00
Redkale
0377dac71f 2020-06-14 01:01:38 +08:00
Redkale
13217a11c0 2020-06-13 09:38:25 +08:00
Redkale
146b81ee5e 2020-06-12 19:21:41 +08:00
Redkale
8502792aad 2020-06-12 18:10:00 +08:00
Redkale
3d9447327a 2020-06-12 17:20:36 +08:00
Redkale
288fc67621 2020-06-12 14:08:04 +08:00
Redkale
d4bc751a20 2020-06-12 09:35:41 +08:00
Redkale
4cf160e5d3 2020-06-11 23:30:43 +08:00
Redkale
98f29d6a6e 2020-06-11 19:24:16 +08:00
Redkale
92bb0a561b 2020-06-11 17:27:25 +08:00
Redkale
1329f6f0e1 2020-06-11 17:11:31 +08:00
Redkale
a3dbfaba88 2020-06-11 16:03:39 +08:00
Redkale
2032f39bf9 2020-06-11 14:51:22 +08:00
Redkale
8840415739 MessageAgent增加timeoutExecutor 2020-06-11 14:25:50 +08:00
Redkale
bdf2fd21c3 增加 @MessageMultiConsumer 功能 2020-06-11 14:06:25 +08:00
Redkale
bd1e326404 Rest配置中开启MQ时,path字段失效 2020-06-11 10:47:14 +08:00
Redkale
ebd31bc3d3 2020-06-11 10:16:48 +08:00
Redkale
7185191d91 2020-06-10 23:06:20 +08:00
Redkale
1a1dd44f34 2020-06-10 22:07:20 +08:00
Redkale
95fd147268 2020-06-10 20:37:24 +08:00
Redkale
88657fdf63 2020-06-10 20:30:50 +08:00
Redkale
03de684940 2020-06-10 17:25:52 +08:00
Redkale
b445b99572 2020-06-10 10:34:38 +08:00
Redkale
bd4343c5d8 2020-06-09 21:31:08 +08:00
Redkale
a58c13cd9f 2020-06-09 21:22:17 +08:00
Redkale
cf23ecc12c 2020-06-09 17:46:50 +08:00
Redkale
86754c2324 2020-06-09 13:10:16 +08:00
Redkale
e6f0a8fdf3 增加 @RestHeaders 功能 2020-06-09 09:53:32 +08:00
Redkale
fa9fc531d0 2020-06-08 22:51:14 +08:00
Redkale
30bf2c1ad3 2020-06-08 22:43:46 +08:00
Redkale
d326da9344 2020-06-08 22:27:30 +08:00
Redkale
8f6494163d 2020-06-08 21:57:55 +08:00
Redkale
490cabefb2 增加 <excludelibs/> 配置项 2020-06-08 21:45:19 +08:00
Redkale
66ff86d6fb 2020-06-08 20:13:26 +08:00
Redkale
0e5479d55e 增加 @RpcTargetTopic 2020-06-08 19:31:29 +08:00
Redkale
3eb3e0104d 2020-06-08 13:42:19 +08:00
Redkale
079e6116b2 2020-06-08 09:13:41 +08:00
Redkale
b6a3adb21c 2020-06-07 22:11:49 +08:00
Redkale
ecf831c0f5 2020-06-07 15:56:55 +08:00
Redkale
adfa0be79e 2020-06-07 11:27:40 +08:00
Redkale
7ec91802b2 2020-06-06 23:51:56 +08:00
Redkale
1e15eb31c7 2020-06-06 23:37:59 +08:00
Redkale
8d5e61a9a2 2020-06-06 22:04:18 +08:00
Redkale
64500b113a 2020-06-06 18:50:18 +08:00
Redkale
a7e5fad571 2020-06-05 18:44:51 +08:00
Redkale
531b00b6fd 2020-06-05 18:01:25 +08:00
Redkale
ce3fc5792b 2020-06-05 17:52:09 +08:00
Redkale
a477b2fb73 2020-06-05 13:50:58 +08:00
Redkale
6a5d121615 2020-06-05 12:01:32 +08:00
Redkale
90960e0574 2020-06-05 11:56:04 +08:00
Redkale
b6980f7cf8 2020-06-05 11:29:54 +08:00
Redkale
2fb3472de0 2020-06-05 10:06:44 +08:00
Redkale
6dacbafc29 2020-06-05 07:50:10 +08:00
Redkale
f1fcbd7396 2020-06-04 22:22:35 +08:00
Redkale
681e389f19 2020-06-04 21:32:18 +08:00
Redkale
06b7274364 2020-06-04 20:56:45 +08:00
Redkale
b4f7de4858 2020-06-04 20:14:16 +08:00
Redkale
6261232ce6 2020-06-04 19:53:41 +08:00
Redkale
eb35b90950 2020-06-04 18:03:02 +08:00
Redkale
958cea4e0d 2020-06-04 17:56:13 +08:00
Redkale
7f184bae50 2020-06-04 17:25:42 +08:00
Redkale
3d68103a19 2020-06-04 17:09:30 +08:00
Redkale
612851bce0 2020-06-04 16:21:28 +08:00
Redkale
e365dae9e4 2020-06-04 14:06:35 +08:00
Redkale
b6d5fc02dc 2020-06-04 10:11:14 +08:00
Redkale
59d139ace2 2020-06-04 09:44:00 +08:00
Redkale
1d7a72f992 2020-06-04 09:39:38 +08:00
Redkale
45f2ce261e 2020-06-04 09:29:04 +08:00
Redkale
f79e49db5a 2020-06-03 23:21:57 +08:00
Redkale
218aebaa0f 2020-06-03 22:34:10 +08:00
Redkale
f11870d6bc 2020-06-03 20:06:48 +08:00
Redkale
ebea6bb92c 2020-06-03 19:58:13 +08:00
Redkale
4806d6ada0 2020-06-03 19:46:25 +08:00
Redkale
6909748bda 2020-06-03 19:26:38 +08:00
Redkale
982fc8440c 2020-06-03 17:53:55 +08:00
Redkale
e4df07a00c 2020-06-03 17:02:23 +08:00
Redkale
0ee30aca7e 2020-06-03 16:45:28 +08:00
Redkale
b994bcbcfb 2020-06-03 16:23:16 +08:00
Redkale
cc23b44409 2020-06-03 15:48:40 +08:00
Redkale
de36ef697d 2020-06-03 15:23:45 +08:00
Redkale
e0d224a330 2020-06-03 14:58:44 +08:00
Redkale
adc2106bec 2020-06-03 14:15:42 +08:00
Redkale
6cdbe957a7 2020-06-03 14:03:46 +08:00
Redkale
14808cb01c 2020-06-03 13:55:08 +08:00
Redkale
1edadbfee8 2020-06-03 12:03:04 +08:00
Redkale
043a23ecdf 2020-06-03 09:29:54 +08:00
Redkale
383ef37989 2020-06-02 23:18:36 +08:00
Redkale
9cd9a8d3ea 2020-06-02 09:56:01 +08:00
Redkale
db8c94f433 2020-06-01 21:19:47 +08:00
Redkale
22fb3e5bef 2020-06-01 13:44:34 +08:00
Redkale
6329722f17 2020-06-01 11:04:03 +08:00
Redkale
2dee61223d 2020-06-01 10:04:20 +08:00
Redkale
f83fc52e9c 2020-05-31 11:59:11 +08:00
Redkale
2f8a04c15d 2020-05-31 08:42:51 +08:00
Redkale
a1501af7a7 2020-05-30 19:55:07 +08:00
Redkale
a1fdfc9cc9 2020-05-30 17:59:28 +08:00
Redkale
2dddf3c2a5 2020-05-30 17:53:59 +08:00
Redkale
84061cf60f 2020-05-30 17:42:28 +08:00
Redkale
557c2c7858 2020-05-30 16:40:33 +08:00
Redkale
cb2d355bc9 2020-05-30 15:57:56 +08:00
Redkale
ed1b642d5b 2020-05-30 14:50:07 +08:00
Redkale
411f5e1951 2020-05-30 11:55:34 +08:00
Redkale
450506ca96 2020-05-30 11:32:16 +08:00
Redkale
bf700aa88f 2020-05-30 10:23:36 +08:00
Redkale
c108ab196c 2020-05-30 10:18:00 +08:00
Redkale
cac8662c01 2020-05-30 09:31:38 +08:00
Redkale
197c58ef98 2020-05-30 09:24:40 +08:00
Redkale
d4c2723759 删除MessageStreams 2020-05-29 23:18:59 +08:00
Redkale
c34a6d8f49 2020-05-29 22:34:53 +08:00
Redkale
5de580c00b 2020-05-29 21:04:00 +08:00
Redkale
ed1fb151d7 2020-05-29 16:06:02 +08:00
Redkale
4c4913c5d0 2020-05-29 13:43:47 +08:00
Redkale
adb0dc5963 2020-05-29 11:15:56 +08:00
Redkale
740bc8ae31 增加@RestUserid 废弃 HttpRequest.currentUser() 方法, 建议 HttpRequest.currentUserid() 2020-05-28 20:20:08 +08:00
Redkale
30a0b12020 2020-05-28 19:11:28 +08:00
Redkale
b0b8b0db3b 2020-05-28 11:40:03 +08:00
Redkale
71244732f2 2020-05-28 11:22:30 +08:00
Redkale
820af60c19 2020-05-28 10:03:13 +08:00
Redkale
b3252359bd 2020-05-28 09:55:05 +08:00
Redkale
9ade568597 2020-05-28 09:51:57 +08:00
Redkale
cf5224a5f6 2020-05-28 09:38:18 +08:00
Redkale
6c4a83d14d 2020-05-27 20:32:57 +08:00
Redkale
0439b95139 2020-05-27 19:18:48 +08:00
Redkale
72a618a0b7 2020-05-27 18:02:51 +08:00
Redkale
0f7520e67b 2020-05-27 17:10:23 +08:00
Redkale
fcd69474a2 2020-05-27 15:22:41 +08:00
Redkale
3130e00bab 2020-05-27 15:08:12 +08:00
Redkale
1c063b57ec 2020-05-27 15:01:36 +08:00
Redkale
efeff3b720 2020-05-27 14:21:01 +08:00
Redkale
ea15634f47 2020-05-27 12:00:32 +08:00
Redkale
e744d001c6 2020-05-27 11:49:44 +08:00
Redkale
06e486e5e2 2020-05-27 11:32:29 +08:00
Redkale
95180ffdef 2020-05-27 11:31:29 +08:00
Redkale
e5bd85e9aa 2020-05-27 11:23:07 +08:00
Redkale
ec4ec2ff77 2020-05-26 22:22:44 +08:00
Redkale
5a1c9215bc 2020-05-26 19:11:12 +08:00
Redkale
53457b73ad 加入MQ功能 2020-05-26 17:59:00 +08:00
Redkale
8cb9fcf01a 2020-05-22 19:45:57 +08:00
Redkale
2c88b0e75e 2020-05-22 16:49:45 +08:00
Redkale
4daffdc31f 2020-05-21 20:23:56 +08:00
Redkale
16dec2cde5 2020-05-17 23:37:22 +08:00
Redkale
82e5fb593e 2020-05-17 22:25:47 +08:00
Redkale
4a60ecb3ff 2020-05-17 22:11:34 +08:00
Redkale
38307405ed 2020-05-17 20:34:36 +08:00
Redkale
fe332d0cbd 2020-05-17 19:08:43 +08:00
Redkale
e45f75c1d6 2020-05-17 16:39:51 +08:00
Redkale
a77f450757 2020-05-17 15:49:32 +08:00
Redkale
9f8e946ea3 2020-05-16 22:40:41 +08:00
Redkale
7633687665 2020-05-16 22:24:37 +08:00
Redkale
a1e37643d0 2020-05-16 22:12:45 +08:00
Redkale
8440b58d6c 2020-05-16 20:34:03 +08:00
Redkale
a97f8efe21 2020-05-16 20:10:21 +08:00
Redkale
d83e7c22ac 2020-05-16 19:59:49 +08:00
Redkale
01a5b32b22 ClusterAgent 2020-05-16 17:51:30 +08:00
Redkale
8834f57582 2020-05-16 17:22:52 +08:00
Redkale
57fd7b96b4 $cluster 2020-05-16 17:03:55 +08:00
Redkale
14fe2fbc84 2020-05-16 14:46:11 +08:00
Redkale
0b86edb654 2020-05-16 14:27:46 +08:00
Redkale
07ab74902c 2020-05-16 14:27:08 +08:00
Redkale
e4c6e860c1 DataCallAttribute移至RpcCallAttribute 2020-05-16 09:12:06 +08:00
Redkale
02cf587fcf 2020-05-16 09:09:43 +08:00
Redkale
081773163b 2020-05-16 09:03:18 +08:00
Redkale
292ff63699 移除RpcMultiRun功能 2020-05-15 22:02:54 +08:00
Redkale
f1a97c0219 移除DataCacheListener功能 2020-05-15 21:53:21 +08:00
Redkale
e545010034 2020-05-15 17:36:19 +08:00
Redkale
ef57fa9a25 2020-05-15 15:01:18 +08:00
Redkale
1bd0d891a4 2020-05-15 14:39:29 +08:00
Redkale
9a51c1de1d Application加入nodeid属性 2020-05-15 14:38:53 +08:00
Redkale
e4bb75dd8e 2020-05-15 14:09:49 +08:00
Redkale
66e1e08ae2 2020-05-05 19:54:13 +08:00
Redkale
708cd5a644 修复DeMember/EnMember中attribute.type值不能精准显示泛型的bug 2020-05-04 22:12:25 +08:00
Redkale
138c9aba97 2020-05-04 20:59:28 +08:00
Redkale
e5e17b4496 2020-05-04 20:52:00 +08:00
Redkale
cadaebefb9 2020-05-04 20:21:49 +08:00
Redkale
e2fb7935f6 2020-05-04 20:02:18 +08:00
Redkale
16b3dab600 2020-05-04 19:51:19 +08:00
Redkale
758972890e 2020-04-17 11:08:30 +08:00
Redkale
a88285d935 回滚Mask convertFrom 方法 2020-04-16 22:08:14 +08:00
Redkale
d7832fb931 Convert增加convertFrom(final Type type, final ConvertMask mask, final byte[] bytes) 方法 2020-04-16 21:42:52 +08:00
Redkale
f073c9a0bc 2020-04-16 21:08:00 +08:00
Redkale
63ef83ec62 WebSocket支持permessage-deflate单向功能 2020-04-16 20:19:49 +08:00
Redkale
51c50415e1 Redkale 2.0.0 正式版 结束 2020-04-08 13:24:51 +08:00
Redkale
2f08fa6476 2020-04-03 15:10:57 +08:00
Redkale
3091972fc5 2020-04-02 17:28:58 +08:00
Redkale
8e14df9a95 2020-04-01 13:33:43 +08:00
Redkale
683cec6f4d 2020-04-01 13:33:04 +08:00
Redkale
ec6e5aa3b1 2020-02-24 07:49:34 +08:00
Redkale
f4c00a9b6f 2020-02-12 11:43:13 +08:00
Redkale
c071ec5d6c 2020-02-09 12:37:40 +08:00
Redkale
29148c4b42 TypeToken.getGenericType加强 2020-02-09 12:37:00 +08:00
Redkale
7340caa4a9 2020-02-09 11:35:07 +08:00
Redkale
7d23dfa73d 2020-02-09 11:34:04 +08:00
Redkale
19932820a9 2020-02-06 16:28:48 +08:00
150 changed files with 7964 additions and 2091 deletions

7
bin/command.bat Normal file
View File

@@ -0,0 +1,7 @@
@ECHO OFF
SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
java -DCMD=%1 -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application

23
bin/command.sh Normal file
View File

@@ -0,0 +1,23 @@
#!/bin/sh
export LC_ALL="zh_CN.UTF-8"
APP_HOME=`dirname "$0"`
cd "$APP_HOME"/..
APP_HOME=`pwd`
if [ ! -f "$APP_HOME"/conf/application.xml ]; then
APP_HOME="$APP_HOME"/..
fi
lib="$APP_HOME"/lib
for jar in `ls $APP_HOME/lib/*.jar`
do
lib=$lib:$jar
done
export CLASSPATH=$CLASSPATH:$lib
echo "$APP_HOME"
java -DCMD=$1 -DAPP_HOME="$APP_HOME" org.redkale.boot.Application

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<application port="2121">
<application nodeid="10" port="2121">
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->

View File

@@ -61,7 +61,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<version>3.8.0</version>
<configuration>
<compilerArgument>-parameters</compilerArgument>
<encoding>UTF-8</encoding>
@@ -74,7 +74,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<version>3.2.0</version>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
@@ -102,7 +102,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<version>3.2.0</version>
<executions>
<execution>
<goals>
@@ -114,7 +114,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
<version>3.2.0</version>
<executions>
<execution>
<goals>
@@ -127,7 +127,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<version>3.2.0</version>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>

View File

@@ -19,11 +19,13 @@
serviceid1_name1 serviceid1_name2 serviceid2_name1 serviceid2_name2
-->
<!--
address: 本地局域网的IP地址 默认值为默认网卡的ip当不使用默认值需要指定值如192.168.1.22
nodeid: int 进程的节点ID用于分布式环境一个系统中节点ID必须全局唯一使用cluster时框架会进行唯一性校验
name: 进程的名称,用于监控识别,命名规则: 字母、数字、下划线
address: 本地局域网的IP地址 默认值为默认网卡的ip当不使用默认值需要指定值如192.168.1.22
port: required 程序的管理Server的端口用于关闭或者与监管系统进行数据交互
lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar;
lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar;
-->
<application port="6560" lib="">
<application nodeid="1000" port="6560" lib="">
<!--
【节点全局唯一】
@@ -34,21 +36,61 @@
<!--
【节点全局唯一】
transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。
threads 线程总数, 默认: <group>节点数*CPU核数*2
bufferCapacity: ByteBuffer的初始化大小 默认: 32K;
bufferPoolSize ByteBuffer池的大小默认: 线程总数*4
readTimeoutSeconds: TCP读取超时秒数, 默认为6秒 为0表示无超时限制
threads 线程总数, 默认: <group>节点数*CPU核数*2
bufferCapacity: ByteBuffer的初始化大小 默认: 32K;
bufferPoolSize ByteBuffer池的大小默认: 线程总数*4
readTimeoutSeconds: TCP读取超时秒数, 默认为6秒 为0表示无超时限制
writeTimeoutSeconds: TCP写入超时秒数, 默认为6秒 为0表示无超时限制
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
-->
<transport bufferCapacity="32K" bufferPoolSize="32" threads="32" readTimeoutSeconds="6" writeTimeoutSeconds="6"/>
<!--
【节点全局唯一】
自动扫描时排除部分包路径
value 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
-->
<excludelibs value="^.*mysql.*$;^.*kafka.*$"/>
<!--
【节点全局唯一】
第三方服务发现管理接口
value 类名必须是org.redkale.cluster.ClusterAgent的子类
waits: 注销服务后是否需要等待检查周期时间后再进行Service销毁默认值为false
当一个Service进行服务注销后不能立刻销毁Service因为健康检测是有间隔时间差的
需要等待一个健康检测周期时间,让其他进程都更新完服务列表。
如果使用MQ可以设置为false如果对服务健壮性要求高建议设置为true
protocols: 服务发现可以处理的协议, 默认值为: SNCP, 多个协议用分号;隔开
ports: 服务发现可以处理的端口, 多个端口用分号;隔开
-->
<!--
<cluster value="org.redkalex.cluster.consul.ConsulClusterAgent" waits="false" protocols="SNCP" ports="7070;7071">
<property name="xxxxxx" value="XXXXXXXX"/>
</cluster>
-->
<!--
MQ管理接口配置
不同MQ节点所配置的MQ集群不能重复。
name: 服务的名称用于监控识别多个mq节点时只能有一个name为空的节点mq.name不能重复,命名规则: 字母、数字、下划线
value 实现类名必须是org.redkale.mq.MessageAgent的子类
MQ节点下的子节点配置没有固定格式, 根据MessageAgent实现方的定义来配置
-->
<!--
<mq name="" value="org.redkalex.mq.kafka.KafkaMessageAgent">
<servers value="127.0.0.1:9101"/>
<consumer>
<property name="xxxxxx" value="XXXXXXXX"/>
</consumer>
<producer>
<property name="xxxxxx" value="XXXXXXXX"/>
</producer>
</mq>
-->
<!--
一个组包含多个node 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内
一个group节点对应一个 Transport 对象。
name: 服务组ID长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
protocol值范围UDP TCP 默认TCP
subprotocol: 子协议,预留字段。默认值为空
name: 服务组ID长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
protocol 值范围UDP TCP 默认TCP
注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息 就必须有group节点信息。
-->
<group name="" protocol="TCP">
@@ -63,10 +105,10 @@
<!--
全局的数据源设置, 可以是CacheSource、DataSource JDBC的DataSource通常通过persistence.xml配置此处多用于CacheSource的配置
name: 资源名,用于依赖注入。
value类名必须是CacheSource或DataSource的子类且必须实现Service接口。如果是DataSource.class系统自动映射成DataJdbcSource.class
groups: 指定groups。
xxx: 其他属性与子节点通过Service.init方法传入的AnyValue获取。
name: 资源名,用于依赖注入。
value 类名必须是CacheSource或DataSource的子类且必须实现Service接口。如果是DataSource.class系统自动映射成DataJdbcSource.class
groups: 指定groups。
xxx: 其他属性与子节点通过Service.init方法传入的AnyValue获取。
-->
<source name="redis" value="org.redkalex.cache.RedisCacheSource" xxx="16">
<node addr="127.0.0.1" port="7070"/>
@@ -107,35 +149,35 @@
</resources>
<!--
protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCH。协议均使用TCP实现; WATCH服务只能存在一个。
name: 服务的名称用于监控识别一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线
host: 服务所占address 默认: 0.0.0.0
port: required 服务所占端口
root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root
lib: server额外的class目录 默认为${APP_HOME}/libs/*;
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8
backlog: 默认10K
threads 线程数, 默认: CPU核数*2最小8个
maxconns最大连接数, 小于1表示无限制 默认: 0
maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 TCP默认: 32K; (HTTP 2.0、WebSocket必须要16k以上); UDP默认: 1350B
bufferPoolSize ByteBuffer池的大小默认: 线程数*4
responsePoolSize Response池的大小默认: 线程数*2
protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCH。协议均使用TCP实现; WATCH服务只能存在一个。
name: 服务的名称用于监控识别一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线
host: 服务所占address 默认: 0.0.0.0
port: required 服务所占端口
root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root
lib: server额外的class目录 默认为${APP_HOME}/libs/*;
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8
backlog: 默认10K
threads 线程数, 默认: CPU核数*2最小8个
maxconns 最大连接数, 小于1表示无限制 默认: 0
maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 TCP默认: 32K; (HTTP 2.0、WebSocket必须要16k以上); UDP默认: 1350B
bufferPoolSize ByteBuffer池的大小默认: 线程数*4
responsePoolSize Response池的大小默认: 线程数*2
aliveTimeoutSeconds: KeepAlive读操作超时秒数 默认30 0表示永久不超时; -1表示禁止KeepAlive
readTimeoutSeconds: 读操作超时秒数, 默认0 表示永久不超时
writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时
netimpl: ProtocolServer的实现类。TCP情况下值可以是aio或nio默认值为aioUDP情况下值可以是bio默认值为bio
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null
readTimeoutSeconds: 读操作超时秒数, 默认0 表示永久不超时
writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时
netimpl: ProtocolServer的实现类。TCP情况下值可以是aio或nio默认值为aioUDP情况下值可以是bio默认值为bio
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null
-->
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
<!--
【节点在<server>中唯一】
value: 创建SSLContext的实现类, 可自定义必须是org.redkale.net.SSLCreator的子类
clientauth: true/false/want
keystorepass: KEY密码
keystorefile: KEY文件
clientauth: true/false/want
keystorepass: KEY密码
keystorefile: KEY文件
truststorepass: TRUST密码
truststorefile: TRUST文件
-->
@@ -146,11 +188,13 @@
在同一个进程中同一个name同一类型的Service将共用同一个实例
autoload="true" 默认值. 自动加载classpath下所有的Service类
autoload="false" 需要显著的指定Service类
mq: 所属的MQ管理器当 protocol == SNCP 时该值才有效, 存在该属性表示Service的SNCP协议采用消息总线代理模式
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
groups: 所属组的节点,多个节点值用;隔开如果配置文件中存在多个SNCP协议的Server节点需要显式指定group属性.
当 protocol == SNCP 时 group表示当前Server与哪些节点组关联。
当 protocol != SNCP 时 group只能是空或者一个group的节点值不能为多个节点值。
groups: 所属组的节点,多个节点值用;隔开如果配置文件中存在多个SNCP协议的Server节点需要显式指定group属性.
当 protocol == SNCP 时 group表示当前Server与哪些节点组关联。
当 protocol != SNCP 时 group只能是空或者一个group的节点值不能为多个节点值。
特殊值"$cluster", 视为通过第三方服务注册发现管理工具来获取远程模式的ip端口信息
-->
<services autoload="true" includes="" excludes="">
@@ -174,8 +218,8 @@
加载所有的Filter服务;
autoload="true" 默认值.
autoload="false" 需要显著的指定Filter类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
-->
<filters autoload="true" includes="" excludes="">
@@ -198,8 +242,9 @@
<!--
REST的核心配置项
当Server为HTTP协议时, rest节点才有效。存在[rest]节点则Server启动时会加载REST服务, 节点可以多个,(WATCH协议不需要设置系统会自动生成)
path: servlet的ContextPath前缀 默认为空
path: servlet的ContextPath前缀 默认为空 【注: 开启MQ时,该字段失效】
base: REST服务的BaseServlet必须是 org.redkale.net.http.HttpServlet 的子类,且子类必须标记@HttpUserType。
mq: 所属的MQ管理器, 存在该属性表示RestService的请求来自于消息总线 【注: 开启MQ时,path字段失效】
autoload默认值"true" 默认值. 加载当前server所能使用的Servce对象;
includes当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
@@ -263,21 +308,21 @@
当Server为HTTP协议时ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点
webroot: web资源的根目录, 默认取server节点中的root值
servlet: 静态资源HttpServlet的实现默认使用HttpResourceServlet
index : 启始页默认值index.html
index : 启始页默认值index.html
-->
<resource-servlet webroot="root" index="index.html">
<!--
【节点在<resource-servlet>中唯一】
资源缓存的配置, 默认存在一个含默认属性的caches节点
limit: 资源缓存最大容量, 默认: 0, 为0表示不缓存 单位可以是B、K、M、G不区分大小写
limit: 资源缓存最大容量, 默认: 0, 为0表示不缓存 单位可以是B、K、M、G不区分大小写
lengthmax: 可缓存的文件大小上限, 默认: 1M超过1M的文件不会被缓存
watch: 是否监控缓存文件的变化, 默认为false不监控
watch: 是否监控缓存文件的变化, 默认为false不监控
-->
<cache limit="0M" lengthmax="1M" watch="false"/>
<!--
支持类似nginx中的rewrite 目前只支持静态资源对静态资源的跳转。
type: 匹配的类型, 目前只支持location(匹配requestURI), 默认: location
match: 匹配的正则表达式
type: 匹配的类型, 目前只支持location(匹配requestURI), 默认: location
match: 匹配的正则表达式
forward: 需跳转后的资源链接
例如下面例子是将/xxx-yyy.html的页面全部跳转到/xxx.html
-->
@@ -285,11 +330,11 @@
</resource-servlet>
<!--
加载所有的Servlet服务;
path: servlet的ContextPath前缀 默认为空
path: servlet的ContextPath前缀 默认为空
autoload="true" 默认值. 自动加载classpath下所有的Servlet类
autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
-->
<servlets path="/pipes" autoload="true" includes="" excludes="">
<!--

View File

@@ -22,6 +22,7 @@ module org.redkale {
exports org.redkale.convert.bson;
exports org.redkale.convert.ext;
exports org.redkale.convert.json;
exports org.redkale.mq;
exports org.redkale.net;
exports org.redkale.net.http;
exports org.redkale.net.sncp;
@@ -30,6 +31,9 @@ module org.redkale {
exports org.redkale.util;
exports org.redkale.watch;
uses org.redkale.cluster.ClusterAgent;
uses org.redkale.mq.MessageAgent;
uses org.redkale.source.CacheSource;
uses org.redkale.source.SourceLoader;
uses org.redkale.util.ResourceInjectLoader;
}

View File

@@ -60,8 +60,8 @@ package org.redkale.asm;
/**
* A visitor to visit a Java annotation. The methods of this class must be
* called in the following order: ( <tt>visit</tt> | <tt>visitEnum</tt> |
* <tt>visitAnnotation</tt> | <tt>visitArray</tt> )* <tt>visitEnd</tt>.
* called in the following order: ( <code>visit</code> | <code>visitEnum</code> |
* <code>visitAnnotation</code> | <code>visitArray</code> )* <code>visitEnd</code>.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
@@ -151,7 +151,7 @@ public abstract class AnnotationVisitor {
* @param desc
* the class descriptor of the nested annotation class.
* @return a visitor to visit the actual nested annotation value, or
* <tt>null</tt> if this visitor is not interested in visiting this
* <code>null</code> if this visitor is not interested in visiting this
* nested annotation. <i>The nested annotation value must be fully
* visited before calling other methods on this annotation
* visitor</i>.
@@ -172,7 +172,7 @@ public abstract class AnnotationVisitor {
* @param name
* the value name.
* @return a visitor to visit the actual array value elements, or
* <tt>null</tt> if this visitor is not interested in visiting these
* <code>null</code> if this visitor is not interested in visiting these
* values. The 'name' parameters passed to the methods of this
* visitor are ignored. <i>All the array values must be visited
* before calling other methods on this annotation visitor</i>.

View File

@@ -77,7 +77,7 @@ final class AnnotationWriter extends AnnotationVisitor {
private int size;
/**
* <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation
* <code>true<code> if values are named, <code>false</code> otherwise. Annotation
* writers used for annotation default and annotation arrays use unnamed
* values.
*/
@@ -122,13 +122,13 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param cw
* the class writer to which this annotation must be added.
* @param named
* <tt>true<tt> if values are named, <tt>false</tt> otherwise.
* <code>true<code> if values are named, <code>false</code> otherwise.
* @param bv
* where the annotation values must be stored.
* @param parent
* where the number of annotation values must be stored.
* @param offset
* where in <tt>parent</tt> the number of annotation values must
* where in <code>parent</code> the number of annotation values must
* be stored.
*/
AnnotationWriter(final ClassWriter cw, final boolean named,
@@ -354,7 +354,7 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param out
* where the type reference and type path must be put.
*/

View File

@@ -77,7 +77,7 @@ class Attribute {
byte[] value;
/**
* The next attribute in this attribute list. May be <tt>null</tt>.
* The next attribute in this attribute list. May be <code>null</code>.
*/
Attribute next;
@@ -92,19 +92,19 @@ class Attribute {
}
/**
* Returns <tt>true</tt> if this type of attribute is unknown. The default
* implementation of this method always returns <tt>true</tt>.
* Returns <code>true</code> if this type of attribute is unknown. The default
* implementation of this method always returns <code>true</code>.
*
* @return <tt>true</tt> if this type of attribute is unknown.
* @return <code>true</code> if this type of attribute is unknown.
*/
public boolean isUnknown() {
return true;
}
/**
* Returns <tt>true</tt> if this type of attribute is a code attribute.
* Returns <code>true</code> if this type of attribute is a code attribute.
*
* @return <tt>true</tt> if this type of attribute is a code attribute.
* @return <code>true</code> if this type of attribute is a code attribute.
*/
public boolean isCodeAttribute() {
return false;
@@ -113,7 +113,7 @@ class Attribute {
/**
* Returns the labels corresponding to this attribute.
*
* @return the labels corresponding to this attribute, or <tt>null</tt> if
* @return the labels corresponding to this attribute, or <code>null</code> if
* this attribute is not a code attribute that contains labels.
*/
protected Label[] getLabels() {
@@ -123,7 +123,7 @@ class Attribute {
/**
* Reads a {@link #type type} attribute. This method must return a
* <i>new</i> {@link Attribute} object, of type {@link #type type},
* corresponding to the <tt>len</tt> bytes starting at the given offset, in
* corresponding to the <code>len</code> bytes starting at the given offset, in
* the given class reader.
*
* @param cr
@@ -146,7 +146,7 @@ class Attribute {
* containing the type and the length of the attribute, are not
* taken into account here.
* @param labels
* the labels of the method's code, or <tt>null</tt> if the
* the labels of the method's code, or <code>null</code> if the
* attribute to be read is not a code attribute.
* @return a <i>new</i> {@link Attribute} object corresponding to the given
* bytes.
@@ -169,11 +169,11 @@ class Attribute {
* class the items that corresponds to this attribute.
* @param code
* the bytecode of the method corresponding to this code
* attribute, or <tt>null</tt> if this attribute is not a code
* attribute, or <code>null</code> if this attribute is not a code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to this
* code attribute, or <tt>null</tt> if this attribute is not a
* code attribute, or <code>null</code> if this attribute is not a
* code attribute.
* @param maxStack
* the maximum stack size of the method corresponding to this
@@ -216,11 +216,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method.
* @param code
* the bytecode of the method corresponding to these code
* attributes, or <tt>null</tt> if these attributes are not code
* attributes, or <code>null</code> if these attributes are not code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to
* these code attributes, or <tt>null</tt> if these attributes
* these code attributes, or <code>null</code> if these attributes
* are not code attributes.
* @param maxStack
* the maximum stack size of the method corresponding to these
@@ -254,11 +254,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method.
* @param code
* the bytecode of the method corresponding to these code
* attributes, or <tt>null</tt> if these attributes are not code
* attributes, or <code>null</code> if these attributes are not code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to
* these code attributes, or <tt>null</tt> if these attributes
* these code attributes, or <code>null</code> if these attributes
* are not code attributes.
* @param maxStack
* the maximum stack size of the method corresponding to these

View File

@@ -332,7 +332,7 @@ public class ByteVector {
* automatically enlarged if necessary.
*
* @param b
* an array of bytes. May be <tt>null</tt> to put <tt>len</tt>
* an array of bytes. May be <code>null</code> to put <code>len</code>
* null bytes into this byte vector.
* @param off
* index of the fist byte of b that must be copied.

View File

@@ -267,7 +267,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}). For interfaces, the
* super class is {@link Object}.
*
* @return the internal name of super class, or <tt>null</tt> for
* @return the internal name of super class, or <code>null</code> for
* {@link Object} class.
*
* @see ClassVisitor#visit(int, int, String, String, String, String[])
@@ -281,7 +281,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}).
*
* @return the array of internal names for all implemented interfaces or
* <tt>null</tt>.
* <code>null</code>.
*
* @see ClassVisitor#visit(int, int, String, String, String, String[])
*/
@@ -1932,7 +1932,7 @@ public class ClassReader {
* @param v
* start offset in {@link #b b} of the annotations to be read.
* @param visible
* <tt>true</tt> if the annotations to be read are visible at
* <code>true</code> if the annotations to be read are visible at
* runtime.
*/
private void readParameterAnnotations(final MethodVisitor mv,
@@ -2474,9 +2474,9 @@ public class ClassReader {
* and the length of the attribute, are not taken into account
* here.
* @param labels
* the labels of the method's code, or <tt>null</tt> if the
* the labels of the method's code, or <code>null</code> if the
* attribute to be read is not a code attribute.
* @return the attribute that has been read, or <tt>null</tt> to skip this
* @return the attribute that has been read, or <code>null</code> to skip this
* attribute.
*/
private Attribute readAttribute(final Attribute[] attrs, final String type,

View File

@@ -60,11 +60,11 @@ package org.redkale.asm;
/**
* A visitor to visit a Java class. The methods of this class must be called in
* the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
* <tt>visitModule</tt> ][ <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* (
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )*
* <tt>visitEnd</tt>.
* the following order: <code>visit</code> [ <code>visitSource</code> ] [
* <code>visitModule</code> ][ <code>visitOuterClass</code> ] ( <code>visitAnnotation</code> |
* <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* (
* <code>visitInnerClass</code> | <code>visitField</code> | <code>visitMethod</code> )*
* <code>visitEnd</code>.
*
* @author Eric Bruneton
*/
@@ -120,18 +120,18 @@ public abstract class ClassVisitor {
* the internal name of the class (see
* {@link Type#getInternalName() getInternalName}).
* @param signature
* the signature of this class. May be <tt>null</tt> if the class
* the signature of this class. May be <code>null</code> if the class
* is not a generic one, and does not extend or implement generic
* classes or interfaces.
* @param superName
* the internal of name of the super class (see
* {@link Type#getInternalName() getInternalName}). For
* interfaces, the super class is {@link Object}. May be
* <tt>null</tt>, but only for the {@link Object} class.
* <code>null</code>, but only for the {@link Object} class.
* @param interfaces
* the internal names of the class's interfaces (see
* {@link Type#getInternalName() getInternalName}). May be
* <tt>null</tt>.
* <code>null</code>.
*/
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
@@ -145,11 +145,11 @@ public abstract class ClassVisitor {
*
* @param source
* the name of the source file from which the class was compiled.
* May be <tt>null</tt>.
* May be <code>null</code>.
* @param debug
* additional debug information to compute the correspondance
* between source and compiled elements of the class. May be
* <tt>null</tt>.
* <code>null</code>.
*/
public void visitSource(String source, String debug) {
if (cv != null) {
@@ -166,7 +166,7 @@ public abstract class ClassVisitor {
* and {@code ACC_MANDATED}.
* @param version
* module version or null.
* @return a visitor to visit the module values, or <tt>null</tt> if
* @return a visitor to visit the module values, or <code>null</code> if
* this visitor is not interested in visiting this module.
*/
public ModuleVisitor visitModule(String name, int access, String version) {
@@ -187,11 +187,11 @@ public abstract class ClassVisitor {
* internal name of the enclosing class of the class.
* @param name
* the name of the method that contains the class, or
* <tt>null</tt> if the class is not enclosed in a method of its
* <code>null</code> if the class is not enclosed in a method of its
* enclosing class.
* @param desc
* the descriptor of the method that contains the class, or
* <tt>null</tt> if the class is not enclosed in a method of its
* <code>null</code> if the class is not enclosed in a method of its
* enclosing class.
*/
public void visitOuterClass(String owner, String name, String desc) {
@@ -206,8 +206,8 @@ public abstract class ClassVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -231,12 +231,12 @@ public abstract class ClassVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
@@ -269,10 +269,10 @@ public abstract class ClassVisitor {
* @param outerName
* the internal name of the class to which the inner class
* belongs (see {@link Type#getInternalName() getInternalName}).
* May be <tt>null</tt> for not member classes.
* May be <code>null</code> for not member classes.
* @param innerName
* the (simple) name of the inner class inside its enclosing
* class. May be <tt>null</tt> for anonymous inner classes.
* class. May be <code>null</code> for anonymous inner classes.
* @param access
* the access flags of the inner class as originally declared in
* the enclosing class.
@@ -295,20 +295,20 @@ public abstract class ClassVisitor {
* @param desc
* the field's descriptor (see {@link Type Type}).
* @param signature
* the field's signature. May be <tt>null</tt> if the field's
* the field's signature. May be <code>null</code> if the field's
* type does not use generic types.
* @param value
* the field's initial value. This parameter, which may be
* <tt>null</tt> if the field does not have an initial value,
* <code>null</code> if the field does not have an initial value,
* must be an {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double} or a {@link String} (for <tt>int</tt>,
* <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
* {@link Double} or a {@link String} (for <code>int</code>,
* <code>float</code>, <code>long</code> or <code>String</code> fields
* respectively). <i>This parameter is only used for static
* fields</i>. Its value is ignored for non static fields, which
* must be initialized through bytecode instructions in
* constructors or methods.
* @return a visitor to visit field annotations and attributes, or
* <tt>null</tt> if this class visitor is not interested in visiting
* <code>null</code> if this class visitor is not interested in visiting
* these annotations and attributes.
*/
public FieldVisitor visitField(int access, String name, String desc,
@@ -321,7 +321,7 @@ public abstract class ClassVisitor {
/**
* Visits a method of the class. This method <i>must</i> return a new
* {@link MethodVisitor} instance (or <tt>null</tt>) each time it is called,
* {@link MethodVisitor} instance (or <code>null</code>) each time it is called,
* i.e., it should not return a previously returned visitor.
*
* @param access
@@ -333,14 +333,14 @@ public abstract class ClassVisitor {
* @param desc
* the method's descriptor (see {@link Type Type}).
* @param signature
* the method's signature. May be <tt>null</tt> if the method
* the method's signature. May be <code>null</code> if the method
* parameters, return type and exceptions do not use generic
* types.
* @param exceptions
* the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be
* <tt>null</tt>.
* @return an object to visit the byte code of the method, or <tt>null</tt>
* <code>null</code>.
* @return an object to visit the byte code of the method, or <code>null</code>
* if this class visitor is not interested in visiting the code of
* this method.
*/

View File

@@ -391,8 +391,8 @@ public class ClassWriter extends ClassVisitor {
* A type table used to temporarily store internal names that will not
* necessarily be stored in the constant pool. This type table is used by
* the control flow and data flow analysis algorithm used to compute stack
* map frames from scratch. This array associates to each index <tt>i</tt>
* the Item whose index is <tt>i</tt>. All Item objects stored in this array
* map frames from scratch. This array associates to each index <code>i</code>
* the Item whose index is <code>i</code>. All Item objects stored in this array
* are also stored in the {@link #items} hash table. These two arrays allow
* to retrieve an Item from its index or, conversely, to get the index of an
* Item from its value. Each Item stores an internal name in its
@@ -556,7 +556,7 @@ public class ClassWriter extends ClassVisitor {
private int compute;
/**
* <tt>true</tt> if some methods have wide forward jumps using ASM pseudo
* <code>true</code> if some methods have wide forward jumps using ASM pseudo
* instructions, which need to be expanded into sequences of standard
* bytecode instructions. In this case the class is re-read and re-written
* with a ClassReader -> ClassWriter chain to perform this transformation.
@@ -1486,7 +1486,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc
* the method's descriptor.
* @param itf
* <tt>true</tt> if <tt>owner</tt> is an interface.
* <code>true</code> if <code>owner</code> is an interface.
* @return a new or already existing method reference item.
*/
Item newMethodItem(final String owner, final String name,
@@ -1515,7 +1515,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc
* the method's descriptor.
* @param itf
* <tt>true</tt> if <tt>owner</tt> is an interface.
* <code>true</code> if <code>owner</code> is an interface.
* @return the index of a new or already existing method reference item.
*/
public int newMethod(final String owner, final String name,
@@ -1778,7 +1778,7 @@ public class ClassWriter extends ClassVisitor {
* @param key
* a constant pool item.
* @return the constant pool's hash table item which is equal to the given
* item, or <tt>null</tt> if there is no such item.
* item, or <code>null</code> if there is no such item.
*/
private Item get(final Item key) {
Item i = items[key.hashCode % items.length];

View File

@@ -60,8 +60,8 @@ package org.redkale.asm;
/**
* A visitor to visit a Java field. The methods of this class must be called in
* the following order: ( <tt>visitAnnotation</tt> |
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* <tt>visitEnd</tt>.
* the following order: ( <code>visitAnnotation</code> |
* <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* <code>visitEnd</code>.
*
* @author Eric Bruneton
*/
@@ -111,8 +111,8 @@ public abstract class FieldVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -132,12 +132,12 @@ public abstract class FieldVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,

View File

@@ -100,28 +100,28 @@ final class FieldWriter extends FieldVisitor {
private int value;
/**
* The runtime visible annotations of this field. May be <tt>null</tt>.
* The runtime visible annotations of this field. May be <code>null</code>.
*/
private AnnotationWriter anns;
/**
* The runtime invisible annotations of this field. May be <tt>null</tt>.
* The runtime invisible annotations of this field. May be <code>null</code>.
*/
private AnnotationWriter ianns;
/**
* The runtime visible type annotations of this field. May be <tt>null</tt>.
* The runtime visible type annotations of this field. May be <code>null</code>.
*/
private AnnotationWriter tanns;
/**
* The runtime invisible type annotations of this field. May be
* <tt>null</tt>.
* <code>null</code>.
*/
private AnnotationWriter itanns;
/**
* The non standard attributes of this field. May be <tt>null</tt>.
* The non standard attributes of this field. May be <code>null</code>.
*/
private Attribute attrs;
@@ -141,9 +141,9 @@ final class FieldWriter extends FieldVisitor {
* @param desc
* the field's descriptor (see {@link Type}).
* @param signature
* the field's signature. May be <tt>null</tt>.
* the field's signature. May be <code>null</code>.
* @param value
* the field's constant value. May be <tt>null</tt>.
* the field's constant value. May be <code>null</code>.
*/
FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) {

View File

@@ -1403,7 +1403,7 @@ class Frame {
/**
* Merges the input frame of the given basic block with the input and output
* frames of this basic block. Returns <tt>true</tt> if the input frame of
* frames of this basic block. Returns <code>true</code> if the input frame of
* the given label has been changed by this operation.
*
* @param cw
@@ -1413,7 +1413,7 @@ class Frame {
* @param edge
* the kind of the {@link Edge} between this label and 'label'.
* See {@link Edge#info}.
* @return <tt>true</tt> if the input frame of the given label has been
* @return <code>true</code> if the input frame of the given label has been
* changed by this operation.
*/
final boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
@@ -1511,7 +1511,7 @@ class Frame {
/**
* Merges the type at the given index in the given type array with the given
* type. Returns <tt>true</tt> if the type array has been modified by this
* type. Returns <code>true</code> if the type array has been modified by this
* operation.
*
* @param cw
@@ -1522,7 +1522,7 @@ class Frame {
* an array of types.
* @param index
* the index of the type that must be merged in 'types'.
* @return <tt>true</tt> if the type array has been modified by this
* @return <code>true</code> if the type array has been modified by this
* operation.
*/
private static boolean merge(final ClassWriter cw, int t,

View File

@@ -82,7 +82,7 @@ class Handler {
/**
* Internal name of the type of exceptions handled by this handler, or
* <tt>null</tt> to catch any exceptions.
* <code>null</code> to catch any exceptions.
*/
String desc;

View File

@@ -306,8 +306,8 @@ final class Item {
* @param i
* the item to be compared to this one. Both items must have the
* same {@link #type}.
* @return <tt>true</tt> if the given item if equal to this one,
* <tt>false</tt> otherwise.
* @return <code>true</code> if the given item if equal to this one,
* <code>false</code> otherwise.
*/
boolean isEqualTo(final Item i) {
switch (type) {

View File

@@ -325,8 +325,8 @@ public class Label {
* the position of first byte of the bytecode instruction that
* contains this label.
* @param wideOffset
* <tt>true</tt> if the reference must be stored in 4 bytes, or
* <tt>false</tt> if it must be stored with 2 bytes.
* <code>true</code> if the reference must be stored in 4 bytes, or
* <code>false</code> if it must be stored with 2 bytes.
* @throws IllegalArgumentException
* if this label has not been created by the given code writer.
*/
@@ -389,7 +389,7 @@ public class Label {
* the position of this label in the bytecode.
* @param data
* the bytecode of the method.
* @return <tt>true</tt> if a blank that was left for this label was too
* @return <code>true</code> if a blank that was left for this label was too
* small to store the offset. In such a case the corresponding jump
* instruction is replaced with a pseudo instruction (using unused
* opcodes) using an unsigned two bytes offset. These pseudo

View File

@@ -60,24 +60,24 @@ package org.redkale.asm;
/**
* A visitor to visit a Java method. The methods of this class must be called in
* the following order: ( <tt>visitParameter</tt> )* [
* <tt>visitAnnotationDefault</tt> ] ( <tt>visitAnnotation</tt> |
* <tt>visitParameterAnnotation</tt> <tt>visitTypeAnnotation</tt> |
* <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> |
* <tt>visit<i>X</i>Insn</tt> | <tt>visitLabel</tt> |
* <tt>visitInsnAnnotation</tt> | <tt>visitTryCatchBlock</tt> |
* <tt>visitTryCatchAnnotation</tt> | <tt>visitLocalVariable</tt> |
* <tt>visitLocalVariableAnnotation</tt> | <tt>visitLineNumber</tt> )*
* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In addition, the
* <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> methods must be called in
* the following order: ( <code>visitParameter</code> )* [
* <code>visitAnnotationDefault</code> ] ( <code>visitAnnotation</code> |
* <code>visitParameterAnnotation</code> <code>visitTypeAnnotation</code> |
* <code>visitAttribute</code> )* [ <code>visitCode</code> ( <code>visitFrame</code> |
* <code>visit<i>X</i>Insn</code> | <code>visitLabel</code> |
* <code>visitInsnAnnotation</code> | <code>visitTryCatchBlock</code> |
* <code>visitTryCatchAnnotation</code> | <code>visitLocalVariable</code> |
* <code>visitLocalVariableAnnotation</code> | <code>visitLineNumber</code> )*
* <code>visitMaxs</code> ] <code>visitEnd</code>. In addition, the
* <code>visit<i>X</i>Insn</code> and <code>visitLabel</code> methods must be called in
* the sequential order of the bytecode instructions of the visited code,
* <tt>visitInsnAnnotation</tt> must be called <i>after</i> the annotated
* instruction, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the
* <code>visitInsnAnnotation</code> must be called <i>after</i> the annotated
* instruction, <code>visitTryCatchBlock</code> must be called <i>before</i> the
* labels passed as arguments have been visited,
* <tt>visitTryCatchBlockAnnotation</tt> must be called <i>after</i> the
* <code>visitTryCatchBlockAnnotation</code> must be called <i>after</i> the
* corresponding try catch block has been visited, and the
* <tt>visitLocalVariable</tt>, <tt>visitLocalVariableAnnotation</tt> and
* <tt>visitLineNumber</tt> methods must be called <i>after</i> the labels
* <code>visitLocalVariable</code>, <code>visitLocalVariableAnnotation</code> and
* <code>visitLineNumber</code> methods must be called <i>after</i> the labels
* passed as arguments have been visited.
*
* @author Eric Bruneton
@@ -132,8 +132,8 @@ public abstract class MethodVisitor {
* @param name
* parameter name or null if none is provided.
* @param access
* the parameter's access flags, only <tt>ACC_FINAL</tt>,
* <tt>ACC_SYNTHETIC</tt> or/and <tt>ACC_MANDATED</tt> are
* the parameter's access flags, only <code>ACC_FINAL</code>,
* <code>ACC_SYNTHETIC</code> or/and <code>ACC_MANDATED</code> are
* allowed (see {@link Opcodes}).
*/
public void visitParameter(String name, int access) {
@@ -146,7 +146,7 @@ public abstract class MethodVisitor {
* Visits the default value of this annotation interface method.
*
* @return a visitor to the visit the actual default value of this
* annotation interface method, or <tt>null</tt> if this visitor is
* annotation interface method, or <code>null</code> if this visitor is
* not interested in visiting this default value. The 'name'
* parameters passed to the methods of this annotation visitor are
* ignored. Moreover, exacly one visit method must be called on this
@@ -165,8 +165,8 @@ public abstract class MethodVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -193,12 +193,12 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
@@ -217,8 +217,8 @@ public abstract class MethodVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitParameterAnnotation(int parameter,
@@ -569,7 +569,7 @@ public abstract class MethodVisitor {
* the constant to be loaded on the stack. This parameter must be
* a non null {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double}, a {@link String}, a {@link Type} of OBJECT or
* ARRAY sort for <tt>.class</tt> constants, for classes whose
* ARRAY sort for <code>.class</code> constants, for classes whose
* version is 49.0, a {@link Type} of METHOD sort or a
* {@link Handle} for MethodType and MethodHandle constants, for
* classes whose version is 51.0.
@@ -604,8 +604,8 @@ public abstract class MethodVisitor {
* @param dflt
* beginning of the default handler block.
* @param labels
* beginnings of the handler blocks. <tt>labels[i]</tt> is the
* beginning of the handler block for the <tt>min + i</tt> key.
* beginnings of the handler blocks. <code>labels[i]</code> is the
* beginning of the handler block for the <code>min + i</code> key.
*/
public void visitTableSwitchInsn(int min, int max, Label dflt,
Label... labels) {
@@ -622,8 +622,8 @@ public abstract class MethodVisitor {
* @param keys
* the values of the keys.
* @param labels
* beginnings of the handler blocks. <tt>labels[i]</tt> is the
* beginning of the handler block for the <tt>keys[i]</tt> key.
* beginnings of the handler blocks. <code>labels[i]</code> is the
* beginning of the handler block for the <code>keys[i]</code> key.
*/
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
if (mv != null) {
@@ -668,12 +668,12 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitInsnAnnotation(int typeRef,
@@ -699,7 +699,7 @@ public abstract class MethodVisitor {
* beginning of the exception handler's code.
* @param type
* internal name of the type of exceptions handled by the
* handler, or <tt>null</tt> to catch any exceptions (for
* handler, or <code>null</code> to catch any exceptions (for
* "finally" blocks).
* @throws IllegalArgumentException
* if one of the labels has already been visited by this visitor
@@ -725,12 +725,12 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
@@ -750,7 +750,7 @@ public abstract class MethodVisitor {
* the type descriptor of this local variable.
* @param signature
* the type signature of this local variable. May be
* <tt>null</tt> if the local variable type does not use generic
* <code>null</code> if the local variable type does not use generic
* types.
* @param start
* the first instruction corresponding to the scope of this local
@@ -782,7 +782,7 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param start
* the fist instructions corresponding to the continuous ranges
* that make the scope of this local variable (inclusive).
@@ -796,8 +796,8 @@ public abstract class MethodVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
@@ -819,7 +819,7 @@ public abstract class MethodVisitor {
* @param start
* the first instruction corresponding to this line number.
* @throws IllegalArgumentException
* if <tt>start</tt> has not already been visited by this
* if <code>start</code> has not already been visited by this
* visitor (by the {@link #visitLabel visitLabel} method).
*/
public void visitLineNumber(int line, Label start) {

View File

@@ -218,41 +218,41 @@ class MethodWriter extends MethodVisitor {
int[] exceptions;
/**
* The annotation default attribute of this method. May be <tt>null</tt>.
* The annotation default attribute of this method. May be <code>null</code>.
*/
private ByteVector annd;
/**
* The runtime visible annotations of this method. May be <tt>null</tt>.
* The runtime visible annotations of this method. May be <code>null</code>.
*/
private AnnotationWriter anns;
/**
* The runtime invisible annotations of this method. May be <tt>null</tt>.
* The runtime invisible annotations of this method. May be <code>null</code>.
*/
private AnnotationWriter ianns;
/**
* The runtime visible type annotations of this method. May be <tt>null</tt>
* The runtime visible type annotations of this method. May be <code>null</code>
* .
*/
private AnnotationWriter tanns;
/**
* The runtime invisible type annotations of this method. May be
* <tt>null</tt>.
* <code>null</code>.
*/
private AnnotationWriter itanns;
/**
* The runtime visible parameter annotations of this method. May be
* <tt>null</tt>.
* <code>null</code>.
*/
private AnnotationWriter[] panns;
/**
* The runtime invisible parameter annotations of this method. May be
* <tt>null</tt>.
* <code>null</code>.
*/
private AnnotationWriter[] ipanns;
@@ -381,12 +381,12 @@ class MethodWriter extends MethodVisitor {
private int lastCodeOffset;
/**
* The runtime visible type annotations of the code. May be <tt>null</tt>.
* The runtime visible type annotations of the code. May be <code>null</code>.
*/
private AnnotationWriter ctanns;
/**
* The runtime invisible type annotations of the code. May be <tt>null</tt>.
* The runtime invisible type annotations of the code. May be <code>null</code>.
*/
private AnnotationWriter ictanns;
@@ -446,7 +446,7 @@ class MethodWriter extends MethodVisitor {
* is relative to the beginning of the current basic block, i.e., the true
* stack size after the last visited instruction is equal to the
* {@link Label#inputStackTop beginStackSize} of the current basic block
* plus <tt>stackSize</tt>.
* plus <code>stackSize</code>.
*/
private int stackSize;
@@ -455,7 +455,7 @@ class MethodWriter extends MethodVisitor {
* This size is relative to the beginning of the current basic block, i.e.,
* the true maximum stack size after the last visited instruction is equal
* to the {@link Label#inputStackTop beginStackSize} of the current basic
* block plus <tt>stackSize</tt>.
* block plus <code>stackSize</code>.
*/
private int maxStackSize;
@@ -475,10 +475,10 @@ class MethodWriter extends MethodVisitor {
* @param desc
* the method's descriptor (see {@link Type}).
* @param signature
* the method's signature. May be <tt>null</tt>.
* the method's signature. May be <code>null</code>.
* @param exceptions
* the internal names of the method's exceptions. May be
* <tt>null</tt>.
* <code>null</code>.
* @param compute
* Indicates what must be automatically computed (see #compute).
*/

View File

@@ -60,9 +60,9 @@ package org.redkale.asm;
/**
* A visitor to visit a Java module. The methods of this class must be called in
* the following order: <tt>visitMainClass</tt> | ( <tt>visitPackage</tt> |
* <tt>visitRequire</tt> | <tt>visitExport</tt> | <tt>visitOpen</tt> |
* <tt>visitUse</tt> | <tt>visitProvide</tt> )* <tt>visitEnd</tt>.
* the following order: <code>visitMainClass</code> | ( <code>visitPackage</code> |
* <code>visitRequire</code> | <code>visitExport</code> | <code>visitOpen</code> |
* <code>visitUse</code> | <code>visitProvide</code> )* <code>visitEnd</code>.
*
* The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)},
* {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)}
@@ -157,7 +157,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can access to
* the public classes of the exported package or
* <tt>null</tt>.
* <code>null</code>.
*/
public void visitExport(String packaze, int access, String... modules) {
if (mv != null) {
@@ -174,7 +174,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can use deep
* reflection to the classes of the open package or
* <tt>null</tt>.
* <code>null</code>.
*/
public void visitOpen(String packaze, int access, String... modules) {
if (mv != null) {

View File

@@ -71,47 +71,47 @@ import java.lang.reflect.Method;
public class Type {
/**
* The sort of the <tt>void</tt> type. See {@link #getSort getSort}.
* The sort of the <code>void</code> type. See {@link #getSort getSort}.
*/
public static final int VOID = 0;
/**
* The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}.
* The sort of the <code>boolean</code> type. See {@link #getSort getSort}.
*/
public static final int BOOLEAN = 1;
/**
* The sort of the <tt>char</tt> type. See {@link #getSort getSort}.
* The sort of the <code>char</code> type. See {@link #getSort getSort}.
*/
public static final int CHAR = 2;
/**
* The sort of the <tt>byte</tt> type. See {@link #getSort getSort}.
* The sort of the <code>byte</code> type. See {@link #getSort getSort}.
*/
public static final int BYTE = 3;
/**
* The sort of the <tt>short</tt> type. See {@link #getSort getSort}.
* The sort of the <code>short</code> type. See {@link #getSort getSort}.
*/
public static final int SHORT = 4;
/**
* The sort of the <tt>int</tt> type. See {@link #getSort getSort}.
* The sort of the <code>int</code> type. See {@link #getSort getSort}.
*/
public static final int INT = 5;
/**
* The sort of the <tt>float</tt> type. See {@link #getSort getSort}.
* The sort of the <code>float</code> type. See {@link #getSort getSort}.
*/
public static final int FLOAT = 6;
/**
* The sort of the <tt>long</tt> type. See {@link #getSort getSort}.
* The sort of the <code>long</code> type. See {@link #getSort getSort}.
*/
public static final int LONG = 7;
/**
* The sort of the <tt>double</tt> type. See {@link #getSort getSort}.
* The sort of the <code>double</code> type. See {@link #getSort getSort}.
*/
public static final int DOUBLE = 8;
@@ -131,55 +131,55 @@ public class Type {
public static final int METHOD = 11;
/**
* The <tt>void</tt> type.
* The <code>void</code> type.
*/
public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24)
| (5 << 16) | (0 << 8) | 0, 1);
/**
* The <tt>boolean</tt> type.
* The <code>boolean</code> type.
*/
public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24)
| (0 << 16) | (5 << 8) | 1, 1);
/**
* The <tt>char</tt> type.
* The <code>char</code> type.
*/
public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24)
| (0 << 16) | (6 << 8) | 1, 1);
/**
* The <tt>byte</tt> type.
* The <code>byte</code> type.
*/
public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24)
| (0 << 16) | (5 << 8) | 1, 1);
/**
* The <tt>short</tt> type.
* The <code>short</code> type.
*/
public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24)
| (0 << 16) | (7 << 8) | 1, 1);
/**
* The <tt>int</tt> type.
* The <code>int</code> type.
*/
public static final Type INT_TYPE = new Type(INT, null, ('I' << 24)
| (0 << 16) | (0 << 8) | 1, 1);
/**
* The <tt>float</tt> type.
* The <code>float</code> type.
*/
public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24)
| (2 << 16) | (2 << 8) | 1, 1);
/**
* The <tt>long</tt> type.
* The <code>long</code> type.
*/
public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24)
| (1 << 16) | (1 << 8) | 2, 1);
/**
* The <tt>double</tt> type.
* The <code>double</code> type.
*/
public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24)
| (3 << 16) | (3 << 8) | 2, 1);
@@ -439,8 +439,8 @@ public class Type {
* @return the size of the arguments of the method (plus one for the
* implicit this argument), argSize, and the size of its return
* value, retSize, packed into a single int i =
* <tt>(argSize &lt;&lt; 2) | retSize</tt> (argSize is therefore equal to
* <tt>i &gt;&gt; 2</tt>, and retSize to <tt>i &amp; 0x03</tt>).
* <code>(argSize &lt;&lt; 2) | retSize</code> (argSize is therefore equal to
* <code>i &gt;&gt; 2</code>, and retSize to <code>i &amp; 0x03</code>).
*/
public static int getArgumentsAndReturnSizes(final String desc) {
int n = 1;
@@ -645,9 +645,9 @@ public class Type {
* @return the size of the arguments (plus one for the implicit this
* argument), argSize, and the size of the return value, retSize,
* packed into a single
* int i = <tt>(argSize &lt;&lt; 2) | retSize</tt>
* (argSize is therefore equal to <tt>i &gt;&gt; 2</tt>,
* and retSize to <tt>i &amp; 0x03</tt>).
* int i = <code>(argSize &lt;&lt; 2) | retSize</code>
* (argSize is therefore equal to <code>i &gt;&gt; 2</code>,
* and retSize to <code>i &amp; 0x03</code>).
*/
public int getArgumentsAndReturnSizes() {
return getArgumentsAndReturnSizes(getDescriptor());
@@ -838,8 +838,8 @@ public class Type {
* Returns the size of values of this type. This method must not be used for
* method types.
*
* @return the size of values of this type, i.e., 2 for <tt>long</tt> and
* <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise.
* @return the size of values of this type, i.e., 2 for <code>long</code> and
* <code>double</code>, 0 for <code>void</code> and 1 otherwise.
*/
public int getSize() {
// the size is in byte 0 of 'off' for primitive types (buf == null)
@@ -855,8 +855,8 @@ public class Type {
* ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG,
* ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
* @return an opcode that is similar to the given opcode, but adapted to
* this Java type. For example, if this type is <tt>float</tt> and
* <tt>opcode</tt> is IRETURN, this method returns FRETURN.
* this Java type. For example, if this type is <code>float</code> and
* <code>opcode</code> is IRETURN, this method returns FRETURN.
*/
public int getOpcode(final int opcode) {
if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
@@ -879,7 +879,7 @@ public class Type {
*
* @param o
* the object to be compared to this type.
* @return <tt>true</tt> if the given object is equal to this type.
* @return <code>true</code> if the given object is equal to this type.
*/
@Override
public boolean equals(final Object o) {

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.boot;
import org.redkale.cluster.ClusterAgent;
import org.redkale.util.RedkaleClassLoader;
import org.redkale.net.TransportGroupInfo;
import java.io.*;
@@ -23,7 +24,8 @@ import javax.xml.parsers.*;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.Convert;
import org.redkale.convert.bson.BsonFactory;
import org.redkale.convert.json.JsonFactory;
import org.redkale.convert.json.*;
import org.redkale.mq.*;
import org.redkale.net.*;
import org.redkale.net.http.MimeType;
import org.redkale.net.sncp.*;
@@ -56,6 +58,11 @@ public final class Application {
*/
public static final String RESNAME_APP_TIME = "APP_TIME";
/**
* 当前进程的名称, 类型String
*/
public static final String RESNAME_APP_NAME = "APP_NAME";
/**
* 当前进程的根目录, 类型String、File、Path、URI
*/
@@ -72,12 +79,12 @@ public final class Application {
public static final String RESNAME_APP_GRES = "APP_GRES";
/**
* 当前进程节点的name 类型:String
* 当前进程节点的nodeid 类型int
*/
public static final String RESNAME_APP_NODE = "APP_NODE";
public static final String RESNAME_APP_NODEID = "APP_NODEID";
/**
* 当前进程节点的IP地址 类型InetAddress、String
* 当前进程节点的IP地址 类型:InetSocketAddress、InetAddress、String
*/
public static final String RESNAME_APP_ADDR = "APP_ADDR";
@@ -106,8 +113,14 @@ public final class Application {
*/
public static final String RESNAME_SERVER_RESFACTORY = Server.RESNAME_SERVER_RESFACTORY;
//本进程节点ID
final int nodeid;
//本进程节点ID
final String name;
//本地IP地址
final InetAddress localAddress;
final InetSocketAddress localAddress;
//CacheSource 资源
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
@@ -115,18 +128,29 @@ public final class Application {
//DataSource 资源
final List<DataSource> dataSources = new CopyOnWriteArrayList<>();
//NodeServer 资源
//NodeServer 资源, 顺序必须是sncps, others, watchs
final List<NodeServer> servers = new CopyOnWriteArrayList<>();
//SNCP传输端的TransportFactory, 注意: 只给SNCP使用
final TransportFactory sncpTransportFactory;
//第三方服务发现管理接口
//@since 2.1.0
final ClusterAgent clusterAgent;
//MQ管理接口
//@since 2.1.0
final MessageAgent[] messageAgents;
//全局根ResourceFactory
final ResourceFactory resourceFactory = ResourceFactory.root();
//服务配置项
final AnyValue config;
//排除的jar路径
final String excludelibs;
//临时计数器
CountDownLatch servicecdl; //会出现两次赋值
@@ -192,24 +216,22 @@ public final class Application {
throw new RuntimeException(e);
}
String localaddr = config.getValue("address", "").trim();
this.localAddress = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, config.getIntValue("port")).getAddress();
this.resourceFactory.register(RESNAME_APP_ADDR, this.localAddress.getHostAddress());
this.resourceFactory.register(RESNAME_APP_ADDR, InetAddress.class, this.localAddress);
InetAddress addr = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, config.getIntValue("port")).getAddress();
this.localAddress = new InetSocketAddress(addr, config.getIntValue("port"));
this.resourceFactory.register(RESNAME_APP_ADDR, addr.getHostAddress());
this.resourceFactory.register(RESNAME_APP_ADDR, InetAddress.class, addr);
this.resourceFactory.register(RESNAME_APP_ADDR, InetSocketAddress.class, this.localAddress);
{
String node = config.getValue("node", "").trim();
if (node.isEmpty()) {
StringBuilder sb = new StringBuilder();
byte[] bs = this.localAddress.getAddress();
int v1 = bs[bs.length - 2] & 0xff;
int v2 = bs[bs.length - 1] & 0xff;
if (v1 <= 0xf) sb.append('0');
sb.append(Integer.toHexString(v1));
if (v2 <= 0xf) sb.append('0');
sb.append(Integer.toHexString(v2));
node = sb.toString();
}
this.resourceFactory.register(RESNAME_APP_NODE, node);
System.setProperty(RESNAME_APP_NODE, node);
int nid = config.getIntValue("nodeid", 0);
this.nodeid = nid;
this.resourceFactory.register(RESNAME_APP_NODEID, nid);
System.setProperty(RESNAME_APP_NODEID, "" + nid);
}
{
this.name = checkName(config.getValue("name", ""));
this.resourceFactory.register(RESNAME_APP_NAME, name);
System.setProperty(RESNAME_APP_NAME, name);
}
//以下是初始化日志配置
final URI logConfURI = "file".equals(confPath.getScheme()) ? new File(new File(confPath), "logging.properties").toURI()
@@ -271,6 +293,9 @@ public final class Application {
AsynchronousChannelGroup transportGroup = null;
final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null;
String excludelib0 = null;
ClusterAgent cluster = null;
MessageAgent[] mqs = null;
int bufferCapacity = 32 * 1024;
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8;
int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
@@ -278,6 +303,8 @@ public final class Application {
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
if (resources != null) {
AnyValue excludelibConf = resources.getAnyValue("excludelibs");
if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value");
AnyValue transportConf = resources.getAnyValue("transport");
int groupsize = resources.getAnyValues("group").length;
if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
@@ -314,6 +341,80 @@ public final class Application {
}
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity / 1024 + "K; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
}
AnyValue clusterConf = resources.getAnyValue("cluster");
if (clusterConf != null) {
try {
String classval = clusterConf.getValue("value");
if (classval == null || classval.isEmpty()) {
Iterator<ClusterAgent> it = ServiceLoader.load(ClusterAgent.class, classLoader).iterator();
while (it.hasNext()) {
ClusterAgent agent = it.next();
if (agent.match(clusterConf)) {
cluster = agent;
cluster.setConfig(clusterConf);
break;
}
}
if (cluster == null) logger.log(Level.SEVERE, "load application cluster resource, but not found name='value' value error: " + clusterConf);
} else {
Class type = classLoader.loadClass(clusterConf.getValue("value"));
if (!ClusterAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application cluster resource, but not found " + ClusterAgent.class.getSimpleName() + " implements class error: " + clusterConf);
} else {
cluster = (ClusterAgent) type.getDeclaredConstructor().newInstance();
cluster.setConfig(clusterConf);
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "load application cluster resource error: " + clusterConf, e);
}
}
AnyValue[] mqConfs = resources.getAnyValues("mq");
if (mqConfs != null && mqConfs.length > 0) {
mqs = new MessageAgent[mqConfs.length];
Set<String> mqnames = new HashSet<>();
for (int i = 0; i < mqConfs.length; i++) {
AnyValue mqConf = mqConfs[0];
String mqname = mqConf.getValue("name", "");
if (mqnames.contains(mqname)) throw new RuntimeException("mq.name(" + mqname + ") is repeat");
mqnames.add(mqname);
String namex = mqConf.getValue("names");
if (namex != null && !namex.isEmpty()) {
for (String n : namex.split(";")) {
if (n.trim().isEmpty()) continue;
if (mqnames.contains(n.trim())) throw new RuntimeException("mq.name(" + n.trim() + ") is repeat");
mqnames.add(n.trim());
}
}
try {
String classval = mqConf.getValue("value");
if (classval == null || classval.isEmpty()) {
Iterator<MessageAgent> it = ServiceLoader.load(MessageAgent.class, classLoader).iterator();
while (it.hasNext()) {
MessageAgent messageAgent = it.next();
if (messageAgent.match(mqConf)) {
mqs[i] = messageAgent;
mqs[i].setConfig(mqConf);
break;
}
}
if (mqs[i] == null) logger.log(Level.SEVERE, "load application mq resource, but not found name='value' value error: " + mqConf);
} else {
Class type = classLoader.loadClass(classval);
if (!MessageAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application mq resource, but not found " + MessageAgent.class.getSimpleName() + " implements class error: " + mqConf);
} else {
mqs[i] = (MessageAgent) type.getDeclaredConstructor().newInstance();
mqs[i].setConfig(mqConf);
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "load application mq resource error: " + mqs[i], e);
}
}
}
}
if (transportGroup == null) {
final AtomicInteger counter = new AtomicInteger();
@@ -338,15 +439,29 @@ public final class Application {
return true;
});
}
this.excludelibs = excludelib0;
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.poolmaxconns", "100"))
.addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.checkinterval", "30"));
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.pool.maxconns", "100"))
.addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.ping.interval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.check.interval", "30"));
this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining());
this.clusterAgent = cluster;
this.messageAgents = mqs;
Thread.currentThread().setContextClassLoader(this.classLoader);
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
}
private String checkName(String name) { //不能含特殊字符
if (name.isEmpty()) return name;
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9");
for (char ch : name.toCharArray()) {
if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符
throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9");
}
}
return name;
}
public ResourceFactory getResourceFactory() {
return resourceFactory;
}
@@ -355,6 +470,22 @@ public final class Application {
return sncpTransportFactory;
}
public ClusterAgent getClusterAgent() {
return clusterAgent;
}
public MessageAgent getMessageAgent(String name) {
if (messageAgents == null) return null;
for (MessageAgent agent : messageAgents) {
if (agent.getName().equals(name)) return agent;
}
return null;
}
public MessageAgent[] getMessageAgents() {
return messageAgents;
}
public RedkaleClassLoader getClassLoader() {
return classLoader;
}
@@ -375,6 +506,14 @@ public final class Application {
return new ArrayList<>(cacheSources);
}
public int getNodeid() {
return nodeid;
}
public String getName() {
return name;
}
public File getHome() {
return home;
}
@@ -418,7 +557,7 @@ public final class Application {
pidstr = "APP_PID = " + pid + "\r\n";
} catch (Throwable t) {
}
logger.log(Level.INFO, pidstr + "APP_JAVA = " + System.getProperty("java.version") + "\r\n" + RESNAME_APP_ADDR + " = " + this.localAddress.getHostAddress() + "\r\n" + RESNAME_APP_HOME + " = " + homepath + "\r\n" + RESNAME_APP_CONF + " = " + confpath);
logger.log(Level.INFO, pidstr + "APP_JAVA = " + System.getProperty("java.version") + "\r\n" + RESNAME_APP_NODEID + " = " + this.nodeid + "\r\n" + RESNAME_APP_ADDR + " = " + this.localAddress.getHostString() + ":" + this.localAddress.getPort() + "\r\n" + RESNAME_APP_HOME + " = " + homepath + "\r\n" + RESNAME_APP_CONF + " = " + confpath);
String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? confpath : (lib + ";" + confpath);
Server.loadLib(classLoader, logger, lib);
@@ -534,6 +673,41 @@ public final class Application {
}, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class);
//--------------------------------------------------------------------------
if (this.clusterAgent != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent initing");
long s = System.currentTimeMillis();
clusterAgent.setTransportFactory(this.sncpTransportFactory);
this.resourceFactory.inject(clusterAgent);
clusterAgent.init(clusterAgent.getConfig());
this.resourceFactory.register(ClusterAgent.class, clusterAgent);
logger.info("ClusterAgent init in " + (System.currentTimeMillis() - s) + " ms");
}
if (this.messageAgents != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent initing");
long s = System.currentTimeMillis();
for (MessageAgent agent : this.messageAgents) {
this.resourceFactory.inject(agent);
agent.init(agent.getConfig());
this.resourceFactory.register(agent.getName(), MessageAgent.class, agent);
this.resourceFactory.register(agent.getName(), HttpMessageClient.class, agent.getHttpMessageClient());
//this.resourceFactory.register(agent.getName(), SncpMessageClient.class, agent.getSncpMessageClient()); //不需要给开发者使用
}
logger.info("MessageAgent init in " + (System.currentTimeMillis() - s) + " ms");
}
//------------------------------------- 注册 DataSource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null) return;
if (clusterAgent == null) return;
HttpMessageClient messageClient = new HttpMessageClusterClient(clusterAgent);
field.set(src, messageClient);
rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpMessageClient.class, messageClient);
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject error", e);
}
}, HttpMessageClient.class);
initResources();
}
@@ -548,7 +722,7 @@ public final class Application {
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
}
TransportGroupInfo ginfo = new TransportGroupInfo(group, protocol, conf.getValue("subprotocol", ""), new LinkedHashSet<>());
TransportGroupInfo ginfo = new TransportGroupInfo(group, protocol, new LinkedHashSet<>());
for (AnyValue node : conf.getAnyValues("node")) {
final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
ginfo.putAddress(addr);
@@ -602,7 +776,8 @@ public final class Application {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
if ("SHUTDOWN".equalsIgnoreCase(new String(bytes))) {
final String cmd = new String(bytes);
if ("SHUTDOWN".equalsIgnoreCase(cmd)) {
try {
long s = System.currentTimeMillis();
logger.info(application.getClass().getSimpleName() + " shutdowning");
@@ -622,7 +797,7 @@ public final class Application {
buffer.flip();
channel.send(buffer, address);
}
} else if ("APIDOC".equalsIgnoreCase(new String(bytes))) {
} else if ("APIDOC".equalsIgnoreCase(cmd)) {
try {
new ApiDocsService(application).run();
buffer.clear();
@@ -635,6 +810,16 @@ public final class Application {
buffer.flip();
channel.send(buffer, address);
}
} else {
long s = System.currentTimeMillis();
logger.info(application.getClass().getSimpleName() + " command " + cmd);
application.command(cmd);
buffer.clear();
buffer.put("COMMAND OK".getBytes());
buffer.flip();
channel.send(buffer, address);
long e = System.currentTimeMillis() - s;
logger.info(application.getClass().getSimpleName() + " command in " + e + " ms");
}
}
} catch (Exception e) {
@@ -645,10 +830,10 @@ public final class Application {
}.start();
}
private void sendCommand(String command) throws Exception {
private static void sendCommand(Logger logger, int port, String command) throws Exception {
final DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.connect(new InetSocketAddress("127.0.0.1", config.getIntValue("port")));
channel.connect(new InetSocketAddress("127.0.0.1", port));
ByteBuffer buffer = ByteBuffer.allocate(128);
buffer.put(command.getBytes());
buffer.flip();
@@ -661,7 +846,7 @@ public final class Application {
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
channel.close();
logger.info("Send: " + command + ", Reply: " + new String(bytes));
if (logger != null) logger.info("Send: " + command + ", Reply: " + new String(bytes));
Thread.sleep(1000);
} catch (Exception e) {
if (e instanceof PortUnreachableException) {
@@ -670,7 +855,7 @@ public final class Application {
application.init();
application.start();
new ApiDocsService(application).run();
logger.info("APIDOC OK");
if (logger != null) logger.info("APIDOC OK");
return;
}
}
@@ -679,6 +864,9 @@ public final class Application {
}
public void start() throws Exception {
if (!singletonrun && this.clusterAgent != null) {
this.clusterAgent.register(this);
}
final AnyValue[] entrys = config.getAnyValues("server");
CountDownLatch timecd = new CountDownLatch(entrys.length);
final List<AnyValue> sncps = new ArrayList<>();
@@ -700,12 +888,40 @@ public final class Application {
runServers(timecd, others);
runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务
timecd.await();
if (this.clusterAgent != null) this.clusterAgent.start();
if (this.messageAgents != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent starting");
long s = System.currentTimeMillis();
final StringBuffer sb = new StringBuffer();
Set<String> names = new HashSet<>();
for (MessageAgent agent : this.messageAgents) {
names.add(agent.getName());
Map<String, Long> map = agent.start().join();
AtomicInteger maxlen = new AtomicInteger();
map.keySet().forEach(str -> {
if (str.length() > maxlen.get()) maxlen.set(str.length());
});
new TreeMap<>(map).forEach((topic, ms) -> sb.append("MessageConsumer(topic=").append(alignString(topic, maxlen.get())).append(") init and start in ").append(ms).append(" ms\r\n")
);
}
if (sb.length() > 0) logger.info(sb.toString().trim());
logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") start in " + (System.currentTimeMillis() - s) + " ms");
}
//if (!singletonrun) signalHandle();
//if (!singletonrun) clearPersistData();
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n");
if (!singletonrun) this.serversLatch.await();
}
private static String alignString(String value, int maxlen) {
StringBuilder sb = new StringBuilder(maxlen);
sb.append(value);
for (int i = 0; i < maxlen - value.length(); i++) {
sb.append(' ');
}
return sb.toString();
}
// private void clearPersistData() {
// File cachedir = new File(home, "cache");
// if (!cachedir.isDirectory()) return;
@@ -784,20 +1000,18 @@ public final class Application {
synchronized (nodeClasses) {
if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS
ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null);
ClassFilter.Loader.load(home, serconf.getValue("excludelibs", "").split(";"), profilter);
ClassFilter.Loader.load(home, ((excludelibs != null ? (excludelibs + ";") : "") + serconf.getValue("excludelibs", "")).split(";"), profilter);
final Set<FilterEntry<NodeServer>> entrys = profilter.getFilterEntrys();
for (FilterEntry<NodeServer> entry : entrys) {
final Class<? extends NodeServer> type = entry.getType();
NodeProtocol pros = type.getAnnotation(NodeProtocol.class);
for (String p : pros.value()) {
p = p.toUpperCase();
if ("SNCP".equals(p) || "HTTP".equals(p)) continue;
final Class<? extends NodeServer> old = nodeClasses.get(p);
if (old != null && old != type) {
throw new RuntimeException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")");
}
nodeClasses.put(p, type);
String p = pros.value().toUpperCase();
if ("SNCP".equals(p) || "HTTP".equals(p)) continue;
final Class<? extends NodeServer> old = nodeClasses.get(p);
if (old != null && old != type) {
throw new RuntimeException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")");
}
nodeClasses.put(p, type);
}
}
}
@@ -832,14 +1046,14 @@ public final class Application {
public static <T extends Service> T singleton(String name, Class<T> serviceClass, Class<? extends Service>... extServiceClasses) throws Exception {
if (serviceClass == null) throw new IllegalArgumentException("serviceClass is null");
final Application application = Application.create(true);
System.setProperty("red" + "kale-singleton-serviceclass", serviceClass.getName());
System.setProperty("red" + "kale.singleton.serviceclass", serviceClass.getName());
if (extServiceClasses != null && extServiceClasses.length > 0) {
StringBuilder sb = new StringBuilder();
for (Class clazz : extServiceClasses) {
if (sb.length() > 0) sb.append(',');
sb.append(clazz.getName());
}
System.setProperty("red" + "kale-singleton-extserviceclasses", sb.toString());
System.setProperty("red" + "kale.singleton.extserviceclasses", sb.toString());
}
application.init();
application.start();
@@ -853,6 +1067,10 @@ public final class Application {
}
public static Application create(final boolean singleton) throws IOException {
return new Application(singleton, loadAppXml());
}
private static AnyValue loadAppXml() throws IOException {
final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/');
System.setProperty(RESNAME_APP_HOME, home);
String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf");
@@ -864,21 +1082,19 @@ public final class Application {
} else {
appconf = new File(new File(home, confsubpath), "application.xml").toURI();
}
return new Application(singleton, load(appconf.toURL().openStream()));
return load(appconf.toURL().openStream());
}
public static void main(String[] args) throws Exception {
Utility.midnight(); //先初始化一下Utility
Thread.currentThread().setName("Redkale-Application-Main-Thread");
//运行主程序
final Application application = Application.create(false);
if (System.getProperty("CMD") != null) {
application.sendCommand(System.getProperty("CMD"));
return;
} else if (System.getProperty("SHUTDOWN") != null) { //兼容旧接口
application.sendCommand("SHUTDOWN");
AnyValue config = loadAppXml();
Application.sendCommand(null, config.getIntValue("port"), System.getProperty("CMD"));
return;
}
final Application application = Application.create(false);
application.init();
application.startSelfServer();
try {
@@ -902,6 +1118,17 @@ public final class Application {
return null;
}
public void command(String cmd) {
List<NodeServer> localServers = new ArrayList<>(servers); //顺序sncps, others, watchs
localServers.stream().forEach((server) -> {
try {
server.command(cmd);
} catch (Exception t) {
logger.log(Level.WARNING, " command server(" + server.getSocketAddress() + ") error", t);
}
});
}
public void shutdown() throws Exception {
for (ApplicationListener listener : this.listeners) {
try {
@@ -910,8 +1137,26 @@ public final class Application {
logger.log(Level.WARNING, listener.getClass() + " preShutdown erroneous", e);
}
}
servers.stream().forEach((server) -> {
List<NodeServer> localServers = new ArrayList<>(servers); //顺序sncps, others, watchs
Collections.reverse(localServers); //倒序, 必须让watchs先关闭watch包含服务发现和注销逻辑
if (this.messageAgents != null) {
Set<String> names = new HashSet<>();
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent stopping");
long s = System.currentTimeMillis();
for (MessageAgent agent : this.messageAgents) {
names.add(agent.getName());
agent.stop().join();
}
logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") stop in " + (System.currentTimeMillis() - s) + " ms");
}
if (clusterAgent != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent destroying");
long s = System.currentTimeMillis();
clusterAgent.deregister(this);
clusterAgent.destroy(clusterAgent.getConfig());
logger.info("ClusterAgent destroy in " + (System.currentTimeMillis() - s) + " ms");
}
localServers.stream().forEach((server) -> {
try {
server.shutdown();
} catch (Exception t) {
@@ -920,7 +1165,16 @@ public final class Application {
serversLatch.countDown();
}
});
if (this.messageAgents != null) {
Set<String> names = new HashSet<>();
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent destroying");
long s = System.currentTimeMillis();
for (MessageAgent agent : this.messageAgents) {
names.add(agent.getName());
agent.destroy(agent.getConfig());
}
logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") destroy in " + (System.currentTimeMillis() - s) + " ms");
}
for (DataSource source : dataSources) {
if (source == null) continue;
try {

View File

@@ -175,8 +175,9 @@ public final class ClassFilter<T> {
}
if (!r && ors != null) {
for (ClassFilter filter : ors) {
if (filter.accept(property, clazzname)) {
if (filter.accept(filter.conf, clazzname)) {
cf = filter;
property = cf.conf;
break;
}
}
@@ -189,11 +190,11 @@ public final class ClassFilter<T> {
if (property == null) {
property = cf.conf;
} else if (property instanceof DefaultAnyValue) {
((DefaultAnyValue) property).addAll(cf.conf);
((DefaultAnyValue) property).addAllStringSet(cf.conf);
} else {
DefaultAnyValue dav = new DefaultAnyValue();
dav.addAll(property);
dav.addAll(cf.conf);
dav.addAllStringSet(property);
dav.addAllStringSet(cf.conf);
property = dav;
}
}
@@ -207,7 +208,9 @@ public final class ClassFilter<T> {
} catch (Throwable cfe) {
if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
&& !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.") && !clazzname.startsWith("META-INF")
&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) {
&& !clazzname.startsWith("com.mysql.") && !clazzname.startsWith("com.microsoft.")
&& !clazzname.startsWith("org.redkale") && (clazzname.contains("Service") || clazzname.contains("Servlet"))) {
//&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) {
logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error for class: " + clazzname + (url == null ? "" : (" in " + url)), cfe);
}
}
@@ -241,7 +244,7 @@ public final class ClassFilter<T> {
}
if (!r && ors != null) {
for (ClassFilter filter : ors) {
if (filter.accept(property, classname)) return true;
if (filter.accept(filter.conf, classname)) return true;
}
}
return r;
@@ -405,7 +408,7 @@ public final class ClassFilter<T> {
str = str.trim();
if (str.endsWith(";")) str = str.substring(0, str.length() - 1);
}
if (str != null) groups.addAll(Arrays.asList(str.split(";")));
if (str != null) this.groups.addAll(Arrays.asList(str.split(";")));
this.property = property;
this.autoload = autoload;
this.expect = expect;
@@ -415,7 +418,7 @@ public final class ClassFilter<T> {
@Override
public String toString() {
return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName()
+ ", type=" + this.type.getSimpleName() + ", name=" + name + ", groups=" + groups + "]";
+ ", type=" + this.type.getSimpleName() + ", name=" + name + ", groups=" + this.groups + "]";
}
@Override
@@ -442,6 +445,10 @@ public final class ClassFilter<T> {
return property;
}
public boolean containsGroup(String group) {
return groups != null && groups.contains(group);
}
public boolean isEmptyGroups() {
return groups == null || groups.isEmpty();
}
@@ -493,7 +500,7 @@ public final class ClassFilter<T> {
boolean skip = false;
for (Pattern p : excludePatterns) {
if (p.matcher(url.toString()).matches()) {
skip = false;
skip = true;
break;
}
}

View File

@@ -14,6 +14,8 @@ import java.util.logging.Level;
import javax.annotation.*;
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.cluster.ClusterAgent;
import org.redkale.mq.MessageAgent;
import org.redkale.net.*;
import org.redkale.net.http.*;
import org.redkale.net.sncp.Sncp;
@@ -30,7 +32,7 @@ import org.redkale.watch.*;
*
* @author zhangjx
*/
@NodeProtocol({"HTTP"})
@NodeProtocol("HTTP")
public class NodeHttpServer extends NodeServer {
protected final boolean rest; //是否加载REST服务 为true加载rest节点信息并将所有可REST化的Service生成RestServlet
@@ -120,7 +122,15 @@ public class NodeHttpServer extends NodeServer {
resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class));
}
if (nodeService == null) {
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
MessageAgent messageAgent = null;
try {
Field c = src.getClass().getDeclaredField("messageAgent");
c.setAccessible(true);
messageAgent = (MessageAgent) c.get(src);
} catch (Exception ex) {
logger.log(Level.WARNING, "WebSocketServlet getMessageAgent error", ex);
}
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, messageAgent, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
regFactory.register(resourceName, WebSocketNode.class, nodeService);
}
resourceFactory.inject(nodeService, self);
@@ -136,7 +146,7 @@ public class NodeHttpServer extends NodeServer {
@SuppressWarnings("unchecked")
protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter<? extends Filter> classFilter) throws Exception {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
for (FilterEntry<? extends Filter> en : list) {
Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType();
@@ -145,7 +155,7 @@ public class NodeHttpServer extends NodeServer {
resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.httpServer.addHttpFilter(filter, filterConf);
if (sb != null) sb.append(threadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
}
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
}
@@ -158,7 +168,7 @@ public class NodeHttpServer extends NodeServer {
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
final String prefix = prefix0;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
List<FilterEntry<? extends Servlet>> list = new ArrayList(servletFilter.getFilterEntrys());
list.sort((FilterEntry<? extends Servlet> o1, FilterEntry<? extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode
boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType());
@@ -176,7 +186,11 @@ public class NodeHttpServer extends NodeServer {
Class<HttpServlet> clazz = (Class<HttpServlet>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue;
WebServlet ws = clazz.getAnnotation(WebServlet.class);
if (ws == null || ws.value().length == 0) continue;
if (ws == null) continue;
if (ws.value().length == 0) {
logger.log(Level.INFO, "not found @WebServlet.value in " + clazz.getName());
continue;
}
final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(servlet, this);
final String[] mappings = ws.value();
@@ -197,7 +211,7 @@ public class NodeHttpServer extends NodeServer {
if (as.getKey().length() > max) max = as.getKey().length();
}
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
sb.append(threadName).append(" Load ").append(as.getKey());
sb.append(localThreadName).append(" Load ").append(as.getKey());
for (int i = 0; i < max - as.getKey().length(); i++) {
sb.append(' ');
}
@@ -222,11 +236,18 @@ public class NodeHttpServer extends NodeServer {
String prefix0 = restConf.getValue("path", "");
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
final String prefix = prefix0;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
String mqname = restConf.getValue("mq");
MessageAgent agent0 = null;
if (mqname != null) {
agent0 = application.getMessageAgent(mqname);
if (agent0 == null) throw new RuntimeException("not found " + MessageAgent.class.getSimpleName() + " config for (name=" + mqname + ")");
}
final MessageAgent messageAgent = agent0;
if (messageAgent != null) prefix0 = ""; //开启MQ时,prefix字段失效
final String prefix = prefix0;
final boolean autoload = restConf.getBoolValue("autoload", true);
{ //加载RestService
String userTypeStr = restConf.getValue("usertype");
@@ -269,7 +290,9 @@ public class NodeHttpServer extends NodeServer {
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this);
//if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
dynServletMap.put(service, servlet);
if (messageAgent != null) messageAgent.putService(this, service, servlet);
//if (finest) logger.finest(localThreadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {
@@ -322,13 +345,13 @@ public class NodeHttpServer extends NodeServer {
return;
}
restedObjects.add(stype); //避免重复创建Rest对象
HttpServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, prefix, en.getProperty());
WebSocketServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, messageAgent, prefix, en.getProperty());
if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(threadName + " " + stype.getName() + " create a RestWebSocketServlet");
if (finest) logger.finest(localThreadName + " " + stype.getName() + " create a RestWebSocketServlet");
if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {
@@ -338,6 +361,7 @@ public class NodeHttpServer extends NodeServer {
}
}
}
if (messageAgent != null) this.messageAgents.put(messageAgent.getName(), messageAgent);
//输出信息
if (ss != null && !ss.isEmpty() && sb != null) {
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
@@ -346,13 +370,30 @@ public class NodeHttpServer extends NodeServer {
if (as.getKey().length() > max) max = as.getKey().length();
}
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
sb.append(threadName).append(" Load ").append(as.getKey());
sb.append(localThreadName).append(" Load ").append(as.getKey());
for (int i = 0; i < max - as.getKey().length(); i++) {
sb.append(' ');
}
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
}
sb.append(threadName).append(" All HttpServlets load cost " + (System.currentTimeMillis() - starts) + " ms" + LINE_SEPARATOR);
sb.append(localThreadName).append(" All HttpServlets load cost ").append(System.currentTimeMillis() - starts).append(" ms").append(LINE_SEPARATOR);
}
}
@Override //loadServlet执行之后调用
protected void postLoadServlets() {
final ClusterAgent cluster = application.clusterAgent;
if (cluster != null) {
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class);
String protocol = pros.value().toUpperCase();
if (!cluster.containsProtocol(protocol)) return;
if (!cluster.containsPort(server.getSocketAddress().getPort())) return;
cluster.register(this, protocol, dynServletMap.keySet(), new HashSet<>());
}
}
@Override
protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) {
cluster.deregister(this, protocol, dynServletMap.keySet(), new HashSet<>());
}
}

View File

@@ -20,5 +20,5 @@ import java.lang.annotation.*;
@Documented
public @interface NodeProtocol {
String[] value();
String value();
}

View File

@@ -5,6 +5,8 @@
*/
package org.redkale.boot;
import org.redkale.cluster.ClusterAgent;
import org.redkale.mq.MessageAgent;
import org.redkale.util.RedkaleClassLoader;
import java.io.*;
import java.lang.annotation.Annotation;
@@ -27,7 +29,6 @@ import org.redkale.net.sncp.*;
import org.redkale.service.*;
import org.redkale.source.*;
import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
/**
* Server节点的初始化配置类
@@ -67,11 +68,13 @@ public abstract class NodeServer {
private InetSocketAddress sncpAddress;
//加载Service时的处理函数
protected Consumer<Service> consumer;
protected BiConsumer<MessageAgent, Service> consumer;
//server节点的配置
protected AnyValue serverConf;
protected final String threadName;
//加载server节点后的拦截器
protected NodeInterceptor interceptor;
@@ -84,11 +87,20 @@ public abstract class NodeServer {
//远程模式的Service对象集合
protected final Set<Service> remoteServices = new LinkedHashSet<>();
protected final Map<Service, Servlet> dynServletMap = new LinkedHashMap<>();
//MessageAgent对象集合
protected final Map<String, MessageAgent> messageAgents = new HashMap<>();
//需要远程模式Service的MessageAgent对象集合
protected final Map<String, MessageAgent> sncpRemoteAgents = new HashMap<>();
private volatile int maxClassNameLength = 0;
private volatile int maxNameLength = 0;
public NodeServer(Application application, Server server) {
this.threadName = Thread.currentThread().getName();
this.application = application;
this.server = server;
this.resourceFactory = server.getResourceFactory();
@@ -111,7 +123,7 @@ public abstract class NodeServer {
this.serverConf = config == null ? AnyValue.create() : config;
if (isSNCP()) { // SNCP协议
String host = this.serverConf.getValue("host", isWATCH() ? "127.0.0.1" : "0.0.0.0").replace("0.0.0.0", "");
this.sncpAddress = new InetSocketAddress(host.isEmpty() ? application.localAddress.getHostAddress() : host, this.serverConf.getIntValue("port"));
this.sncpAddress = new InetSocketAddress(host.isEmpty() ? application.localAddress.getAddress().getHostAddress() : host, this.serverConf.getIntValue("port"));
this.sncpGroup = application.sncpTransportFactory.findGroupName(this.sncpAddress);
//单向SNCP服务不需要对等group
//if (this.sncpGroup == null) throw new RuntimeException("Server (" + String.valueOf(config).replaceAll("\\s+", " ") + ") not found <group> info");
@@ -142,9 +154,9 @@ public abstract class NodeServer {
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
server.init(this.serverConf);
//init之后才有Executor
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getWorkExecutor());
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
String interceptorClass = this.serverConf.getValue("interceptor", "");
@@ -155,8 +167,8 @@ public abstract class NodeServer {
ClassFilter<Service> serviceFilter = createServiceClassFilter();
if (application.singletonrun) { //singleton模式下只加载指定的Service
final String ssc = System.getProperty("red" + "kale-singleton-serviceclass");
final String extssc = System.getProperty("red" + "kale-singleton-extserviceclasses");
final String ssc = System.getProperty("red" + "kale.singleton.serviceclass");
final String extssc = System.getProperty("red" + "kale.singleton.extserviceclasses");
if (ssc != null) {
final List<String> sscList = new ArrayList<>();
sscList.add(ssc);
@@ -172,13 +184,14 @@ public abstract class NodeServer {
ClassFilter<Servlet> servletFilter = createServletClassFilter();
ClassFilter otherFilter = createOtherClassFilter();
long s = System.currentTimeMillis();
ClassFilter.Loader.load(application.getHome(), serverConf.getValue("excludelibs", "").split(";"), serviceFilter, filterFilter, servletFilter, otherFilter);
ClassFilter.Loader.load(application.getHome(), ((application.excludelibs != null ? (application.excludelibs + ";") : "") + serverConf.getValue("excludelibs", "")).split(";"), serviceFilter, filterFilter, servletFilter, otherFilter);
long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
loadService(serviceFilter, otherFilter); //必须在servlet之前
if (!application.singletonrun) { //非singleton模式下才加载Filter、Servlet
loadFilter(filterFilter, otherFilter);
loadServlet(servletFilter, otherFilter);
postLoadServlets();
}
if (this.interceptor != null) this.resourceFactory.inject(this.interceptor);
}
@@ -198,7 +211,20 @@ public abstract class NodeServer {
if (resources != null) {
for (AnyValue sourceConf : resources.getAnyValues("source")) {
try {
Class type = serverClassLoader.loadClass(sourceConf.getValue("value"));
String classval = sourceConf.getValue("value");
Class type = null;
if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator();
while (it.hasNext()) {
CacheSource s = it.next();
if (s.match(sourceConf)) {
type = s.getClass();
break;
}
}
} else {
type = serverClassLoader.loadClass(classval);
}
if (type == DataSource.class) {
type = DataMemorySource.class;
for (AnyValue itemConf : sourceConf.getAnyValues("property")) {
@@ -271,9 +297,7 @@ public abstract class NodeServer {
SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
}
}
}
@@ -284,20 +308,6 @@ public abstract class NodeServer {
application.dataSources.add(source);
appResFactory.register(resourceName, DataSource.class, source);
SncpClient client = Sncp.getSncpClient((Service) src);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
if ((src instanceof DataSource) && sncpAddr != null && resourceFactory.find(resourceName, DataCacheListener.class) == null) { //只有DataSourceService 才能赋值 DataCacheListener
final NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
Service cacheListenerService = Sncp.createLocalService(serverClassLoader, resourceName, DataCacheListenerService.class, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
localServices.add(cacheListenerService);
sncpServer.consumerAccept(cacheListenerService);
rf.inject(cacheListenerService, self);
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + cacheListenerService);
}
field.set(src, source);
rf.inject(source, self); // 给其可能包含@Resource的字段赋值;
//NodeServer.this.watchFactory.inject(src);
@@ -318,20 +328,32 @@ public abstract class NodeServer {
final Service srcService = (Service) src;
SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
SimpleEntry<Class, AnyValue> resEntry = cacheResource.get(resourceName);
AnyValue sourceConf = resEntry == null ? null : resEntry.getValue();
if (sourceConf == null) {
SimpleEntry<Class, AnyValue> resEntry2 = dataResources.get(resourceName);
sourceConf = resEntry2 == null ? null : resEntry2.getValue();
}
final Class sourceType = sourceConf == null ? CacheMemorySource.class : serverClassLoader.loadClass(sourceConf.getValue("value"));
Class sourceType0 = CacheMemorySource.class;
if (sourceConf != null) {
String classval = sourceConf.getValue("value");
if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator();
while (it.hasNext()) {
CacheSource s = it.next();
if (s.match(sourceConf)) {
sourceType0 = s.getClass();
break;
}
}
} else {
sourceType0 = serverClassLoader.loadClass(classval);
}
}
final Class sourceType = sourceType0;
Object source = null;
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource
source = (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
source = Modifier.isFinal(sourceType.getModifiers()) ? sourceType.getConstructor().newInstance() : (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, null, Sncp.getConf(srcService));
Type genericType = field.getGenericType();
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
Type valType = pt == null ? null : pt.getActualTypeArguments()[0];
@@ -376,18 +398,21 @@ public abstract class NodeServer {
if (nodeService == null) {
final HashSet<String> groups = new HashSet<>();
if (groups.isEmpty() && isSNCP() && NodeServer.this.sncpGroup != null) groups.add(NodeServer.this.sncpGroup);
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getSncpTransportFactory(), NodeServer.this.sncpAddress, groups, (AnyValue) null);
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, Sncp.getMessageAgent((Service) src), application.getResourceFactory(), application.getSncpTransportFactory(), NodeServer.this.sncpAddress, groups, (AnyValue) null);
(isSNCP() ? appResFactory : resourceFactory).register(resourceName, WebSocketNode.class, nodeService);
((WebSocketNodeService) nodeService).setName(resourceName);
}
resourceFactory.inject(nodeService, self);
MessageAgent messageAgent = Sncp.getMessageAgent((Service) src);
if (messageAgent != null && Sncp.getMessageAgent(nodeService) == null) Sncp.setMessageAgent(nodeService, messageAgent);
field.set(src, nodeService);
if (Sncp.isRemote(nodeService)) {
remoteServices.add(nodeService);
} else {
if (field != null) rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源
rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源
localServices.add(nodeService);
interceptorServices.add(nodeService);
if (consumer != null) consumer.accept(nodeService);
if (consumer != null) consumer.accept(null, nodeService);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
@@ -405,7 +430,7 @@ public abstract class NodeServer {
protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
if (serviceFilter == null) return;
final long starts = System.currentTimeMillis();
final String threadName = "[" + Thread.currentThread().getName() + "] ";
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
final Set<FilterEntry<? extends Service>> entrys = (Set) serviceFilter.getAllFilterEntrys();
ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory;
final ResourceFactory appResourceFactory = application.getResourceFactory();
@@ -418,12 +443,11 @@ public abstract class NodeServer {
if (Modifier.isAbstract(serviceImplClass.getModifiers())) continue; //修饰abstract的类跳过
if (DataSource.class.isAssignableFrom(serviceImplClass)) continue;
if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue;
if (DataCacheListener.class.isAssignableFrom(serviceImplClass)) continue;
}
if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
Service oldother = resourceFactory.find(entry.getName(), serviceImplClass);
if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。
interceptorServices.add(oldother);
if (!Sncp.isRemote(oldother)) interceptorServices.add(oldother);
continue;
}
final HashSet<String> groups = entry.getGroups(); //groups.isEmpty()表示<services>没有配置groups属性。
@@ -437,16 +461,28 @@ public abstract class NodeServer {
final ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
try {
if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) { //class没有可用的方法且没有标记启动优先级的 通常为BaseService
if (!serviceImplClass.getName().startsWith("org.redkale.")) logger.log(Level.FINE, serviceImplClass + " cannot load because not found less one public non-final method");
if (!serviceImplClass.getName().startsWith("org.redkale.") && !serviceImplClass.getSimpleName().contains("Base")) {
logger.log(Level.FINE, serviceImplClass + " cannot load because not found less one public non-final method");
}
return;
}
MessageAgent agent = null;
if (entry.getProperty() != null && entry.getProperty().getValue("mq") != null) {
agent = application.getMessageAgent(entry.getProperty().getValue("mq"));
if (agent != null) messageAgents.put(agent.getName(), agent);
}
Service service;
boolean ws = src instanceof WebSocketServlet;
final boolean ws = src instanceof WebSocketServlet;
if (ws || localed) { //本地模式
service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, appResourceFactory, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, agent, appResourceFactory, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
} else {
service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, agent, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
}
if (service instanceof WebSocketNodeService) {
((WebSocketNodeService) service).setName(resourceName);
if (agent != null) Sncp.setMessageAgent(service, agent);
}
final Class restype = Sncp.getResourceType(service);
if (rf.find(resourceName, restype) == null) {
@@ -456,11 +492,12 @@ public abstract class NodeServer {
}
if (Sncp.isRemote(service)) {
remoteServices.add(service);
if (agent != null) sncpRemoteAgents.put(agent.getName(), agent);
} else {
if (field != null) rf.inject(service); //动态加载的Service也存在按需加载的注入资源
localServices.add(service);
interceptorServices.add(service);
if (consumer != null) consumer.accept(service);
if (consumer != null) consumer.accept(agent, service);
}
} catch (RuntimeException ex) {
throw ex;
@@ -493,7 +530,13 @@ public abstract class NodeServer {
if (sb != null) {
remoteServices.forEach(y -> {
sb.append(threadName).append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" load and inject").append(LINE_SEPARATOR);
sb.append(localThreadName).append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" load and inject").append(LINE_SEPARATOR);
});
}
if (isSNCP() && !sncpRemoteAgents.isEmpty()) {
sncpRemoteAgents.values().forEach(agent -> {
// agent.putSncpResp((NodeSncpServer) this);
// agent.startSncpRespConsumer();
});
}
//----------------- init -----------------
@@ -510,21 +553,25 @@ public abstract class NodeServer {
localServices.clear();
localServices.addAll(swlist);
//this.loadPersistData();
long preinits = System.currentTimeMillis();
preInitServices(localServices, remoteServices);
long preinite = System.currentTimeMillis() - preinits;
final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>();
localServices.stream().forEach(y -> {
long s = System.currentTimeMillis();
y.init(Sncp.getConf(y));
long e = System.currentTimeMillis() - s;
String serstr = Sncp.toSimpleString(y, maxNameLength, maxClassNameLength);
if (slist != null) slist.add(new StringBuilder().append(threadName).append(serstr).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
if (slist != null) slist.add(new StringBuilder().append(localThreadName).append(serstr).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
});
if (slist != null && sb != null) {
List<String> wlist = new ArrayList<>(slist); //直接使用CopyOnWriteArrayList偶尔会出现莫名的异常(CopyOnWriteArrayList源码1185行)
for (String s : wlist) {
sb.append(s);
}
sb.append(threadName).append("All Services load cost " + (System.currentTimeMillis() - starts) + " ms" + LINE_SEPARATOR);
sb.append(localThreadName).append("All Services load cost ").append(System.currentTimeMillis() - starts).append(" ms" + LINE_SEPARATOR);
}
if (sb != null && preinite > 10) sb.append(localThreadName).append(ClusterAgent.class.getSimpleName()).append(" register ").append(preinite).append(" ms" + LINE_SEPARATOR);
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
}
@@ -533,6 +580,44 @@ public abstract class NodeServer {
maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1);
}
//Service.init执行之前调用
protected void preInitServices(Set<Service> localServices, Set<Service> remoteServices) {
final ClusterAgent cluster = application.clusterAgent;
if (cluster == null) return;
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class);
String protocol = pros.value().toUpperCase();
if (!cluster.containsProtocol(protocol)) return;
if (!cluster.containsPort(server.getSocketAddress().getPort())) return;
cluster.register(this, protocol, localServices, remoteServices);
}
//loadServlet执行之后调用
protected void postLoadServlets() {
}
//Service.destroy执行之前调用
protected void preDestroyServices(Set<Service> localServices, Set<Service> remoteServices) {
if (application.clusterAgent != null) { //服务注销
final ClusterAgent cluster = application.clusterAgent;
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class);
String protocol = pros.value().toUpperCase();
if (cluster.containsProtocol(protocol) && cluster.containsPort(server.getSocketAddress().getPort())) {
cluster.deregister(this, protocol, localServices, remoteServices);
afterClusterDeregisterOnPreDestroyServices(cluster, protocol);
}
}
if (!this.messageAgents.isEmpty()) { //MQ
}
}
protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) {
}
//Server.start执行之后调用
protected void postStartServer(Set<Service> localServices, Set<Service> remoteServices) {
}
protected abstract ClassFilter<Filter> createFilterClassFilter();
protected abstract ClassFilter<Servlet> createServletClassFilter();
@@ -563,22 +648,24 @@ public abstract class NodeServer {
}
cf = null;
for (AnyValue list : proplist) {
DefaultAnyValue prop = null;
AnyValue.DefaultAnyValue prop = null;
String sc = list.getValue("groups");
String mq = list.getValue("mq");
if (sc != null) {
sc = sc.trim();
if (sc.endsWith(";")) sc = sc.substring(0, sc.length() - 1);
}
if (sc == null) sc = localGroup;
if (sc != null) {
if (sc != null || mq != null) {
prop = new AnyValue.DefaultAnyValue();
prop.addValue("groups", sc);
if (sc != null) prop.addValue("groups", sc);
if (mq != null) prop.addValue("mq", mq);
}
ClassFilter filter = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, prop);
for (AnyValue av : list.getAnyValues(property)) { // <service>、<filter>、<servlet> 节点
final AnyValue[] items = av.getAnyValues("property");
if (av instanceof DefaultAnyValue && items.length > 0) { //存在 <property>节点
DefaultAnyValue dav = DefaultAnyValue.create();
if (av instanceof AnyValue.DefaultAnyValue && items.length > 0) { //存在 <property>节点
AnyValue.DefaultAnyValue dav = AnyValue.DefaultAnyValue.create();
final AnyValue.Entry<String>[] strings = av.getStringEntrys();
if (strings != null) { //将<service>、<filter>、<servlet>节点的属性值传给dav
for (AnyValue.Entry<String> en : strings) {
@@ -591,7 +678,7 @@ public abstract class NodeServer {
if (!"property".equals(en.name)) dav.addValue(en.name, en.getValue());
}
}
DefaultAnyValue ps = DefaultAnyValue.create();
AnyValue.DefaultAnyValue ps = AnyValue.DefaultAnyValue.create();
for (AnyValue item : items) {
ps.addValue(item.getValue("name"), item.getValue("value"));
}
@@ -627,6 +714,10 @@ public abstract class NodeServer {
return false;
}
public Application getApplication() {
return application;
}
public ResourceFactory getResourceFactory() {
return resourceFactory;
}
@@ -660,12 +751,14 @@ public abstract class NodeServer {
public void start() throws IOException {
if (interceptor != null) interceptor.preStart(this);
server.start();
postStartServer(localServices, remoteServices);
}
public void shutdown() throws IOException {
if (interceptor != null) interceptor.preShutdown(this);
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final boolean finest = logger.isLoggable(Level.FINEST);
preDestroyServices(localServices, remoteServices);
localServices.forEach(y -> {
long s = System.currentTimeMillis();
if (finest) logger.finest(y + " is destroying");
@@ -680,6 +773,42 @@ public abstract class NodeServer {
server.shutdown();
}
public void command(String cmd) throws IOException {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final boolean finest = logger.isLoggable(Level.FINEST);
localServices.forEach(y -> {
Set<Method> methods = new HashSet<>();
Class loop = y.getClass();
//do { public方法不用递归
for (Method m : loop.getMethods()) {
Command c = m.getAnnotation(Command.class);
if (c == null) continue;
if (Modifier.isStatic(m.getModifiers())) continue;
if (m.getReturnType() != void.class) continue;
if (m.getParameterCount() != 1) continue;
if (m.getParameterTypes()[0] != String.class) continue;
methods.add(m);
}
//} while ((loop = loop.getSuperclass()) != Object.class);
if (methods.isEmpty()) return;
long s = System.currentTimeMillis();
Method one = null;
try {
for (Method method : methods) {
one = method;
method.invoke(y, cmd);
}
} catch (Exception ex) {
logger.log(Level.SEVERE, one + " run error, cmd = " + cmd, ex);
}
long e = System.currentTimeMillis() - s;
if (e > 10 && sb != null) {
sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" command (").append(cmd).append(") ").append(e).append("ms").append(LINE_SEPARATOR);
}
});
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
}
public <T extends Server> T getServer() {
return (T) server;
}
@@ -696,4 +825,7 @@ public abstract class NodeServer {
return new LinkedHashSet<>(remoteServices);
}
public String getThreadName() {
return this.threadName;
}
}

View File

@@ -10,9 +10,10 @@ import java.net.*;
import java.util.*;
import java.util.logging.Level;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.mq.MessageAgent;
import org.redkale.net.*;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.service.*;
import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
@@ -24,7 +25,7 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
*
* @author zhangjx
*/
@NodeProtocol({"SNCP"})
@NodeProtocol("SNCP")
public class NodeSncpServer extends NodeServer {
protected final SncpServer sncpServer;
@@ -32,14 +33,15 @@ public class NodeSncpServer extends NodeServer {
private NodeSncpServer(Application application, AnyValue serconf) {
super(application, createServer(application, serconf));
this.sncpServer = (SncpServer) this.server;
this.consumer = sncpServer == null || application.singletonrun ? null : x -> sncpServer.addSncpServlet(x); //singleton模式下不生成SncpServlet
this.consumer = sncpServer == null || application.singletonrun ? null : (agent, x) -> {//singleton模式下不生成SncpServlet
if (x.getClass().getAnnotation(Local.class) != null) return;
SncpDynServlet servlet = sncpServer.addSncpServlet(x);
dynServletMap.put(x, servlet);
if (agent != null) agent.putService(this, x, servlet);
};
}
public static NodeServer createNodeServer(Application application, AnyValue serconf) {
if (serconf != null && serconf.getAnyValue("rest") != null) {
((AnyValue.DefaultAnyValue) serconf).addValue("_$sncp", "true");
return new NodeHttpServer(application, serconf);
}
return new NodeSncpServer(application, serconf);
}
@@ -52,8 +54,8 @@ public class NodeSncpServer extends NodeServer {
return sncpServer == null ? null : sncpServer.getSocketAddress();
}
public void consumerAccept(Service service) {
if (this.consumer != null) this.consumer.accept(service);
public void consumerAccept(MessageAgent messageAgent, Service service) {
if (this.consumer != null) this.consumer.accept(messageAgent, service);
}
@Override
@@ -62,11 +64,11 @@ public class NodeSncpServer extends NodeServer {
//-------------------------------------------------------------------
if (sncpServer == null) return; //调试时server才可能为null
final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
List<SncpServlet> servlets = sncpServer.getSncpServlets();
Collections.sort(servlets);
for (SncpServlet en : servlets) {
if (sb != null) sb.append(threadName).append(" Load ").append(en).append(LINE_SEPARATOR);
if (sb != null) sb.append(localThreadName).append(" Load ").append(en).append(LINE_SEPARATOR);
}
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
}
@@ -88,7 +90,7 @@ public class NodeSncpServer extends NodeServer {
@SuppressWarnings("unchecked")
protected void loadSncpFilter(final AnyValue servletsConf, final ClassFilter<? extends Filter> classFilter) throws Exception {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
for (FilterEntry<? extends Filter> en : list) {
Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType();
@@ -97,7 +99,7 @@ public class NodeSncpServer extends NodeServer {
resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.sncpServer.addSncpFilter(filter, filterConf);
if (sb != null) sb.append(threadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
}
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
}

View File

@@ -16,7 +16,7 @@ import org.redkale.watch.*;
*
* @author zhangjx
*/
@NodeProtocol({"WATCH"})
@NodeProtocol("WATCH")
public class NodeWatchServer extends NodeHttpServer {
public NodeWatchServer(Application application, AnyValue serconf) {

View File

@@ -81,7 +81,7 @@ public class ServerWatchService extends AbstractWatchService {
protocol += "/HTTP";
} else {
NodeProtocol np = node.getClass().getAnnotation(NodeProtocol.class);
if (np != null && np.value().length > 0) protocol += "/" + np.value()[0];
protocol += "/" + np.value();
}
rs.put("name", server.getName());
rs.put("protocol", protocol);

View File

@@ -72,15 +72,6 @@ public class TransportWatchService extends AbstractWatchService {
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
client.getRemoteGroupTransport().addRemoteAddresses(address);
}
} else {
if (group.equals(client.getSameGroup())) {
client.getSameGroupTransport().addRemoteAddresses(address);
}
if (client.getDiffGroups() != null && client.getDiffGroups().contains(group)) {
for (Transport transport : client.getDiffGroupTransports()) {
transport.addRemoteAddresses(address);
}
}
}
}
DefaultAnyValue node = DefaultAnyValue.create("addr", addr).addValue("port", port);
@@ -114,15 +105,6 @@ public class TransportWatchService extends AbstractWatchService {
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
client.getRemoteGroupTransport().removeRemoteAddresses(address);
}
} else {
if (group.equals(client.getSameGroup())) {
client.getSameGroupTransport().removeRemoteAddresses(address);
}
if (client.getDiffGroups() != null && client.getDiffGroups().contains(group)) {
for (Transport transport : client.getDiffGroupTransports()) {
transport.removeRemoteAddresses(address);
}
}
}
}
for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) {

View File

@@ -0,0 +1,328 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.cluster;
import java.lang.ref.WeakReference;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Logger;
import javax.annotation.Resource;
import org.redkale.boot.*;
import static org.redkale.boot.Application.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.mq.MessageMultiConsumer;
import org.redkale.net.*;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.*;
import org.redkale.util.*;
/**
* 第三方服务发现管理接口cluster
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public abstract class ClusterAgent {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
@Resource(name = RESNAME_APP_NODEID)
protected int nodeid;
@Resource(name = RESNAME_APP_NAME)
protected String appName = "";
@Resource(name = RESNAME_APP_ADDR)
protected InetSocketAddress appAddress;
protected String name;
protected boolean waits;
protected String[] protocols; //必须全大写
protected int[] ports;
protected AnyValue config;
protected TransportFactory transportFactory;
protected final ConcurrentHashMap<String, ClusterEntry> localEntrys = new ConcurrentHashMap<>();
protected final ConcurrentHashMap<String, ClusterEntry> remoteEntrys = new ConcurrentHashMap<>();
public void init(AnyValue config) {
this.config = config;
this.name = config.getValue("name", "");
this.waits = config.getBoolValue("waits", false);
{
String ps = config.getValue("protocols", "").toUpperCase();
if (ps == null || ps.isEmpty()) ps = "SNCP;HTTP";
this.protocols = ps.split(";");
}
String ts = config.getValue("ports", "");
if (ts != null && !ts.isEmpty()) {
String[] its = ts.split(";");
List<Integer> list = new ArrayList<>();
for (String str : its) {
if (str.trim().isEmpty()) continue;
list.add(Integer.parseInt(str.trim()));
}
if (!list.isEmpty()) this.ports = list.stream().mapToInt(x -> x).toArray();
}
}
public void destroy(AnyValue config) {
}
//ServiceLoader时判断配置是否符合当前实现类
public abstract boolean match(AnyValue config);
public boolean containsProtocol(String protocol) {
if (protocol == null || protocol.isEmpty()) return false;
return protocols == null || Utility.contains(protocols, protocol.toUpperCase());
}
public boolean containsPort(int port) {
if (ports == null || ports.length == 0) return true;
return Utility.contains(ports, port);
}
public abstract void register(Application application);
public abstract void deregister(Application application);
//注册服务, 在NodeService调用Service.init方法之前调用
public void register(NodeServer ns, String protocol, Set<Service> localServices, Set<Service> remoteServices) {
if (localServices.isEmpty()) return;
//注册本地模式
for (Service service : localServices) {
if (!canRegister(protocol, service)) continue;
register(ns, protocol, service);
ClusterEntry htentry = new ClusterEntry(ns, protocol, service);
localEntrys.put(htentry.serviceid, htentry);
if (protocol.toLowerCase().startsWith("http")) {
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
if (mmc != null) {
register(ns, "mqtp", service);
ClusterEntry mqentry = new ClusterEntry(ns, "mqtp", service);
localEntrys.put(mqentry.serviceid, mqentry);
}
}
}
//远程模式加载IP列表, 只支持SNCP协议
if (ns.isSNCP()) {
for (Service service : remoteServices) {
ClusterEntry entry = new ClusterEntry(ns, protocol, service);
updateSncpTransport(entry);
remoteEntrys.put(entry.serviceid, entry);
}
}
}
//注销服务, 在NodeService调用Service.destroy 方法之前调用
public void deregister(NodeServer ns, String protocol, Set<Service> localServices, Set<Service> remoteServices) {
//注销本地模式 远程模式不注册
for (Service service : localServices) {
if (!canRegister(protocol, service)) continue;
deregister(ns, protocol, service);
}
afterDeregister(ns, protocol);
}
protected boolean canRegister(String protocol, Service service) {
if ("SNCP".equalsIgnoreCase(protocol) && service.getClass().getAnnotation(Local.class) != null) return false;
if (service instanceof WebSocketNode) {
if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false;
}
return true;
}
public void start() {
}
protected void afterDeregister(NodeServer ns, String protocol) {
if (!this.waits) return;
int s = intervalCheckSeconds();
if (s > 0) { //暂停,弥补其他依赖本进程服务的周期偏差
try {
Thread.sleep(s * 1000);
} catch (InterruptedException ex) {
}
logger.info(this.getClass().getSimpleName() + " wait for " + s * 1000 + "ms after deregister");
}
}
public int intervalCheckSeconds() {
return 10;
}
//获取MQTP的HTTP远程服务的可用ip列表, key = servicename的后半段
public abstract CompletableFuture<Map<String, Collection<InetSocketAddress>>> queryMqtpAddress(String protocol, String module, String resname);
//获取HTTP远程服务的可用ip列表
public abstract CompletableFuture<Collection<InetSocketAddress>> queryHttpAddress(String protocol, String module, String resname);
//获取远程服务的可用ip列表
protected abstract CompletableFuture<Collection<InetSocketAddress>> queryAddress(ClusterEntry entry);
//注册服务
protected abstract void register(NodeServer ns, String protocol, Service service);
//注销服务
protected abstract void deregister(NodeServer ns, String protocol, Service service);
//格式: protocol:classtype-resourcename
protected void updateSncpTransport(ClusterEntry entry) {
Service service = entry.serviceref.get();
if (service == null) return;
Collection<InetSocketAddress> addrs = ClusterAgent.this.queryAddress(entry).join();
Sncp.updateTransport(service, transportFactory, Sncp.getResourceType(service).getName() + "-" + Sncp.getResourceName(service), entry.netprotocol, entry.address, null, addrs);
}
protected String generateApplicationServiceName() {
return "application" + (appName == null || appName.isEmpty() ? "" : ("." + appName)) + ".node" + this.nodeid;
}
protected String generateApplicationServiceId() { //与servicename相同
return generateApplicationServiceName();
}
protected String generateApplicationCheckName() {
return "check-" + generateApplicationServiceName();
}
protected String generateApplicationCheckId() {
return "check-" + generateApplicationServiceId();
}
//也会提供给HttpMessageClusterAgent适用
public String generateHttpServiceName(String protocol, String module, String resname) {
return protocol.toLowerCase() + ":" + module + (resname == null || resname.isEmpty() ? "" : ("-" + resname));
}
//格式: protocol:classtype-resourcename
protected String generateServiceName(NodeServer ns, String protocol, Service service) {
if (protocol.toLowerCase().startsWith("http")) { //HTTP使用RestService.name方式是为了与MessageClient中的module保持一致, 因为HTTP依靠的url中的module无法知道Service类名
String resname = Sncp.getResourceName(service);
String module = Rest.getRestModule(service).toLowerCase();
return protocol.toLowerCase() + ":" + module + (resname.isEmpty() ? "" : ("-" + resname));
}
if ("mqtp".equalsIgnoreCase(protocol)) {
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
String selfmodule = Rest.getRestModule(service).toLowerCase();
return protocol.toLowerCase() + ":" + mmc.module() + ":" + selfmodule;
}
if (!Sncp.isSncpDyn(service)) return protocol.toLowerCase() + ":" + service.getClass().getName();
String resname = Sncp.getResourceName(service);
return protocol.toLowerCase() + ":" + Sncp.getResourceType(service).getName() + (resname.isEmpty() ? "" : ("-" + resname));
}
//格式: protocol:classtype-resourcename:nodeid
protected String generateServiceId(NodeServer ns, String protocol, Service service) {
return generateServiceName(ns, protocol, service) + ":" + this.nodeid;
}
protected String generateCheckName(NodeServer ns, String protocol, Service service) {
return "check-" + generateServiceName(ns, protocol, service);
}
protected String generateCheckId(NodeServer ns, String protocol, Service service) {
return "check-" + generateServiceId(ns, protocol, service);
}
protected ConcurrentHashMap<String, ClusterEntry> getLocalEntrys() {
return localEntrys;
}
protected ConcurrentHashMap<String, ClusterEntry> getRemoteEntrys() {
return remoteEntrys;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
public TransportFactory getTransportFactory() {
return transportFactory;
}
public void setTransportFactory(TransportFactory transportFactory) {
this.transportFactory = transportFactory;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getProtocols() {
return protocols;
}
public void setProtocols(String[] protocols) {
this.protocols = protocols;
}
public int[] getPorts() {
return ports;
}
public void setPorts(int[] ports) {
this.ports = ports;
}
public AnyValue getConfig() {
return config;
}
public void setConfig(AnyValue config) {
this.config = config;
}
public class ClusterEntry {
public String serviceid;
public String servicename;
public String checkid;
public String checkname;
public String protocol;
public String netprotocol;
public WeakReference<Service> serviceref;
public InetSocketAddress address;
public ScheduledFuture checkScheduledFuture;
public ClusterEntry(NodeServer ns, String protocol, Service service) {
this.serviceid = generateServiceId(ns, protocol, service);
this.servicename = generateServiceName(ns, protocol, service);
this.checkid = generateCheckId(ns, protocol, service);
this.checkname = generateCheckName(ns, protocol, service);
this.protocol = protocol;
this.address = ns.getSocketAddress();
this.serviceref = new WeakReference(service);
Server server = ns.getServer();
this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_PROTOCOL;
}
}
}

View File

@@ -54,6 +54,10 @@ public abstract class Convert<R extends Reader, W extends Writer> {
public abstract <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers);
public abstract byte[] convertToBytes(final Object value);
public abstract byte[] convertToBytes(final Type type, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value);

View File

@@ -17,8 +17,10 @@ public enum ConvertType {
JSON(1),
BSON(2),
DIY(64),
ALL(127);
PROTOBUF(64),
PROTOBUF_JSON(64 + 1),
DIY(256),
ALL(1023);
private final int value;
@@ -26,8 +28,19 @@ public enum ConvertType {
this.value = v;
}
public int getValue() {
return value;
}
public boolean contains(ConvertType type) {
if (type == null) return false;
return this.value >= type.value && (this.value & type.value) > 0;
}
public static ConvertType find(int value) {
for (ConvertType t : ConvertType.values()) {
if (value == t.value) return t;
}
return null;
}
}

View File

@@ -101,7 +101,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
fieldCoder = factory.loadDecoder(t);
}
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), fieldCoder);
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, field, null, null), fieldCoder);
if (ref != null) member.index = ref.getIndex();
list.add(member);
}
@@ -131,7 +131,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type);
fieldCoder = factory.loadDecoder(t);
}
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), fieldCoder);
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, null, null, method), fieldCoder);
if (ref != null) member.index = ref.getIndex();
list.add(member);
}
@@ -149,7 +149,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
try {
Field f = clazz.getDeclaredField(constructorField);
Type t = TypeToken.createClassType(f.getGenericType(), this.type);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, f, null, null), factory.loadDecoder(t)));
list.add(new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, f, null, null), factory.loadDecoder(t)));
} catch (NoSuchFieldException nsfe) { //不存在field 可能存在getter方法
char[] fs = constructorField.toCharArray();
fs[0] = Character.toUpperCase(fs[0]);
@@ -161,7 +161,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
getter = clazz.getMethod("is" + mn);
}
Type t = TypeToken.createClassType(TypeToken.getGenericType(getter.getGenericParameterTypes()[0], this.type), this.type);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, getter, null), factory.loadDecoder(t)));
list.add(new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, null, getter, null), factory.loadDecoder(t)));
}
}
}

View File

@@ -82,7 +82,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
fieldCoder = factory.loadEncoder(t);
}
EnMember member = new EnMember(createAttribute(factory, clazz, field, null, null), fieldCoder);
EnMember member = new EnMember(createAttribute(factory, type, clazz, field, null, null), fieldCoder);
if (ref != null) member.index = ref.getIndex();
list.add(member);
}
@@ -111,7 +111,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type);
fieldCoder = factory.loadEncoder(t);
}
EnMember member = new EnMember(createAttribute(factory, clazz, null, method, null), fieldCoder);
EnMember member = new EnMember(createAttribute(factory, type, clazz, null, method, null), fieldCoder);
if (ref != null) member.index = ref.getIndex();
list.add(member);
}
@@ -263,7 +263,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
}
}
static Attribute createAttribute(final ConvertFactory factory, Class clazz, final Field field, final Method getter, final Method setter) {
static Attribute createAttribute(final ConvertFactory factory, Type realType, Class clazz, final Field field, final Method getter, final Method setter) {
String fieldalias;
if (field != null) { // public field
ConvertColumnEntry ref = factory.findRef(clazz, field);
@@ -289,7 +289,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
}
fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name();
}
return Attribute.create(clazz, fieldalias, field, getter, setter);
return Attribute.create(realType, clazz, fieldalias, null, field, getter, setter, null);
}
}

View File

@@ -20,9 +20,7 @@ import java.util.*;
*/
public class OptionalCoder<R extends Reader, W extends Writer, T> extends SimpledCoder<R, W, Optional<T>> {
private final Type type;
private final Type componentType;
protected final Type componentType;
protected final Class componentClass;

View File

@@ -177,6 +177,16 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
return result;
}
@Override
public byte[] convertToBytes(final Object value) {
return convertTo(value);
}
@Override
public byte[] convertToBytes(final Type type, final Object value) {
return convertTo(type, value);
}
@Override
public byte[] convertMapTo(final Object... values) {
if (values == null) return null;

View File

@@ -69,6 +69,10 @@ public class JsonByteBufferReader extends JsonReader {
*/
@Override
protected final char nextChar() {
return nextChar(null);
}
protected final char nextChar(StringBuilder sb) {
if (currentChar != 0) {
char ch = currentChar;
this.currentChar = 0;
@@ -78,14 +82,18 @@ public class JsonByteBufferReader extends JsonReader {
int remain = this.currentBuffer.remaining();
if (remain == 0 && this.currentIndex + 1 >= this.buffers.length) return 0;
}
byte b1 = nextByte();
if (b1 >= 0) {// 1 byte, 7 bits: 0xxxxxxx
return (char) b1;
} else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { // 2 bytes, 11 bits: 110xxxxx 10xxxxxx
return (char) (((b1 << 6) ^ nextByte()) ^ (((byte) 0xC0 << 6) ^ ((byte) 0x80)));
} else if ((b1 >> 4) == -2) { // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx
return (char) ((b1 << 12) ^ (nextByte() << 6) ^ (nextByte() ^ (((byte) 0xE0 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80))));
} else { // 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
byte b = nextByte();
if (b >= 0) {// 1 byte, 7 bits: 0xxxxxxx
return (char) b;
} else if ((b >> 5) == -2 && (b & 0x1e) != 0) { // 2 bytes, 11 bits: 110xxxxx 10xxxxxx
return (char) (((b << 6) ^ nextByte()) ^ (((byte) 0xC0 << 6) ^ ((byte) 0x80)));
} else if ((b >> 4) == -2) { // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx
return (char) ((b << 12) ^ (nextByte() << 6) ^ (nextByte() ^ (((byte) 0xE0 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80))));
} else if ((b >> 3) == -2) {// 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
int uc = ((b << 18) ^ (nextByte() << 12) ^ (nextByte() << 6) ^ (nextByte() ^ (((byte) 0xF0 << 18) ^ ((byte) 0x80 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80))));
if (sb != null) sb.append(Character.highSurrogate(uc));
return Character.lowSurrogate(uc);
} else {
throw new RuntimeException(new UnmappableCharacterException(4));
}
}
@@ -208,9 +216,9 @@ public class JsonByteBufferReader extends JsonReader {
if (ch == '"' || ch == '\'') {
final char quote = ch;
for (;;) {
ch = nextChar();
ch = nextChar(sb);
if (ch == '\\') {
char c = nextChar();
char c = nextChar(sb);
switch (c) {
case '"':
case '\'':
@@ -249,9 +257,9 @@ public class JsonByteBufferReader extends JsonReader {
} else {
sb.append(ch);
for (;;) {
ch = nextChar();
ch = nextChar(sb);
if (ch == '\\') {
char c = nextChar();
char c = nextChar(sb);
switch (c) {
case '"':
case '\'':

View File

@@ -11,6 +11,7 @@ import java.nio.*;
import java.nio.charset.*;
import java.util.function.*;
import org.redkale.convert.*;
import org.redkale.service.RetResult;
import org.redkale.util.*;
/**
@@ -26,6 +27,9 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.HashMap<String, String>>() {
}.getType();
public static final Type TYPE_RETRESULT_STRING = new TypeToken<RetResult<String>>() {
}.getType();
private static final ObjectPool<JsonReader> readerPool = JsonReader.createPool(Integer.getInteger("convert.json.pool.size", 16));
private static final ObjectPool<JsonWriter> writerPool = JsonWriter.createPool(Integer.getInteger("convert.json.pool.size", 16));
@@ -214,6 +218,25 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
return result;
}
@Override
public byte[] convertToBytes(final Object value) {
if (value == null) return null;
String result = convertTo(value.getClass(), value);
return result == null ? null : result.getBytes(StandardCharsets.UTF_8);
}
@Override
public byte[] convertToBytes(final Type type, final Object value) {
if (type == null) return null;
if (value == null) return null;
final JsonWriter writer = pollJsonWriter();
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
String result = writer.toString();
writerPool.accept(writer);
return result == null ? null : result.getBytes(StandardCharsets.UTF_8);
}
@Override
public String convertMapTo(final Object... values) {
if (values == null) return "null";

View File

@@ -0,0 +1,238 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
import org.redkale.convert.ConvertType;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.*;
/**
* 不依赖MessageRecord则可兼容RPC方式
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpMessageClient extends MessageClient {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
protected HttpMessageClient(MessageAgent messageAgent) {
super(messageAgent);
if (messageAgent != null) { // //RPC方式下无messageAgent
this.respTopic = messageAgent.generateHttpRespTopic();
}
}
//格式: http.req.user
public String generateHttpReqTopic(String module) {
return MessageAgent.generateHttpReqTopic(module);
}
//格式: http.req.user-n10
public String generateHttpReqTopic(String module, String resname) {
return MessageAgent.generateHttpReqTopic(module, resname);
}
public String generateHttpReqTopic(HttpSimpleRequest request, String path) {
String module = request.getRequestURI();
if (path != null && !path.isEmpty() && module.startsWith(path)) module = module.substring(path.length());
module = module.substring(1); //去掉/
module = module.substring(0, module.indexOf('/'));
Map<String, String> headers = request.getHeaders();
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return MessageAgent.generateHttpReqTopic(module, resname);
}
public final void produceMessage(HttpSimpleRequest request) {
produceMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, null);
}
public final void produceMessage(HttpSimpleRequest request, AtomicLong counter) {
produceMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, counter);
}
public final void produceMessage(int userid, HttpSimpleRequest request) {
produceMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, null, request, null);
}
public final void produceMessage(int userid, String groupid, HttpSimpleRequest request) {
produceMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, null);
}
public final void produceMessage(int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
produceMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, counter);
}
public final void produceMessage(String topic, HttpSimpleRequest request) {
produceMessage(topic, ConvertType.JSON, 0, null, request, null);
}
public final void produceMessage(String topic, HttpSimpleRequest request, AtomicLong counter) {
produceMessage(topic, ConvertType.JSON, 0, null, request, counter);
}
public final void produceMessage(String topic, ConvertType convertType, HttpSimpleRequest request) {
produceMessage(topic, convertType, 0, null, request, null);
}
public final void produceMessage(String topic, ConvertType convertType, HttpSimpleRequest request, AtomicLong counter) {
produceMessage(topic, convertType, 0, null, request, counter);
}
public final void produceMessage(String topic, int userid, String groupid, HttpSimpleRequest request) {
produceMessage(topic, ConvertType.JSON, userid, groupid, request, null);
}
public final void produceMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
produceMessage(topic, ConvertType.JSON, userid, groupid, request, counter);
}
public final void produceMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request) {
produceMessage(topic, convertType, userid, groupid, request, null);
}
public final void broadcastMessage(HttpSimpleRequest request) {
broadcastMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, null);
}
public final void broadcastMessage(HttpSimpleRequest request, AtomicLong counter) {
broadcastMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, counter);
}
public final void broadcastMessage(int userid, HttpSimpleRequest request) {
broadcastMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, null, request, null);
}
public final void broadcastMessage(int userid, String groupid, HttpSimpleRequest request) {
broadcastMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, null);
}
public final void broadcastMessage(int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
broadcastMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, counter);
}
public final void broadcastMessage(String topic, HttpSimpleRequest request) {
broadcastMessage(topic, ConvertType.JSON, 0, null, request, null);
}
public final void broadcastMessage(String topic, HttpSimpleRequest request, AtomicLong counter) {
broadcastMessage(topic, ConvertType.JSON, 0, null, request, counter);
}
public final void broadcastMessage(String topic, ConvertType convertType, HttpSimpleRequest request) {
broadcastMessage(topic, convertType, 0, null, request, null);
}
public final void broadcastMessage(String topic, ConvertType convertType, HttpSimpleRequest request, AtomicLong counter) {
broadcastMessage(topic, convertType, 0, null, request, counter);
}
public final void broadcastMessage(String topic, int userid, String groupid, HttpSimpleRequest request) {
broadcastMessage(topic, ConvertType.JSON, userid, groupid, request, null);
}
public final void broadcastMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
broadcastMessage(topic, ConvertType.JSON, userid, groupid, request, counter);
}
public final void broadcastMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request) {
broadcastMessage(topic, convertType, userid, groupid, request, null);
}
public final <T> CompletableFuture<T> sendMessage(HttpSimpleRequest request, Type type) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, null).thenApply((HttpResult<byte[]> httbs) -> {
if (httbs == null || httbs.getResult() == null) return null;
return JsonConvert.root().convertFrom(type, httbs.getResult());
});
}
public final <T> CompletableFuture<T> sendMessage(int userid, HttpSimpleRequest request, Type type) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, null, request, null).thenApply((HttpResult<byte[]> httbs) -> {
if (httbs == null || httbs.getResult() == null) return null;
return JsonConvert.root().convertFrom(type, httbs.getResult());
});
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(HttpSimpleRequest request) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(HttpSimpleRequest request, AtomicLong counter) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, counter);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(int userid, HttpSimpleRequest request) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, null, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(int userid, String groupid, HttpSimpleRequest request) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, counter);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, HttpSimpleRequest request) {
return sendMessage(topic, ConvertType.JSON, 0, null, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, HttpSimpleRequest request, AtomicLong counter) {
return sendMessage(topic, ConvertType.JSON, 0, null, request, counter);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, ConvertType convertType, HttpSimpleRequest request) {
return sendMessage(topic, convertType, 0, null, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, ConvertType convertType, HttpSimpleRequest request, AtomicLong counter) {
return sendMessage(topic, convertType, 0, null, request, counter);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, int userid, String groupid, HttpSimpleRequest request) {
return sendMessage(topic, ConvertType.JSON, userid, groupid, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
return sendMessage(topic, ConvertType.JSON, userid, groupid, request, counter);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request) {
return sendMessage(topic, convertType, userid, groupid, request, null);
}
public CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
MessageRecord message = new MessageRecord(convertType, topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
message.userid(userid).groupid(groupid);
return sendMessage(message, true, counter).thenApply(r -> r.decodeContent(HttpResultCoder.getInstance()));
}
public void broadcastMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
MessageRecord message = new MessageRecord(convertType, topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
message.userid(userid).groupid(groupid);
sendMessage(message, false, counter);
}
public void produceMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
MessageRecord message = new MessageRecord(convertType, topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
message.userid(userid).groupid(groupid);
sendMessage(message, false, counter);
}
@Override
protected MessageProducers getProducer() {
return messageAgent.getHttpProducer();
}
}

View File

@@ -0,0 +1,150 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.*;
import java.util.logging.Level;
import org.redkale.cluster.ClusterAgent;
import org.redkale.convert.ConvertType;
import org.redkale.net.http.*;
/**
* 没有配置MQ的情况下依赖ClusterAgent实现的默认HttpMessageClient实例
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpMessageClusterClient extends HttpMessageClient {
//jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET
private static final Set<String> DISALLOWED_HEADERS_SET = Set.of("connection", "content-length",
"date", "expect", "from", "host", "origin",
"referer", "upgrade", "via", "warning");
protected ClusterAgent clusterAgent;
protected java.net.http.HttpClient httpClient;
public HttpMessageClusterClient(ClusterAgent clusterAgent) {
super(null);
Objects.requireNonNull(clusterAgent);
this.clusterAgent = clusterAgent;
this.httpClient = java.net.http.HttpClient.newHttpClient();
}
@Override
public CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
return httpAsync(userid, request);
}
@Override
public void produceMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
httpAsync(userid, request);
}
@Override
public void broadcastMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
mqtpAsync(userid, request);
}
private CompletableFuture<HttpResult<byte[]>> mqtpAsync(int userid, HttpSimpleRequest req) {
final boolean finest = logger.isLoggable(Level.FINEST);
String module = req.getRequestURI();
module = module.substring(1); //去掉/
module = module.substring(0, module.indexOf('/'));
Map<String, String> headers = req.getHeaders();
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> {
if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture();
java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
if (headers != null) headers.forEach((n, v) -> {
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
});
builder.header("Content-Type", "x-www-form-urlencoded");
String paramstr = req.getParametersToString();
if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
List<CompletableFuture> futures = new ArrayList<>();
for (Map.Entry<String, Collection<InetSocketAddress>> en : addrmap.entrySet()) {
String realmodule = en.getKey();
Collection<InetSocketAddress> addrs = en.getValue();
if (addrs == null || addrs.isEmpty()) continue;
String suburi = req.getRequestURI();
suburi = suburi.substring(1); //跳过 /
suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/'));
futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, builder, addrs.iterator()));
}
if (futures.isEmpty()) return CompletableFuture.completedFuture(null);
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> null);
});
}
private CompletableFuture<HttpResult<byte[]>> httpAsync(int userid, HttpSimpleRequest req) {
final boolean finest = logger.isLoggable(Level.FINEST);
String module = req.getRequestURI();
module = module.substring(1); //去掉/
module = module.substring(0, module.indexOf('/'));
Map<String, String> headers = req.getHeaders();
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> {
if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture();
java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
if (headers != null) headers.forEach((n, v) -> {
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
});
builder.header("Content-Type", "x-www-form-urlencoded");
String paramstr = req.getParametersToString();
if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), builder, addrs.iterator());
});
}
private CompletableFuture<HttpResult<byte[]>> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, java.net.http.HttpRequest.Builder builder, Iterator<InetSocketAddress> it) {
if (!it.hasNext()) return CompletableFuture.completedFuture(null);
InetSocketAddress addr = it.next();
String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi;
return httpClient.sendAsync(builder.copy().uri(URI.create(url)).build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()).thenCompose(resp -> {
if (resp.statusCode() != 200) return forEachCollectionFuture(finest, userid, req, requesturi, builder, it);
HttpResult rs = new HttpResult();
java.net.http.HttpHeaders hs = resp.headers();
if (hs != null) {
Map<String, List<String>> hm = hs.map();
if (hm != null) {
for (Map.Entry<String, List<String>> en : hm.entrySet()) {
if ("date".equals(en.getKey()) || "content-type".equals(en.getKey())
|| "server".equals(en.getKey()) || "connection".equals(en.getKey())) continue;
List<String> val = en.getValue();
if (val != null && val.size() == 1) {
rs.header(en.getKey(), val.get(0));
}
}
}
}
rs.setResult(resp.body());
if (finest) {
StringBuilder sb = new StringBuilder();
Map<String, String> params = req.getParams();
if (params != null && !params.isEmpty()) {
params.forEach((n, v) -> sb.append('&').append(n).append('=').append(v));
}
logger.log(Level.FINEST, url + "?userid=" + userid + sb + ", result = " + new String(resp.body(), StandardCharsets.UTF_8));
}
return CompletableFuture.completedFuture(rs);
});
}
}

View File

@@ -0,0 +1,126 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.concurrent.*;
import java.util.logging.*;
import org.redkale.boot.NodeHttpServer;
import org.redkale.net.http.*;
import org.redkale.service.Service;
import org.redkale.util.ThreadHashExecutor;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpMessageProcessor implements MessageProcessor {
protected final boolean finest;
protected final Logger logger;
protected final MessageProducers producer;
protected final NodeHttpServer server;
protected final ThreadHashExecutor workExecutor;
protected final Service service;
protected final HttpServlet servlet;
protected final boolean multiconsumer;
protected final String restmodule; // 前后有/, 例如: /user/
protected final String multimodule; // 前后有/, 例如: /userstat/
protected CountDownLatch cdl;
protected final Runnable innerCallback = () -> {
if (cdl != null) cdl.countDown();
};
public HttpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageProducers producer, NodeHttpServer server, Service service, HttpServlet servlet) {
this.logger = logger;
this.finest = logger.isLoggable(Level.FINEST);
this.producer = producer;
this.server = server;
this.service = service;
this.servlet = servlet;
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
this.multiconsumer = mmc != null;
this.restmodule = "/" + Rest.getRestModule(service) + "/";
this.multimodule = mmc != null ? ("/" + mmc.module() + "/") : null;
this.workExecutor = workExecutor;
}
@Override
public void begin(final int size) {
if (this.workExecutor != null) this.cdl = new CountDownLatch(size);
}
@Override
public void process(final MessageRecord message, final Runnable callback) {
if (this.workExecutor == null) {
execute(message, innerCallback);
} else {
this.workExecutor.execute(message.hash(), () -> execute(message, innerCallback));
}
}
private void execute(final MessageRecord message, final Runnable callback) {
try {
if (finest) logger.log(Level.FINEST, "HttpMessageProcessor.process message: " + message);
if (multiconsumer) message.setResptopic(null); //不容许有响应
HttpContext context = server.getHttpServer().getContext();
HttpMessageRequest request = new HttpMessageRequest(context, message);
if (multiconsumer) {
request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule));
}
HttpMessageResponse response = new HttpMessageResponse(context, request, callback, null, null, producer.getProducer(message));
servlet.execute(request, response);
} catch (Throwable ex) {
if (message.getResptopic() != null && !message.getResptopic().isEmpty()) {
HttpMessageResponse.finishHttpResult(finest, message, callback, producer.getProducer(message), message.getResptopic(), new HttpResult().status(500));
}
logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex);
}
}
@Override
public void commit() {
if (this.cdl != null) {
try {
this.cdl.await(30, TimeUnit.SECONDS);
} catch (Exception ex) {
}
this.cdl = null;
}
}
public MessageProducers getProducer() {
return producer;
}
public NodeHttpServer getServer() {
return server;
}
public Service getService() {
return service;
}
public HttpServlet getServlet() {
return servlet;
}
}

View File

@@ -0,0 +1,32 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import org.redkale.net.http.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpMessageRequest extends HttpRequest {
protected MessageRecord message;
public HttpMessageRequest(HttpContext context, MessageRecord message) {
super(context, message.decodeContent(HttpSimpleRequestCoder.getInstance()));
this.message = message;
this.currentUserid = message.getUserid();
}
public void setRequestURI(String uri) {
this.requestURI = uri;
}
}

View File

@@ -0,0 +1,167 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;
import org.redkale.convert.*;
import org.redkale.net.Response;
import org.redkale.net.http.*;
import org.redkale.service.RetResult;
import org.redkale.util.ObjectPool;
/**
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpMessageResponse extends HttpResponse {
protected MessageRecord message;
protected MessageProducer producer;
protected boolean finest;
protected Runnable callback;
public HttpMessageResponse(HttpContext context, HttpMessageRequest request, Runnable callback,
ObjectPool<Response> responsePool, HttpResponseConfig config, MessageProducer producer) {
super(context, request, responsePool, config);
this.message = request.message;
this.callback = callback;
this.producer = producer;
this.finest = producer.logger.isLoggable(Level.FINEST);
}
public HttpMessageResponse(HttpContext context, MessageRecord message, Runnable callback, HttpResponseConfig config, MessageProducer producer) {
super(context, new HttpMessageRequest(context, message), null, config);
this.message = message;
this.callback = callback;
this.producer = producer;
}
public void finishHttpResult(HttpResult result) {
finishHttpResult(this.finest, this.message, this.callback, this.producer, message.getResptopic(), result);
}
public static void finishHttpResult(boolean finest, MessageRecord msg, Runnable callback, MessageProducer producer, String resptopic, HttpResult result) {
if (callback != null) callback.run();
if (resptopic == null || resptopic.isEmpty()) return;
if (result.getResult() instanceof RetResult) {
RetResult ret = (RetResult) result.getResult();
//必须要塞入retcode 开发者可以无需反序列化ret便可确定操作是否返回成功
if (!ret.isSuccess()) result.header("retcode", String.valueOf(ret.getRetcode()));
}
ConvertType format = result.convert() == null ? null : result.convert().getFactory().getConvertType();
if (finest) {
Object innerrs = result.getResult();
if (innerrs instanceof byte[]) innerrs = new String((byte[]) innerrs, StandardCharsets.UTF_8);
producer.logger.log(Level.FINEST, "HttpMessageProcessor.process seqid=" + msg.getSeqid() + ", content: " + innerrs + ", status: " + result.getStatus() + ", headers: " + result.getHeaders());
}
byte[] content = HttpResultCoder.getInstance().encode(result);
producer.apply(new MessageRecord(msg.getSeqid(), format, resptopic, null, content));
}
@Override
public void finishJson(org.redkale.service.RetResult ret) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(ret.clearConvert(), ret));
}
@Override
public void finish(String obj) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(obj == null ? "" : obj));
}
@Override
public void finish404() {
finish(404, null);
}
@Override
public void finish(int status, String message) {
if (finest) producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status);
if (this.message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(message == null ? "" : message).status(status));
}
@Override
public void finish(final Convert convert, HttpResult result) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
if (convert != null) result.convert(convert);
finishHttpResult(result);
}
@Override
public void finish(final byte[] bs) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(bs));
}
@Override
public void finish(final String contentType, final byte[] bs) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(bs).contentType(contentType));
}
@Override
public void finish(boolean kill, ByteBuffer buffer) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
byte[] bs = new byte[buffer.remaining()];
buffer.get(bs);
finishHttpResult(new HttpResult(bs));
}
@Override
public void finish(boolean kill, ByteBuffer... buffers) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
int size = 0;
for (ByteBuffer buf : buffers) {
size += buf.remaining();
}
byte[] bs = new byte[size];
int index = 0;
for (ByteBuffer buf : buffers) {
int r = buf.remaining();
buf.get(bs, index, r);
index += r;
}
finishHttpResult(new HttpResult(bs));
}
}

View File

@@ -0,0 +1,132 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.net.HttpCookie;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import static org.redkale.mq.MessageCoder.*;
import org.redkale.net.http.HttpResult;
import org.redkale.util.Utility;
/**
* HttpResult的MessageCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpResultCoder implements MessageCoder<HttpResult> {
private static final HttpResultCoder instance = new HttpResultCoder();
public static HttpResultCoder getInstance() {
return instance;
}
@Override
public byte[] encode(HttpResult data) {
if (data == null) return null;
byte[] contentType = MessageCoder.getBytes(data.getContentType());
byte[] headers = MessageCoder.getBytes(data.getHeaders());
byte[] cookies = getBytes(data.getCookies());
byte[] content;
if (data.getResult() == null) {
content = new byte[0]; //""
} else if (data.getResult() instanceof byte[]) {
content = (byte[]) data.getResult();
} else if (data.getResult() instanceof CharSequence) {
content = MessageCoder.getBytes(data.getResult().toString());
} else {
Convert cc = data.convert();
if (cc == null || !(cc instanceof TextConvert)) cc = JsonConvert.root();
content = cc.convertToBytes(data.getResult());
}
int count = 4 + 2 + contentType.length + headers.length + cookies.length + 4 + (content == null ? 0 : content.length);
final byte[] bs = new byte[count];
ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.putInt(data.getStatus());
buffer.putChar((char) contentType.length);
if (contentType.length > 0) buffer.put(contentType);
buffer.put(headers);
buffer.put(cookies);
if (content == null || content.length == 0) {
buffer.putInt(0);
} else {
buffer.putInt(content.length);
buffer.put(content);
}
return bs;
}
@Override
public HttpResult<byte[]> decode(byte[] data) {
if (data == null) return null;
ByteBuffer buffer = ByteBuffer.wrap(data);
HttpResult result = new HttpResult();
result.setStatus(buffer.getInt());
result.setContentType(MessageCoder.getShortString(buffer));
result.setHeaders(MessageCoder.getMap(buffer));
result.setCookies(getCookieList(buffer));
int len = buffer.getInt();
if (len > 0) {
byte[] bs = new byte[len];
buffer.get(bs);
result.setResult(bs);
}
return result;
}
public static byte[] getBytes(final List<HttpCookie> list) {
if (list == null || list.isEmpty()) return new byte[2];
final AtomicInteger len = new AtomicInteger(2);
list.forEach(cookie -> {
len.addAndGet(2 + (cookie.getName() == null ? 0 : Utility.encodeUTF8Length(cookie.getName())));
len.addAndGet(2 + (cookie.getValue() == null ? 0 : Utility.encodeUTF8Length(cookie.getValue())));
len.addAndGet(2 + (cookie.getDomain() == null ? 0 : Utility.encodeUTF8Length(cookie.getDomain())));
len.addAndGet(2 + (cookie.getPath() == null ? 0 : Utility.encodeUTF8Length(cookie.getPath())));
len.addAndGet(2 + (cookie.getPortlist() == null ? 0 : Utility.encodeUTF8Length(cookie.getPortlist())));
len.addAndGet(8 + 1 + 1); //maxage Secure HttpOnly
});
final byte[] bs = new byte[len.get()];
final ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.putChar((char) list.size());
list.forEach(cookie -> {
putShortString(buffer, cookie.getName());
putShortString(buffer, cookie.getValue());
putShortString(buffer, cookie.getDomain());
putShortString(buffer, cookie.getPath());
putShortString(buffer, cookie.getPortlist());
buffer.putLong(cookie.getMaxAge());
buffer.put(cookie.getSecure() ? (byte) 1 : (byte) 0);
buffer.put(cookie.isHttpOnly() ? (byte) 1 : (byte) 0);
});
return bs;
}
public static List<HttpCookie> getCookieList(ByteBuffer buffer) {
int len = buffer.getChar();
if (len == 0) return null;
final List<HttpCookie> list = new ArrayList<>(len);
for (int i = 0; i < len; i++) {
HttpCookie cookie = new HttpCookie(getShortString(buffer), getShortString(buffer));
cookie.setDomain(getShortString(buffer));
cookie.setPath(getShortString(buffer));
cookie.setPortlist(getShortString(buffer));
cookie.setMaxAge(buffer.getLong());
cookie.setSecure(buffer.get() == 1);
cookie.setHttpOnly(buffer.get() == 1);
list.add(cookie);
}
return list;
}
}

View File

@@ -0,0 +1,93 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.redkale.net.http.HttpSimpleRequest;
/**
* HttpSimpleRequest的MessageCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
private static final HttpSimpleRequestCoder instance = new HttpSimpleRequestCoder();
public static HttpSimpleRequestCoder getInstance() {
return instance;
}
@Override
public byte[] encode(HttpSimpleRequest data) {
byte[] requestURI = MessageCoder.getBytes(data.getRequestURI()); //long-string
byte[] path = MessageCoder.getBytes(data.getRequestURI()); //short-string
byte[] remoteAddr = MessageCoder.getBytes(data.getRemoteAddr());//short-string
byte[] sessionid = MessageCoder.getBytes(data.getSessionid());//short-string
byte[] contentType = MessageCoder.getBytes(data.getContentType());//short-string
byte[] headers = MessageCoder.getBytes(data.getHeaders());
byte[] params = MessageCoder.getBytes(data.getParams());
byte[] body = MessageCoder.getBytes(data.getBody());
int count = 1 + 4 + requestURI.length + 2 + path.length + 2 + remoteAddr.length + 2 + sessionid.length
+ 2 + contentType.length + 4 + headers.length + params.length + 4 + body.length;
byte[] bs = new byte[count];
ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.put((byte) (data.isRpc() ? 'T' : 'F'));
buffer.putInt(requestURI.length);
if (requestURI.length > 0) buffer.put(requestURI);
buffer.putChar((char) path.length);
if (path.length > 0) buffer.put(path);
buffer.putChar((char) remoteAddr.length);
if (remoteAddr.length > 0) buffer.put(remoteAddr);
buffer.putChar((char) sessionid.length);
if (sessionid.length > 0) buffer.put(sessionid);
buffer.putChar((char) contentType.length);
if (contentType.length > 0) buffer.put(contentType);
buffer.putInt(data.getCurrentUserid());
buffer.put(headers);
buffer.put(params);
buffer.putInt(body.length);
if (body.length > 0) buffer.put(body);
return bs;
}
@Override
public HttpSimpleRequest decode(byte[] data) {
if (data == null) return null;
ByteBuffer buffer = ByteBuffer.wrap(data);
HttpSimpleRequest req = new HttpSimpleRequest();
req.setRpc(buffer.get() == 'T');
req.setRequestURI(MessageCoder.getLongString(buffer));
req.setPath(MessageCoder.getShortString(buffer));
req.setRemoteAddr(MessageCoder.getShortString(buffer));
req.setSessionid(MessageCoder.getShortString(buffer));
req.setContentType(MessageCoder.getShortString(buffer));
req.setCurrentUserid(buffer.getInt());
req.setHeaders(MessageCoder.getMap(buffer));
req.setParams(MessageCoder.getMap(buffer));
int len = buffer.getInt();
if (len > 0) {
byte[] bs = new byte[len];
buffer.get(bs);
req.setBody(bs);
}
return req;
}
protected static String getString(ByteBuffer buffer) {
int len = buffer.getInt();
if (len == 0) return null;
byte[] bs = new byte[len];
buffer.get(bs);
return new String(bs, StandardCharsets.UTF_8);
}
}

View File

@@ -0,0 +1,308 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.redkale.boot.*;
import static org.redkale.boot.Application.RESNAME_APP_NODEID;
import org.redkale.net.Servlet;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.util.*;
/**
* MQ管理器
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public abstract class MessageAgent {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
@Resource(name = RESNAME_APP_NODEID)
protected int nodeid;
protected String name;
protected AnyValue config;
protected MessageProducers httpProducer;
protected MessageProducers sncpProducer;
protected final Object httpProducerLock = new Object();
protected final Object sncpProducerLock = new Object();
protected HttpMessageClient httpMessageClient;
protected SncpMessageClient sncpMessageClient;
protected ScheduledThreadPoolExecutor timeoutExecutor;
protected ThreadHashExecutor workExecutor;
//本地Service消息接收处理器 key:consumer
protected HashMap<String, MessageConsumerNode> messageNodes = new LinkedHashMap<>();
public void init(AnyValue config) {
this.name = checkName(config.getValue("name", ""));
this.httpMessageClient = new HttpMessageClient(this);
this.sncpMessageClient = new SncpMessageClient(this);
this.workExecutor = new ThreadHashExecutor(Math.max(4, config.getIntValue("threads", Runtime.getRuntime().availableProcessors())));
// application (it doesn't execute completion handlers).
this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> {
Thread t = new Thread(r);
t.setName("MessageAgent-Timeout-Thread");
t.setDaemon(true);
return t;
});
this.timeoutExecutor.setRemoveOnCancelPolicy(true);
}
public CompletableFuture<Map<String, Long>> start() {
final LinkedHashMap<String, Long> map = new LinkedHashMap<>();
final List<CompletableFuture> futures = new ArrayList<>();
this.messageNodes.values().forEach(node -> {
long s = System.currentTimeMillis();
futures.add(node.consumer.startup().whenComplete((r, t) -> map.put(node.consumer.consumerid, System.currentTimeMillis() - s)));
});
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(r -> map);
}
//Application.shutdown 在执行server.shutdown之前执行
public CompletableFuture<Void> stop() {
List<CompletableFuture> futures = new ArrayList<>();
this.messageNodes.values().forEach(node -> {
futures.add(node.consumer.shutdown());
});
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
}
//Application.shutdown 在所有server.shutdown执行后执行
public void destroy(AnyValue config) {
this.httpMessageClient.close().join();
this.sncpMessageClient.close().join();
this.workExecutor.shutdown();
if (this.timeoutExecutor != null) this.timeoutExecutor.shutdown();
if (this.sncpProducer != null) this.sncpProducer.shutdown().join();
if (this.httpProducer != null) this.httpProducer.shutdown().join();
}
protected List<MessageConsumer> getAllMessageConsumer() {
List<MessageConsumer> consumers = new ArrayList<>();
MessageConsumer one = this.httpMessageClient == null ? null : this.httpMessageClient.consumer;
if (one != null) consumers.add(one);
one = this.sncpMessageClient == null ? null : this.sncpMessageClient.consumer;
if (one != null) consumers.add(one);
consumers.addAll(messageNodes.values().stream().map(mcn -> mcn.consumer).collect(Collectors.toList()));
return consumers;
}
protected List<MessageProducer> getAllMessageProducer() {
List<MessageProducer> producers = new ArrayList<>();
if (this.httpProducer != null) producers.addAll(List.of(this.httpProducer.producers));
if (this.sncpProducer != null) producers.addAll(List.of(this.sncpProducer.producers));
MessageProducers one = this.httpMessageClient == null ? null : this.httpMessageClient.getProducer();
if (one != null) producers.addAll(List.of(one.producers));
one = this.sncpMessageClient == null ? null : this.sncpMessageClient.getProducer();
if (one != null) producers.addAll(List.of(one.producers));
return producers;
}
public Logger getLogger() {
return logger;
}
public String getName() {
return name;
}
public AnyValue getConfig() {
return config;
}
public void setConfig(AnyValue config) {
this.config = config;
}
public HttpMessageClient getHttpMessageClient() {
return httpMessageClient;
}
public SncpMessageClient getSncpMessageClient() {
return sncpMessageClient;
}
protected String checkName(String name) { //不能含特殊字符
if (name.isEmpty()) return name;
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9");
for (char ch : name.toCharArray()) {
if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符
throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9");
}
}
return name;
}
//获取指定topic的生产处理器
public MessageProducers getSncpProducer() {
if (this.sncpProducer == null) {
synchronized (sncpProducerLock) {
if (this.sncpProducer == null) {
MessageProducer[] producers = new MessageProducer[Runtime.getRuntime().availableProcessors()];
for (int i = 0; i < producers.length; i++) {
MessageProducer producer = createProducer("SncpProducer");
producer.startup().join();
producers[i] = producer;
}
this.sncpProducer = new MessageProducers(producers);
}
}
}
return this.sncpProducer;
}
public MessageProducers getHttpProducer() {
if (this.httpProducer == null) {
synchronized (httpProducerLock) {
if (this.httpProducer == null) {
MessageProducer[] producers = new MessageProducer[Runtime.getRuntime().availableProcessors()];
for (int i = 0; i < producers.length; i++) {
MessageProducer producer = createProducer("HttpProducer");
producer.startup().join();
producers[i] = producer;
}
this.httpProducer = new MessageProducers(producers);
}
}
}
return this.httpProducer;
}
//创建指定topic的生产处理器
protected abstract MessageProducer createProducer(String name);
//创建topic如果已存在则跳过
public abstract boolean createTopic(String... topics);
//删除topic如果不存在则跳过
public abstract boolean deleteTopic(String... topics);
//查询所有topic
public abstract List<String> queryTopic();
//ServiceLoader时判断配置是否符合当前实现类
public abstract boolean match(AnyValue config);
//创建指定topic的消费处理器
public abstract MessageConsumer createConsumer(String[] topics, String group, MessageProcessor processor);
public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) {
String[] topics = generateHttpReqTopics(service);
String consumerid = generateHttpConsumerid(topics, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
HttpMessageProcessor processor = new HttpMessageProcessor(this.logger, this.workExecutor, getHttpProducer(), ns, service, servlet);
this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(topics, consumerid, processor)));
}
public final synchronized void putService(NodeSncpServer ns, Service service, SncpServlet servlet) {
String topic = generateSncpReqTopic(service);
String consumerid = generateSncpConsumerid(topic, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
SncpMessageProcessor processor = new SncpMessageProcessor(this.logger, this.workExecutor, getSncpProducer(), ns, service, servlet);
this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(new String[]{topic}, consumerid, processor)));
}
//格式: sncp.req.user
public final String generateSncpReqTopic(Service service) {
if (service instanceof WebSocketNode) {
String resname = Sncp.getResourceName(service);
return "sncp.req.ws" + (resname.isEmpty() ? "" : ("-" + resname)) + ".node" + nodeid;
}
String resname = Sncp.getResourceName(service);
return "sncp.req." + Sncp.getResourceType(service).getSimpleName().replaceAll("Service.*$", "").toLowerCase() + (resname.isEmpty() ? "" : ("-" + resname));
}
//格式: consumer-sncp.req.user 不提供外部使用
protected final String generateSncpConsumerid(String topic, Service service) {
return "consumer-" + topic;
}
//格式: http.req.user
public static String generateHttpReqTopic(String module) {
return "http.req." + module.toLowerCase();
}
//格式: http.req.user
public static String generateHttpReqTopic(String module, String resname) {
return "http.req." + module.toLowerCase() + (resname == null || resname.isEmpty() ? "" : ("-" + resname));
}
//格式: sncp.resp.node10
protected String generateSncpRespTopic() {
return "sncp.resp.node" + nodeid;
}
//格式: http.resp.node10
protected String generateHttpRespTopic() {
return "http.resp.node" + nodeid;
}
//格式: http.req.user
protected String[] generateHttpReqTopics(Service service) {
String resname = Sncp.getResourceName(service);
String module = Rest.getRestModule(service).toLowerCase();
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
if (mmc != null) return new String[]{generateHttpReqTopic(mmc.module()) + (resname.isEmpty() ? "" : ("-" + resname))};
return new String[]{"http.req." + module + (resname.isEmpty() ? "" : ("-" + resname))};
}
//格式: consumer-http.req.user
protected String generateHttpConsumerid(String[] topics, Service service) {
String resname = Sncp.getResourceName(service);
String key = Rest.getRestModule(service).toLowerCase();
return "consumer-http.req." + key + (resname.isEmpty() ? "" : ("-" + resname));
}
//格式: xxxx.resp.node10
protected String generateRespTopic(String protocol) {
return protocol + ".resp.node" + nodeid;
}
protected static class MessageConsumerNode {
public final NodeServer server;
public final Service service;
public final Servlet servlet;
public final MessageProcessor processor;
public final MessageConsumer consumer;
public MessageConsumerNode(NodeServer server, Service service, Servlet servlet, MessageProcessor processor, MessageConsumer consumer) {
this.server = server;
this.service = service;
this.servlet = servlet;
this.processor = processor;
this.consumer = consumer;
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import org.redkale.convert.ConvertType;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public abstract class MessageClient {
protected final ConcurrentHashMap<Long, MessageRespFutureNode> respNodes = new ConcurrentHashMap<>();
protected final MessageAgent messageAgent;
protected MessageConsumer consumer;
protected String respTopic;
protected String respConsumerid;
protected ConvertType convertType;
protected MessageClient(MessageAgent messageAgent) {
this.messageAgent = messageAgent;
}
protected CompletableFuture<Void> close() {
if (this.consumer == null) return CompletableFuture.completedFuture(null);
return this.consumer.shutdown();
}
protected CompletableFuture<MessageRecord> sendMessage(MessageRecord message, boolean needresp, AtomicLong counter) {
CompletableFuture<MessageRecord> future = new CompletableFuture<>();
try {
if (this.consumer == null) {
synchronized (this) {
if (this.respConsumerid == null) this.respConsumerid = "consumer-" + this.respTopic;
if (this.consumer == null) {
MessageProcessor processor = (msg, callback) -> {
MessageRespFutureNode node = respNodes.remove(msg.getSeqid());
if (node == null) {
messageAgent.logger.log(Level.WARNING, MessageClient.this.getClass().getSimpleName() + " process " + msg + " error not found msgnode");
return;
}
if (node.getCounter() != null) node.getCounter().decrementAndGet();
node.future.complete(msg);
};
MessageConsumer one = messageAgent.createConsumer(new String[]{respTopic}, respConsumerid, processor);
one.startup().join();
this.consumer = one;
}
}
}
if (convertType != null) message.setFormat(convertType);
if (needresp && (message.getResptopic() == null || message.getResptopic().isEmpty())) {
message.setResptopic(respTopic);
}
if (counter != null) counter.incrementAndGet();
getProducer().apply(message);
if (needresp) {
MessageRespFutureNode node = new MessageRespFutureNode(message, respNodes, counter, future);
respNodes.put(message.getSeqid(), node);
ScheduledThreadPoolExecutor executor = messageAgent.timeoutExecutor;
if (executor != null) executor.schedule(node, 30, TimeUnit.SECONDS);
} else {
future.complete(null);
}
} catch (Exception ex) {
future.completeExceptionally(ex);
} finally {
return future;
}
}
protected abstract MessageProducers getProducer();
}

View File

@@ -0,0 +1,107 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.redkale.util.Utility;
/**
* 将MessageRecord.content内容加解密
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*
* @param <T> 泛型
*/
public interface MessageCoder<T> {
//编码
public byte[] encode(T data);
//解码
public T decode(byte[] data);
public static byte[] getBytes(byte[] value) {
if (value == null) return MessageRecord.EMPTY_BYTES;
return value;
}
public static byte[] getBytes(String value) {
if (value == null || value.isEmpty()) return MessageRecord.EMPTY_BYTES;
return value.getBytes(StandardCharsets.UTF_8);
}
public static byte[] getBytes(final Map<String, String> map) {
if (map == null || map.isEmpty()) return new byte[2];
final AtomicInteger len = new AtomicInteger(2);
map.forEach((key, value) -> {
len.addAndGet(2 + (key == null ? 0 : Utility.encodeUTF8Length(key)));
len.addAndGet(4 + (value == null ? 0 : Utility.encodeUTF8Length(value)));
});
final byte[] bs = new byte[len.get()];
final ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.putChar((char) map.size());
map.forEach((key, value) -> {
putShortString(buffer, key);
putLongString(buffer, value);
});
return bs;
}
public static void putLongString(ByteBuffer buffer, String value) {
if (value == null || value.isEmpty()) {
buffer.putInt(0);
} else {
byte[] bs = value.getBytes(StandardCharsets.UTF_8);
buffer.putInt(bs.length);
buffer.put(bs);
}
}
public static String getLongString(ByteBuffer buffer) {
int len = buffer.getInt();
if (len == 0) return null;
byte[] bs = new byte[len];
buffer.get(bs);
return new String(bs, StandardCharsets.UTF_8);
}
public static void putShortString(ByteBuffer buffer, String value) {
if (value == null || value.isEmpty()) {
buffer.putChar((char) 0);
} else {
byte[] bs = value.getBytes(StandardCharsets.UTF_8);
buffer.putChar((char) bs.length);
buffer.put(bs);
}
}
public static String getShortString(ByteBuffer buffer) {
int len = buffer.getChar();
if (len == 0) return null;
byte[] bs = new byte[len];
buffer.get(bs);
return new String(bs, StandardCharsets.UTF_8);
}
public static Map<String, String> getMap(ByteBuffer buffer) {
int len = buffer.getChar();
if (len == 0) return null;
Map<String, String> map = new HashMap<>(len);
for (int i = 0; i < len; i++) {
map.put(getShortString(buffer), getLongString(buffer));
}
return map;
}
}

View File

@@ -0,0 +1,63 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
/**
*
* <p>
* 详情见: https://redkale.org
*
*
* @author zhangjx
*
* @since 2.1.0
*/
public abstract class MessageConsumer {
protected final String[] topics;
protected final String consumerid;
protected MessageAgent messageAgent;
protected final MessageProcessor processor;
protected final Logger logger;
protected volatile boolean closed;
protected MessageConsumer(MessageAgent messageAgent, String[] topics, final String consumerid, MessageProcessor processor) {
Objects.requireNonNull(messageAgent);
Objects.requireNonNull(topics);
Objects.requireNonNull(consumerid);
Objects.requireNonNull(processor);
this.messageAgent = messageAgent;
this.logger = messageAgent.logger;
this.topics = topics;
this.consumerid = consumerid;
this.processor = processor;
}
public MessageProcessor getProcessor() {
return processor;
}
public String[] getTopics() {
return topics;
}
public abstract CompletableFuture<Void> startup();
public boolean isClosed() {
return closed;
}
public abstract CompletableFuture<Void> shutdown();
}

View File

@@ -0,0 +1,61 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 多消费组,需要同 &#64;RestService 一起使用
* <p>
* 通常一个topic只会被一个RestService消费 当一个topic需要被其他RestService消费时就需要使用&#64;MessageMultiConsumer
*
* <blockquote><pre>
* &#64;RestService(name = "user", comment = "用户服务")
* public class UserService implements Service{
*
* &#64;RestMapping(comment = "用户登录")
* public RetResult login(LoginBean bean){
* //do something
* }
* }
* </pre></blockquote>
*
* 需求:统计用户登录次数, 可以创建一个MessageMultiConsumer 的 RestService
* <blockquote><pre>
* <b>&#64;MessageMultiConsumer(module = "user") </b>
* &#64;RestService(name = "loginstat", comment = "用户统计服务")
* public class LoginStatService implements Service{
*
* private AtomicLong counter = new AtomicLong();
*
* &#64;RestMapping(name = "login", comment = "用户登录统计")
* public void stat(LoginBean bean){ //参数必须和UserService.login方法一致
* counter.incrementAndGet();
* }
* }
* </pre></blockquote>
*
* <p>
* 注: 标记 &#64;MessageMultiConsumer 的Service的&#64;RestMapping方法都只能是void返回类型
*
* <p>
* 详情见: https://redkale.org
*
*
* @author zhangjx
*
* @since 2.1.0
*/
@Inherited
@Documented
@Target({TYPE})
@Retention(RUNTIME)
public @interface MessageMultiConsumer {
String module();
}

View File

@@ -0,0 +1,26 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public interface MessageProcessor {
default void begin(int size) {
}
public void process(MessageRecord message, Runnable callback);
default void commit() {
}
}

View File

@@ -0,0 +1,42 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public abstract class MessageProducer {
protected final Logger logger;
protected final String name;
protected volatile boolean closed;
protected MessageProducer(String name, Logger logger) {
this.name = name;
this.logger = logger;
}
public abstract CompletableFuture<Void> apply(MessageRecord message);
public abstract CompletableFuture<Void> startup();
public boolean isClosed() {
return closed;
}
public abstract CompletableFuture<Void> shutdown();
}

View File

@@ -0,0 +1,61 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class MessageProducers {
protected final MessageProducer[] producers;
protected final AtomicInteger index = new AtomicInteger();
public MessageProducers(MessageProducer[] producers) {
this.producers = producers;
}
public MessageProducer getProducer(MessageRecord message) {
int hash = index.incrementAndGet();
if (index.get() > 1000 * producers.length) {
synchronized (index) {
if (index.get() > 1000 * producers.length) {
index.addAndGet(-1000 * producers.length);
}
}
}
return producers[hash % producers.length];
}
public CompletableFuture<Void> apply(MessageRecord message) {
return getProducer(message).apply(message);
}
public CompletableFuture<Void> startup() {
CompletableFuture[] futures = new CompletableFuture[producers.length];
for (int i = 0; i < producers.length; i++) {
futures[i] = producers[i].startup();
}
return CompletableFuture.allOf(futures);
}
public CompletableFuture<Void> shutdown() {
CompletableFuture[] futures = new CompletableFuture[producers.length];
for (int i = 0; i < producers.length; i++) {
futures[i] = producers[i].shutdown();
}
return CompletableFuture.allOf(futures);
}
}

View File

@@ -0,0 +1,320 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.Comment;
/**
* 存在MQ里面的数据结构<p>
* groupid + userid 来确定partition 优先使用 groupid
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class MessageRecord implements Serializable {
static final byte[] EMPTY_BYTES = new byte[0];
@ConvertColumn(index = 1)
@Comment("消息序列号")
protected long seqid;
@ConvertColumn(index = 2)
@Comment("版本")
protected int version;
@ConvertColumn(index = 3)
@Comment("内容的格式, 只能是JSON、BSON、PROTOBUF、DIY和null, 普通文本也归于JSON")
protected ConvertType format;
@ConvertColumn(index = 4)
@Comment("标记位, 自定义时使用")
protected int flag;
@ConvertColumn(index = 5)
@Comment("创建时间")
protected long createtime;
@ConvertColumn(index = 6)
@Comment("用户ID无用户信息视为0")
protected int userid;
@ConvertColumn(index = 7)
@Comment("组ID")
protected String groupid;
@ConvertColumn(index = 8)
@Comment("当前topic")
protected String topic;
@ConvertColumn(index = 9)
@Comment("目标topic, 为空表示无目标topic")
protected String resptopic;
@ConvertColumn(index = 10)
@Comment("消息内容")
protected byte[] content;
public MessageRecord() {
}
public MessageRecord(String resptopic, String content) {
this(System.nanoTime(), content == null ? null : ConvertType.JSON, 0, 0, null, null, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
}
public MessageRecord(String topic, String resptopic, String content) {
this(System.nanoTime(), content == null ? null : ConvertType.JSON, 0, 0, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
}
public MessageRecord(int userid, String topic, String resptopic, String content) {
this(System.nanoTime(), content == null ? null : ConvertType.JSON, 0, userid, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
}
public MessageRecord(ConvertType format, String topic, String resptopic, byte[] content) {
this(System.nanoTime(), format, 0, 0, null, topic, resptopic, content);
}
public MessageRecord(long seqid, ConvertType format, String topic, String resptopic, byte[] content) {
this(seqid, format, 0, null, topic, resptopic, content);
}
public MessageRecord(long seqid, ConvertType format, int userid, String groupid, String topic, String resptopic, byte[] content) {
this(seqid, format, 0, userid, groupid, topic, resptopic, content);
}
public MessageRecord(String topic, String resptopic, Convert convert, Object bean) {
this(0, null, topic, resptopic, convert, bean);
}
public MessageRecord(int userid, String topic, String resptopic, Convert convert, Object bean) {
this(userid, null, topic, resptopic, convert, bean);
}
public MessageRecord(int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) {
this(0, userid, groupid, topic, resptopic, convert, bean);
}
public MessageRecord(int flag, int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) {
this(System.nanoTime(), convert.getFactory().getConvertType(), flag, userid, groupid, topic, resptopic, convert.convertToBytes(bean));
}
public MessageRecord(long seqid, ConvertType format, int flag, int userid, String groupid, String topic, String resptopic, byte[] content) {
this(seqid, 1, format, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, content);
}
public MessageRecord(long seqid, int version, ConvertType format, int flag, long createtime, int userid, String groupid, String topic, String resptopic, byte[] content) {
this.seqid = seqid;
this.version = version;
this.format = format;
this.flag = flag;
this.createtime = createtime;
this.userid = userid;
this.groupid = groupid;
this.topic = topic;
this.resptopic = resptopic;
this.content = content;
}
public String contentString() {
return content == null ? null : new String(content, StandardCharsets.UTF_8);
}
@ConvertDisabled
public boolean isEmptyTopic() {
return this.topic == null || this.topic.isEmpty();
}
@ConvertDisabled
public boolean isEmptyResptopic() {
return this.resptopic == null || this.resptopic.isEmpty();
}
public <T> T convertFromContent(Convert convert, java.lang.reflect.Type type) {
if (this.content == null || this.content.length == 0) return null;
return (T) convert.convertFrom(type, this.content);
}
public <T> T decodeContent(MessageCoder<T> coder) {
if (this.content == null || this.content.length == 0) return null;
return (T) coder.decode(this.content);
}
public <T> MessageRecord encodeContent(MessageCoder<T> coder, T data) {
this.content = coder.encode(data);
return this;
}
public int hash() {
if (groupid != null && !groupid.isEmpty()) {
return groupid.hashCode();
} else if (userid > 0) {
return userid;
} else {
return 0;
}
}
public MessageRecord version(int version) {
this.version = version;
return this;
}
public MessageRecord format(ConvertType format) {
this.format = format;
return this;
}
public MessageRecord flag(int flag) {
this.flag = flag;
return this;
}
public MessageRecord createtime(long createtime) {
this.createtime = createtime;
return this;
}
public MessageRecord userid(int userid) {
this.userid = userid;
return this;
}
public MessageRecord groupid(String groupid) {
this.groupid = groupid;
return this;
}
public MessageRecord topic(String topic) {
this.topic = topic;
return this;
}
public MessageRecord resptopic(String resptopic) {
this.resptopic = resptopic;
return this;
}
public MessageRecord content(byte[] content) {
this.content = content;
return this;
}
public MessageRecord contentString(String content) {
this.content = content == null ? null : content.getBytes(StandardCharsets.UTF_8);
return this;
}
public long getSeqid() {
return seqid;
}
public void setSeqid(long seqid) {
this.seqid = seqid;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public ConvertType getFormat() {
return format;
}
public void setFormat(ConvertType format) {
this.format = format;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
public long getCreatetime() {
return createtime;
}
public void setCreatetime(long createtime) {
this.createtime = createtime;
}
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getGroupid() {
return groupid;
}
public void setGroupid(String groupid) {
this.groupid = groupid;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public String getResptopic() {
return resptopic;
}
public void setResptopic(String resptopic) {
this.resptopic = resptopic;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
@Override
public String toString() {
//return JsonConvert.root().convertTo(this);
StringBuilder sb = new StringBuilder(128);
sb.append("{\"seqid\":").append(this.seqid);
sb.append(",\"version\":").append(this.version);
if (this.format != null) sb.append(",\"format\":\"").append(this.format).append("\"");
if (this.flag != 0) sb.append(",\"flag\":").append(this.flag);
if (this.createtime != 0) sb.append(",\"createtime\":").append(this.createtime);
if (this.userid != 0) sb.append(",\"userid\":").append(this.userid);
if (this.groupid != null) sb.append(",\"groupid\":\"").append(this.groupid).append("\"");
if (this.topic != null) sb.append(",\"topic\":\"").append(this.topic).append("\"");
if (this.resptopic != null) sb.append(",\"resptopic\":\"").append(this.resptopic).append("\"");
if (this.content != null) sb.append(",\"content\":").append(this.format == ConvertType.JSON ? ("\"" + new String(this.content, StandardCharsets.UTF_8) + "\"") : JsonConvert.root().convertTo(this.content));
sb.append("}");
return sb.toString();
}
// public static void main(String[] args) throws Throwable {
// System.out.println(new MessageRecord(333, ConvertType.JSON, 2, 3, null, "tt", null, "xxx".getBytes()));
// }
}

View File

@@ -0,0 +1,84 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.nio.ByteBuffer;
import org.redkale.convert.ConvertType;
/**
* MessageRecord的MessageCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class MessageRecordCoder implements MessageCoder<MessageRecord> {
private static final MessageRecordCoder instance = new MessageRecordCoder();
public static MessageRecordCoder getInstance() {
return instance;
}
@Override
public byte[] encode(MessageRecord data) {
if (data == null) return null;
byte[] stopics = MessageCoder.getBytes(data.getTopic());
byte[] dtopics = MessageCoder.getBytes(data.getResptopic());
byte[] groupid = MessageCoder.getBytes(data.getGroupid());
int count = 8 + 4 + 4 + 4 + 8 + 4 + 2 + stopics.length + 2 + dtopics.length + 2 + groupid.length + 4 + (data.getContent() == null ? 0 : data.getContent().length);
final byte[] bs = new byte[count];
ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.putLong(data.getSeqid());
buffer.putInt(data.getVersion());
buffer.putInt(data.getFormat() == null ? 0 : data.getFormat().getValue());
buffer.putInt(data.getFlag());
buffer.putLong(data.getCreatetime());
buffer.putInt(data.getUserid());
buffer.putChar((char) groupid.length);
if (groupid.length > 0) buffer.put(groupid);
buffer.putChar((char) stopics.length);
if (stopics.length > 0) buffer.put(stopics);
buffer.putChar((char) dtopics.length);
if (dtopics.length > 0) buffer.put(dtopics);
if (data.getContent() == null) {
buffer.putInt(0);
} else {
buffer.putInt(data.getContent().length);
buffer.put(data.getContent());
}
return bs;
}
@Override
public MessageRecord decode(byte[] data) {
if (data == null) return null;
ByteBuffer buffer = ByteBuffer.wrap(data);
long seqid = buffer.getLong();
int version = buffer.getInt();
ConvertType format = ConvertType.find(buffer.getInt());
int flag = buffer.getInt();
long createtime = buffer.getLong();
int userid = buffer.getInt();
String groupid = MessageCoder.getShortString(buffer);
String topic = MessageCoder.getShortString(buffer);
String resptopic = MessageCoder.getShortString(buffer);
byte[] content = null;
int contentlen = buffer.getInt();
if (contentlen > 0) {
content = new byte[contentlen];
buffer.get(content);
}
return new MessageRecord(seqid, version, format, flag,
createtime, userid, groupid, topic, resptopic, content);
}
}

View File

@@ -0,0 +1,62 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* MQ管理器
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class MessageRespFutureNode implements Runnable {
protected final long seqid;
protected final long createtime;
protected final AtomicLong counter;
protected final CompletableFuture<MessageRecord> future;
protected final ConcurrentHashMap<Long, MessageRespFutureNode> respNodes;
public MessageRespFutureNode(MessageRecord message, ConcurrentHashMap<Long, MessageRespFutureNode> respNodes, AtomicLong counter, CompletableFuture<MessageRecord> future) {
this.seqid = message.getSeqid();
this.respNodes = respNodes;
this.counter = counter;
this.future = future;
this.createtime = System.currentTimeMillis();
}
@Override //超时后被timeoutExecutor调用
public void run() { //timeout
respNodes.remove(this.seqid);
future.completeExceptionally(new TimeoutException());
}
public long getSeqid() {
return seqid;
}
public long getCreatetime() {
return createtime;
}
public AtomicLong getCounter() {
return counter;
}
public CompletableFuture<MessageRecord> getFuture() {
return future;
}
}

View File

@@ -0,0 +1,20 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public interface MessageResponse {
public void finish(MessageRecord message);
}

View File

@@ -0,0 +1,58 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.convert.ConvertType;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class SncpMessageClient extends MessageClient {
protected SncpMessageClient(MessageAgent messageAgent) {
super(messageAgent);
this.respTopic = messageAgent.generateSncpRespTopic();
this.convertType = ConvertType.BSON;
}
@Override
protected MessageProducers getProducer() {
return messageAgent.getSncpProducer();
}
public String getRespTopic() {
return this.respTopic;
}
//只发送消息,不需要响应
public final void produceMessage(MessageRecord message) {
produceMessage(message, null);
}
//只发送消息,不需要响应
public final void produceMessage(MessageRecord message, AtomicLong counter) {
sendMessage(message, false, counter);
}
//发送消息,需要响应
public final CompletableFuture<MessageRecord> sendMessage(MessageRecord message) {
return sendMessage(message, null);
}
//发送消息,需要响应
public final CompletableFuture<MessageRecord> sendMessage(MessageRecord message, AtomicLong counter) {
return sendMessage(message, true, counter);
}
}

View File

@@ -0,0 +1,107 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.concurrent.*;
import java.util.logging.*;
import org.redkale.boot.NodeSncpServer;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.util.ThreadHashExecutor;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class SncpMessageProcessor implements MessageProcessor {
protected final Logger logger;
protected final MessageProducers producer;
protected final NodeSncpServer server;
protected final ThreadHashExecutor workExecutor;
protected final Service service;
protected final SncpServlet servlet;
protected CountDownLatch cdl;
protected final Runnable innerCallback = () -> {
if (cdl != null) cdl.countDown();
};
public SncpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageProducers producer, NodeSncpServer server, Service service, SncpServlet servlet) {
this.logger = logger;
this.producer = producer;
this.server = server;
this.service = service;
this.servlet = servlet;
this.workExecutor = workExecutor;
}
@Override
public void begin(final int size) {
if (this.workExecutor != null) this.cdl = new CountDownLatch(size);
}
@Override
public void process(final MessageRecord message, final Runnable callback) {
if (this.workExecutor == null) {
execute(message, innerCallback);
} else {
this.workExecutor.execute(message.hash(), () -> execute(message, innerCallback));
}
}
private void execute(final MessageRecord message, final Runnable callback) {
SncpMessageResponse response = null;
try {
SncpContext context = server.getSncpServer().getContext();
SncpMessageRequest request = new SncpMessageRequest(context, message);
response = new SncpMessageResponse(context, request, callback, null, producer.getProducer(message));
servlet.execute(request, response);
} catch (Throwable ex) {
if (response != null) response.finish(SncpResponse.RETCODE_ILLSERVICEID, null);
logger.log(Level.SEVERE, SncpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex);
}
}
@Override
public void commit() {
if (this.cdl != null) {
try {
this.cdl.await(30, TimeUnit.SECONDS);
} catch (Exception ex) {
}
this.cdl = null;
}
}
public MessageProducers getProducer() {
return producer;
}
public NodeSncpServer getServer() {
return server;
}
public Service getService() {
return service;
}
public SncpServlet getServlet() {
return servlet;
}
}

View File

@@ -0,0 +1,31 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.nio.ByteBuffer;
import org.redkale.net.sncp.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class SncpMessageRequest extends SncpRequest {
protected MessageRecord message;
@SuppressWarnings("OverridableMethodCallInConstructor")
public SncpMessageRequest(SncpContext context, MessageRecord message) {
super(context, null);
this.message = message;
readHeader(ByteBuffer.wrap(message.getContent()));
}
}

View File

@@ -0,0 +1,61 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.nio.ByteBuffer;
import org.redkale.convert.ConvertType;
import org.redkale.convert.bson.BsonWriter;
import org.redkale.net.Response;
import org.redkale.net.sncp.*;
import static org.redkale.net.sncp.SncpRequest.HEADER_SIZE;
import org.redkale.util.ObjectPool;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class SncpMessageResponse extends SncpResponse {
protected MessageRecord message;
protected MessageProducer producer;
protected Runnable callback;
public SncpMessageResponse(SncpContext context, SncpMessageRequest request, Runnable callback, ObjectPool<Response> responsePool, MessageProducer producer) {
super(context, request, responsePool);
this.message = request.message;
this.callback = callback;
this.producer = producer;
}
public SncpMessageResponse(SncpContext context, MessageRecord message, Runnable callback, ObjectPool<Response> responsePool, MessageProducer producer) {
super(context, new SncpMessageRequest(context, message), responsePool);
this.message = message;
this.callback = callback;
this.producer = producer;
}
@Override
public void finish(final int retcode, final BsonWriter out) {
if (callback != null) callback.run();
if (out == null) {
final byte[] result = new byte[SncpRequest.HEADER_SIZE];
fillHeader(ByteBuffer.wrap(result), 0, retcode);
producer.apply(new MessageRecord(message.getSeqid(), ConvertType.BSON, message.getResptopic(), null, (byte[]) null));
return;
}
final int respBodyLength = out.count(); //body总长度
final byte[] result = out.toArray();
fillHeader(ByteBuffer.wrap(result), respBodyLength - HEADER_SIZE, retcode);
producer.apply(new MessageRecord(message.getSeqid(), ConvertType.BSON, message.getResptopic(), null, result));
}
}

View File

@@ -5,7 +5,7 @@
*/
package org.redkale.net;
import java.io.IOException;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
@@ -23,45 +23,49 @@ import org.redkale.util.*;
*
* @author zhangjx
*/
public abstract class AsyncConnection implements ReadableByteChannel, WritableByteChannel, AutoCloseable {
public abstract class AsyncConnection implements AutoCloseable {
protected SSLContext sslContext;
private SSLContext sslContext;
protected Map<String, Object> attributes; //用于存储绑定在Connection上的对象集合
private Map<String, Object> attributes; //用于存储绑定在Connection上的对象集合
protected Object subobject; //用于存储绑定在Connection上的对象 同attributes 只绑定单个对象时尽量使用subobject而非attributes
private Object subobject; //用于存储绑定在Connection上的对象 同attributes 只绑定单个对象时尽量使用subobject而非attributes
protected volatile long readtime;
protected volatile long writetime;
protected final Supplier<ByteBuffer> bufferSupplier;
private final Supplier<ByteBuffer> bufferSupplier;
protected final Consumer<ByteBuffer> bufferConsumer;
private final Consumer<ByteBuffer> bufferConsumer;
protected ByteBuffer readBuffer;
private ByteBuffer readBuffer;
//在线数
protected AtomicLong livingCounter;
private AtomicLong livingCounter;
//关闭数
protected AtomicLong closedCounter;
private AtomicLong closedCounter;
protected Consumer<AsyncConnection> beforeCloseListener;
private Consumer<AsyncConnection> beforeCloseListener;
//关联的事件数, 小于1表示没有事件
protected final AtomicInteger eventing = new AtomicInteger();
private final AtomicInteger eventing = new AtomicInteger();
protected AsyncConnection(ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext) {
this(bufferPool, bufferPool, sslContext);
protected AsyncConnection(ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
this(bufferPool, bufferPool, sslContext, livingCounter, closedCounter);
}
protected AsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, SSLContext sslContext) {
protected AsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, SSLContext sslContext,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
Objects.requireNonNull(bufferSupplier);
Objects.requireNonNull(bufferConsumer);
this.bufferSupplier = bufferSupplier;
this.bufferConsumer = bufferConsumer;
this.sslContext = sslContext;
this.livingCounter = livingCounter;
this.closedCounter = closedCounter;
}
public Supplier<ByteBuffer> getBufferSupplier() {
@@ -88,7 +92,6 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
return eventing.decrementAndGet();
}
@Override
public abstract boolean isOpen();
public abstract boolean isTCP();
@@ -113,13 +116,11 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
public abstract void setWriteTimeoutSeconds(int writeTimeoutSeconds);
@Override
public abstract int read(ByteBuffer dst) throws IOException;
public abstract ReadableByteChannel readableByteChannel();
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
@Override
public abstract int write(ByteBuffer src) throws IOException;
public abstract WritableByteChannel writableByteChannel();
public abstract <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler);
@@ -140,40 +141,23 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
this.readBuffer = null;
return rs;
}
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// return ((IOThread) thread).getBufferPool().get();
// }
return bufferSupplier.get();
}
public void offerBuffer(ByteBuffer buffer) {
if (buffer == null) return;
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// ((IOThread) thread).getBufferPool().accept((ByteBuffer) buffer);
// return;
// }
bufferConsumer.accept(buffer);
}
public void offerBuffer(ByteBuffer... buffers) {
if (buffers == null) return;
Consumer<ByteBuffer> consumer = this.bufferConsumer;
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// consumer = ((IOThread) thread).getBufferPool();
// }
for (ByteBuffer buffer : buffers) {
consumer.accept(buffer);
}
}
public ByteBuffer pollWriteBuffer() {
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// return ((IOThread) thread).getBufferPool().get();
// }
return bufferSupplier.get();
}

View File

@@ -1,61 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import org.redkale.util.*;
/**
* 协议处理的IO线程类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class IOThread extends Thread {
protected Thread localThread;
protected final ExecutorService executor;
protected ObjectPool<ByteBuffer> bufferPool;
public IOThread(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, Runnable runner) {
super(runner);
this.executor = executor;
this.bufferPool = bufferPool;
this.setDaemon(true);
}
public void runAsync(Runnable runner) {
executor.execute(runner);
}
public ExecutorService getExecutor() {
return executor;
}
public ObjectPool<ByteBuffer> getBufferPool() {
return bufferPool;
}
@Override
public void run() {
this.localThread = Thread.currentThread();
super.run();
}
public boolean inSameThread() {
return this.localThread == Thread.currentThread();
}
public boolean inSameThread(Thread thread) {
return this.localThread == thread;
}
}

View File

@@ -73,7 +73,7 @@ public abstract class ProtocolServer {
} else if ("aio".equalsIgnoreCase(netimpl)) {
return new TcpAioProtocolServer(context);
} else if ("nio".equalsIgnoreCase(netimpl)) {
return null;// return new TcpNioProtocolServer(context);
return new TcpNioProtocolServer(context);
}
} else if ("UDP".equalsIgnoreCase(protocol)) {
if (netimpl == null || netimpl.isEmpty()) {

View File

@@ -27,7 +27,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected final C context;
protected final ObjectPool<Response> responsePool;
protected final ObjectPool<Response> responsePool; //虚拟构建的Response可能不存在responsePool
protected final R request;
@@ -223,7 +223,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
public void finish(final byte[] bs) {
if (!this.inited) return; //避免重复关闭
if (this.context.bufferCapacity == bs.length) {
ByteBuffer buffer = channel.bufferSupplier.get();
ByteBuffer buffer = channel.getBufferSupplier().get();
buffer.put(bs);
buffer.flip();
this.finish(buffer);

View File

@@ -86,7 +86,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
protected int threads;
//线程池
protected ThreadPoolExecutor executor;
protected ThreadPoolExecutor workExecutor;
//ByteBuffer池大小
protected int bufferPoolSize;
@@ -151,8 +151,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
final AtomicInteger counter = new AtomicInteger();
final Format f = createFormat();
final String n = name;
this.executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new WorkThread(executor, r);
this.workExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new WorkThread(workExecutor, r);
t.setName("Redkale-" + n + "-ServletThread-" + f.format(counter.incrementAndGet()));
return t;
});
@@ -187,8 +187,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return resourceFactory;
}
public ThreadPoolExecutor getExecutor() {
return executor;
public ThreadPoolExecutor getWorkExecutor() {
return workExecutor;
}
public InetSocketAddress getSocketAddress() {
@@ -285,7 +285,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
final String threadName = "[" + Thread.currentThread().getName() + "] ";
postStart();
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address
+ ", threads: " + threads + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
+ ", cpu: " + Runtime.getRuntime().availableProcessors() + ", threads: " + threads + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
+ ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
}

View File

@@ -5,7 +5,7 @@
*/
package org.redkale.net;
import java.io.IOException;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
@@ -22,10 +22,9 @@ import javax.net.ssl.SSLContext;
*
* @author zhangjx
*/
public class TcpAioAsyncConnection extends AsyncConnection {
class TcpAioAsyncConnection extends AsyncConnection {
//private final Semaphore semaphore = new Semaphore(1);
private int readTimeoutSeconds;
private int writeTimeoutSeconds;
@@ -40,7 +39,7 @@ public class TcpAioAsyncConnection extends AsyncConnection {
final AsynchronousSocketChannel ch, final SSLContext sslContext, final SocketAddress addr0,
final int readTimeoutSeconds, final int writeTimeoutSeconds,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext);
super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch;
this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSeconds = writeTimeoutSeconds;
@@ -53,8 +52,6 @@ public class TcpAioAsyncConnection extends AsyncConnection {
}
}
this.remoteAddress = addr;
this.livingCounter = livingCounter;
this.closedCounter = closedCounter;
}
@Override
@@ -132,7 +129,6 @@ public class TcpAioAsyncConnection extends AsyncConnection {
// semaphore.release();
// }
// }
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
write(true, src, attachment, handler);
@@ -232,21 +228,55 @@ public class TcpAioAsyncConnection extends AsyncConnection {
}
@Override
public final int read(ByteBuffer dst) throws IOException {
try {
return channel.read(dst).get();
} catch (InterruptedException | ExecutionException e) {
throw new IOException(e);
}
public final ReadableByteChannel readableByteChannel() {
return new ReadableByteChannel() {
@Override
public int read(ByteBuffer dst) throws IOException {
try {
return channel.read(dst).get(readTimeoutSeconds > 0 ? readTimeoutSeconds : 6, TimeUnit.SECONDS);
} catch (Exception e) {
throw new IOException(e);
}
}
@Override
@SuppressWarnings("InfiniteRecursion")
public boolean isOpen() {
return isOpen();
}
@Override
@SuppressWarnings("InfiniteRecursion")
public void close() throws IOException {
close();
}
};
}
@Override
public final int write(ByteBuffer src) throws IOException {
try {
return channel.write(src).get();
} catch (InterruptedException | ExecutionException e) {
throw new IOException(e);
}
public final WritableByteChannel writableByteChannel() {
return new WritableByteChannel() {
@Override
public int write(ByteBuffer src) throws IOException {
try {
return channel.write(src).get(readTimeoutSeconds > 0 ? readTimeoutSeconds : 6, TimeUnit.SECONDS);
} catch (Exception e) {
throw new IOException(e);
}
}
@Override
@SuppressWarnings("InfiniteRecursion")
public boolean isOpen() {
return isOpen();
}
@Override
@SuppressWarnings("InfiniteRecursion")
public void close() throws IOException {
close();
}
};
}
@Override
@@ -318,13 +348,13 @@ public class TcpAioAsyncConnection extends AsyncConnection {
return;
}
// try {
writeHandler.completed(writeCount, attachment);
writeHandler.completed(writeCount, attachment);
// } finally {
// nextWrite(null, attachment);
// }
} else {
// try {
writeHandler.completed(result.intValue(), attachment);
writeHandler.completed(result.intValue(), attachment);
// } finally {
// nextWrite(null, attachment);
// }
@@ -334,7 +364,7 @@ public class TcpAioAsyncConnection extends AsyncConnection {
@Override
public void failed(Throwable exc, A attachment) {
// try {
writeHandler.failed(exc, attachment);
writeHandler.failed(exc, attachment);
// } finally {
// nextWrite(exc, attachment);
// }
@@ -365,7 +395,7 @@ public class TcpAioAsyncConnection extends AsyncConnection {
return;
}
// try {
writeHandler.completed(result, attachment);
writeHandler.completed(result, attachment);
// } finally {
// nextWrite(null, attachment);
// }
@@ -375,7 +405,7 @@ public class TcpAioAsyncConnection extends AsyncConnection {
@Override
public void failed(Throwable exc, A attachment) {
// try {
writeHandler.failed(exc, attachment);
writeHandler.failed(exc, attachment);
// } finally {
// nextWrite(exc, attachment);
// }

View File

@@ -0,0 +1,545 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
import org.redkale.net.AsyncConnection;
import org.redkale.net.nio.NioCompletionHandler;
import org.redkale.net.nio.NioThread;
import org.redkale.net.nio.NioThreadGroup;
import org.redkale.util.ObjectPool;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class TcpNioAsyncConnection extends AsyncConnection {
private int readTimeoutSeconds;
private int writeTimeoutSeconds;
private final SocketAddress remoteAddress;
final SocketChannel channel;
final NioThread ioThread;
final NioThreadGroup ioGroup;
final ExecutorService workExecutor;
//连
private Object connectAttachment;
private CompletionHandler<Void, Object> connectCompletionHandler;
private boolean connectPending;
private SelectionKey connectKey;
//读操作
private ByteBuffer readByteBuffer;
private CompletionHandler<Integer, ByteBuffer> readCompletionHandler;
private boolean readPending;
private SelectionKey readKey;
//写操作, 二选一要么writeByteBuffer有值要么writeByteBuffers、writeOffset、writeLength有值
private ByteBuffer writeByteBuffer;
private ByteBuffer[] writeByteBuffers;
private int writeOffset;
private int writeLength;
private Object writeAttachment;
private CompletionHandler<Integer, Object> writeCompletionHandler;
private boolean writePending;
private SelectionKey writeKey;
public TcpNioAsyncConnection(NioThreadGroup ioGroup, NioThread ioThread, ExecutorService workExecutor,
ObjectPool<ByteBuffer> bufferPool, SocketChannel ch,
SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(bufferPool, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.workExecutor = workExecutor;
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
public TcpNioAsyncConnection(NioThreadGroup ioGroup, NioThread ioThread, ExecutorService workExecutor,
Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
SocketChannel ch, SSLContext sslContext, final SocketAddress addr0,
AtomicLong livingCounter, AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.workExecutor = workExecutor;
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public boolean isOpen() {
return this.channel.isOpen();
}
@Override
public boolean isTCP() {
return true;
}
@Override
public boolean shutdownInput() {
try {
this.channel.shutdownInput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public boolean shutdownOutput() {
try {
this.channel.shutdownOutput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public <T> boolean setOption(SocketOption<T> name, T value) {
try {
this.channel.setOption(name, value);
return true;
} catch (IOException e) {
return false;
}
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return this.channel.supportedOptions();
}
@Override
public SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
this.readTimeoutSeconds = readTimeoutSeconds;
}
@Override
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
this.writeTimeoutSeconds = writeTimeoutSeconds;
}
@Override
public int getReadTimeoutSeconds() {
return this.readTimeoutSeconds;
}
@Override
public int getWriteTimeoutSeconds() {
return this.writeTimeoutSeconds;
}
@Override
public ReadableByteChannel readableByteChannel() {
return this.channel;
}
@Override
public void read(CompletionHandler<Integer, ByteBuffer> handler) {
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
if (this.workExecutor == null) {
handler.failed(new NotYetConnectedException(), pollReadBuffer());
} else {
this.workExecutor.execute(() -> handler.failed(new NotYetConnectedException(), pollReadBuffer()));
}
return;
}
if (this.readPending) {
if (this.workExecutor == null) {
handler.failed(new ReadPendingException(), pollReadBuffer());
} else {
this.workExecutor.execute(() -> handler.failed(new ReadPendingException(), pollReadBuffer()));
}
return;
}
this.readPending = true;
if (this.readTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, this.readByteBuffer);
this.readCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.readCompletionHandler = handler;
}
doRead();
}
@Override
public WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(src);
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
if (this.workExecutor == null) {
handler.failed(new NotYetConnectedException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new NotYetConnectedException(), attachment));
}
return;
}
if (this.writePending) {
if (this.workExecutor == null) {
handler.failed(new WritePendingException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new WritePendingException(), attachment));
}
return;
}
this.writePending = true;
this.writeByteBuffer = src;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite();
}
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(srcs);
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
if (this.workExecutor == null) {
handler.failed(new NotYetConnectedException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new NotYetConnectedException(), attachment));
}
return;
}
if (this.writePending) {
if (this.workExecutor == null) {
handler.failed(new WritePendingException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new WritePendingException(), attachment));
}
return;
}
this.writePending = true;
this.writeByteBuffers = srcs;
this.writeOffset = offset;
this.writeLength = length;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite();
}
public void doConnect() {
try {
boolean connected = channel.isConnectionPending();
if (connected || channel.connect(remoteAddress)) {
connected = channel.finishConnect();
}
if (connected) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (this.workExecutor == null) {
handler.completed(null, attach);
} else {
this.workExecutor.execute(() -> handler.completed(null, attach));
}
}
} else if (connectKey == null) {
ioThread.register(selector -> {
try {
connectKey = channel.register(selector, SelectionKey.OP_CONNECT);
connectKey.attach(this);
} catch (ClosedChannelException e) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
});
} else {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
IOException e = new IOException();
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
} catch (IOException e) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
}
private void clearConnect() {
this.connectCompletionHandler = null;
this.connectAttachment = null;
this.connectPending = false;//必须放最后
}
public void doRead() {
try {
final boolean invokeDirect = this.ioThread.inSameThread();
int totalCount = 0;
boolean hasRemain = true;
if (invokeDirect && this.readByteBuffer == null) {
this.readByteBuffer = pollReadBuffer();
if (this.readTimeoutSeconds > 0) {
((NioCompletionHandler) this.readCompletionHandler).setAttachment(this.readByteBuffer);
}
}
while (invokeDirect && hasRemain) {
int readCount = this.channel.read(readByteBuffer);
hasRemain = readByteBuffer.hasRemaining();
if (readCount <= 0) {
if (totalCount == 0) totalCount = readCount;
break;
}
totalCount += readCount;
}
if (totalCount != 0 || !hasRemain) {
if (readKey != null) readKey.interestOps(readKey.interestOps() & ~SelectionKey.OP_READ);
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (this.workExecutor == null) {
handler.completed(totalCount, attach);
} else {
final int totalCount0 = totalCount;
this.workExecutor.execute(() -> handler.completed(totalCount0, attach));
}
}
} else if (readKey == null) {
ioThread.register(selector -> {
try {
readKey = channel.register(selector, SelectionKey.OP_READ);
readKey.attach(this);
} catch (ClosedChannelException e) {
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
});
} else {
ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ);
}
} catch (Exception e) {
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
}
private void clearRead() {
this.readCompletionHandler = null;
this.readByteBuffer = null;
this.readPending = false; //必须放最后
}
public void doWrite() {
try {
final boolean invokeDirect = this.ioThread.inSameThread();
int totalCount = 0;
boolean hasRemain = true;
while (invokeDirect && hasRemain) {
int writeCount;
if (writeByteBuffer != null) {
writeCount = channel.write(writeByteBuffer);
hasRemain = writeByteBuffer.hasRemaining();
} else {
writeCount = (int) channel.write(writeByteBuffers, writeOffset, writeLength);
boolean remain = false;
for (int i = writeByteBuffers.length - 1; i >= writeOffset; i--) {
if (writeByteBuffers[i].hasRemaining()) {
remain = true;
break;
}
}
hasRemain = remain;
}
if (writeCount <= 0) {
if (totalCount == 0) totalCount = writeCount;
break;
}
totalCount += writeCount;
}
if (totalCount > 0 || !hasRemain) {
if (writeKey != null) writeKey.interestOps(writeKey.interestOps() & ~SelectionKey.OP_WRITE);
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
if (this.workExecutor == null) {
handler.completed(totalCount, attach);
} else {
final int totalCount0 = totalCount;
this.workExecutor.execute(() -> handler.completed(totalCount0, attach));
}
}
} else if (writeKey == null) {
ioThread.register(selector -> {
try {
writeKey = channel.register(selector, SelectionKey.OP_WRITE);
writeKey.attach(this);
} catch (ClosedChannelException e) {
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
});
} else {
ioGroup.interestOpsOr(ioThread, writeKey, SelectionKey.OP_WRITE);
}
} catch (IOException e) {
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
}
private void clearWrite() {
this.writeCompletionHandler = null;
this.writeAttachment = null;
this.writeByteBuffer = null;
this.writeByteBuffers = null;
this.writeOffset = 0;
this.writeLength = 0;
this.writePending = false; //必须放最后
}
@Override
public final void close() throws IOException {
super.close();
if (this.connectKey != null) this.connectKey.cancel();
if (this.readKey != null) this.readKey.cancel();
if (this.writeKey != null) this.writeKey.cancel();
channel.close();
}
}

View File

@@ -0,0 +1,146 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.net.*;
import org.redkale.net.nio.*;
import org.redkale.util.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class TcpNioProtocolServer extends ProtocolServer {
private ObjectPool<ByteBuffer> bufferPool;
private ObjectPool<Response> responsePool;
private ServerSocketChannel serverChannel;
private Selector selector;
private NioThreadGroup ioGroup;
private Thread acceptThread;
private boolean closed;
public TcpNioProtocolServer(Context context) {
super(context);
}
@Override
public void open(AnyValue config) throws IOException {
this.serverChannel = ServerSocketChannel.open();
this.serverChannel.configureBlocking(false);
this.selector = Selector.open();
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
if (options.contains(StandardSocketOptions.TCP_NODELAY)) {
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) {
this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
}
if (options.contains(StandardSocketOptions.SO_REUSEADDR)) {
this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
}
if (options.contains(StandardSocketOptions.SO_RCVBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
}
if (options.contains(StandardSocketOptions.SO_SNDBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
}
}
@Override
public void bind(SocketAddress local, int backlog) throws IOException {
this.serverChannel.bind(local, backlog);
}
@Override
public <T> Set<SocketOption<?>> supportedOptions() {
return this.serverChannel.supportedOptions();
}
@Override
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
this.serverChannel.setOption(name, value);
}
@Override
public void accept(Server server) throws IOException {
this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
this.bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
this.responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
this.responsePool.setCreator(server.createResponseCreator(bufferPool, responsePool));
this.ioGroup = new NioThreadGroup(Runtime.getRuntime().availableProcessors(), context.executor, bufferPool);
this.ioGroup.start();
this.acceptThread = new Thread() {
@Override
public void run() {
while (!closed) {
try {
int count = selector.select();
if (count == 0) continue;
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable()) accept(key);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
};
this.acceptThread.start();
}
private void accept(SelectionKey key) throws IOException {
SocketChannel channel = this.serverChannel.accept();
channel.configureBlocking(false);
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
NioThread ioThread = ioGroup.nextThread();
AsyncConnection conn = new TcpNioAsyncConnection(ioGroup, ioThread, context.executor, bufferPool, channel, context.getSSLContext(), null, livingCounter, closedCounter);
new PrepareRunner(context, responsePool, conn, null, null).run();
}
@Override
public void close() throws IOException {
if (this.closed) return;
this.closed = true;
this.selector.wakeup();
this.ioGroup.close();
this.serverChannel.close();
this.selector.close();
}
}

View File

@@ -38,8 +38,6 @@ public final class Transport {
protected final String name; //即<group>的name属性
protected final String subprotocol; //即<group>的subprotocol属性
protected final boolean tcp;
protected final String protocol;
@@ -58,18 +56,19 @@ public final class Transport {
//负载均衡策略
protected final TransportStrategy strategy;
protected Transport(String name, String subprotocol, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
//连接上限, 为null表示无限制
protected Semaphore semaphore;
protected Transport(String name, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this(name, DEFAULT_PROTOCOL, subprotocol, factory, transportBufferPool, transportChannelGroup, sslContext, clientAddress, addresses, strategy);
this(name, DEFAULT_PROTOCOL, factory, transportBufferPool, transportChannelGroup, sslContext, clientAddress, addresses, strategy);
}
protected Transport(String name, String protocol, String subprotocol,
final TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
protected Transport(String name, String protocol, final TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this.name = name;
this.subprotocol = subprotocol == null ? "" : subprotocol.trim();
this.protocol = protocol;
this.factory = factory;
factory.transportReferences.add(new WeakReference<>(this));
@@ -82,26 +81,46 @@ public final class Transport {
updateRemoteAddresses(addresses);
}
public Semaphore getSemaphore() {
return semaphore;
}
public void setSemaphore(Semaphore semaphore) {
this.semaphore = semaphore;
}
public final InetSocketAddress[] updateRemoteAddresses(final Collection<InetSocketAddress> addresses) {
final TransportNode[] oldNodes = this.transportNodes;
synchronized (this) {
List<TransportNode> list = new ArrayList<>();
if (addresses != null) {
for (InetSocketAddress addr : addresses) {
if (clientAddress != null && clientAddress.equals(addr)) continue;
boolean hasold = false;
for (TransportNode oldAddr : oldNodes) {
if (oldAddr.getAddress().equals(addr)) {
list.add(oldAddr);
hasold = true;
break;
}
boolean same = false;
if (this.transportNodes != null && addresses != null && this.transportNodes.length == addresses.size()) {
same = true;
for (TransportNode node : this.transportNodes) {
if (!addresses.contains(node.getAddress())) {
same = false;
break;
}
if (hasold) continue;
list.add(new TransportNode(factory.poolmaxconns, addr));
}
}
this.transportNodes = list.toArray(new TransportNode[list.size()]);
if (!same) {
List<TransportNode> list = new ArrayList<>();
if (addresses != null) {
for (InetSocketAddress addr : addresses) {
if (clientAddress != null && clientAddress.equals(addr)) continue;
boolean hasold = false;
for (TransportNode oldAddr : oldNodes) {
if (oldAddr.getAddress().equals(addr)) {
list.add(oldAddr);
hasold = true;
break;
}
}
if (hasold) continue;
list.add(new TransportNode(factory.poolmaxconns, addr));
}
}
this.transportNodes = list.toArray(new TransportNode[list.size()]);
}
}
InetSocketAddress[] rs = new InetSocketAddress[oldNodes.length];
for (int i = 0; i < rs.length; i++) {
@@ -138,10 +157,6 @@ public final class Transport {
return name;
}
public String getSubprotocol() {
return subprotocol;
}
public void close() {
TransportNode[] nodes = this.transportNodes;
if (nodes == null) return;
@@ -198,10 +213,52 @@ public final class Transport {
return group;
}
public String getProtocol() {
return protocol;
}
public boolean isTCP() {
return tcp;
}
protected CompletableFuture<AsyncConnection> pollAsync(TransportNode node, SocketAddress addr, Supplier<CompletableFuture<AsyncConnection>> func, final int count) {
if (count >= 5) {
CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException("create AsyncConnection error"));
return future;
}
final BlockingQueue<AsyncConnection> queue = node.conns;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.isOpen()) {
return CompletableFuture.completedFuture(conn);
} else {
conn.dispose();
}
}
}
if (semaphore != null && !semaphore.tryAcquire()) {
return CompletableFuture.supplyAsync(() -> {
try {
return queue.poll(1, TimeUnit.SECONDS);
} catch (Exception t) {
return null;
}
}, factory.executor).thenCompose((conn2) -> {
if (conn2 != null && conn2.isOpen()) {
return CompletableFuture.completedFuture(conn2);
}
return pollAsync(node, addr, func, count + 1);
});
}
return func.get().thenApply(conn -> {
if (conn != null && semaphore != null) conn.beforeCloseListener((c) -> semaphore.release());
return conn;
});
}
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr0) {
if (this.strategy != null) return strategy.pollConnection(addr0, this);
final TransportNode[] nodes = this.transportNodes;
@@ -222,18 +279,7 @@ public final class Transport {
if (node == null) {
return AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
}
final BlockingQueue<AsyncConnection> queue = node.conns;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.isOpen()) {
return CompletableFuture.completedFuture(conn);
} else {
conn.dispose();
}
}
}
return AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
return pollAsync(node, addr, () -> AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds), 1);
}
//---------------------随机取地址------------------------
@@ -257,45 +303,53 @@ public final class Transport {
}
}
}
CompletableFuture future = new CompletableFuture();
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
channel.connect(one.address, one, new CompletionHandler<Void, TransportNode>() {
@Override
public void completed(Void result, TransportNode attachment) {
attachment.disabletime = 0;
AsyncConnection asyncConn = AsyncConnection.create(bufferPool, channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
if (future.isDone()) {
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
} else {
future.complete(asyncConn);
}
return pollAsync(one, one.getAddress(), () -> {
CompletableFuture future = new CompletableFuture();
AsynchronousSocketChannel channel0 = null;
try {
channel0 = AsynchronousSocketChannel.open(group);
channel0.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel0.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel0.setOption(StandardSocketOptions.SO_REUSEADDR, true);
} catch (Exception ex) {
ex.printStackTrace();
}
@Override
public void failed(Throwable exc, TransportNode attachment) {
attachment.disabletime = now;
try {
channel.close();
} catch (Exception e) {
final AsynchronousSocketChannel channel = channel0;
channel.connect(one.address, one, new CompletionHandler<Void, TransportNode>() {
@Override
public void completed(Void result, TransportNode attachment) {
attachment.disabletime = 0;
AsyncConnection asyncConn = AsyncConnection.create(bufferPool, channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
if (future.isDone()) {
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
} else {
future.complete(asyncConn);
}
}
try {
pollConnection0(nodes, one, now).whenComplete((r, t) -> {
if (t != null) {
future.completeExceptionally(t);
} else {
future.complete(r);
}
});
} catch (Exception e) {
future.completeExceptionally(e);
@Override
public void failed(Throwable exc, TransportNode attachment) {
attachment.disabletime = now;
try {
channel.close();
} catch (Exception e) {
}
try {
pollConnection0(nodes, one, now).whenComplete((r, t) -> {
if (t != null) {
future.completeExceptionally(t);
} else {
future.complete(r);
}
});
} catch (Exception e) {
future.completeExceptionally(e);
}
}
}
});
return future;
});
return future;
}, 1);
}
return pollConnection0(nodes, null, now);
} catch (Exception ex) {

View File

@@ -21,8 +21,8 @@ import org.redkale.service.Service;
import org.redkale.util.*;
/**
* System.getProperty("net.transport.pinginterval", "30") 心跳周期默认30秒
* System.getProperty("net.transport.checkinterval", "30") 检查不可用地址周期默认30秒
* System.getProperty("net.transport.ping.interval", "30") 心跳周期默认30秒
* System.getProperty("net.transport.check.interval", "30") 检查不可用地址周期默认30秒
*
* <p>
* 详情见: https://redkale.org
@@ -65,10 +65,10 @@ public class TransportFactory {
protected final List<WeakReference<Transport>> transportReferences = new CopyOnWriteArrayList<>();
//连接池大小
protected int poolmaxconns = Integer.getInteger("net.transport.poolmaxconns", Math.max(100, Runtime.getRuntime().availableProcessors() * 16)); //最少是wsthreads的两倍
protected int poolmaxconns = Integer.getInteger("net.transport.pool.maxconns", Math.max(100, Runtime.getRuntime().availableProcessors() * 16)); //最少是wsthreads的两倍
//检查不可用地址周期, 单位:秒
protected int checkinterval = Integer.getInteger("net.transport.checkinterval", 30);
protected int checkinterval = Integer.getInteger("net.transport.check.interval", 30);
//心跳周期, 单位:秒
protected int pinginterval;
@@ -203,16 +203,11 @@ public class TransportFactory {
}
public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, "TCP", "", this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
return new Transport(name, "TCP", this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
}
public Transport createTransport(String name, String protocol, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, protocol, "", this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
}
public Transport createTransport(String name, String protocol, String subprotocol,
final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, protocol, subprotocol, this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
return new Transport(name, protocol, this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
}
public String findGroupName(InetSocketAddress addr) {
@@ -251,7 +246,6 @@ public class TransportFactory {
if (!checkName(info.name)) throw new RuntimeException("Transport.group.name only 0-9 a-z A-Z _ cannot begin 0-9");
TransportGroupInfo old = groupInfos.get(info.name);
if (old != null && !old.protocol.equals(info.protocol)) throw new RuntimeException("Transport.group.name repeat but protocol is different");
if (old != null && !old.subprotocol.equals(info.subprotocol)) throw new RuntimeException("Transport.group.name repeat but subprotocol is different");
for (InetSocketAddress addr : info.addresses) {
if (!groupAddrs.getOrDefault(addr, info.name).equals(info.name)) throw new RuntimeException(addr + " repeat but different group.name");
}
@@ -266,23 +260,7 @@ public class TransportFactory {
return true;
}
public Transport loadSameGroupTransport(InetSocketAddress sncpAddress) {
return loadTransport(groupAddrs.get(sncpAddress), sncpAddress);
}
public Transport[] loadDiffGroupTransports(InetSocketAddress sncpAddress, final Set<String> diffGroups) {
if (diffGroups == null) return null;
final String sncpGroup = groupAddrs.get(sncpAddress);
final List<Transport> transports = new ArrayList<>();
for (String group : diffGroups) {
if (sncpGroup == null || !sncpGroup.equals(group)) {
transports.add(loadTransport(group, sncpAddress));
}
}
return transports.toArray(new Transport[transports.size()]);
}
public Transport loadRemoteTransport(InetSocketAddress sncpAddress, final Set<String> groups) {
public Transport loadTransport(InetSocketAddress sncpAddress, final Set<String> groups) {
if (groups == null) return null;
Set<InetSocketAddress> addresses = new HashSet<>();
TransportGroupInfo info = null;
@@ -293,14 +271,7 @@ public class TransportFactory {
}
if (info == null) info = new TransportGroupInfo("TCP");
if (sncpAddress != null) addresses.remove(sncpAddress);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, this.sslContext, sncpAddress, addresses, this.strategy);
}
private Transport loadTransport(final String groupName, InetSocketAddress sncpAddress) {
if (groupName == null) return null;
TransportGroupInfo info = groupInfos.get(groupName);
if (info == null) return null;
return new Transport(groupName, info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, this.sslContext, sncpAddress, info.addresses, this.strategy);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, this, this.bufferPool, this.channelGroup, this.sslContext, sncpAddress, addresses, this.strategy);
}
public ExecutorService getExecutor() {

View File

@@ -24,30 +24,27 @@ public class TransportGroupInfo {
protected String protocol; //协议 取值范围: TCP、UDP
protected String subprotocol; //子协议,预留使用
protected Set<InetSocketAddress> addresses; //地址列表, 对应 resources-&#62;group-&#62;node节点信息
public TransportGroupInfo() {
}
public TransportGroupInfo(String name, InetSocketAddress... addrs) {
this(name, "TCP", "", Utility.ofSet(addrs));
this(name, "TCP", Utility.ofSet(addrs));
}
public TransportGroupInfo(String name, Set<InetSocketAddress> addrs) {
this(name, "TCP", "", addrs);
this(name, "TCP", addrs);
}
public TransportGroupInfo(String name, String protocol, String subprotocol, InetSocketAddress... addrs) {
this(name, protocol, subprotocol, Utility.ofSet(addrs));
public TransportGroupInfo(String name, String protocol, InetSocketAddress... addrs) {
this(name, protocol, Utility.ofSet(addrs));
}
public TransportGroupInfo(String name, String protocol, String subprotocol, Set<InetSocketAddress> addrs) {
public TransportGroupInfo(String name, String protocol, Set<InetSocketAddress> addrs) {
Objects.requireNonNull(name, "Transport.group.name can not null");
this.name = name;
this.protocol = protocol == null ? "TCP" : protocol;
this.subprotocol = subprotocol == null ? "" : subprotocol;
this.addresses = addrs;
}
@@ -68,15 +65,6 @@ public class TransportGroupInfo {
this.protocol = protocol == null ? "TCP" : protocol;
}
public String getSubprotocol() {
return subprotocol;
}
public void setSubprotocol(String subprotocol) {
this.subprotocol = subprotocol == null ? "" : subprotocol;
this.subprotocol = subprotocol;
}
public Set<InetSocketAddress> getAddresses() {
return addresses;
}

View File

@@ -5,7 +5,7 @@
*/
package org.redkale.net;
import java.io.IOException;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
@@ -21,7 +21,7 @@ import javax.net.ssl.SSLContext;
*
* @author zhangjx
*/
public class UdpBioAsyncConnection extends AsyncConnection {
class UdpBioAsyncConnection extends AsyncConnection {
private int readTimeoutSeconds;
@@ -37,7 +37,7 @@ public class UdpBioAsyncConnection extends AsyncConnection {
final DatagramChannel ch, final SSLContext sslContext, SocketAddress addr0, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext);
super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch;
this.client = client0;
this.readTimeoutSeconds = readTimeoutSeconds0;
@@ -51,8 +51,6 @@ public class UdpBioAsyncConnection extends AsyncConnection {
}
}
this.remoteAddress = addr;
this.livingCounter = livingCounter;
this.closedCounter = closedCounter;
}
@Override
@@ -142,10 +140,13 @@ public class UdpBioAsyncConnection extends AsyncConnection {
}
@Override
public int read(ByteBuffer dst) throws IOException {
int rs = channel.read(dst);
this.readtime = System.currentTimeMillis();
return rs;
public final ReadableByteChannel readableByteChannel() {
return this.channel;
}
@Override
public final WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
@@ -159,13 +160,6 @@ public class UdpBioAsyncConnection extends AsyncConnection {
}
}
@Override
public int write(ByteBuffer src) throws IOException {
int rs = channel.send(src, remoteAddress);
this.writetime = System.currentTimeMillis();
return rs;
}
@Override
public final void close() throws IOException {
super.close();

View File

@@ -30,6 +30,11 @@ public @interface HttpMapping {
*/
int actionid() default 0;
/**
* 请求地址
*
* @return String
*/
String url();
/**
@@ -41,6 +46,13 @@ public @interface HttpMapping {
*/
int cacheseconds() default 0;
/**
* 是否只接受RPC请求 默认为false
*
* @return 默认false
*/
boolean rpconly() default false;
/**
* 是否鉴权,默认需要鉴权 <br>
*

View File

@@ -1,5 +1,5 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this license headers, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
@@ -14,9 +14,9 @@ import java.nio.channels.Channels;
import java.nio.charset.*;
import java.util.*;
import java.util.logging.Level;
import org.redkale.convert.ConvertDisabled;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.*;
/**
@@ -34,39 +34,41 @@ import org.redkale.util.*;
*/
public class HttpRequest extends Request<HttpContext> {
protected static final Serializable CURRUSERID_NIL = new Serializable() {
};
public static final String SESSIONID_NAME = "JSESSIONID";
@Comment("Method GET/POST/...")
private String method;
protected boolean rpc;
private String protocol;
@Comment("Method GET/POST/...")
protected String method;
protected String protocol;
protected String requestURI;
private byte[] queryBytes;
protected byte[] queryBytes;
private long contentLength = -1;
protected long contentLength = -1;
private String contentType;
protected String contentType;
private String host;
protected String host;
private String connection;
protected String connection;
@Comment("原始的cookie字符串解析后值赋给HttpCookie[] cookies")
protected String cookie;
private HttpCookie[] cookies;
protected HttpCookie[] cookies;
protected String newsessionid;
protected final DefaultAnyValue header = new DefaultAnyValue();
//protected final DefaultAnyValue headers = new DefaultAnyValue();
protected final Map<String, String> headers = new HashMap<>();
protected final DefaultAnyValue params = new DefaultAnyValue();
private final ByteArray array = new ByteArray();
private boolean bodyparsed = false;
protected final Map<String, String> params = new HashMap<>();
protected boolean boundary = false;
@@ -76,8 +78,17 @@ public class HttpRequest extends Request<HttpContext> {
protected Annotation[] annotations;
// @since 2.1.0
protected Serializable currentUserid = CURRUSERID_NIL;
protected Object currentUser;
protected String remoteAddr;
private final ByteArray array = new ByteArray();
private boolean bodyparsed = false;
private final String remoteAddrHeader;
Object attachment; //仅供HttpServlet传递Entry使用
@@ -87,6 +98,52 @@ public class HttpRequest extends Request<HttpContext> {
this.remoteAddrHeader = context.remoteAddrHeader;
}
public HttpRequest(HttpContext context, HttpSimpleRequest req) {
super(context, null);
this.remoteAddrHeader = null;
if (req != null) {
this.rpc = req.rpc;
if (req.getBody() != null) this.array.write(req.getBody());
if (req.getHeaders() != null) this.headers.putAll(req.getHeaders());
if (req.getParams() != null) this.params.putAll(req.getParams());
if (req.getCurrentUserid() != 0) this.currentUserid = req.getCurrentUserid();
this.contentType = req.getContentType();
this.remoteAddr = req.getRemoteAddr();
this.requestURI = req.getRequestURI();
this.method = "POST";
if (req.getSessionid() != null && !req.getSessionid().isEmpty()) {
this.cookies = new HttpCookie[]{new HttpCookie(SESSIONID_NAME, req.getSessionid())};
}
}
}
public HttpSimpleRequest createSimpleRequest(String prefix) {
HttpSimpleRequest req = new HttpSimpleRequest();
req.setBody(array.size() == 0 ? null : array.getBytes());
if (!headers.isEmpty()) {
if (headers.containsKey(Rest.REST_HEADER_RPC_NAME)
|| headers.containsKey(Rest.REST_HEADER_CURRUSERID_NAME)) { //外部request不能包含RPC的header信息
req.setHeaders(new HashMap<>(headers));
req.removeHeader(Rest.REST_HEADER_RPC_NAME);
req.removeHeader(Rest.REST_HEADER_CURRUSERID_NAME);
} else {
req.setHeaders(headers);
}
}
req.setParams(params.isEmpty() ? null : params);
req.setRemoteAddr(getRemoteAddr());
req.setContentType(getContentType());
req.setPath(prefix);
String uri = this.requestURI;
if (prefix != null && !prefix.isEmpty() && uri.startsWith(prefix)) {
uri = uri.substring(prefix.length());
}
req.setRequestURI(uri);
req.setSessionid(getSessionid(false));
req.setRpc(this.rpc);
return req;
}
protected boolean isWebSocket() {
return connection != null && connection.contains("Upgrade") && "GET".equalsIgnoreCase(method) && "websocket".equalsIgnoreCase(getHeader("Upgrade"));
}
@@ -176,10 +233,18 @@ public class HttpRequest extends Request<HttpContext> {
}
break;
case "user-agent":
header.addValue("User-Agent", value);
headers.put("User-Agent", value);
break;
case Rest.REST_HEADER_RPC_NAME:
this.rpc = "true".equalsIgnoreCase(value);
headers.put(name, value);
break;
case Rest.REST_HEADER_CURRUSERID_NAME:
this.currentUserid = value;
headers.put(name, value);
break;
default:
header.addValue(name, value);
headers.put(name, value);
}
}
if (this.contentType != null && this.contentType.contains("boundary=")) this.boundary = true;
@@ -214,10 +279,10 @@ public class HttpRequest extends Request<HttpContext> {
private void parseBody() {
if (this.boundary || bodyparsed) return;
bodyparsed = true;
if (this.contentType != null && this.contentType.toLowerCase().contains("x-www-form-urlencoded")) {
addParameter(array, 0, array.size());
}
bodyparsed = true;
}
private void addParameter(final ByteArray array, final int offset, final int len) {
@@ -234,7 +299,7 @@ public class HttpRequest extends Request<HttpContext> {
if (name.charAt(0) == '<') return; //内容可能是xml格式; 如: <?xml version="1.0"
++keypos;
String value = array.toDecodeString(keypos, (valpos < 0) ? (limit - keypos) : (valpos - keypos), charset);
this.params.addValue(name, value);
this.params.put(name, value);
if (valpos >= 0) {
addParameter(array, valpos + 1, limit - valpos - 1);
}
@@ -273,6 +338,53 @@ public class HttpRequest extends Request<HttpContext> {
}
/**
* 设置当前用户ID, 通常在HttpServlet.preExecute方法里设置currentUserid <br>
* 数据类型只能是int、long、String、JavaBean
*
* @param <T> 泛型
* @param userid 用户ID
*
* @return HttpRequest
*
* @since 2.1.0
*/
public <T extends Serializable> HttpRequest setCurrentUserid(T userid) {
this.currentUserid = userid;
return this;
}
/**
* 获取当前用户ID<br>
*
* @param <T> 数据类型只能是int、long、String、JavaBean
* @param type 类型
*
* @return 用户ID
*
* @since 2.1.0
*/
@SuppressWarnings("unchecked")
public <T extends Serializable> T currentUserid(Class<T> type) {
if (currentUserid == CURRUSERID_NIL || currentUserid == null) {
if (type == int.class) return (T) (Integer) (int) 0;
if (type == long.class) return (T) (Long) (long) 0;
return null;
}
if (type == int.class) {
if (this.currentUserid instanceof Number) return (T) (Integer) ((Number) this.currentUserid).intValue();
return (T) (Integer) Integer.parseInt(this.currentUserid.toString());
}
if (type == long.class) {
if (this.currentUserid instanceof Number) return (T) (Long) ((Number) this.currentUserid).longValue();
return (T) (Long) Long.parseLong(this.currentUserid.toString());
}
if (type == String.class) return (T) this.currentUserid.toString();
if (this.currentUserid instanceof CharSequence) return JsonConvert.root().convertFrom(type, this.currentUserid.toString());
return (T) this.currentUserid;
}
/**
* 建议使用 setCurrentUserid, 通过userid从Service或缓存中获取用户信息<br>
* 设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser <br>
* 数据类型由&#64;HttpUserType指定
*
@@ -281,12 +393,14 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return HttpRequest
*/
@Deprecated
public <T> HttpRequest setCurrentUser(T user) {
this.currentUser = user;
return this;
}
/**
* 建议使用 currentUserid, 通过userid从Service或缓存中获取用户信息<br>
* 获取当前用户信息<br>
* 数据类型由&#64;HttpUserType指定
*
@@ -294,6 +408,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return 用户信息
*/
@Deprecated
@SuppressWarnings("unchecked")
public <T> T currentUser() {
return (T) this.currentUser;
@@ -304,6 +419,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return 模块ID
*/
@ConvertDisabled
public int getModuleid() {
return this.moduleid;
}
@@ -313,6 +429,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return 模块ID
*/
@ConvertDisabled
public int getActionid() {
return this.actionid;
}
@@ -322,6 +439,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return Annotation[]
*/
@ConvertDisabled
public Annotation[] getAnnotations() {
if (this.annotations == null) return new Annotation[0];
Annotation[] newanns = new Annotation[this.annotations.length];
@@ -371,6 +489,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return 地址
*/
@ConvertDisabled
public SocketAddress getRemoteAddress() {
return this.channel == null || !this.channel.isOpen() ? null : this.channel.getRemoteAddress();
}
@@ -382,14 +501,22 @@ public class HttpRequest extends Request<HttpContext> {
* @return 地址
*/
public String getRemoteAddr() {
if (this.remoteAddr != null) return this.remoteAddr;
if (remoteAddrHeader != null) {
String val = getHeader(remoteAddrHeader);
if (val != null) return val;
if (val != null) {
this.remoteAddr = val;
return val;
}
}
SocketAddress addr = getRemoteAddress();
if (addr == null) return "";
if (addr instanceof InetSocketAddress) return ((InetSocketAddress) addr).getAddress().getHostAddress();
return String.valueOf(addr);
if (addr instanceof InetSocketAddress) {
this.remoteAddr = ((InetSocketAddress) addr).getAddress().getHostAddress();
return this.remoteAddr;
}
this.remoteAddr = String.valueOf(addr);
return this.remoteAddr;
}
/**
@@ -408,6 +535,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return 内容
*/
@ConvertDisabled
public String getBodyUTF8() {
return array.toString(StandardCharsets.UTF_8);
}
@@ -447,7 +575,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容
*/
public byte[] getBody() {
return array.getBytes();
return array.size() == 0 ? null : array.getBytes();
}
/**
@@ -455,6 +583,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return body对象
*/
@ConvertDisabled
protected ByteArray getDirectBody() {
return array;
}
@@ -466,7 +595,20 @@ public class HttpRequest extends Request<HttpContext> {
+ ", \r\n remoteAddr: " + this.getRemoteAddr() + ", \r\n cookies: " + this.cookie + ", \r\n contentType: " + this.contentType
+ ", \r\n connection: " + this.connection + ", \r\n protocol: " + this.protocol + ", \r\n host: " + this.host
+ ", \r\n contentLength: " + this.contentLength + ", \r\n bodyLength: " + this.array.size() + (this.boundary || this.array.isEmpty() ? "" : (", \r\n bodyContent: " + this.getBodyUTF8()))
+ ", \r\n params: " + this.params.toString(4) + ", \r\n header: " + this.header.toString(4) + "\r\n}";
+ ", \r\n params: " + toMapString(this.params, 4) + ", \r\n header: " + toMapString(this.headers, 4) + "\r\n}"; //this.headers.toString(4)
}
private static CharSequence toMapString(Map<String, String> map, int indent) {
char[] chars = new char[indent];
Arrays.fill(chars, ' ');
final String space = new String(chars);
StringBuilder sb = new StringBuilder();
sb.append("{\r\n");
for (Map.Entry en : map.entrySet()) {
sb.append(space).append(" '").append(en.getKey()).append("': '").append(en.getValue()).append("',\r\n");
}
sb.append(space).append('}');
return sb;
}
/**
@@ -474,9 +616,10 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return 文件上传对象
*/
@ConvertDisabled
public final MultiContext getMultiContext() {
return new MultiContext(context.getCharset(), this.getContentType(), this.params,
new BufferedInputStream(Channels.newInputStream(this.channel), Math.max(array.size(), 8192)) {
new BufferedInputStream(Channels.newInputStream(this.channel.readableByteChannel()), Math.max(array.size(), 8192)) {
{
array.copyTo(this.buf);
this.count = array.size();
@@ -491,6 +634,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @throws IOException IO异常
*/
@ConvertDisabled
public final Iterable<MultiPart> multiParts() throws IOException {
return getMultiContext().parts();
}
@@ -513,11 +657,13 @@ public class HttpRequest extends Request<HttpContext> {
this.moduleid = 0;
this.actionid = 0;
this.annotations = null;
this.currentUserid = CURRUSERID_NIL;
this.currentUser = null;
this.remoteAddr = null;
this.attachment = null;
this.header.clear();
this.headers.clear();
this.params.clear();
this.array.clear();
super.recycle();
@@ -530,6 +676,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return sessionid
*/
@ConvertDisabled
public String getSessionid(boolean create) {
String sessionid = getCookie(SESSIONID_NAME, null);
if (create && (sessionid == null || sessionid.isEmpty())) {
@@ -575,7 +722,7 @@ public class HttpRequest extends Request<HttpContext> {
*/
public HttpCookie[] getCookies() {
if (this.cookies == null) this.cookies = parseCookies(this.cookie);
return this.cookies;
return this.cookies.length == 0 ? null : this.cookies;
}
/**
@@ -598,7 +745,9 @@ public class HttpRequest extends Request<HttpContext> {
* @return cookie值
*/
public String getCookie(String name, String dfvalue) {
for (HttpCookie c : getCookies()) {
HttpCookie[] cs = getCookies();
if (cs == null) return dfvalue;
for (HttpCookie c : cs) {
if (name.equals(c.getName())) return c.getValue();
}
return dfvalue;
@@ -697,6 +846,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return String
*/
@ConvertDisabled
public String getRequstURILastPath() {
if (requestURI == null) return "";
return requestURI.substring(requestURI.lastIndexOf('/') + 1);
@@ -1046,8 +1196,8 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return AnyValue
*/
public AnyValue getHeaders() {
return header;
public Map<String, String> getHeaders() {
return headers;
}
/**
@@ -1057,10 +1207,11 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return Map
*/
@ConvertDisabled
public Map<String, String> getHeadersToMap(Map<String, String> map) {
if (map == null) map = new LinkedHashMap<>();
final Map<String, String> map0 = map;
header.forEach((k, v) -> map0.put(k, v));
headers.forEach((k, v) -> map0.put(k, v));
return map0;
}
@@ -1069,8 +1220,10 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return header名数组
*/
@ConvertDisabled
public String[] getHeaderNames() {
return header.getNames();
Set<String> names = headers.keySet();
return names.toArray(new String[names.size()]);
}
/**
@@ -1081,7 +1234,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public String getHeader(String name) {
return header.getValue(name);
return headers.get(name);
}
/**
@@ -1093,7 +1246,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public String getHeader(String name, String defaultValue) {
return header.getValue(name, defaultValue);
return headers.getOrDefault(name, defaultValue);
}
/**
@@ -1134,7 +1287,9 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public boolean getBooleanHeader(String name, boolean defaultValue) {
return header.getBoolValue(name, defaultValue);
//return headers.getBoolValue(name, defaultValue);
String value = headers.get(name);
return value == null || value.length() == 0 ? defaultValue : Boolean.parseBoolean(value);
}
/**
@@ -1146,7 +1301,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public short getShortHeader(String name, short defaultValue) {
return header.getShortValue(name, defaultValue);
//return headers.getShortValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Short.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1159,7 +1321,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public short getShortHeader(int radix, String name, short defaultValue) {
return header.getShortValue(name, defaultValue);
//return headers.getShortValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1171,7 +1340,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public short getShortHeader(String name, int defaultValue) {
return header.getShortValue(name, (short) defaultValue);
//return headers.getShortValue(name, (short) defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return (short) defaultValue;
try {
return Short.decode(value);
} catch (NumberFormatException e) {
return (short) defaultValue;
}
}
/**
@@ -1184,7 +1360,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public short getShortHeader(int radix, String name, int defaultValue) {
return header.getShortValue(radix, name, (short) defaultValue);
//return headers.getShortValue(radix, name, (short) defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return (short) defaultValue;
try {
return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix));
} catch (NumberFormatException e) {
return (short) defaultValue;
}
}
/**
@@ -1196,7 +1379,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public int getIntHeader(String name, int defaultValue) {
return header.getIntValue(name, defaultValue);
//return headers.getIntValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1209,7 +1399,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public int getIntHeader(int radix, String name, int defaultValue) {
return header.getIntValue(radix, name, defaultValue);
//return headers.getIntValue(radix, name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1221,7 +1418,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public long getLongHeader(String name, long defaultValue) {
return header.getLongValue(name, defaultValue);
//return headers.getLongValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Long.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1234,7 +1438,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public long getLongHeader(int radix, String name, long defaultValue) {
return header.getLongValue(radix, name, defaultValue);
//return headers.getLongValue(radix, name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1246,7 +1457,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public float getFloatHeader(String name, float defaultValue) {
return header.getFloatValue(name, defaultValue);
//return headers.getFloatValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Float.parseFloat(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1258,7 +1476,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值
*/
public double getDoubleHeader(String name, double defaultValue) {
return header.getDoubleValue(name, defaultValue);
//return headers.getDoubleValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Double.parseDouble(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
//------------------------------------------------------------------------------
@@ -1267,7 +1492,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return AnyValue
*/
public AnyValue getParameters() {
public Map<String, String> getParameters() {
parseBody();
return params;
}
@@ -1279,6 +1504,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return Map
*/
@ConvertDisabled
public Map<String, String> getParametersToMap(Map<String, String> map) {
if (map == null) map = new LinkedHashMap<>();
final Map<String, String> map0 = map;
@@ -1293,6 +1519,7 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return String
*/
@ConvertDisabled
public String getParametersToString() {
return getParametersToString(null);
}
@@ -1318,9 +1545,11 @@ public class HttpRequest extends Request<HttpContext> {
*
* @return 参数名数组
*/
@ConvertDisabled
public String[] getParameterNames() {
parseBody();
return params.getNames();
Set<String> names = params.keySet();
return names.toArray(new String[names.size()]);
}
/**
@@ -1332,7 +1561,7 @@ public class HttpRequest extends Request<HttpContext> {
*/
public String getParameter(String name) {
parseBody();
return params.getValue(name);
return params.get(name);
}
/**
@@ -1345,7 +1574,7 @@ public class HttpRequest extends Request<HttpContext> {
*/
public String getParameter(String name, String defaultValue) {
parseBody();
return params.getValue(name, defaultValue);
return params.getOrDefault(name, defaultValue);
}
/**
@@ -1387,7 +1616,8 @@ public class HttpRequest extends Request<HttpContext> {
*/
public boolean getBooleanParameter(String name, boolean defaultValue) {
parseBody();
return params.getBoolValue(name, defaultValue);
String value = params.get(name);
return value == null || value.length() == 0 ? defaultValue : Boolean.parseBoolean(value);
}
/**
@@ -1400,7 +1630,13 @@ public class HttpRequest extends Request<HttpContext> {
*/
public short getShortParameter(String name, short defaultValue) {
parseBody();
return params.getShortValue(name, defaultValue);
String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Short.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1414,7 +1650,13 @@ public class HttpRequest extends Request<HttpContext> {
*/
public short getShortParameter(int radix, String name, short defaultValue) {
parseBody();
return params.getShortValue(radix, name, defaultValue);
String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1427,7 +1669,13 @@ public class HttpRequest extends Request<HttpContext> {
*/
public short getShortParameter(String name, int defaultValue) {
parseBody();
return params.getShortValue(name, (short) defaultValue);
String value = params.get(name);
if (value == null || value.length() == 0) return (short) defaultValue;
try {
return Short.decode(value);
} catch (NumberFormatException e) {
return (short) defaultValue;
}
}
/**
@@ -1440,7 +1688,13 @@ public class HttpRequest extends Request<HttpContext> {
*/
public int getIntParameter(String name, int defaultValue) {
parseBody();
return params.getIntValue(name, defaultValue);
String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Integer.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1454,7 +1708,13 @@ public class HttpRequest extends Request<HttpContext> {
*/
public int getIntParameter(int radix, String name, int defaultValue) {
parseBody();
return params.getIntValue(radix, name, defaultValue);
String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1467,7 +1727,13 @@ public class HttpRequest extends Request<HttpContext> {
*/
public long getLongParameter(String name, long defaultValue) {
parseBody();
return params.getLongValue(name, defaultValue);
String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Long.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1481,7 +1747,13 @@ public class HttpRequest extends Request<HttpContext> {
*/
public long getLongParameter(int radix, String name, long defaultValue) {
parseBody();
return params.getLongValue(radix, name, defaultValue);
String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1494,7 +1766,13 @@ public class HttpRequest extends Request<HttpContext> {
*/
public float getFloatParameter(String name, float defaultValue) {
parseBody();
return params.getFloatValue(name, defaultValue);
String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Float.parseFloat(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
@@ -1507,7 +1785,13 @@ public class HttpRequest extends Request<HttpContext> {
*/
public double getDoubleParameter(String name, double defaultValue) {
parseBody();
return params.getDoubleValue(name, defaultValue);
String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Double.parseDouble(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**

View File

@@ -160,16 +160,16 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
public HttpResponse(HttpContext context, HttpRequest request, ObjectPool<Response> responsePool, HttpResponseConfig config) {
super(context, request, responsePool);
this.plainContentType = config.plainContentType == null || config.plainContentType.isEmpty() ? "text/plain; charset=utf-8" : config.plainContentType;
this.jsonContentType = config.jsonContentType == null || config.jsonContentType.isEmpty() ? "application/json; charset=utf-8" : config.jsonContentType;
this.plainContentType = config == null || config.plainContentType == null || config.plainContentType.isEmpty() ? "text/plain; charset=utf-8" : config.plainContentType;
this.jsonContentType = config == null || config.jsonContentType == null || config.jsonContentType.isEmpty() ? "application/json; charset=utf-8" : config.jsonContentType;
this.plainContentTypeBytes = ("Content-Type: " + this.plainContentType + "\r\n").getBytes();
this.jsonContentTypeBytes = ("Content-Type: " + this.jsonContentType + "\r\n").getBytes();
this.defaultAddHeaders = config.defaultAddHeaders;
this.defaultSetHeaders = config.defaultSetHeaders;
this.defaultCookie = config.defaultCookie;
this.autoOptions = config.autoOptions;
this.dateSupplier = config.dateSupplier;
this.renders = config.renders;
this.defaultAddHeaders = config == null ? null : config.defaultAddHeaders;
this.defaultSetHeaders = config == null ? null : config.defaultSetHeaders;
this.defaultCookie = config == null ? null : config.defaultCookie;
this.autoOptions = config == null ? false : config.autoOptions;
this.dateSupplier = config == null ? null : config.dateSupplier;
this.renders = config == null ? null : config.renders;
this.hasRender = renders != null && !renders.isEmpty();
this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null;
this.contentType = this.plainContentType;
@@ -430,6 +430,26 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
finish(convert.convertTo(getBodyBufferSupplier(), ret));
}
/**
* 将HttpResult对象输出
*
* @param convert 指定的Convert
* @param result HttpResult输出对象
*/
public void finish(final Convert convert, HttpResult result) {
if (result.getContentType() != null) setContentType(result.getContentType());
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
if (result.getResult() == null) {
finish("");
} else if (result.getResult() instanceof CharSequence) {
finish(result.getResult().toString());
} else {
Convert cc = result.convert();
if (cc == null || !(cc instanceof TextConvert)) cc = convert;
finish(cc, result.getResult());
}
}
/**
* 将CompletableFuture的结果对象以JSON格式输出
*
@@ -521,18 +541,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} else if (obj instanceof org.redkale.service.RetResult) {
finishJson((org.redkale.service.RetResult) obj);
} else if (obj instanceof HttpResult) {
HttpResult result = (HttpResult) obj;
if (result.getContentType() != null) setContentType(result.getContentType());
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
if (result.getResult() == null) {
finish("");
} else if (result.getResult() instanceof CharSequence) {
finish(result.getResult().toString());
} else {
Convert cc = result.convert();
if (cc == null || !(cc instanceof TextConvert)) cc = convert;
finish(cc, result.getResult());
}
finish(convert, (HttpResult) obj);
} else {
if (hasRender) {
if (onlyoneHttpRender != null) {
@@ -566,6 +575,10 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())).addValue("retinfo", ret.getRetinfo());
}
}
if (this.channel == null) { //虚拟的HttpResponse
finish(type == null ? convert.convertToBytes(obj) : convert.convertToBytes(type, obj));
return;
}
ByteBuffer[] buffers = type == null ? convert.convertTo(getBodyBufferSupplier(), obj)
: convert.convertTo(getBodyBufferSupplier(), type, obj);
finish(buffers);
@@ -1013,7 +1026,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (defaultCookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defaultCookie.getDomain());
if (defaultCookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defaultCookie.getPath());
}
buffer.put(("Set-Cookie: " + genString(cookie) + "\r\n").getBytes());
buffer.put(("Set-Cookie: " + cookieString(cookie) + "\r\n").getBytes());
}
}
buffer.put(LINE);
@@ -1022,7 +1035,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return buffer;
}
private CharSequence genString(HttpCookie cookie) {
private CharSequence cookieString(HttpCookie cookie) {
StringBuilder sb = new StringBuilder();
sb.append(cookie.getName()).append("=").append(cookie.getValue()).append("; Version=1");
if (cookie.getDomain() != null) sb.append("; Domain=").append(cookie.getDomain());

View File

@@ -7,7 +7,9 @@ package org.redkale.net.http;
import java.io.Serializable;
import java.net.HttpCookie;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
@@ -23,17 +25,20 @@ public class HttpResult<T> {
public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME;
protected Map<String, String> headers;
protected List<HttpCookie> cookies;
protected String contentType;
protected T result;
@ConvertColumn(index = 1)
protected int status = 0; //不设置则为 200
protected String message;
@ConvertColumn(index = 2)
protected String contentType;
@ConvertColumn(index = 3)
protected Map<String, String> headers;
@ConvertColumn(index = 4)
protected List<HttpCookie> cookies;
@ConvertColumn(index = 5)
protected T result;
protected Convert convert;
@@ -85,17 +90,29 @@ public class HttpResult<T> {
return this;
}
public HttpResult<T> message(String message) {
this.message = message;
return this;
}
public Convert convert() {
return convert;
}
public void convert(Convert convert) {
public HttpResult<T> convert(Convert convert) {
this.convert = convert;
return this;
}
public String getHeader(String name) {
return headers == null ? null : headers.get(name);
}
public String getHeader(String name, String dfvalue) {
return headers == null ? null : headers.getOrDefault(name, dfvalue);
}
public CompletableFuture<HttpResult<T>> toFuture() {
return CompletableFuture.completedFuture(this);
}
public CompletableFuture toAnyFuture() {
return CompletableFuture.completedFuture(this);
}
public Map<String, String> getHeaders() {
@@ -138,16 +155,17 @@ public class HttpResult<T> {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
if (this.result instanceof byte[]) {
HttpResult tmp = new HttpResult();
tmp.contentType = this.contentType;
tmp.cookies = this.cookies;
tmp.headers = this.headers;
tmp.status = this.status;
tmp.result = new String((byte[]) this.result, StandardCharsets.UTF_8);
return JsonConvert.root().convertTo(tmp);
}
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -16,6 +16,7 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.logging.Level;
import org.redkale.mq.MessageAgent;
import org.redkale.net.*;
import org.redkale.net.http.HttpContext.HttpContextConfig;
import org.redkale.net.http.HttpResponse.HttpResponseConfig;
@@ -215,13 +216,14 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
* @param <T> HttpServlet
* @param classLoader ClassLoader
* @param webSocketType WebSocket的类型
* @param messageAgent MessageAgent
* @param prefix url前缀
* @param conf 配置信息
*
* @return RestServlet
*/
public <S extends WebSocket, T extends HttpServlet> T addRestWebSocketServlet(final ClassLoader classLoader, final Class<S> webSocketType, final String prefix, final AnyValue conf) {
T servlet = Rest.createRestWebSocketServlet(classLoader, webSocketType);
public <S extends WebSocket, T extends WebSocketServlet> T addRestWebSocketServlet(final ClassLoader classLoader, final Class<S> webSocketType, MessageAgent messageAgent, final String prefix, final AnyValue conf) {
T servlet = Rest.createRestWebSocketServlet(classLoader, webSocketType, messageAgent);
if (servlet != null) this.prepare.addServlet(servlet, prefix, conf);
return servlet;
}
@@ -434,7 +436,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
final HttpContextConfig contextConfig = new HttpContextConfig();
contextConfig.serverStartTime = this.serverStartTime;
contextConfig.logger = this.logger;
contextConfig.executor = this.executor;
contextConfig.executor = this.workExecutor;
contextConfig.sslContext = this.sslContext;
contextConfig.bufferCapacity = this.bufferCapacity;
contextConfig.maxconns = this.maxconns;

View File

@@ -44,9 +44,13 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
InnerActionEntry entry = (InnerActionEntry) request.attachment;
if (entry.rpconly && !request.rpc) {
response.finish(503, null);
return;
}
if (entry.cacheseconds > 0) {//有缓存设置
CacheEntry ce = entry.cache.get(request.getRequestURI());
if (ce != null && ce.time + entry.cacheseconds > System.currentTimeMillis()) { //缓存有效
if (ce != null && ce.time + entry.cacheseconds * 1000 > System.currentTimeMillis()) { //缓存有效
response.setStatus(ce.status);
response.setContentType(ce.contentType);
response.finish(ce.getBuffers());
@@ -82,7 +86,8 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
return;
}
}
throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
response.finish404();
//throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
}
};
@@ -215,18 +220,19 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
protected static final class InnerActionEntry {
InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) {
this(moduleid, actionid, name, methods, method, auth(method), cacheseconds(method), servlet);
this(moduleid, actionid, name, methods, method, rpconly(method), auth(method), cacheseconds(method), servlet);
this.annotations = annotations(method);
}
//供Rest类使用参数不能随便更改
public InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, boolean auth, int cacheseconds, HttpServlet servlet) {
public InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, boolean rpconly, boolean auth, int cacheseconds, HttpServlet servlet) {
this.moduleid = moduleid;
this.actionid = actionid;
this.name = name;
this.methods = methods;
this.method = method; //rest构建会为null
this.servlet = servlet;
this.rpconly = rpconly;
this.auth = auth;
this.cacheseconds = cacheseconds;
this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null;
@@ -244,6 +250,11 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
return mapping == null || mapping.auth();
}
protected static boolean rpconly(Method method) {
HttpMapping mapping = method.getAnnotation(HttpMapping.class);
return mapping == null || mapping.rpconly();
}
protected static int cacheseconds(Method method) {
HttpMapping mapping = method.getAnnotation(HttpMapping.class);
return mapping == null ? 0 : mapping.cacheseconds();
@@ -272,6 +283,8 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
final int cacheseconds;
final boolean rpconly;
final boolean auth;
final int moduleid;

View File

@@ -0,0 +1,317 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.*;
/**
* HttpRequest的缩减版, 只提供部分字段
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpSimpleRequest implements java.io.Serializable {
@ConvertColumn(index = 1)
@Comment("是否RPC请求, 该类通常是为RPC创建的故默认是true")
protected boolean rpc = true;
@ConvertColumn(index = 2)
@Comment("请求的URI")
protected String requestURI;
@ConvertColumn(index = 3)
@Comment("请求的前缀")
protected String path;
@ConvertColumn(index = 4)
@Comment("客户端IP")
protected String remoteAddr;
@ConvertColumn(index = 5)
@Comment("会话ID")
protected String sessionid;
@ConvertColumn(index = 6)
@Comment("Content-Type")
protected String contentType;
@ConvertColumn(index = 7)
protected int currentUserid;
@ConvertColumn(index = 8)
@Comment("http header信息")
protected Map<String, String> headers;
@ConvertColumn(index = 9)
@Comment("参数信息")
protected Map<String, String> params;
@ConvertColumn(index = 10)
@Comment("http body信息")
protected byte[] body; //对应HttpRequest.array
public static HttpSimpleRequest create(String requestURI) {
return new HttpSimpleRequest().requestURI(requestURI);
}
public static HttpSimpleRequest create(String requestURI, Object... params) {
HttpSimpleRequest req = new HttpSimpleRequest().requestURI(requestURI);
int len = params.length / 2;
for (int i = 0; i < len; i++) {
req.param(params[i * 2].toString(), params[i * 2 + 1]);
}
return req;
}
@ConvertDisabled
public String getParametersToString() {
if (this.params == null || this.params.isEmpty()) return null;
final StringBuilder sb = new StringBuilder();
AtomicBoolean no2 = new AtomicBoolean(false);
this.params.forEach((n, v) -> {
if (no2.get()) sb.append('&');
sb.append(n).append('=').append(URLEncoder.encode(v, StandardCharsets.UTF_8));
no2.set(true);
});
return sb.toString();
}
public HttpSimpleRequest rpc(boolean rpc) {
this.rpc = rpc;
return this;
}
public HttpSimpleRequest requestURI(String requestURI) {
this.requestURI = requestURI;
return this;
}
public HttpSimpleRequest path(String path) {
this.path = path;
return this;
}
public HttpSimpleRequest remoteAddr(String remoteAddr) {
this.remoteAddr = remoteAddr;
return this;
}
public HttpSimpleRequest sessionid(String sessionid) {
this.sessionid = sessionid;
return this;
}
public HttpSimpleRequest contentType(String contentType) {
this.contentType = contentType;
return this;
}
public HttpSimpleRequest currentUserid(int userid) {
this.currentUserid = userid;
return this;
}
public HttpSimpleRequest removeHeader(String name) {
if (this.headers != null) this.headers.remove(name);
return this;
}
public HttpSimpleRequest removeParam(String name) {
if (this.params != null) this.params.remove(name);
return this;
}
public HttpSimpleRequest headers(Map<String, String> headers) {
this.headers = headers;
return this;
}
public HttpSimpleRequest params(Map<String, String> params) {
this.params = params;
return this;
}
public HttpSimpleRequest header(String key, String value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(key, value);
return this;
}
public HttpSimpleRequest header(String key, JsonConvert convert, Object value) {
if (value == null) return this;
if (this.headers == null) this.headers = new HashMap<>();
if (convert == null) convert = JsonConvert.root();
this.headers.put(key, convert.convertTo(value));
return this;
}
public HttpSimpleRequest header(String key, Object value) {
if (value == null) return this;
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(key, JsonConvert.root().convertTo(value));
return this;
}
public HttpSimpleRequest header(String key, int value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(key, String.valueOf(value));
return this;
}
public HttpSimpleRequest header(String key, long value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(key, String.valueOf(value));
return this;
}
public HttpSimpleRequest param(String key, String value) {
if (this.params == null) this.params = new HashMap<>();
this.params.put(key, value);
return this;
}
public HttpSimpleRequest param(String key, JsonConvert convert, Object value) {
if (value == null) return this;
if (this.params == null) this.params = new HashMap<>();
if (convert == null) convert = JsonConvert.root();
this.params.put(key, convert.convertTo(value));
return this;
}
public HttpSimpleRequest param(String key, Object value) {
if (value == null) return this;
if (this.params == null) this.params = new HashMap<>();
this.params.put(key, value instanceof CharSequence ? value.toString() : JsonConvert.root().convertTo(value));
return this;
}
public HttpSimpleRequest body(byte[] body) {
this.body = body;
return this;
}
public HttpSimpleRequest clearParams() {
this.params = null;
return this;
}
public HttpSimpleRequest clearHeaders() {
this.headers = null;
return this;
}
public HttpSimpleRequest clearRemoteAddr() {
this.remoteAddr = null;
return this;
}
public HttpSimpleRequest clearSessionid() {
this.sessionid = null;
return this;
}
public HttpSimpleRequest clearContentType() {
this.contentType = null;
return this;
}
public boolean isRpc() {
return rpc;
}
public void setRpc(boolean rpc) {
this.rpc = rpc;
}
public String getRequestURI() {
return requestURI;
}
public void setRequestURI(String requestURI) {
this.requestURI = requestURI;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getSessionid() {
return sessionid;
}
public void setSessionid(String sessionid) {
this.sessionid = sessionid;
}
public String getRemoteAddr() {
return remoteAddr;
}
public void setRemoteAddr(String remoteAddr) {
this.remoteAddr = remoteAddr;
}
public int getCurrentUserid() {
return currentUserid;
}
public void setCurrentUserid(int currentUserid) {
this.currentUserid = currentUserid;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public Map<String, String> getParams() {
return params;
}
public void setParams(Map<String, String> params) {
this.params = params;
}
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
this.body = body;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -12,7 +12,6 @@ import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
import java.util.regex.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
/**
* HTTP的文件上传请求的上下文对象
@@ -38,7 +37,7 @@ public final class MultiContext {
private final ByteArray buf = new ByteArray(64);
private final DefaultAnyValue parameters;
private final Map<String, String> parameters;
private final Pattern fielnamePattern;
@@ -55,7 +54,7 @@ public final class MultiContext {
}
};
public MultiContext(final Charset charsetName, final String contentType, final DefaultAnyValue params, final InputStream in, String fielnameRegex) {
public MultiContext(final Charset charsetName, final String contentType, final Map<String, String> params, final InputStream in, String fielnameRegex) {
this.charset = charsetName == null ? StandardCharsets.UTF_8 : charsetName;
this.contentType = contentType == null ? "" : contentType.trim();
this.parameters = params;
@@ -205,7 +204,7 @@ public final class MultiContext {
final byte[] boundarray = ("\n" + boundarystr).getBytes();
final byte[] buffer = new byte[boundarray.length];
final InputStream input = this.in;
final DefaultAnyValue params = this.parameters;
final Map<String, String> params = this.parameters;
final AtomicBoolean finaled = new AtomicBoolean(false);
return () -> new Iterator<MultiPart>() {
@@ -305,7 +304,7 @@ public final class MultiContext {
return true;
} else { //不是文件
readLine(); //读掉空白
params.addValue(parseValue(disposition, "name"), readLine());
params.put(parseValue(disposition, "name"), readLine());
this.boundaryline = null;
this.lastentry = null;
return this.hasNext();

View File

@@ -22,7 +22,9 @@ import static org.redkale.asm.Opcodes.*;
import org.redkale.asm.Type;
import org.redkale.convert.*;
import org.redkale.convert.json.*;
import org.redkale.mq.*;
import org.redkale.net.Cryptor;
import org.redkale.net.sncp.Sncp;
import org.redkale.service.*;
import org.redkale.util.*;
import org.redkale.source.Flipper;
@@ -38,6 +40,10 @@ public final class Rest {
public static final String REST_HEADER_RESOURCE_NAME = "rest-resource-name";
public static final String REST_HEADER_RPC_NAME = "rest-rpc-name";
public static final String REST_HEADER_CURRUSERID_NAME = "rest-curruserid-name";
static final String REST_SERVICE_FIELD_NAME = "_redkale_service";
static final String REST_TOSTRINGOBJ_FIELD_NAME = "_redkale_tostringsupplier";
@@ -48,6 +54,9 @@ public final class Rest {
private static final String REST_PARAMTYPES_FIELD_NAME = "_redkale_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标 第二维度是参数的下标
private static final java.lang.reflect.Type TYPE_MAP_STRING_STRING = new TypeToken<Map<String, String>>() {
}.getType();
private static final Set<String> EXCLUDERMETHODS = new HashSet<>();
static {
@@ -205,7 +214,19 @@ public final class Rest {
}
}
public static <T extends HttpServlet> T createRestWebSocketServlet(final ClassLoader classLoader, final Class<? extends WebSocket> webSocketType) {
public static String getRestModule(Service service) {
final RestService controller = service.getClass().getAnnotation(RestService.class);
if (controller != null && !controller.name().isEmpty()) return controller.name();
final Class serviceType = Sncp.getServiceType(service);
return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
}
//仅供Rest动态构建里 currentUserid() 使用
public static <T> T orElse(T t, T defValue) {
return t == null ? defValue : t;
}
public static <T extends WebSocketServlet> T createRestWebSocketServlet(final ClassLoader classLoader, final Class<? extends WebSocket> webSocketType, MessageAgent messageAgent) {
if (webSocketType == null) throw new RuntimeException("Rest WebSocket Class is null on createRestWebSocketServlet");
if (Modifier.isAbstract(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot abstract on createRestWebSocketServlet");
if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot final on createRestWebSocketServlet");
@@ -713,6 +734,7 @@ public final class Rest {
cryptorField.setAccessible(true);
cryptorField.set(servlet, cryptor);
}
if (messageAgent != null) ((WebSocketServlet) servlet).messageAgent = messageAgent;
return servlet;
} catch (Exception e) {
throw new RuntimeException(e);
@@ -726,6 +748,7 @@ public final class Rest {
if (!java.lang.reflect.Modifier.isPublic(mod)) throw new RuntimeException(baseServletType + " is not Public Class on createRestServlet");
if (java.lang.reflect.Modifier.isAbstract(mod)) throw new RuntimeException(baseServletType + " cannot a abstract Class on createRestServlet");
final String restInternalName = Type.getInternalName(Rest.class);
final String serviceDesc = Type.getDescriptor(serviceType);
final String webServletDesc = Type.getDescriptor(WebServlet.class);
final String resDesc = Type.getDescriptor(Resource.class);
@@ -762,6 +785,7 @@ public final class Rest {
final String supDynName = baseServletType.getName().replace('.', '/');
final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller != null && controller.ignore()) throw new RuntimeException(serviceType + " is ignore Rest Service Class"); //标记为ignore=true不创建Servlet
final boolean serrpconly = controller != null && controller.rpconly();
ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
String stname = serviceType.getSimpleName();
if (stname.startsWith("Service")) { //类似ServiceWatchService这样的类保留第一个Service字样
@@ -800,13 +824,19 @@ public final class Rest {
//获取所有可以转换成HttpMapping的方法
int methodidex = 0;
final List<java.lang.reflect.Type[]> paramtypes = new ArrayList<>();
final MessageMultiConsumer mmc = serviceType.getAnnotation(MessageMultiConsumer.class);
if (mmc != null && (mmc.module() == null || mmc.module().isEmpty())) {
throw new RuntimeException("@" + MessageMultiConsumer.class.getSimpleName() + ".module can not empty in " + serviceType.getName());
}
for (final Method method : serviceType.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue;
if (method.isSynthetic()) continue;
if (EXCLUDERMETHODS.contains(method.getName())) continue;
if ("init".equals(method.getName())) continue;
if ("destroy".equals(method.getName())) continue;
if ("version".equals(method.getName())) continue;
if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == AnyValue.class) {
if ("init".equals(method.getName())) continue;
if ("stop".equals(method.getName())) continue;
if ("destroy".equals(method.getName())) continue;
}
if (controller == null) continue;
RestMapping[] mappings = method.getAnnotationsByType(RestMapping.class);
@@ -828,14 +858,17 @@ public final class Rest {
}
}
}
if (mmc != null && method.getReturnType() != void.class) {
throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method(" + method + ") with return void by @" + MessageMultiConsumer.class.getSimpleName() + " Service");
}
paramtypes.add(TypeToken.getGenericType(method.getGenericParameterTypes(), serviceType));
if (mappings.length == 0) { //没有Mapping设置一个默认值
MappingEntry entry = new MappingEntry(methodidex, null, bigmodulename, method);
MappingEntry entry = new MappingEntry(serrpconly, methodidex, null, bigmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry);
} else {
for (RestMapping mapping : mappings) {
MappingEntry entry = new MappingEntry(methodidex, mapping, defmodulename, method);
MappingEntry entry = new MappingEntry(serrpconly, methodidex, mapping, defmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry);
}
@@ -1067,6 +1100,31 @@ public final class Rest {
comment = annuri.comment();
}
RestUserid userid = param.getAnnotation(RestUserid.class);
if (userid != null) {
if (annhead != null) throw new RuntimeException("@RestUserid and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestUserid and @RestCookie cannot on the same Parameter in " + method);
if (annsid != null) throw new RuntimeException("@RestUserid and @RestSessionid cannot on the same Parameter in " + method);
if (annaddr != null) throw new RuntimeException("@RestUserid and @RestAddress cannot on the same Parameter in " + method);
if (annbody != null) throw new RuntimeException("@RestUserid and @RestBody cannot on the same Parameter in " + method);
if (annfile != null) throw new RuntimeException("@RestUserid and @RestUploadFile cannot on the same Parameter in " + method);
if (!ptype.isPrimitive() && !java.io.Serializable.class.isAssignableFrom(ptype)) throw new RuntimeException("@RestUserid must on java.io.Serializable Parameter in " + method);
comment = "";
}
RestHeaders annheaders = param.getAnnotation(RestHeaders.class);
if (annheaders != null) {
if (annhead != null) throw new RuntimeException("@RestHeaders and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestHeaders and @RestCookie cannot on the same Parameter in " + method);
if (annsid != null) throw new RuntimeException("@RestHeaders and @RestSessionid cannot on the same Parameter in " + method);
if (annaddr != null) throw new RuntimeException("@RestHeaders and @RestAddress cannot on the same Parameter in " + method);
if (annbody != null) throw new RuntimeException("@RestHeaders and @RestBody cannot on the same Parameter in " + method);
if (annfile != null) throw new RuntimeException("@RestHeaders and @RestUploadFile cannot on the same Parameter in " + method);
if (userid != null) throw new RuntimeException("@RestHeaders and @RestUserid cannot on the same Parameter in " + method);
if (!TYPE_MAP_STRING_STRING.equals(param.getParameterizedType())) throw new RuntimeException("@RestHeaders must on Map<String, String> Parameter in " + method);
comment = "";
}
RestParam annpara = param.getAnnotation(RestParam.class);
if (annpara != null) radix = annpara.radix();
if (annpara != null) comment = annpara.comment();
@@ -1100,7 +1158,8 @@ public final class Rest {
}
} while ((loop = loop.getSuperclass()) != Object.class);
}
paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, param.getParameterizedType()});
java.lang.reflect.Type paramtype = TypeToken.getGenericType( param.getParameterizedType(),serviceType);
paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, userid, annheaders, paramtype});
}
Map<String, Object> mappingMap = new LinkedHashMap<>();
@@ -1116,6 +1175,7 @@ public final class Rest {
av0 = mv.visitAnnotation(mappingDesc, true);
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name + (reqpath ? "/" : "");
av0.visit("url", url);
av0.visit("rpconly", entry.rpconly);
av0.visit("auth", entry.auth);
av0.visit("cacheseconds", entry.cacheseconds);
av0.visit("actionid", entry.actionid);
@@ -1132,6 +1192,7 @@ public final class Rest {
av0.visitEnd();
mappingMap.put("url", url);
mappingMap.put("rpconly", entry.rpconly);
mappingMap.put("auth", entry.auth);
mappingMap.put("cacheseconds", entry.cacheseconds);
mappingMap.put("actionid", entry.actionid);
@@ -1218,7 +1279,10 @@ public final class Rest {
RestBody annbody = (RestBody) ps[11];
RestUploadFile annfile = (RestUploadFile) ps[12];
RestURI annuri = (RestURI) ps[13];
java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[14];
RestUserid userid = (RestUserid) ps[14];
RestHeaders annheaders = (RestHeaders) ps[15];
java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[16];
final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter
final boolean iscookie = anncookie != null; //是否取getCookie
@@ -1252,6 +1316,11 @@ public final class Rest {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRemoteAddr", "()Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (annheaders != null) { //HttpRequest.getHeaders
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeaders", "()Ljava/util/Map;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (annbody != null) { //HttpRequest.getBodyUTF8 / HttpRequest.getBody
if (ptype == String.class) {
mv.visitVarInsn(ALOAD, 1);
@@ -1283,6 +1352,42 @@ public final class Rest {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequestURI", "()Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (userid != null) { //HttpRequest.currentUserid
mv.visitVarInsn(ALOAD, 1);
if (ptype == int.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
} else if (ptype == long.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;");
} else {
mv.visitLdcInsn(Type.getType(Type.getInternalName(ptype)));
}
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "currentUserid", "(Ljava/lang/Class;)Ljava/io/Serializable;", false);
if (ptype == int.class) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
mv.visitInsn(ICONST_0);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
mv.visitMethodInsn(INVOKESTATIC, restInternalName, "orElse", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == long.class) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
mv.visitInsn(LCONST_0);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
mv.visitMethodInsn(INVOKESTATIC, restInternalName, "orElse", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
mv.visitVarInsn(LSTORE, maxLocals);
varInsns.add(new int[]{LLOAD, maxLocals});
maxLocals++;
} else {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype));
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
}
} else if ("#".equals(pname)) { //从request.getRequstURI 中取参数
if (ptype == boolean.class) {
mv.visitVarInsn(ALOAD, 1);
@@ -1848,8 +1953,8 @@ public final class Rest {
// HashMap<String, InnerActionEntry> _createRestInnerActionEntry() {
// HashMap<String, InnerActionEntry> map = new HashMap<>();
// map.put("asyncfind3", new InnerActionEntry(100000,200000,"asyncfind3", new String[]{},null,false,0, new _Dync_asyncfind3_HttpServlet()));
// map.put("asyncfind2", new InnerActionEntry(1,2,"asyncfind2", new String[]{"GET", "POST"},null,true,0, new _Dync_asyncfind2_HttpServlet()));
// map.put("asyncfind3", new InnerActionEntry(100000,200000,"asyncfind3", new String[]{},null,false,false,0, new _Dync_asyncfind3_HttpServlet()));
// map.put("asyncfind2", new InnerActionEntry(1,2,"asyncfind2", new String[]{"GET", "POST"},null,false,true,0, new _Dync_asyncfind2_HttpServlet()));
// return map;
// }
Map<String, Method> mappingurlToMethod = new HashMap<>();
@@ -1878,14 +1983,15 @@ public final class Rest {
mv.visitLdcInsn(entry.methods[i]);
mv.visitInsn(AASTORE);
}
mv.visitInsn(ACONST_NULL); //method
mv.visitInsn(ACONST_NULL); //method
mv.visitInsn(entry.rpconly ? ICONST_1 : ICONST_0); //rpconly
mv.visitInsn(entry.auth ? ICONST_1 : ICONST_0); //auth
pushInt(mv, entry.cacheseconds); //cacheseconds
mv.visitTypeInsn(NEW, newDynName + "$" + entry.newActionClassName);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, newDynName + "$" + entry.newActionClassName, "<init>", "(L" + newDynName + ";)V", false);
mv.visitMethodInsn(INVOKESPECIAL, innerEntryName, "<init>", "(IILjava/lang/String;[Ljava/lang/String;Ljava/lang/reflect/Method;ZILorg/redkale/net/http/HttpServlet;)V", false);
mv.visitMethodInsn(INVOKESPECIAL, innerEntryName, "<init>", "(IILjava/lang/String;[Ljava/lang/String;Ljava/lang/reflect/Method;ZZILorg/redkale/net/http/HttpServlet;)V", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitInsn(POP);
}
@@ -2044,7 +2150,7 @@ public final class Rest {
}
}
public MappingEntry(int methodidx, RestMapping mapping, final String defmodulename, Method method) {
public MappingEntry(final boolean serrpconly, int methodidx, RestMapping mapping, final String defmodulename, Method method) {
if (mapping == null) mapping = DEFAULT__MAPPING;
this.methodidx = methodidx;
this.ignore = mapping.ignore();
@@ -2058,6 +2164,7 @@ public final class Rest {
this.mappingMethod = method;
this.methods = mapping.methods();
this.auth = mapping.auth();
this.rpconly = serrpconly || mapping.rpconly();
this.actionid = mapping.actionid();
this.cacheseconds = mapping.cacheseconds();
this.comment = mapping.comment();
@@ -2091,6 +2198,8 @@ public final class Rest {
public final String[] methods;
public final boolean rpconly;
public final boolean auth;
public final int actionid;

View File

@@ -0,0 +1,28 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能注解于RestService类的方法的参数或参数内的Map&#60;String, String&#62;字段
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
@Inherited
@Documented
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface RestHeaders {
}

View File

@@ -46,6 +46,13 @@ public @interface RestMapping {
*/
String comment() default "";
/**
* 是否只接收RPC请求, 对应&#64;HttpMapping.rpconly
*
* @return boolean
*/
boolean rpconly() default false;
/**
* 是否鉴权,默认需要鉴权, 对应&#64;HttpMapping.auth
*

View File

@@ -43,6 +43,13 @@ public @interface RestService {
*/
int moduleid() default 0;
/**
* 是否只接受RPC请求 默认为false, 为true则覆盖所有&#64;RestMapping的方法的rpconly值都转为true
*
* @return 默认false
*/
boolean rpconly() default false;
/**
* 没有标记&#64;RestMapping的方法是否转换 默认为false
*

View File

@@ -0,0 +1,29 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能注解于Service类的方法的参数或参数内的Serializable字段
* <p>
* 用于获取HTTP请求端的用户ID HttpRequest.currentUserid
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
@Inherited
@Documented
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface RestUserid {
}

View File

@@ -14,6 +14,7 @@ import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import java.util.stream.Stream;
import java.util.zip.*;
import org.redkale.convert.Convert;
import org.redkale.net.AsyncConnection;
import org.redkale.util.Comment;
@@ -103,7 +104,11 @@ public abstract class WebSocket<G extends Serializable, T> {
java.lang.reflect.Type _messageTextType; //不可能为空
private long createtime = System.currentTimeMillis();
Deflater deflater; //压缩
Inflater inflater; //解压
long createtime = System.currentTimeMillis();
private long pingtime;
@@ -517,7 +522,7 @@ public abstract class WebSocket<G extends Serializable, T> {
*
* @return 地址列表
*/
public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
public CompletableFuture<Collection<WebSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
if (_engine.node == null) return CompletableFuture.completedFuture(null);
return _engine.node.getRpcNodeAddresses(userid);
}
@@ -531,7 +536,7 @@ public abstract class WebSocket<G extends Serializable, T> {
*
* @return 地址集合
*/
public CompletableFuture<Map<InetSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) {
public CompletableFuture<Map<WebSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) {
if (_engine.node == null) return CompletableFuture.completedFuture(null);
return _engine.node.getRpcNodeWebSocketAddresses(userid);
}
@@ -560,6 +565,17 @@ public abstract class WebSocket<G extends Serializable, T> {
return _engine.node.forceCloseWebSocket(userid);
}
/**
* 获取WebSocketNode
*
*
* @return WebSocketNode
*/
@Comment("获取WebSocketNode")
public final WebSocketNode webSocketNode() {
return _engine.node;
}
/**
* 获取当前WebSocket下的属性非线程安全
*
@@ -826,7 +842,7 @@ public abstract class WebSocket<G extends Serializable, T> {
}
/**
* 当Single模式下用户重复登时回调函数,默认处理方式: 关闭旧连接
* 当Single模式下用户重复登时回调函数,默认处理方式: 关闭旧连接
*
* @return Future 可以为null, 为null或者Future值为false表示关闭新连接 Future值为true表示关闭旧连接
*/
@@ -883,6 +899,8 @@ public abstract class WebSocket<G extends Serializable, T> {
* 显式地关闭WebSocket
*/
public final void close() {
if (this.deflater != null) this.deflater.end();
if (this.inflater != null) this.inflater.end();
if (this._runner != null) {
CompletableFuture<Void> future = this._runner.closeRunner(CLOSECODE_SERVERCLOSE, "user close");
if (future != null) future.join();

View File

@@ -35,6 +35,14 @@ public class WebSocketAction implements Serializable {
this.attach = attach;
}
public String findAttach(String name) {
return attach == null ? null : attach.get(name);
}
public String findAttach(String name, String defvalue) {
return attach == null ? defvalue : attach.getOrDefault(name, defvalue);
}
public String getAction() {
return action;
}

View File

@@ -0,0 +1,74 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.util.Objects;
import org.redkale.convert.json.JsonConvert;
/**
* 存放用户WS连接的SNCP地址和MQ topic 当消息使用MQ代理时topic才会有值
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class WebSocketAddress implements Serializable {
protected InetSocketAddress addr;
protected String topic;
public WebSocketAddress() {
}
public WebSocketAddress(String topic, InetSocketAddress addr) {
this.topic = topic;
this.addr = addr;
}
@Override
public int hashCode() {
int hash = 7;
hash = 37 * hash + Objects.hashCode(this.addr);
hash = 37 * hash + Objects.hashCode(this.topic);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final WebSocketAddress other = (WebSocketAddress) obj;
return Objects.equals(this.topic, other.topic) && Objects.equals(this.addr, other.addr);
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public InetSocketAddress getAddr() {
return addr;
}
public void setAddr(InetSocketAddress addr) {
this.addr = addr;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -152,6 +152,7 @@ public class WebSocketEngine {
@Comment("从WebSocketEngine删除指定WebSocket")
CompletableFuture<Void> removeLocalThenClose(WebSocket socket) {
Serializable userid = socket._userid;
if (userid == null) return null; //尚未登录成功
if (single) {
currconns.decrementAndGet();
websockets.remove(userid);
@@ -244,7 +245,7 @@ public class WebSocketEngine {
if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor));
packet.encodePacket(bufferSupplier, bufferConsumer, cryptor);
}
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
@@ -255,7 +256,7 @@ public class WebSocketEngine {
if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor));
packet.encodePacket(bufferSupplier, bufferConsumer, cryptor);
}
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
@@ -312,7 +313,7 @@ public class WebSocketEngine {
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, false, message, last));
//packet.setSendBuffers(packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor));
//packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor);
CompletableFuture<Integer> future = null;
if (single) {
for (Serializable userid : userids) {
@@ -321,7 +322,7 @@ public class WebSocketEngine {
if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor));
packet.encodePacket(bufferSupplier, bufferConsumer, cryptor);
}
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
@@ -333,7 +334,7 @@ public class WebSocketEngine {
if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor));
packet.encodePacket(bufferSupplier, bufferConsumer, cryptor);
}
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}

View File

@@ -16,6 +16,7 @@ import javax.annotation.*;
import org.redkale.boot.*;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.mq.MessageAgent;
import org.redkale.service.*;
import org.redkale.source.*;
import org.redkale.util.*;
@@ -33,7 +34,7 @@ public abstract class WebSocketNode {
public static final String SOURCE_SNCP_USERID_PREFIX = "sncpws_uid:";
@Comment("存储当前SNCP节点列表的key")
public static final String SOURCE_SNCP_ADDRS_KEY = "sncpws_addrs";
public static final String SOURCE_SNCP_NODES_KEY = "sncpws_nodes";
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
@@ -41,6 +42,10 @@ public abstract class WebSocketNode {
@Resource(name = Application.RESNAME_SNCP_ADDR)
protected InetSocketAddress localSncpAddress; //为SncpServer的服务address
protected WebSocketAddress wsaddress;
protected String name;
//如果不是分布式(没有SNCP) 值为null
@RpcRemote
protected WebSocketNode remoteNode;
@@ -48,15 +53,17 @@ public abstract class WebSocketNode {
@Resource(name = "$_sendconvert")
protected Convert sendConvert;
//存放所有用户分布在节点上的队列信息,Set<InetSocketAddress> 为 sncpnode 的集合, key: groupid
//存放所有用户分布在节点上的队列信息,Set<WebSocketAddress> 为 sncpnode 的集合, key: groupid
//集合包含 localSncpAddress
//如果不是分布式(没有SNCP)sncpNodeAddresses 将不会被用到
//如果不是分布式(没有SNCP)source 将不会被用到
@Resource(name = "$")
protected CacheSource<InetSocketAddress> sncpNodeAddresses;
protected CacheSource source;
//当前节点的本地WebSocketEngine
protected WebSocketEngine localEngine;
protected MessageAgent messageAgent;
protected Semaphore semaphore;
private int tryAcquireSeconds = 12;
@@ -64,14 +71,18 @@ public abstract class WebSocketNode {
public void init(AnyValue conf) {
this.tryAcquireSeconds = Integer.getInteger("WebSocketNode.tryAcquireSeconds", 12);
if (sncpNodeAddresses != null && "memory".equals(sncpNodeAddresses.getType())) {
sncpNodeAddresses.initValueType(InetSocketAddress.class);
if (source != null && "memory".equals(source.getType())) {
source.initValueType(WebSocketAddress.class);
}
if (localEngine != null) {
int wsthreads = localEngine.wsthreads;
if (wsthreads == 0) wsthreads = Runtime.getRuntime().availableProcessors() * 8;
if (wsthreads > 0) this.semaphore = new Semaphore(wsthreads);
}
String mqtopic = this.messageAgent == null ? null : this.messageAgent.generateSncpReqTopic((Service)this);
if (mqtopic != null || this.localSncpAddress != null) {
this.wsaddress = new WebSocketAddress(mqtopic, localSncpAddress);
}
}
public void destroy(AnyValue conf) {
@@ -82,50 +93,59 @@ public abstract class WebSocketNode {
return semaphore;
}
@Local
public final MessageAgent getMessageAgent() {
return messageAgent;
}
@Local
protected void postDestroy(AnyValue conf) {
if (this.localEngine == null) return;
//关掉所有本地本地WebSocket
this.localEngine.getLocalWebSockets().forEach(g -> g.close());
if (sncpNodeAddresses != null && localSncpAddress != null) {
sncpNodeAddresses.removeSetItem(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class, localSncpAddress);
if (source != null && wsaddress != null) {
source.removeSetItem(SOURCE_SNCP_NODES_KEY, WebSocketAddress.class, this.wsaddress);
}
}
protected abstract CompletableFuture<List<String>> getWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid);
protected abstract CompletableFuture<List<String>> getWebSocketAddresses(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Serializable userid);
protected abstract CompletableFuture<Integer> sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable... userids);
protected abstract CompletableFuture<Integer> sendMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable... userids);
protected abstract CompletableFuture<Integer> broadcastMessage(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketRange wsrange, Object message, boolean last);
protected abstract CompletableFuture<Integer> broadcastMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketRange wsrange, Object message, boolean last);
protected abstract CompletableFuture<Integer> sendAction(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action, Serializable... userids);
protected abstract CompletableFuture<Integer> sendAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action, Serializable... userids);
protected abstract CompletableFuture<Integer> broadcastAction(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action);
protected abstract CompletableFuture<Integer> broadcastAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action);
protected abstract CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr);
protected abstract CompletableFuture<Void> connect(Serializable userid, WebSocketAddress wsaddr);
protected abstract CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress sncpAddr);
protected abstract CompletableFuture<Void> disconnect(Serializable userid, WebSocketAddress wsaddr);
protected abstract CompletableFuture<Void> changeUserid(Serializable fromuserid, Serializable touserid, InetSocketAddress sncpAddr);
protected abstract CompletableFuture<Void> changeUserid(Serializable fromuserid, Serializable touserid, WebSocketAddress wsaddr);
protected abstract CompletableFuture<Boolean> existsWebSocket(Serializable userid, @RpcTargetAddress InetSocketAddress targetAddress);
protected abstract CompletableFuture<Boolean> existsWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress);
protected abstract CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, @RpcTargetAddress InetSocketAddress targetAddress);
protected abstract CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress);
//--------------------------------------------------------------------------------
final CompletableFuture<Void> connect(final Serializable userid) {
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket connect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return connect(userid, localSncpAddress);
if (logger.isLoggable(Level.FINEST)) logger.finest(this.wsaddress + " receive websocket connect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return connect(userid, this.wsaddress);
}
final CompletableFuture<Void> disconnect(final Serializable userid) {
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket disconnect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return disconnect(userid, localSncpAddress);
if (logger.isLoggable(Level.FINEST)) logger.finest(this.wsaddress + " receive websocket disconnect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return disconnect(userid, this.wsaddress);
}
final CompletableFuture<Void> changeUserid(Serializable olduserid, final Serializable newuserid) {
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket changeUserid event (from " + olduserid + " to " + newuserid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return changeUserid(olduserid, newuserid, localSncpAddress);
if (logger.isLoggable(Level.FINEST)) logger.finest(this.wsaddress + " receive websocket changeUserid event (from " + olduserid + " to " + newuserid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return changeUserid(olduserid, newuserid, this.wsaddress);
}
public final String getName() {
return name;
}
//--------------------------------------------------------------------------------
@@ -133,15 +153,16 @@ public abstract class WebSocketNode {
* 获取目标地址 <br>
* 该方法仅供内部调用
*
* @param topic RpcTargetTopic
* @param targetAddress InetSocketAddress
* @param userid Serializable
*
* @return 客户端地址列表
*/
protected CompletableFuture<List<String>> remoteWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid) {
protected CompletableFuture<List<String>> remoteWebSocketAddresses(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Serializable userid) {
if (remoteNode == null) return CompletableFuture.completedFuture(null);
try {
return remoteNode.getWebSocketAddresses(targetAddress, userid);
return remoteNode.getWebSocketAddresses(topic, targetAddress, userid);
} catch (Exception e) {
logger.log(Level.WARNING, "remote " + targetAddress + " websocket getOnlineRemoteAddresses error", e);
return CompletableFuture.completedFuture(null);
@@ -150,21 +171,21 @@ public abstract class WebSocketNode {
/**
* 获取用户在线的SNCP节点地址列表不是分布式则返回元素数量为1且元素值为null的列表<br>
* InetSocketAddress 为 SNCP节点地址
* WebSocketAddress 为 SNCP节点地址
*
* @param userid Serializable
*
* @return 地址列表
*/
public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
if (this.sncpNodeAddresses != null) {
public CompletableFuture<Collection<WebSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
if (this.source != null) {
tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> result = this.sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
CompletableFuture<Collection<WebSocketAddress>> result = this.source.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, WebSocketAddress.class);
if (semaphore != null) result.whenComplete((r, e) -> releaseSemaphore());
return result;
}
List<InetSocketAddress> rs = new ArrayList<>();
rs.add(this.localSncpAddress);
List<WebSocketAddress> rs = new ArrayList<>();
rs.add(this.wsaddress);
return CompletableFuture.completedFuture(rs);
}
@@ -177,14 +198,14 @@ public abstract class WebSocketNode {
*
* @return 地址集合
*/
public CompletableFuture<Map<InetSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) {
CompletableFuture<Collection<InetSocketAddress>> sncpFuture = getRpcNodeAddresses(userid);
return sncpFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
public CompletableFuture<Map<WebSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) {
CompletableFuture<Collection<WebSocketAddress>> sncpFuture = getRpcNodeAddresses(userid);
return sncpFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(new HashMap<>());
CompletableFuture<Map<InetSocketAddress, List<String>>> future = null;
for (final InetSocketAddress nodeAddress : addrs) {
CompletableFuture<Map<InetSocketAddress, List<String>>> mapFuture = getWebSocketAddresses(nodeAddress, userid)
CompletableFuture<Map<WebSocketAddress, List<String>>> future = null;
for (final WebSocketAddress nodeAddress : addrs) {
CompletableFuture<Map<WebSocketAddress, List<String>>> mapFuture = getWebSocketAddresses(nodeAddress.getTopic(), nodeAddress.getAddr(), userid)
.thenCompose((List<String> list) -> CompletableFuture.completedFuture(Utility.ofMap(nodeAddress, list)));
future = future == null ? mapFuture : future.thenCombine(mapFuture, (a, b) -> Utility.merge(a, b));
}
@@ -199,11 +220,12 @@ public abstract class WebSocketNode {
* @return boolean
*/
public CompletableFuture<Integer> getUserSize() {
if (this.localEngine != null && this.sncpNodeAddresses == null) {
if (this.localEngine != null && this.source == null) {
return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize());
}
tryAcquireSemaphore();
CompletableFuture<Integer> rs = this.sncpNodeAddresses.queryKeysStartsWithAsync(SOURCE_SNCP_USERID_PREFIX).thenApply(v -> v.size());
CompletableFuture<List<String>> listFuture = this.source.queryKeysStartsWithAsync(SOURCE_SNCP_USERID_PREFIX);
CompletableFuture<Integer> rs = listFuture.thenApply(v -> v.size());
if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore());
return rs;
}
@@ -215,11 +237,12 @@ public abstract class WebSocketNode {
* @return boolean
*/
public CompletableFuture<Set<String>> getUserSet() {
if (this.localEngine != null && this.sncpNodeAddresses == null) {
if (this.localEngine != null && this.source == null) {
return CompletableFuture.completedFuture(new LinkedHashSet<>(this.localEngine.getLocalUserSet().stream().map(x -> String.valueOf(x)).collect(Collectors.toList())));
}
tryAcquireSemaphore();
CompletableFuture<Set<String>> rs = this.sncpNodeAddresses.queryKeysStartsWithAsync(SOURCE_SNCP_USERID_PREFIX).thenApply(v -> new LinkedHashSet<>(v.stream().map(x -> x.substring(SOURCE_SNCP_USERID_PREFIX.length())).collect(Collectors.toList())));
CompletableFuture<List<String>> listFuture = this.source.queryKeysStartsWithAsync(SOURCE_SNCP_USERID_PREFIX);
CompletableFuture<Set<String>> rs = listFuture.thenApply(v -> new LinkedHashSet<>(v.stream().map(x -> x.substring(SOURCE_SNCP_USERID_PREFIX.length())).collect(Collectors.toList())));
if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore());
return rs;
}
@@ -236,23 +259,23 @@ public abstract class WebSocketNode {
if (userid instanceof WebSocketUserAddress) return existsWebSocket((WebSocketUserAddress) userid);
CompletableFuture<Boolean> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid));
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return localFuture;
return localFuture == null ? CompletableFuture.completedFuture(false) : localFuture;
}
//远程节点关闭
tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
CompletableFuture<Collection<WebSocketAddress>> addrsFuture = source.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Boolean> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
CompletableFuture<Boolean> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
//if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false);
CompletableFuture<Boolean> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.existsWebSocket(userid, addr)
: future.thenCombine(remoteNode.existsWebSocket(userid, addr), (a, b) -> a | b);
for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.existsWebSocket(userid, addr.getTopic(), addr.getAddr())
: future.thenCombine(remoteNode.existsWebSocket(userid, addr.getTopic(), addr.getAddr()), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(false) : future;
});
@@ -268,25 +291,24 @@ public abstract class WebSocketNode {
*/
@Local
public CompletableFuture<Boolean> existsWebSocket(final WebSocketUserAddress userAddress) {
CompletableFuture<Boolean> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userAddress.userid()));
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (this.localEngine != null && localEngine.existsLocalWebSocket(userAddress.userid())) return CompletableFuture.completedFuture(true);
if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return localFuture;
return CompletableFuture.completedFuture(false);
}
Collection<InetSocketAddress> addrs = userAddress.sncpAddresses();
Collection<WebSocketAddress> addrs = userAddress.addresses();
if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值
if (userAddress.sncpAddress() != null) {
if (userAddress.address() != null) {
if (addrs == null) addrs = new ArrayList<>();
addrs.add(userAddress.sncpAddress());
addrs.add(userAddress.address());
}
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false);
CompletableFuture<Boolean> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.existsWebSocket(userAddress.userid(), addr)
: future.thenCombine(remoteNode.existsWebSocket(userAddress.userid(), addr), (a, b) -> a | b);
for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.existsWebSocket(userAddress.userid(), addr.getTopic(), addr.getAddr())
: future.thenCombine(remoteNode.existsWebSocket(userAddress.userid(), addr.getTopic(), addr.getAddr()), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(false) : future;
}
@@ -318,35 +340,35 @@ public abstract class WebSocketNode {
private CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid, final WebSocketUserAddress userAddress) {
CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userAddress == null ? userid : userAddress.userid()));
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return localFuture;
return localFuture == null ? CompletableFuture.completedFuture(0) : localFuture;
}
//远程节点关闭
CompletableFuture<Collection<InetSocketAddress>> addrsFuture;
CompletableFuture<Collection<WebSocketAddress>> addrsFuture;
if (userAddress == null) {
tryAcquireSemaphore();
addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
addrsFuture = source.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
} else {
Collection<InetSocketAddress> addrs = userAddress.sncpAddresses();
Collection<WebSocketAddress> addrs = userAddress.addresses();
if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值
if (userAddress.sncpAddress() != null) {
if (userAddress.address() != null) {
if (addrs == null) addrs = new ArrayList<>();
addrs.add(userAddress.sncpAddress());
addrs.add(userAddress.address());
}
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
addrsFuture = CompletableFuture.completedFuture(addrs);
}
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.forceCloseWebSocket(userid, addr)
: future.thenCombine(remoteNode.forceCloseWebSocket(userid, addr), (a, b) -> a + b);
for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.forceCloseWebSocket(userid, addr.getTopic(), addr.getAddr())
: future.thenCombine(remoteNode.forceCloseWebSocket(userid, addr.getTopic(), addr.getAddr()), (a, b) -> a + b);
}
return future == null ? CompletableFuture.completedFuture(0) : future;
});
@@ -497,7 +519,7 @@ public abstract class WebSocketNode {
}
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, userids));
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.sendLocalMessage(message, last, userids);
}
final Object remoteMessage = formatRemoteMessage(message);
@@ -512,16 +534,16 @@ public abstract class WebSocketNode {
keyuser.put(keys[i], userids[i]);
}
tryAcquireSemaphore();
CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(true, InetSocketAddress.class, keys);
CompletableFuture<Map<String, Collection<WebSocketAddress>>> addrsFuture = source.getCollectionMapAsync(true, WebSocketAddress.class, keys);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
rsfuture = addrsFuture.thenCompose((Map<String, Collection<InetSocketAddress>> addrs) -> {
rsfuture = addrsFuture.thenCompose((Map<String, Collection<WebSocketAddress>> addrs) -> {
if (addrs == null || addrs.isEmpty()) {
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node ");
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
}
Map<InetSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
Map<WebSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
addrs.forEach((key, as) -> {
for (InetSocketAddress a : as) {
for (WebSocketAddress a : as) {
addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key));
}
});
@@ -529,7 +551,7 @@ public abstract class WebSocketNode {
logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found message-addr-userids: " + addrUsers);
}
CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
for (Map.Entry<WebSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids)
: future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b);
@@ -556,17 +578,17 @@ public abstract class WebSocketNode {
if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, useraddrs));
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.sendLocalMessage(message, last, userAddressToUserids(useraddrs));
}
final Object remoteMessage = formatRemoteMessage(message);
final Map<InetSocketAddress, List<Serializable>> addrUsers = userAddressToAddrMap(useraddrs);
final Map<WebSocketAddress, List<Serializable>> addrUsers = userAddressToAddrMap(useraddrs);
if (logger.isLoggable(Level.FINEST)) {
logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found message-addr-userids: " + addrUsers);
}
CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
for (Map.Entry<WebSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids)
: future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b);
@@ -582,7 +604,7 @@ public abstract class WebSocketNode {
}
CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = localEngine.sendLocalMessage(message, last, userid);
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
@@ -590,40 +612,40 @@ public abstract class WebSocketNode {
//远程节点发送消息
final Object remoteMessage = formatRemoteMessage(message);
tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
CompletableFuture<Collection<WebSocketAddress>> addrsFuture = source.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (addrs == null || addrs.isEmpty()) {
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node ");
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
}
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs);
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + wsaddress + ") found userid:" + userid + " on " + addrs);
CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.sendMessage(addr, remoteMessage, last, userid)
: future.thenCombine(remoteNode.sendMessage(addr, remoteMessage, last, userid), (a, b) -> a | b);
for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userid)
: future.thenCombine(remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userid), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
});
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
}
protected CompletableFuture<Integer> sendOneAddrMessage(final InetSocketAddress sncpAddr, final Object message, final boolean last, final Serializable... userids) {
if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneAddrMessage(sncpAddr, msg, last, userids));
protected CompletableFuture<Integer> sendOneAddrMessage(final WebSocketAddress addr, final Object message, final boolean last, final Serializable... userids) {
if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneAddrMessage(addr, msg, last, userids));
if (logger.isLoggable(Level.FINEST) && this.localEngine == null) { //只打印远程模式的
logger.finest("websocket want send message {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + sncpAddr + ", content:" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : (message instanceof CharSequence ? message : JsonConvert.root().convertTo(message))) + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
logger.finest("websocket want send message {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + addr + ", content:" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : (message instanceof CharSequence ? message : JsonConvert.root().convertTo(message))) + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
}
if (Objects.equals(sncpAddr, this.localSncpAddress)) {
if (Objects.equals(addr, this.wsaddress)) {
return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalMessage(message, last, userids);
}
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
}
final Object remoteMessage = formatRemoteMessage(message);
return remoteNode.sendMessage(sncpAddr, remoteMessage, last, userids);
return remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userids);
}
protected Serializable[] userAddressToUserids(WebSocketUserAddress... useraddrs) {
@@ -635,14 +657,14 @@ public abstract class WebSocketNode {
return set.toArray(new Serializable[set.size()]);
}
protected Map<InetSocketAddress, List<Serializable>> userAddressToAddrMap(WebSocketUserAddress... useraddrs) {
final Map<InetSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
protected Map<WebSocketAddress, List<Serializable>> userAddressToAddrMap(WebSocketUserAddress... useraddrs) {
final Map<WebSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
for (WebSocketUserAddress userAddress : useraddrs) {
if (userAddress.sncpAddress() != null) {
addrUsers.computeIfAbsent(userAddress.sncpAddress(), k -> new ArrayList<>()).add(userAddress.userid());
if (userAddress.address() != null) {
addrUsers.computeIfAbsent(userAddress.address(), k -> new ArrayList<>()).add(userAddress.userid());
}
if (userAddress.sncpAddresses() != null) {
for (InetSocketAddress addr : userAddress.sncpAddresses()) {
if (userAddress.addresses() != null) {
for (WebSocketAddress addr : userAddress.addresses()) {
if (addr != null) {
addrUsers.computeIfAbsent(addr, k -> new ArrayList<>()).add(userAddress.userid());
}
@@ -759,22 +781,22 @@ public abstract class WebSocketNode {
public CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message0, final boolean last) {
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> broadcastMessage(wsrange, convert, msg, last));
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.broadcastLocalMessage(wsrange, message, last);
}
final Object remoteMessage = formatRemoteMessage(message);
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalMessage(wsrange, message, last);
tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class);
CompletableFuture<Collection<WebSocketAddress>> addrsFuture = source.getCollectionAsync(SOURCE_SNCP_NODES_KEY, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message (" + remoteMessage + ") on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.broadcastMessage(addr, wsrange, remoteMessage, last)
: future.thenCombine(remoteNode.broadcastMessage(addr, wsrange, remoteMessage, last), (a, b) -> a | b);
for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.broadcastMessage(addr.getTopic(), addr.getAddr(), wsrange, remoteMessage, last)
: future.thenCombine(remoteNode.broadcastMessage(addr.getTopic(), addr.getAddr(), wsrange, remoteMessage, last), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(0) : future;
});
@@ -790,21 +812,21 @@ public abstract class WebSocketNode {
*/
@Local
public CompletableFuture<Integer> broadcastAction(final WebSocketAction action) {
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.broadcastLocalAction(action);
}
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalAction(action);
tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class);
CompletableFuture<Collection<WebSocketAddress>> addrsFuture = source.getCollectionAsync(SOURCE_SNCP_NODES_KEY, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast action (" + action + ") on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.broadcastAction(addr, action)
: future.thenCombine(remoteNode.broadcastAction(addr, action), (a, b) -> a | b);
for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.broadcastAction(addr.getTopic(), addr.getAddr(), action)
: future.thenCombine(remoteNode.broadcastAction(addr.getTopic(), addr.getAddr(), action), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(0) : future;
});
@@ -830,7 +852,7 @@ public abstract class WebSocketNode {
}
return sendAction(action, useraddrs);
}
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.sendLocalAction(action, userids);
}
CompletableFuture<Integer> rsfuture;
@@ -844,16 +866,16 @@ public abstract class WebSocketNode {
keyuser.put(keys[i], userids[i]);
}
tryAcquireSemaphore();
CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(true, InetSocketAddress.class, keys);
CompletableFuture<Map<String, Collection<WebSocketAddress>>> addrsFuture = source.getCollectionMapAsync(true, WebSocketAddress.class, keys);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
rsfuture = addrsFuture.thenCompose((Map<String, Collection<InetSocketAddress>> addrs) -> {
rsfuture = addrsFuture.thenCompose((Map<String, Collection<WebSocketAddress>> addrs) -> {
if (addrs == null || addrs.isEmpty()) {
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node ");
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
}
Map<InetSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
Map<WebSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
addrs.forEach((key, as) -> {
for (InetSocketAddress a : as) {
for (WebSocketAddress a : as) {
addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key));
}
});
@@ -861,7 +883,7 @@ public abstract class WebSocketNode {
logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found action-userid-addrs: " + addrUsers);
}
CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
for (Map.Entry<WebSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids)
: future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b);
@@ -884,16 +906,16 @@ public abstract class WebSocketNode {
@Local
public CompletableFuture<Integer> sendAction(final WebSocketAction action, final WebSocketUserAddress... useraddrs) {
if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.sendLocalAction(action, userAddressToUserids(useraddrs));
}
final Map<InetSocketAddress, List<Serializable>> addrUsers = userAddressToAddrMap(useraddrs);
final Map<WebSocketAddress, List<Serializable>> addrUsers = userAddressToAddrMap(useraddrs);
if (logger.isLoggable(Level.FINEST)) {
logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found action-userid-addrs: " + addrUsers);
}
CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
for (Map.Entry<WebSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids)
: future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b);
@@ -907,45 +929,45 @@ public abstract class WebSocketNode {
}
CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = localEngine.sendLocalAction(action, userid);
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
}
//远程节点发送操作
tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
CompletableFuture<Collection<WebSocketAddress>> addrsFuture = source.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (addrs == null || addrs.isEmpty()) {
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node ");
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
}
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs);
CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.sendAction(addr, action, userid)
: future.thenCombine(remoteNode.sendAction(addr, action, userid), (a, b) -> a | b);
for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userid)
: future.thenCombine(remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userid), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
});
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
}
protected CompletableFuture<Integer> sendOneAddrAction(final InetSocketAddress sncpAddr, final WebSocketAction action, final Serializable... userids) {
protected CompletableFuture<Integer> sendOneAddrAction(final WebSocketAddress addr, final WebSocketAction action, final Serializable... userids) {
if (logger.isLoggable(Level.FINEST) && this.localEngine == null) { //只打印远程模式的
logger.finest("websocket want send action {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + sncpAddr + ", action:" + action + " from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
logger.finest("websocket want send action {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + addr + ", action:" + action + " from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
}
if (Objects.equals(sncpAddr, this.localSncpAddress)) {
if (Objects.equals(addr, this.wsaddress)) {
return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalAction(action, userids);
}
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
}
return remoteNode.sendAction(sncpAddr, action, userids);
return remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userids);
}
protected Object formatRemoteMessage(Object message) {

View File

@@ -9,9 +9,10 @@ import org.redkale.util.Utility;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.AbstractMap;
import java.util.*;
import java.util.function.*;
import java.util.logging.*;
import java.util.zip.*;
import org.redkale.convert.*;
import org.redkale.net.Cryptor;
import org.redkale.util.*;
@@ -29,6 +30,8 @@ public final class WebSocketPacket {
static final WebSocketPacket NONE = new WebSocketPacket();
private static final byte[] EOM_BYTES = new byte[]{0, 0, -1, -1};
public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]);
public static enum MessageType {
@@ -82,6 +85,8 @@ public final class WebSocketPacket {
//---------------接收------------------------
MessageType receiveType;
boolean receiveCompress;
int receiveCount;
int receiveLength;
@@ -148,16 +153,6 @@ public final class WebSocketPacket {
if (mapconvable && !(json instanceof Object[])) throw new IllegalArgumentException();
}
WebSocketPacket(ByteBuffer[] sendBuffers, FrameType type, boolean fin) {
this.type = type;
this.last = fin;
this.setSendBuffers(sendBuffers);
}
void setSendBuffers(ByteBuffer[] sendBuffers) {
this.sendBuffers = sendBuffers;
}
ByteBuffer[] duplicateSendBuffers() {
ByteBuffer[] rs = new ByteBuffer[this.sendBuffers.length];
for (int i = 0; i < this.sendBuffers.length; i++) {
@@ -223,7 +218,7 @@ public final class WebSocketPacket {
*
* @return ByteBuffer[]
*/
ByteBuffer[] encode(final Supplier<ByteBuffer> supplier, final Consumer<ByteBuffer> consumer, final Cryptor cryptor) {
void encodePacket(final Supplier<ByteBuffer> supplier, final Consumer<ByteBuffer> consumer, final Cryptor cryptor) {
final byte opcode = (byte) (this.type.getValue() | 0x80);
if (this.sendConvert != null) {
Supplier<ByteBuffer> newsupplier = new Supplier<ByteBuffer>() {
@@ -263,7 +258,8 @@ public final class WebSocketPacket {
firstbuf.put(1, (byte) 0x7F); //127
firstbuf.putInt(2, contentLength);
}
return buffers;
this.sendBuffers = buffers;
return;
}
ByteBuffer buffer = supplier.get(); //确保ByteBuffer的capacity不能小于128
@@ -294,7 +290,8 @@ public final class WebSocketPacket {
buffer.put((byte) len);
buffer.put(content);
buffer.flip();
return new ByteBuffer[]{buffer};
this.sendBuffers = new ByteBuffer[]{buffer};
return;
}
if (len <= 0xFFFF) { // 65535
buffer.put(opcode);
@@ -310,7 +307,8 @@ public final class WebSocketPacket {
if (pend <= 0) {
buffer.put(content);
buffer.flip();
return new ByteBuffer[]{buffer};
this.sendBuffers = new ByteBuffer[]{buffer};
return;
}
buffer.put(content, 0, buffer.remaining());
buffer.flip();
@@ -325,7 +323,7 @@ public final class WebSocketPacket {
start += capacity;
pend -= capacity;
}
return buffers;
this.sendBuffers = buffers;
}
// public static void main(String[] args) throws Throwable {
@@ -391,10 +389,10 @@ public final class WebSocketPacket {
*
* @return 返回NONE表示Buffer内容不够 返回this表示解析完成或部分解析完成返回null表示解析异常
*/
WebSocketPacket decode(final Logger logger, final WebSocketRunner runner, final WebSocket webSocket, final int wsmaxbody,
WebSocketPacket decodePacket(final Logger logger, final WebSocketRunner runner, final WebSocket webSocket, final int wsmaxbody,
final AbstractMap.SimpleEntry<String, byte[]> halfBytes, final ByteBuffer buffer) {
//开始
final boolean debug = false; //调试开关
final boolean debug = true; //调试开关
if (debug) logger.log(Level.FINEST, "read websocket message's length = " + buffer.remaining());
if (!buffer.hasRemaining()) return NONE;
if (buffer.remaining() < 2) {
@@ -404,20 +402,9 @@ public final class WebSocketPacket {
return NONE;
}
final byte opcode = buffer.get(); //第一个字节
this.last = (opcode & 0b1000_0000) != 0;
this.type = FrameType.valueOf(opcode & 0xF);
this.last = (opcode & 0B1000_0000) != 0;
this.type = FrameType.valueOf(opcode & 0B0000_1111);
if (type == FrameType.CLOSE) {
if (debug) logger.log(Level.FINEST, " receive close command from websocket client");
}
if (type == null) {
logger.log(Level.SEVERE, " receive unknown frametype(opcode=" + (opcode & 0xF) + ") from websocket client");
}
final boolean checkrsv = false;//暂时不校验
if (checkrsv && (opcode & 0b0111_0000) != 0) {
if (debug) logger.log(Level.FINE, "rsv1 rsv2 rsv3 must be 0, but not (" + opcode + ")");
return null; //rsv1 rsv2 rsv3 must be 0
}
//0x00 表示一个后续帧
//0x01 表示一个文本帧
//0x02 表示一个二进制帧
@@ -426,12 +413,26 @@ public final class WebSocketPacket {
//0x9 表示一个ping
//0xA 表示一个pong
//0x0B-0F 为以后的控制帧保留
final boolean control = (opcode & 0b0000_1000) != 0; //是否控制帧
final boolean control = (opcode & 0B0000_1000) != 0; //是否控制帧
this.receiveCompress = !control && webSocket.inflater != null && (opcode & 0B0100_0000) != 0; //rsv1 为 1
if (type == FrameType.CLOSE) {
if (debug) logger.log(Level.FINEST, " receive close command from websocket client");
}
if (type == null) {
logger.log(Level.SEVERE, " receive unknown frametype(opcode=" + (opcode & 0B0000_1111) + ") from websocket client");
}
final boolean checkrsv = false;//暂时不校验
if (checkrsv && (opcode & 0B0111_0000) != 0) {
if (debug) logger.log(Level.FINE, "rsv1 rsv2 rsv3 must be 0, but not (" + opcode + ")");
return null; //rsv1 rsv2 rsv3 must be 0
}
final byte crcode = buffer.get(); //第二个字节
byte lengthCode = crcode;
final boolean masked = (lengthCode & 0x80) == 0x80;
if (masked) lengthCode ^= 0x80; //mask
if (debug) logger.log(Level.FINEST, " receive type=" + type + ", control=" + control + ", opcode=" + opcode + ", masked=" + masked + ", remaining=" + buffer.remaining());
//判断Buffer剩余内容够不够基本信息的创建
int minBufferLength = ((lengthCode <= 0x7D) ? 0 : (lengthCode == 0x7E ? 2 : 4)) + (masked ? 4 : 0);
@@ -441,6 +442,7 @@ public final class WebSocketPacket {
bs[1] = crcode;
buffer.get(bs, 2, buffer.remaining());
halfBytes.setValue(bs);
if (debug) logger.log(Level.FINEST, " receive not enough bufferlength =" + bs.length);
return NONE;
}
@@ -506,14 +508,18 @@ public final class WebSocketPacket {
if (selfType == FrameType.TEXT) {
Convert textConvert = webSocket.getTextConvert();
if (textConvert == null || (!runner.mergemsg && (series || !this.last))) {
this.receiveMessage = new String(this.getReceiveBytes(buffers), StandardCharsets.UTF_8);
this.receiveMessage = new String(this.getReceiveBytes(webSocket, selfType, buffers), StandardCharsets.UTF_8);
this.receiveType = MessageType.STRING;
} else {
if (this.last || !runner.mergemsg) {
if (runner.currSeriesMergeMessage == null) {
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
if (this.receiveCompress) {
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.getReceiveBytes(webSocket, selfType, buffers));
} else {
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
}
} else {
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers));
runner.currSeriesMergeMessage.write(this.getReceiveBytes(webSocket, selfType, buffers));
try {
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, runner.currSeriesMergeMessage.getBytes());
} finally {
@@ -522,7 +528,7 @@ public final class WebSocketPacket {
}
} else {
if (runner.currSeriesMergeMessage == null) runner.currSeriesMergeMessage = new ByteArray();
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers));
runner.currSeriesMergeMessage.write(this.getReceiveBytes(webSocket, selfType, buffers));
this.receiveMessage = MESSAGE_NIL;
}
this.receiveCount = this.receiveLength;
@@ -531,14 +537,18 @@ public final class WebSocketPacket {
} else if (selfType == FrameType.BINARY) {
Convert binaryConvert = webSocket.getBinaryConvert();
if (binaryConvert == null || (!runner.mergemsg && (series || !this.last))) {
this.receiveMessage = this.getReceiveBytes(buffers);
this.receiveMessage = this.getReceiveBytes(webSocket, selfType, buffers);
this.receiveType = MessageType.BYTES;
} else {
if (this.last || !runner.mergemsg) {
if (runner.currSeriesMergeMessage == null) {
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
if (this.receiveCompress) {
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, this.getReceiveBytes(webSocket, selfType, buffers));
} else {
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
}
} else {
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers));
runner.currSeriesMergeMessage.write(this.getReceiveBytes(webSocket, selfType, buffers));
try {
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, runner.currSeriesMergeMessage.getBytes());
} finally {
@@ -547,20 +557,20 @@ public final class WebSocketPacket {
}
} else {
if (runner.currSeriesMergeMessage == null) runner.currSeriesMergeMessage = new ByteArray();
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers));
runner.currSeriesMergeMessage.write(this.getReceiveBytes(webSocket, selfType, buffers));
this.receiveMessage = MESSAGE_NIL;
}
this.receiveCount = this.receiveLength;
this.receiveType = MessageType.OBJECT;
}
} else if (selfType == FrameType.PING) {
this.receiveMessage = this.getReceiveBytes(buffers);
this.receiveMessage = this.getReceiveBytes(webSocket, selfType, buffers);
this.receiveType = MessageType.BYTES;
} else if (selfType == FrameType.PONG) {
this.receiveMessage = this.getReceiveBytes(buffers);
this.receiveMessage = this.getReceiveBytes(webSocket, selfType, buffers);
this.receiveType = MessageType.BYTES;
} else if (selfType == FrameType.CLOSE) {
this.receiveMessage = this.getReceiveBytes(buffers);
this.receiveMessage = this.getReceiveBytes(webSocket, selfType, buffers);
this.receiveType = MessageType.BYTES;
}
}
@@ -569,7 +579,7 @@ public final class WebSocketPacket {
return this.receiveLength <= this.receiveCount;
}
byte[] getReceiveBytes(ByteBuffer... buffers) {
byte[] getReceiveBytes(WebSocket webSocket, FrameType frameType, ByteBuffer... buffers) {
final int length = this.receiveLength;
if (length == 0) return new byte[0];
byte[] bs = new byte[length];
@@ -587,6 +597,24 @@ public final class WebSocketPacket {
bs[i] = mask.unmask(bs[i]);
}
}
if (bs.length > 6 && this.receiveCompress && (frameType == FrameType.BINARY || frameType == FrameType.TEXT || frameType == FrameType.SERIES)) {
Inflater inflater = webSocket.inflater;
inflater.reset();
ByteArrayOutputStream baos = new ByteArrayOutputStream(bs.length);
inflater.setInput(Utility.append(bs, EOM_BYTES));
byte[] buff = new byte[1024];
try {
while (!inflater.finished()) {
int count = inflater.inflate(buff);
if (count == 0) break;
baos.write(buff, 0, count);
}
return baos.toByteArray();
} catch (Exception ex) {
ex.printStackTrace();
return bs;
}
}
return bs;
}

View File

@@ -108,7 +108,7 @@ class WebSocketRunner implements Runnable {
if (onePacket != null) packets.add(onePacket);
try {
while (true) {
WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), self, webSocket, wsmaxbody, halfBytes, readBuffer);
WebSocketPacket packet = new WebSocketPacket().decodePacket(context.getLogger(), self, webSocket, wsmaxbody, halfBytes, readBuffer);
if (packet == WebSocketPacket.NONE) break; //解析完毕但是buffer有多余字节
if (packet != null && !packet.isReceiveFinished()) {
unfinishPacket = packet;
@@ -230,7 +230,8 @@ class WebSocketRunner implements Runnable {
//System.out.println("推送消息");
final CompletableFuture<Integer> futureResult = new CompletableFuture<>();
try {
ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(webSocket._channel.getBufferSupplier(), webSocket._channel.getBufferConsumer(), webSocket._engine.cryptor);
if (packet.sendBuffers == null) packet.encodePacket(webSocket._channel.getBufferSupplier(), webSocket._channel.getBufferConsumer(), webSocket._engine.cryptor);
ByteBuffer[] buffers = packet.duplicateSendBuffers();
//if (debug) context.getLogger().log(Level.FINEST, "wsrunner.sending websocket message: " + packet);
CompletionHandler<Integer, ByteBuffer[]> handler = new CompletionHandler<Integer, ByteBuffer[]>() {

View File

@@ -15,8 +15,10 @@ import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.*;
import java.util.logging.*;
import java.util.zip.*;
import javax.annotation.*;
import org.redkale.convert.Convert;
import org.redkale.mq.MessageAgent;
import org.redkale.net.Cryptor;
import org.redkale.service.*;
import org.redkale.util.*;
@@ -97,6 +99,10 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
//同RestWebSocket.cryptor, 变量名不可改, 被Rest.createRestWebSocketServlet用到
protected Cryptor cryptor;
protected boolean permessageDeflate = false;
protected MessageAgent messageAgent;
@Resource(name = "jsonconvert")
protected Convert jsonConvert;
@@ -146,6 +152,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
}
if (this.node.sendConvert == null) this.node.sendConvert = this.sendConvert;
if (this.messageAgent != null) this.node.messageAgent = this.messageAgent;
{
AnyValue props = conf;
if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties");
@@ -210,6 +217,10 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
webSocket._remoteAddress = request.getRemoteAddress();
webSocket._remoteAddr = request.getRemoteAddr();
webSocket._sncpAddress = this.node.localSncpAddress;
if (this.permessageDeflate && request.getHeader("Sec-WebSocket-Extensions", "").contains("permessage-deflate")) {
webSocket.deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
webSocket.inflater = new Inflater(true);
}
initRestWebSocket(webSocket);
CompletableFuture<String> sessionFuture = webSocket.onOpen(request);
if (sessionFuture == null) {
@@ -233,6 +244,8 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
response.setHeader("Connection", "Upgrade");
response.addHeader("Upgrade", "websocket");
response.addHeader("Sec-WebSocket-Accept", Base64.getEncoder().encodeToString(bytes));
if (webSocket.deflater != null) response.addHeader("Sec-WebSocket-Extensions", "permessage-deflate");
response.sendBody((ByteBuffer) null, null, new CompletionHandler<Integer, Void>() {
WebSocketRunner temprunner = null;

View File

@@ -20,44 +20,57 @@ public interface WebSocketUserAddress extends Serializable {
Serializable userid();
InetSocketAddress sncpAddress();
WebSocketAddress address();
Collection<InetSocketAddress> sncpAddresses();
Collection<WebSocketAddress> addresses();
public static WebSocketUserAddress create(WebSocketUserAddress userAddress) {
return new SimpleWebSocketUserAddress(userAddress);
}
public static WebSocketUserAddress create(Serializable userid, InetSocketAddress sncpAddress) {
return new SimpleWebSocketUserAddress(userid, sncpAddress, null);
public static WebSocketUserAddress createTopic(Serializable userid, String mqtopic, InetSocketAddress sncpAddress) {
return new SimpleWebSocketUserAddress(userid, mqtopic, sncpAddress);
}
public static WebSocketUserAddress create(Serializable userid, Collection<InetSocketAddress> sncpAddresses) {
return new SimpleWebSocketUserAddress(userid, null, sncpAddresses);
public static WebSocketUserAddress create(Serializable userid, WebSocketAddress address) {
return new SimpleWebSocketUserAddress(userid, address);
}
public static WebSocketUserAddress create(Serializable userid, Collection<WebSocketAddress> addresses) {
return new SimpleWebSocketUserAddress(userid, addresses);
}
public static class SimpleWebSocketUserAddress implements WebSocketUserAddress {
private Serializable userid;
private InetSocketAddress sncpAddress;
private WebSocketAddress address;
private Collection<InetSocketAddress> sncpAddresses;
private Collection<WebSocketAddress> addresses;
public SimpleWebSocketUserAddress() {
}
public SimpleWebSocketUserAddress(Serializable userid, InetSocketAddress sncpAddress, Collection<InetSocketAddress> sncpAddresses) {
public SimpleWebSocketUserAddress(Serializable userid, String mqtopic, InetSocketAddress sncpAddress) {
this.userid = userid;
this.sncpAddress = sncpAddress;
this.sncpAddresses = sncpAddresses;
this.address = new WebSocketAddress(mqtopic, sncpAddress);
}
public SimpleWebSocketUserAddress(Serializable userid, WebSocketAddress address) {
this.userid = userid;
this.address = address;
}
public SimpleWebSocketUserAddress(Serializable userid, Collection<WebSocketAddress> addresses) {
this.userid = userid;
this.addresses = addresses;
}
public SimpleWebSocketUserAddress(WebSocketUserAddress userAddress) {
if (userAddress == null) return;
this.userid = userAddress.userid();
this.sncpAddress = userAddress.sncpAddress();
this.sncpAddresses = userAddress.sncpAddresses();
this.address = userAddress.address();
this.addresses = userAddress.addresses();
}
@Override
@@ -66,13 +79,13 @@ public interface WebSocketUserAddress extends Serializable {
}
@Override
public InetSocketAddress sncpAddress() {
return sncpAddress;
public WebSocketAddress address() {
return address;
}
@Override
public Collection<InetSocketAddress> sncpAddresses() {
return sncpAddresses;
public Collection<WebSocketAddress> addresses() {
return addresses;
}
public Serializable getUserid() {
@@ -83,20 +96,20 @@ public interface WebSocketUserAddress extends Serializable {
this.userid = userid;
}
public InetSocketAddress getSncpAddress() {
return sncpAddress;
public WebSocketAddress getAddress() {
return address;
}
public void setSncpAddress(InetSocketAddress sncpAddress) {
this.sncpAddress = sncpAddress;
public void setAddress(WebSocketAddress address) {
this.address = address;
}
public Collection<InetSocketAddress> getSncpAddresses() {
return sncpAddresses;
public Collection<WebSocketAddress> getAddresses() {
return addresses;
}
public void setSncpAddresses(Collection<InetSocketAddress> sncpAddresses) {
this.sncpAddresses = sncpAddresses;
public void setAddresses(Collection<WebSocketAddress> addresses) {
this.addresses = addresses;
}
@Override

View File

@@ -0,0 +1,62 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.nio;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, Runnable {
private final CompletionHandler<Integer, A> handler;
private A attachment;
public ScheduledFuture timeoutFuture;
public NioCompletionHandler(CompletionHandler<Integer, A> handler, A attachment) {
this.handler = handler;
this.attachment = attachment;
}
public void setAttachment(A attachment) {
this.attachment = attachment;
}
@Override
public void completed(Integer result, A attach) {
ScheduledFuture future = this.timeoutFuture;
if (future != null) {
this.timeoutFuture = null;
future.cancel(true);
}
handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, A attach) {
ScheduledFuture future = this.timeoutFuture;
if (future != null) {
this.timeoutFuture = null;
future.cancel(true);
}
handler.failed(exc, attachment);
}
@Override
public void run() {
handler.failed(new TimeoutException(), attachment);
}
}

View File

@@ -0,0 +1,97 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.nio;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Consumer;
import org.redkale.net.TcpNioAsyncConnection;
import org.redkale.util.*;
/**
* 协议处理的IO线程类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class NioThread extends Thread {
final Selector selector;
private final ExecutorService executor;
private final ObjectPool<ByteBuffer> bufferPool;
private final ConcurrentLinkedQueue<Consumer<Selector>> registers = new ConcurrentLinkedQueue<>();
private Thread localThread;
private boolean closed;
public NioThread(Selector selector, ExecutorService executor, ObjectPool<ByteBuffer> bufferPool) {
super();
this.selector = selector;
this.executor = executor;
this.bufferPool = bufferPool;
this.setDaemon(true);
}
public void register(Consumer<Selector> consumer) {
registers.offer(consumer);
selector.wakeup();
}
@Override
public void run() {
this.localThread = Thread.currentThread();
while (!this.closed) {
try {
Consumer<Selector> register;
while ((register = registers.poll()) != null) {
register.accept(selector);
}
int count = selector.select();
if (count == 0) continue;
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
TcpNioAsyncConnection conn = (TcpNioAsyncConnection) key.attachment();
if (key.isWritable()) {
//key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
conn.doWrite();
} else if (key.isReadable()) {
conn.doRead();
} else if (key.isConnectable()) {
conn.doConnect();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public boolean inSameThread() {
return this.localThread == Thread.currentThread();
}
public boolean inSameThread(Thread thread) {
return this.localThread == thread;
}
public void close() {
this.closed = true;
this.interrupt();
}
}

Some files were not shown because too many files have changed in this diff Show More