441 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
Redkale
6b59c54087 Redkale 2.0.0.rc4 结束 2020-02-02 10:48:54 +08:00
Redkale
58f5ee999a 2020-01-30 12:04:56 +08:00
Redkale
32e8c033ea 2020-01-30 12:02:42 +08:00
Redkale
84a96f750f 2020-01-30 11:53:17 +08:00
Redkale
6560e71e48 2020-01-30 11:44:46 +08:00
Redkale
ee8a18a528 2020-01-30 11:35:05 +08:00
Redkale
c36fc36964 2020-01-29 12:31:11 +08:00
Redkale
a34f85bfc9 2020-01-29 11:35:49 +08:00
Redkale
6f00efa077 2020-01-29 11:28:24 +08:00
Redkale
469dff8478 2020-01-27 18:39:11 +08:00
Redkale
c50eb79b1d 2020-01-27 15:57:13 +08:00
Redkale
985bf6ed25 2020-01-27 15:56:41 +08:00
Redkale
bd51696e08 2020-01-27 14:03:53 +08:00
Redkale
2764d59a4f 2020-01-27 13:01:43 +08:00
Redkale
c1509bb712 2020-01-27 12:32:22 +08:00
Redkale
b14f32deb3 2020-01-27 12:31:34 +08:00
Redkale
62145a2aad 2020-01-27 12:23:33 +08:00
Redkale
8fb88a57b2 2020-01-26 20:20:17 +08:00
Redkale
44f12ae003 2020-01-26 15:51:05 +08:00
Redkale
bf97ef3a08 2020-01-26 14:03:49 +08:00
Redkale
aeefc3b8a2 2020-01-17 14:43:33 +08:00
Redkale
5763718816 WebSocketRunner加入写队列 2020-01-17 14:39:19 +08:00
Redkale
3b142b7504 PoolSource的默认超时时间从3秒改成6秒 2020-01-17 14:04:15 +08:00
Redkale
0f52d32424 TcpAioAsyncConnection去掉队列写 2020-01-17 13:57:41 +08:00
Redkale
f5f3c48f38 2020-01-16 17:24:46 +08:00
Redkale
9f9f5aa000 HttpResponse增加retResultHandler属性 2020-01-16 16:59:37 +08:00
Redkale
be4ca0287b HttpResponse增加retResultHandler属性 2020-01-16 11:11:34 +08:00
Redkale
a84ed72f28 2020-01-14 16:16:28 +08:00
Redkale
8eb5f56f42 2020-01-14 10:59:45 +08:00
Redkale
df1aa2b379 2020-01-11 22:02:42 +08:00
Redkale
da2befcb97 2020-01-11 21:22:34 +08:00
Redkale
92d0f7e796 2020-01-11 21:17:11 +08:00
Redkale
6aa3949d05 DataSource增加可group by的queryColumnMap系列方法,缓存EntityCache部分未实现 2020-01-11 13:32:31 +08:00
Redkale
fa833d9224 2020-01-10 20:30:08 +08:00
Redkale
c9261f8475 增加ofArray方法 2020-01-10 19:25:17 +08:00
Redkale
a1df62af08 2020-01-10 19:20:32 +08:00
Redkale
aa12413f4e 2020-01-10 19:15:37 +08:00
Redkale
2ccd9ba10f ColumnExpress增加减法DEC 2020-01-10 19:10:10 +08:00
Redkale
81ae68c571 ColumnExpress增加减法DEC 2020-01-10 19:08:50 +08:00
Redkale
51b45f4713 DataSource.insert增加Collection、Stream方法 2020-01-08 13:36:59 +08:00
Redkale
66e1f58879 2020-01-08 10:42:08 +08:00
Redkale
06bb5180cf 【不兼容】WebSocket中onConnected、onClose方法的返回值由void改成CompletableFuture 2020-01-08 10:36:54 +08:00
Redkale
d83d7f879c 2020-01-07 16:32:03 +08:00
Redkale
b8e92c949e 2020-01-07 16:31:32 +08:00
Redkale
91548a0ca9 2020-01-07 16:31:02 +08:00
Redkale
dbca25cd54 2020-01-07 16:29:38 +08:00
Redkale
539ea15ae5 2020-01-07 16:28:11 +08:00
Redkale
ad1d9f33d4 WebSocket增加getUserSet方法 2020-01-07 13:07:04 +08:00
Redkale
df98c1a58e 优化querySet和queryColumnSet系列方法 2020-01-07 11:48:30 +08:00
Redkale
56d1969c96 2020-01-07 11:46:34 +08:00
Redkale
4b341436af 2020-01-07 11:01:08 +08:00
Redkale
4d3d73b4c1 WebSocketRange增加几个小方法 2020-01-07 10:21:01 +08:00
Redkale
55ab279e7f 2020-01-02 09:51:37 +08:00
Redkale
82ab994608 2019-12-19 10:55:11 +08:00
Redkale
1c4035e677 2019-12-18 23:50:29 +08:00
Redkale
afdc9e7207 2019-12-18 23:00:23 +08:00
Redkale
9b83abb06a Attribute增加subclass参数用于识别泛型的子类 2019-12-18 22:06:54 +08:00
Redkale
26d1a10bd0 2019-12-18 19:51:35 +08:00
Redkale
7e55dcc46d FilterNode兼容String的 >= > < <= 2019-12-11 19:01:45 +08:00
Redkale
45802d2403 修复2019.6.20改动时remoteAddrHeader带来的bug 2019-12-07 21:02:13 +08:00
Redkale
3660a2a4e5 2019-12-07 19:43:43 +08:00
Redkale
bed81bd93d 修复WriteMoreCompletionHandler的bug 2019-11-30 11:14:44 +08:00
Redkale
131855cdc5 2019-11-30 09:04:01 +08:00
Redkale
8b69e7d02b Redkale 2.0.0.rc3 结束 2019-11-25 16:33:48 +08:00
Redkale
02a10bf014 2019-11-23 10:55:29 +08:00
Redkale
004b83172e 2019-11-23 09:28:18 +08:00
Redkale
fee4555cef 2019-11-23 09:04:08 +08:00
Redkale
758bd7de72 2019-11-23 09:01:53 +08:00
Redkale
b2dd366640 2019-11-19 20:47:16 +08:00
Redkale
934c82eadd 优化JsonWrite.writeSmallString方法 2019-11-18 13:42:55 +08:00
Redkale
c7ed6574cc 优化JsonWrite.writeSmallString方法 2019-11-18 13:34:51 +08:00
Redkale
2ea2667fa7 Utility增加byteArray方法 2019-11-18 13:25:51 +08:00
Redkale
34ae2d38c5 2019-11-15 15:54:34 +08:00
Redkale
a1c95544cb 2019-11-15 15:19:16 +08:00
Redkale
c6dc38c35c PoolTcpSource增加ping接口 2019-11-14 12:04:22 +08:00
Redkale
39203ab598 2019-11-13 10:11:35 +08:00
Redkale
51a95a84aa 去掉AsyncConnection内的ByteBufferPool,HttpResponse合并header和body的Buffer 2019-11-13 10:07:48 +08:00
Redkale
8a8d45e642 DataSource增加判断字符串字段值长度的FilterExpress 2019-11-11 11:10:32 +08:00
Redkale
52eb7dbc0c 2019-11-07 09:55:16 +08:00
Redkale
0e14b60f12 2019-11-05 21:21:57 +08:00
Redkale
d373ab7204 2019-11-05 09:31:38 +08:00
Redkale
4f9a563ba7 2019-11-05 09:26:15 +08:00
Redkale
852da19b1e Redkale 2.0.0.rc2 结束 2019-11-05 09:21:49 +08:00
Redkale
ddfc040a53 2019-11-05 09:16:34 +08:00
Redkale
df3ccb763a 2019-11-04 11:53:11 +08:00
Redkale
f42561ca93 convert支持sql包的几个date类型 2019-11-03 11:47:54 +08:00
Redkale
580e28519a 2019-11-02 15:08:38 +08:00
Redkale
9ecc1d8f19 2019-11-02 14:20:07 +08:00
Redkale
40629ed7b9 2019-10-28 13:14:38 +08:00
Redkale
5790135add 2019-10-28 11:59:23 +08:00
Redkale
fd862ed6c6 Convert兼容java.util.Map.Entry 2019-10-28 11:56:45 +08:00
Redkale
33763af96c 兼容TypeToken.typeToClass 方法 2019-10-26 16:03:36 +08:00
Redkale
7c05df3cfb 2019-10-26 15:32:16 +08:00
Redkale
f471e2d4c5 配置支持远程地址 2019-10-26 09:45:33 +08:00
Redkale
d4fd093521 2019-10-25 11:42:50 +08:00
Redkale
40003c7789 2019-10-24 11:45:51 +08:00
Redkale
b94f99f338 2019-10-22 09:11:37 +08:00
Redkale
bd21644571 Redkale 2.0.0.rc1 结束 2019-10-18 08:42:28 +08:00
Redkale
5f3599d9b8 2019-10-17 18:51:08 +08:00
Redkale
1e4a30bd70 2019-10-16 15:17:04 +08:00
Redkale
e7dc5de9f2 2019-10-15 22:18:44 +08:00
Redkale
ccb9cb28f5 修复JsonByteBufferWriter的utf8问题 2019-10-15 21:55:31 +08:00
Redkale
4d9b72c922 2019-10-15 21:43:45 +08:00
Redkale
a51ae13a39 2019-10-15 18:02:34 +08:00
Redkale
dfca186688 2019-10-15 18:01:34 +08:00
Redkale
fadd229a89 2019-10-14 15:29:28 +08:00
Redkale
7acc69adc4 2019-10-14 12:32:53 +08:00
Redkale
d88e4120a1 2019-10-13 17:29:12 +08:00
Redkale
ef98edd91a Convert.newConvert 增加第2个Function参数 2019-10-13 12:55:00 +08:00
Redkale
f4548bbe34 2019-10-11 11:13:41 +08:00
Redkale
11a5faca1d Utility增加reverseSort方法 2019-10-11 08:59:40 +08:00
Redkale
c37b0e8cb5 Redkale 2.0.0.beta5 结束 2019-10-10 09:21:48 +08:00
Redkale
a20570a6eb 2019-10-10 09:17:42 +08:00
Redkale
5e3edb7e1d 2019-10-09 13:43:11 +08:00
Redkale
ad8f1d2da6 增加util.ResourceInjectLoader功能 2019-10-09 13:42:29 +08:00
Redkale
24b23c894f 修复FileSimpledCoder的instance写错的bug 2019-10-09 13:41:39 +08:00
Redkale
c551d5fb81 2019-09-26 16:32:36 +08:00
Redkale
fba43894c1 2019-09-26 16:27:42 +08:00
Redkale
22cc7e086c RetResult增加Convert属性 2019-09-26 16:22:56 +08:00
Redkale
1791008729 2019-09-25 15:41:19 +08:00
Redkale
90e15dd253 2019-09-25 15:40:47 +08:00
Redkale
7db73c076c 2019-09-25 15:33:18 +08:00
Redkale
95ad6e99d9 2019-09-23 18:57:45 +08:00
Redkale
0b2a5d0f61 2019-09-23 08:59:03 +08:00
197 changed files with 10931 additions and 2735 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"?> <?xml version="1.0" encoding="UTF-8"?>
<application port="2121"> <application nodeid="10" port="2121">
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml --> <!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->

View File

@@ -1 +0,0 @@
<EFBFBD>Լ<EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><EFBFBD>jarĬ<EFBFBD>Ϸ<EFBFBD><EFBFBD>ڴ˴<EFBFBD>

View File

@@ -16,10 +16,6 @@
<directory>${project.basedir}/conf</directory> <directory>${project.basedir}/conf</directory>
<outputDirectory>conf</outputDirectory> <outputDirectory>conf</outputDirectory>
</fileSet> </fileSet>
<fileSet>
<directory>${project.basedir}/libs</directory>
<outputDirectory>libs</outputDirectory>
</fileSet>
<fileSet> <fileSet>
<directory>${project.basedir}/logs</directory> <directory>${project.basedir}/logs</directory>
<outputDirectory>logs</outputDirectory> <outputDirectory>logs</outputDirectory>

View File

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

View File

@@ -19,11 +19,13 @@
serviceid1_name1 serviceid1_name2 serviceid2_name1 serviceid2_name2 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的端口用于关闭或者与监管系统进行数据交互 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的池参数没配置该节点将自动创建一个。 transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。
threads 线程总数, 默认: <group>节点数*CPU核数*2 threads 线程总数, 默认: <group>节点数*CPU核数*2
bufferCapacity: ByteBuffer的初始化大小 默认: 32K; bufferCapacity: ByteBuffer的初始化大小 默认: 32K;
bufferPoolSize ByteBuffer池的大小默认: 线程总数*4 bufferPoolSize ByteBuffer池的大小默认: 线程总数*4
readTimeoutSeconds: TCP读取超时秒数, 默认为6秒 为0表示无超时限制 readTimeoutSeconds: TCP读取超时秒数, 默认为6秒 为0表示无超时限制
writeTimeoutSeconds: 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"/> <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内的进程必须在同一机房或局域网内 一个组包含多个node 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内
一个group节点对应一个 Transport 对象。 一个group节点对应一个 Transport 对象。
name: 服务组ID长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。 name: 服务组ID长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
protocol值范围UDP TCP 默认TCP protocol 值范围UDP TCP 默认TCP
subprotocol: 子协议,预留字段。默认值为空
注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息 就必须有group节点信息。 注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息 就必须有group节点信息。
--> -->
<group name="" protocol="TCP"> <group name="" protocol="TCP">
@@ -63,10 +105,10 @@
<!-- <!--
全局的数据源设置, 可以是CacheSource、DataSource JDBC的DataSource通常通过persistence.xml配置此处多用于CacheSource的配置 全局的数据源设置, 可以是CacheSource、DataSource JDBC的DataSource通常通过persistence.xml配置此处多用于CacheSource的配置
name: 资源名,用于依赖注入。 name: 资源名,用于依赖注入。
value类名必须是CacheSource或DataSource的子类且必须实现Service接口。如果是DataSource.class系统自动映射成DataJdbcSource.class value 类名必须是CacheSource或DataSource的子类且必须实现Service接口。如果是DataSource.class系统自动映射成DataJdbcSource.class
groups: 指定groups。 groups: 指定groups。
xxx: 其他属性与子节点通过Service.init方法传入的AnyValue获取。 xxx: 其他属性与子节点通过Service.init方法传入的AnyValue获取。
--> -->
<source name="redis" value="org.redkalex.cache.RedisCacheSource" xxx="16"> <source name="redis" value="org.redkalex.cache.RedisCacheSource" xxx="16">
<node addr="127.0.0.1" port="7070"/> <node addr="127.0.0.1" port="7070"/>
@@ -107,35 +149,35 @@
</resources> </resources>
<!-- <!--
protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCH。协议均使用TCP实现; WATCH服务只能存在一个。 protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCH。协议均使用TCP实现; WATCH服务只能存在一个。
name: 服务的名称用于监控识别一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线 name: 服务的名称用于监控识别一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线
host: 服务所占address 默认: 0.0.0.0 host: 服务所占address 默认: 0.0.0.0
port: required 服务所占端口 port: required 服务所占端口
root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root
lib: server额外的class目录 默认为${APP_HOME}/libs/*; lib: server额外的class目录 默认为${APP_HOME}/libs/*;
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开 excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8 charset: 文本编码, 默认: UTF-8
backlog: 默认10K backlog: 默认10K
threads 线程数, 默认: CPU核数*32 threads 线程数, 默认: CPU核数*2最小8个
maxconns最大连接数, 小于1表示无限制 默认: 0 maxconns 最大连接数, 小于1表示无限制 默认: 0
maxbody: request.body最大值 默认: 64K maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 TCP默认: 32K; (HTTP 2.0、WebSocket必须要16k以上); UDP默认: 1350B bufferCapacity: ByteBuffer的初始化大小 TCP默认: 32K; (HTTP 2.0、WebSocket必须要16k以上); UDP默认: 1350B
bufferPoolSize ByteBuffer池的大小默认: 线程数*4 bufferPoolSize ByteBuffer池的大小默认: 线程数*4
responsePoolSize Response池的大小默认: 线程数*2 responsePoolSize Response池的大小默认: 线程数*2
aliveTimeoutSeconds: KeepAlive读操作超时秒数 默认30 0表示永久不超时; -1表示禁止KeepAlive aliveTimeoutSeconds: KeepAlive读操作超时秒数 默认30 0表示永久不超时; -1表示禁止KeepAlive
readTimeoutSeconds: 读操作超时秒数, 默认0 表示永久不超时 readTimeoutSeconds: 读操作超时秒数, 默认0 表示永久不超时
writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时 writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时
netimpl: ProtocolServer的实现类。TCP情况下值可以是aio或nio默认值为aioUDP情况下值可以是bio默认值为bio netimpl: ProtocolServer的实现类。TCP情况下值可以是aio或nio默认值为aioUDP情况下值可以是bio默认值为bio
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null
--> -->
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib=""> <server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
<!-- <!--
【节点在<server>中唯一】 【节点在<server>中唯一】
value: 创建SSLContext的实现类, 可自定义必须是org.redkale.net.SSLCreator的子类 value: 创建SSLContext的实现类, 可自定义必须是org.redkale.net.SSLCreator的子类
clientauth: true/false/want clientauth: true/false/want
keystorepass: KEY密码 keystorepass: KEY密码
keystorefile: KEY文件 keystorefile: KEY文件
truststorepass: TRUST密码 truststorepass: TRUST密码
truststorefile: TRUST文件 truststorefile: TRUST文件
--> -->
@@ -146,11 +188,13 @@
在同一个进程中同一个name同一类型的Service将共用同一个实例 在同一个进程中同一个name同一类型的Service将共用同一个实例
autoload="true" 默认值. 自动加载classpath下所有的Service类 autoload="true" 默认值. 自动加载classpath下所有的Service类
autoload="false" 需要显著的指定Service类 autoload="false" 需要显著的指定Service类
mq: 所属的MQ管理器当 protocol == SNCP 时该值才有效, 存在该属性表示Service的SNCP协议采用消息总线代理模式
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
groups: 所属组的节点,多个节点值用;隔开如果配置文件中存在多个SNCP协议的Server节点需要显式指定group属性. groups: 所属组的节点,多个节点值用;隔开如果配置文件中存在多个SNCP协议的Server节点需要显式指定group属性.
当 protocol == SNCP 时 group表示当前Server与哪些节点组关联。 当 protocol == SNCP 时 group表示当前Server与哪些节点组关联。
当 protocol != SNCP 时 group只能是空或者一个group的节点值不能为多个节点值。 当 protocol != SNCP 时 group只能是空或者一个group的节点值不能为多个节点值。
特殊值"$cluster", 视为通过第三方服务注册发现管理工具来获取远程模式的ip端口信息
--> -->
<services autoload="true" includes="" excludes=""> <services autoload="true" includes="" excludes="">
@@ -174,8 +218,8 @@
加载所有的Filter服务; 加载所有的Filter服务;
autoload="true" 默认值. autoload="true" 默认值.
autoload="false" 需要显著的指定Filter类 autoload="false" 需要显著的指定Filter类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
--> -->
<filters autoload="true" includes="" excludes=""> <filters autoload="true" includes="" excludes="">
@@ -198,8 +242,9 @@
<!-- <!--
REST的核心配置项 REST的核心配置项
当Server为HTTP协议时, rest节点才有效。存在[rest]节点则Server启动时会加载REST服务, 节点可以多个,(WATCH协议不需要设置系统会自动生成) 当Server为HTTP协议时, rest节点才有效。存在[rest]节点则Server启动时会加载REST服务, 节点可以多个,(WATCH协议不需要设置系统会自动生成)
path: servlet的ContextPath前缀 默认为空 path: servlet的ContextPath前缀 默认为空 【注: 开启MQ时,该字段失效】
base: REST服务的BaseServlet必须是 org.redkale.net.http.HttpServlet 的子类,且子类必须标记@HttpUserType。 base: REST服务的BaseServlet必须是 org.redkale.net.http.HttpServlet 的子类,且子类必须标记@HttpUserType。
mq: 所属的MQ管理器, 存在该属性表示RestService的请求来自于消息总线 【注: 开启MQ时,path字段失效】
autoload默认值"true" 默认值. 加载当前server所能使用的Servce对象; autoload默认值"true" 默认值. 加载当前server所能使用的Servce对象;
includes当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
@@ -263,21 +308,21 @@
当Server为HTTP协议时ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点 当Server为HTTP协议时ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点
webroot: web资源的根目录, 默认取server节点中的root值 webroot: web资源的根目录, 默认取server节点中的root值
servlet: 静态资源HttpServlet的实现默认使用HttpResourceServlet servlet: 静态资源HttpServlet的实现默认使用HttpResourceServlet
index : 启始页默认值index.html index : 启始页默认值index.html
--> -->
<resource-servlet webroot="root" index="index.html"> <resource-servlet webroot="root" index="index.html">
<!-- <!--
【节点在<resource-servlet>中唯一】 【节点在<resource-servlet>中唯一】
资源缓存的配置, 默认存在一个含默认属性的caches节点 资源缓存的配置, 默认存在一个含默认属性的caches节点
limit: 资源缓存最大容量, 默认: 0, 为0表示不缓存 单位可以是B、K、M、G不区分大小写 limit: 资源缓存最大容量, 默认: 0, 为0表示不缓存 单位可以是B、K、M、G不区分大小写
lengthmax: 可缓存的文件大小上限, 默认: 1M超过1M的文件不会被缓存 lengthmax: 可缓存的文件大小上限, 默认: 1M超过1M的文件不会被缓存
watch: 是否监控缓存文件的变化, 默认为false不监控 watch: 是否监控缓存文件的变化, 默认为false不监控
--> -->
<cache limit="0M" lengthmax="1M" watch="false"/> <cache limit="0M" lengthmax="1M" watch="false"/>
<!-- <!--
支持类似nginx中的rewrite 目前只支持静态资源对静态资源的跳转。 支持类似nginx中的rewrite 目前只支持静态资源对静态资源的跳转。
type: 匹配的类型, 目前只支持location(匹配requestURI), 默认: location type: 匹配的类型, 目前只支持location(匹配requestURI), 默认: location
match: 匹配的正则表达式 match: 匹配的正则表达式
forward: 需跳转后的资源链接 forward: 需跳转后的资源链接
例如下面例子是将/xxx-yyy.html的页面全部跳转到/xxx.html 例如下面例子是将/xxx-yyy.html的页面全部跳转到/xxx.html
--> -->
@@ -285,11 +330,11 @@
</resource-servlet> </resource-servlet>
<!-- <!--
加载所有的Servlet服务; 加载所有的Servlet服务;
path: servlet的ContextPath前缀 默认为空 path: servlet的ContextPath前缀 默认为空
autoload="true" 默认值. 自动加载classpath下所有的Servlet类 autoload="true" 默认值. 自动加载classpath下所有的Servlet类
autoload="false" 需要显著的指定Service类 autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
--> -->
<servlets path="/pipes" autoload="true" includes="" 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.bson;
exports org.redkale.convert.ext; exports org.redkale.convert.ext;
exports org.redkale.convert.json; exports org.redkale.convert.json;
exports org.redkale.mq;
exports org.redkale.net; exports org.redkale.net;
exports org.redkale.net.http; exports org.redkale.net.http;
exports org.redkale.net.sncp; exports org.redkale.net.sncp;
@@ -30,5 +31,10 @@ module org.redkale {
exports org.redkale.util; exports org.redkale.util;
exports org.redkale.watch; 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 * 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> | * called in the following order: ( <code>visit</code> | <code>visitEnum</code> |
* <tt>visitAnnotation</tt> | <tt>visitArray</tt> )* <tt>visitEnd</tt>. * <code>visitAnnotation</code> | <code>visitArray</code> )* <code>visitEnd</code>.
* *
* @author Eric Bruneton * @author Eric Bruneton
* @author Eugene Kuleshov * @author Eugene Kuleshov
@@ -151,7 +151,7 @@ public abstract class AnnotationVisitor {
* @param desc * @param desc
* the class descriptor of the nested annotation class. * the class descriptor of the nested annotation class.
* @return a visitor to visit the actual nested annotation value, or * @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 * nested annotation. <i>The nested annotation value must be fully
* visited before calling other methods on this annotation * visited before calling other methods on this annotation
* visitor</i>. * visitor</i>.
@@ -172,7 +172,7 @@ public abstract class AnnotationVisitor {
* @param name * @param name
* the value name. * the value name.
* @return a visitor to visit the actual array value elements, or * @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 * values. The 'name' parameters passed to the methods of this
* visitor are ignored. <i>All the array values must be visited * visitor are ignored. <i>All the array values must be visited
* before calling other methods on this annotation visitor</i>. * before calling other methods on this annotation visitor</i>.

View File

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

View File

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

View File

@@ -332,7 +332,7 @@ public class ByteVector {
* automatically enlarged if necessary. * automatically enlarged if necessary.
* *
* @param b * @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. * null bytes into this byte vector.
* @param off * @param off
* index of the fist byte of b that must be copied. * 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 * {@link Type#getInternalName() getInternalName}). For interfaces, the
* super class is {@link Object}. * 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. * {@link Object} class.
* *
* @see ClassVisitor#visit(int, int, String, String, String, String[]) * @see ClassVisitor#visit(int, int, String, String, String, String[])
@@ -281,7 +281,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}). * {@link Type#getInternalName() getInternalName}).
* *
* @return the array of internal names for all implemented interfaces or * @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[]) * @see ClassVisitor#visit(int, int, String, String, String, String[])
*/ */
@@ -1932,7 +1932,7 @@ public class ClassReader {
* @param v * @param v
* start offset in {@link #b b} of the annotations to be read. * start offset in {@link #b b} of the annotations to be read.
* @param visible * @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. * runtime.
*/ */
private void readParameterAnnotations(final MethodVisitor mv, private void readParameterAnnotations(final MethodVisitor mv,
@@ -2474,9 +2474,9 @@ public class ClassReader {
* and the length of the attribute, are not taken into account * and the length of the attribute, are not taken into account
* here. * here.
* @param labels * @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. * 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. * attribute.
*/ */
private Attribute readAttribute(final Attribute[] attrs, final String type, 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 * 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> ] [ * the following order: <code>visit</code> [ <code>visitSource</code> ] [
* <tt>visitModule</tt> ][ <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> | * <code>visitModule</code> ][ <code>visitOuterClass</code> ] ( <code>visitAnnotation</code> |
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* ( * <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* (
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )* * <code>visitInnerClass</code> | <code>visitField</code> | <code>visitMethod</code> )*
* <tt>visitEnd</tt>. * <code>visitEnd</code>.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
@@ -120,18 +120,18 @@ public abstract class ClassVisitor {
* the internal name of the class (see * the internal name of the class (see
* {@link Type#getInternalName() getInternalName}). * {@link Type#getInternalName() getInternalName}).
* @param signature * @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 * is not a generic one, and does not extend or implement generic
* classes or interfaces. * classes or interfaces.
* @param superName * @param superName
* the internal of name of the super class (see * the internal of name of the super class (see
* {@link Type#getInternalName() getInternalName}). For * {@link Type#getInternalName() getInternalName}). For
* interfaces, the super class is {@link Object}. May be * 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 * @param interfaces
* the internal names of the class's interfaces (see * the internal names of the class's interfaces (see
* {@link Type#getInternalName() getInternalName}). May be * {@link Type#getInternalName() getInternalName}). May be
* <tt>null</tt>. * <code>null</code>.
*/ */
public void visit(int version, int access, String name, String signature, public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) { String superName, String[] interfaces) {
@@ -145,11 +145,11 @@ public abstract class ClassVisitor {
* *
* @param source * @param source
* the name of the source file from which the class was compiled. * the name of the source file from which the class was compiled.
* May be <tt>null</tt>. * May be <code>null</code>.
* @param debug * @param debug
* additional debug information to compute the correspondance * additional debug information to compute the correspondance
* between source and compiled elements of the class. May be * between source and compiled elements of the class. May be
* <tt>null</tt>. * <code>null</code>.
*/ */
public void visitSource(String source, String debug) { public void visitSource(String source, String debug) {
if (cv != null) { if (cv != null) {
@@ -166,7 +166,7 @@ public abstract class ClassVisitor {
* and {@code ACC_MANDATED}. * and {@code ACC_MANDATED}.
* @param version * @param version
* module version or null. * 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. * this visitor is not interested in visiting this module.
*/ */
public ModuleVisitor visitModule(String name, int access, String version) { 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. * internal name of the enclosing class of the class.
* @param name * @param name
* the name of the method that contains the class, or * 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. * enclosing class.
* @param desc * @param desc
* the descriptor of the method that contains the class, or * 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. * enclosing class.
*/ */
public void visitOuterClass(String owner, String name, String desc) { public void visitOuterClass(String owner, String name, String desc) {
@@ -206,8 +206,8 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -231,12 +231,12 @@ public abstract class ClassVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * 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 * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,
@@ -269,10 +269,10 @@ public abstract class ClassVisitor {
* @param outerName * @param outerName
* the internal name of the class to which the inner class * the internal name of the class to which the inner class
* belongs (see {@link Type#getInternalName() getInternalName}). * 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 * @param innerName
* the (simple) name of the inner class inside its enclosing * 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 * @param access
* the access flags of the inner class as originally declared in * the access flags of the inner class as originally declared in
* the enclosing class. * the enclosing class.
@@ -295,20 +295,20 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the field's descriptor (see {@link Type Type}). * the field's descriptor (see {@link Type Type}).
* @param signature * @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. * type does not use generic types.
* @param value * @param value
* the field's initial value. This parameter, which may be * 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 * must be an {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double} or a {@link String} (for <tt>int</tt>, * {@link Double} or a {@link String} (for <code>int</code>,
* <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields * <code>float</code>, <code>long</code> or <code>String</code> fields
* respectively). <i>This parameter is only used for static * respectively). <i>This parameter is only used for static
* fields</i>. Its value is ignored for non static fields, which * fields</i>. Its value is ignored for non static fields, which
* must be initialized through bytecode instructions in * must be initialized through bytecode instructions in
* constructors or methods. * constructors or methods.
* @return a visitor to visit field annotations and attributes, or * @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. * these annotations and attributes.
*/ */
public FieldVisitor visitField(int access, String name, String desc, 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 * 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. * i.e., it should not return a previously returned visitor.
* *
* @param access * @param access
@@ -333,14 +333,14 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the method's descriptor (see {@link Type Type}). * the method's descriptor (see {@link Type Type}).
* @param signature * @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 * parameters, return type and exceptions do not use generic
* types. * types.
* @param exceptions * @param exceptions
* the internal names of the method's exception classes (see * the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be * {@link Type#getInternalName() getInternalName}). May be
* <tt>null</tt>. * <code>null</code>.
* @return an object to visit the byte code of the method, or <tt>null</tt> * @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 * if this class visitor is not interested in visiting the code of
* this method. * 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 * 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 * 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 * 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> * map frames from scratch. This array associates to each index <code>i</code>
* the Item whose index is <tt>i</tt>. All Item objects stored in this array * 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 * 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 * 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 * Item from its value. Each Item stores an internal name in its
@@ -556,7 +556,7 @@ public class ClassWriter extends ClassVisitor {
private int compute; 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 * instructions, which need to be expanded into sequences of standard
* bytecode instructions. In this case the class is re-read and re-written * bytecode instructions. In this case the class is re-read and re-written
* with a ClassReader -> ClassWriter chain to perform this transformation. * with a ClassReader -> ClassWriter chain to perform this transformation.
@@ -1486,7 +1486,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc * @param desc
* the method's descriptor. * the method's descriptor.
* @param itf * @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. * @return a new or already existing method reference item.
*/ */
Item newMethodItem(final String owner, final String name, Item newMethodItem(final String owner, final String name,
@@ -1515,7 +1515,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc * @param desc
* the method's descriptor. * the method's descriptor.
* @param itf * @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. * @return the index of a new or already existing method reference item.
*/ */
public int newMethod(final String owner, final String name, public int newMethod(final String owner, final String name,
@@ -1778,7 +1778,7 @@ public class ClassWriter extends ClassVisitor {
* @param key * @param key
* a constant pool item. * a constant pool item.
* @return the constant pool's hash table item which is equal to the given * @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) { private Item get(final Item key) {
Item i = items[key.hashCode % items.length]; 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 * A visitor to visit a Java field. The methods of this class must be called in
* the following order: ( <tt>visitAnnotation</tt> | * the following order: ( <code>visitAnnotation</code> |
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* <tt>visitEnd</tt>. * <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* <code>visitEnd</code>.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
@@ -111,8 +111,8 @@ public abstract class FieldVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -132,12 +132,12 @@ public abstract class FieldVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * 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 * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,

View File

@@ -100,28 +100,28 @@ final class FieldWriter extends FieldVisitor {
private int value; 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; 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; 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; private AnnotationWriter tanns;
/** /**
* The runtime invisible type annotations of this field. May be * The runtime invisible type annotations of this field. May be
* <tt>null</tt>. * <code>null</code>.
*/ */
private AnnotationWriter itanns; 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; private Attribute attrs;
@@ -141,9 +141,9 @@ final class FieldWriter extends FieldVisitor {
* @param desc * @param desc
* the field's descriptor (see {@link Type}). * the field's descriptor (see {@link Type}).
* @param signature * @param signature
* the field's signature. May be <tt>null</tt>. * the field's signature. May be <code>null</code>.
* @param value * @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, FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) { 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 * 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. * the given label has been changed by this operation.
* *
* @param cw * @param cw
@@ -1413,7 +1413,7 @@ class Frame {
* @param edge * @param edge
* the kind of the {@link Edge} between this label and 'label'. * the kind of the {@link Edge} between this label and 'label'.
* See {@link Edge#info}. * 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. * changed by this operation.
*/ */
final boolean merge(final ClassWriter cw, final Frame frame, final int edge) { 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 * 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. * operation.
* *
* @param cw * @param cw
@@ -1522,7 +1522,7 @@ class Frame {
* an array of types. * an array of types.
* @param index * @param index
* the index of the type that must be merged in 'types'. * 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. * operation.
*/ */
private static boolean merge(final ClassWriter cw, int t, 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 * 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; String desc;

View File

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

View File

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

View File

@@ -218,41 +218,41 @@ class MethodWriter extends MethodVisitor {
int[] exceptions; 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; 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; 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; 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; private AnnotationWriter tanns;
/** /**
* The runtime invisible type annotations of this method. May be * The runtime invisible type annotations of this method. May be
* <tt>null</tt>. * <code>null</code>.
*/ */
private AnnotationWriter itanns; private AnnotationWriter itanns;
/** /**
* The runtime visible parameter annotations of this method. May be * The runtime visible parameter annotations of this method. May be
* <tt>null</tt>. * <code>null</code>.
*/ */
private AnnotationWriter[] panns; private AnnotationWriter[] panns;
/** /**
* The runtime invisible parameter annotations of this method. May be * The runtime invisible parameter annotations of this method. May be
* <tt>null</tt>. * <code>null</code>.
*/ */
private AnnotationWriter[] ipanns; private AnnotationWriter[] ipanns;
@@ -381,12 +381,12 @@ class MethodWriter extends MethodVisitor {
private int lastCodeOffset; 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; 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; 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 * 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 * stack size after the last visited instruction is equal to the
* {@link Label#inputStackTop beginStackSize} of the current basic block * {@link Label#inputStackTop beginStackSize} of the current basic block
* plus <tt>stackSize</tt>. * plus <code>stackSize</code>.
*/ */
private int stackSize; 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., * 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 * the true maximum stack size after the last visited instruction is equal
* to the {@link Label#inputStackTop beginStackSize} of the current basic * to the {@link Label#inputStackTop beginStackSize} of the current basic
* block plus <tt>stackSize</tt>. * block plus <code>stackSize</code>.
*/ */
private int maxStackSize; private int maxStackSize;
@@ -475,10 +475,10 @@ class MethodWriter extends MethodVisitor {
* @param desc * @param desc
* the method's descriptor (see {@link Type}). * the method's descriptor (see {@link Type}).
* @param signature * @param signature
* the method's signature. May be <tt>null</tt>. * the method's signature. May be <code>null</code>.
* @param exceptions * @param exceptions
* the internal names of the method's exceptions. May be * the internal names of the method's exceptions. May be
* <tt>null</tt>. * <code>null</code>.
* @param compute * @param compute
* Indicates what must be automatically computed (see #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 * 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> | * the following order: <code>visitMainClass</code> | ( <code>visitPackage</code> |
* <tt>visitRequire</tt> | <tt>visitExport</tt> | <tt>visitOpen</tt> | * <code>visitRequire</code> | <code>visitExport</code> | <code>visitOpen</code> |
* <tt>visitUse</tt> | <tt>visitProvide</tt> )* <tt>visitEnd</tt>. * <code>visitUse</code> | <code>visitProvide</code> )* <code>visitEnd</code>.
* *
* The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)}, * The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)},
* {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)} * {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)}
@@ -157,7 +157,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}. * {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can access to * @param modules the qualified names of the modules that can access to
* the public classes of the exported package or * the public classes of the exported package or
* <tt>null</tt>. * <code>null</code>.
*/ */
public void visitExport(String packaze, int access, String... modules) { public void visitExport(String packaze, int access, String... modules) {
if (mv != null) { if (mv != null) {
@@ -174,7 +174,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}. * {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can use deep * @param modules the qualified names of the modules that can use deep
* reflection to the classes of the open package or * reflection to the classes of the open package or
* <tt>null</tt>. * <code>null</code>.
*/ */
public void visitOpen(String packaze, int access, String... modules) { public void visitOpen(String packaze, int access, String... modules) {
if (mv != null) { if (mv != null) {

View File

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

View File

@@ -196,7 +196,7 @@ public final class ApiDocsService {
final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "apidoc.json")); final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "apidoc.json"));
out.write(json.getBytes("UTF-8")); out.write(json.getBytes("UTF-8"));
out.close(); out.close();
File doctemplate = new File(app.getConfPath(), "apidoc-template.html"); File doctemplate = new File(app.getConfPath().toString(), "apidoc-template.html");
InputStream in = null; InputStream in = null;
if (doctemplate.isFile() && doctemplate.canRead()) { if (doctemplate.isFile() && doctemplate.canRead()) {
in = new FileInputStream(doctemplate); in = new FileInputStream(doctemplate);

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import org.redkale.cluster.ClusterAgent;
import org.redkale.util.RedkaleClassLoader; import org.redkale.util.RedkaleClassLoader;
import org.redkale.net.TransportGroupInfo; import org.redkale.net.TransportGroupInfo;
import java.io.*; import java.io.*;
@@ -23,7 +24,8 @@ import javax.xml.parsers.*;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.Convert; import org.redkale.convert.Convert;
import org.redkale.convert.bson.BsonFactory; 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.*;
import org.redkale.net.http.MimeType; import org.redkale.net.http.MimeType;
import org.redkale.net.sncp.*; import org.redkale.net.sncp.*;
@@ -57,12 +59,17 @@ public final class Application {
public static final String RESNAME_APP_TIME = "APP_TIME"; public static final String RESNAME_APP_TIME = "APP_TIME";
/** /**
* 当前进程的根目录 类型String、File、Path * 当前进程的名称 类型String
*/
public static final String RESNAME_APP_NAME = "APP_NAME";
/**
* 当前进程的根目录, 类型String、File、Path、URI
*/ */
public static final String RESNAME_APP_HOME = "APP_HOME"; public static final String RESNAME_APP_HOME = "APP_HOME";
/** /**
* 当前进程的配置目录如果不是绝对路径则视为HOME目录下的相对路径 类型String、File、Path * 当前进程的配置目录如果不是绝对路径则视为HOME目录下的相对路径 类型String、File、Path、URI
*/ */
public static final String RESNAME_APP_CONF = "APP_CONF"; public static final String RESNAME_APP_CONF = "APP_CONF";
@@ -72,12 +79,12 @@ public final class Application {
public static final String RESNAME_APP_GRES = "APP_GRES"; 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"; 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; public static final String RESNAME_SERVER_RESFACTORY = Server.RESNAME_SERVER_RESFACTORY;
//本进程节点ID
final int nodeid;
//本进程节点ID
final String name;
//本地IP地址 //本地IP地址
final InetAddress localAddress; final InetSocketAddress localAddress;
//CacheSource 资源 //CacheSource 资源
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>(); final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
@@ -115,18 +128,29 @@ public final class Application {
//DataSource 资源 //DataSource 资源
final List<DataSource> dataSources = new CopyOnWriteArrayList<>(); final List<DataSource> dataSources = new CopyOnWriteArrayList<>();
//NodeServer 资源 //NodeServer 资源, 顺序必须是sncps, others, watchs
final List<NodeServer> servers = new CopyOnWriteArrayList<>(); final List<NodeServer> servers = new CopyOnWriteArrayList<>();
//SNCP传输端的TransportFactory, 注意: 只给SNCP使用 //SNCP传输端的TransportFactory, 注意: 只给SNCP使用
final TransportFactory sncpTransportFactory; final TransportFactory sncpTransportFactory;
//第三方服务发现管理接口
//@since 2.1.0
final ClusterAgent clusterAgent;
//MQ管理接口
//@since 2.1.0
final MessageAgent[] messageAgents;
//全局根ResourceFactory //全局根ResourceFactory
final ResourceFactory resourceFactory = ResourceFactory.root(); final ResourceFactory resourceFactory = ResourceFactory.root();
//服务配置项 //服务配置项
final AnyValue config; final AnyValue config;
//排除的jar路径
final String excludelibs;
//临时计数器 //临时计数器
CountDownLatch servicecdl; //会出现两次赋值 CountDownLatch servicecdl; //会出现两次赋值
@@ -143,7 +167,7 @@ public final class Application {
private final File home; private final File home;
//配置文件目录 //配置文件目录
private final File confPath; private final URI confPath;
//日志 //日志
private final Logger logger; private final Logger logger;
@@ -176,44 +200,46 @@ public final class Application {
this.resourceFactory.register(RESNAME_APP_TIME, long.class, this.startTime); this.resourceFactory.register(RESNAME_APP_TIME, long.class, this.startTime);
this.resourceFactory.register(RESNAME_APP_HOME, Path.class, root.toPath()); this.resourceFactory.register(RESNAME_APP_HOME, Path.class, root.toPath());
this.resourceFactory.register(RESNAME_APP_HOME, File.class, root); this.resourceFactory.register(RESNAME_APP_HOME, File.class, root);
this.resourceFactory.register(RESNAME_APP_HOME, URI.class, root.toURI());
try { try {
this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath()); this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath());
this.home = root.getCanonicalFile(); this.home = root.getCanonicalFile();
String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf"); String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf");
if (confsubpath.charAt(0) == '/' || confsubpath.indexOf(':') > 0) { if (confsubpath.contains("://")) {
this.confPath = new File(confsubpath).getCanonicalFile(); this.confPath = new URI(confsubpath);
} else if (confsubpath.charAt(0) == '/' || confsubpath.indexOf(':') > 0) {
this.confPath = new File(confsubpath).getCanonicalFile().toURI();
} else { } else {
this.confPath = new File(this.home, confsubpath).getCanonicalFile(); this.confPath = new File(this.home, confsubpath).getCanonicalFile().toURI();
} }
} catch (IOException e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
String localaddr = config.getValue("address", "").trim(); String localaddr = config.getValue("address", "").trim();
this.localAddress = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, config.getIntValue("port")).getAddress(); InetAddress addr = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, config.getIntValue("port")).getAddress();
this.resourceFactory.register(RESNAME_APP_ADDR, this.localAddress.getHostAddress()); this.localAddress = new InetSocketAddress(addr, config.getIntValue("port"));
this.resourceFactory.register(RESNAME_APP_ADDR, InetAddress.class, this.localAddress); 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(); int nid = config.getIntValue("nodeid", 0);
if (node.isEmpty()) { this.nodeid = nid;
StringBuilder sb = new StringBuilder(); this.resourceFactory.register(RESNAME_APP_NODEID, nid);
byte[] bs = this.localAddress.getAddress(); System.setProperty(RESNAME_APP_NODEID, "" + nid);
int v1 = bs[bs.length - 2] & 0xff; }
int v2 = bs[bs.length - 1] & 0xff; {
if (v1 <= 0xf) sb.append('0'); this.name = checkName(config.getValue("name", ""));
sb.append(Integer.toHexString(v1)); this.resourceFactory.register(RESNAME_APP_NAME, name);
if (v2 <= 0xf) sb.append('0'); System.setProperty(RESNAME_APP_NAME, name);
sb.append(Integer.toHexString(v2));
node = sb.toString();
}
this.resourceFactory.register(RESNAME_APP_NODE, node);
System.setProperty(RESNAME_APP_NODE, node);
} }
//以下是初始化日志配置 //以下是初始化日志配置
final File logconf = new File(confPath, "logging.properties"); final URI logConfURI = "file".equals(confPath.getScheme()) ? new File(new File(confPath), "logging.properties").toURI()
if (logconf.isFile() && logconf.canRead()) { : URI.create(confPath.toString() + (confPath.toString().endsWith("/") ? "" : "/") + "logging.properties");
if (!"file".equals(confPath.getScheme()) || (new File(logConfURI).isFile() && new File(logConfURI).canRead())) {
try { try {
final String rootpath = root.getCanonicalPath().replace('\\', '/'); final String rootpath = root.getCanonicalPath().replace('\\', '/');
FileInputStream fin = new FileInputStream(logconf); InputStream fin = logConfURI.toURL().openStream();
Properties properties = new Properties(); Properties properties = new Properties();
properties.load(fin); properties.load(fin);
fin.close(); fin.close();
@@ -267,6 +293,9 @@ public final class Application {
AsynchronousChannelGroup transportGroup = null; AsynchronousChannelGroup transportGroup = null;
final AnyValue resources = config.getAnyValue("resources"); final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null; TransportStrategy strategy = null;
String excludelib0 = null;
ClusterAgent cluster = null;
MessageAgent[] mqs = null;
int bufferCapacity = 32 * 1024; int bufferCapacity = 32 * 1024;
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8; int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8;
int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS; int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
@@ -274,6 +303,8 @@ public final class Application {
AtomicLong createBufferCounter = new AtomicLong(); AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong(); AtomicLong cycleBufferCounter = new AtomicLong();
if (resources != null) { if (resources != null) {
AnyValue excludelibConf = resources.getAnyValue("excludelibs");
if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value");
AnyValue transportConf = resources.getAnyValue("transport"); AnyValue transportConf = resources.getAnyValue("transport");
int groupsize = resources.getAnyValues("group").length; int groupsize = resources.getAnyValues("group").length;
if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue(); if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
@@ -301,7 +332,7 @@ public final class Application {
transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> { transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r); Thread t = new Thread(r);
t.setDaemon(true); t.setDaemon(true);
t.setName("Transport-Thread-" + counter.incrementAndGet()); t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet());
return t; return t;
}); });
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1); transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
@@ -310,13 +341,87 @@ public final class Application {
} }
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity / 1024 + "K; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";"); 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) { if (transportGroup == null) {
final AtomicInteger counter = new AtomicInteger(); final AtomicInteger counter = new AtomicInteger();
transportExec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 8, (Runnable r) -> { transportExec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 8, (Runnable r) -> {
Thread t = new Thread(r); Thread t = new Thread(r);
t.setDaemon(true); t.setDaemon(true);
t.setName("Transport-Thread-" + counter.incrementAndGet()); t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet());
return t; return t;
}); });
try { try {
@@ -334,15 +439,29 @@ public final class Application {
return true; return true;
}); });
} }
this.excludelibs = excludelib0;
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSeconds, writeTimeoutSeconds, strategy); 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")) DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.pool.maxconns", "100"))
.addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30")) .addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.ping.interval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.checkinterval", "30")); .addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.check.interval", "30"));
this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining()); this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining());
this.clusterAgent = cluster;
this.messageAgents = mqs;
Thread.currentThread().setContextClassLoader(this.classLoader); Thread.currentThread().setContextClassLoader(this.classLoader);
this.serverClassLoader = new RedkaleClassLoader(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() { public ResourceFactory getResourceFactory() {
return resourceFactory; return resourceFactory;
} }
@@ -351,6 +470,22 @@ public final class Application {
return sncpTransportFactory; 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() { public RedkaleClassLoader getClassLoader() {
return classLoader; return classLoader;
} }
@@ -371,11 +506,19 @@ public final class Application {
return new ArrayList<>(cacheSources); return new ArrayList<>(cacheSources);
} }
public int getNodeid() {
return nodeid;
}
public String getName() {
return name;
}
public File getHome() { public File getHome() {
return home; return home;
} }
public File getConfPath() { public URI getConfPath() {
return confPath; return confPath;
} }
@@ -398,10 +541,14 @@ public final class Application {
System.setProperty("convert.bson.writer.buffer.defsize", "4096"); System.setProperty("convert.bson.writer.buffer.defsize", "4096");
System.setProperty("convert.json.writer.buffer.defsize", "4096"); System.setProperty("convert.json.writer.buffer.defsize", "4096");
File persist = new File(this.confPath, "persistence.xml"); final String confpath = this.confPath.toString();
final String homepath = this.home.getCanonicalPath(); final String homepath = this.home.getCanonicalPath();
final String confpath = this.confPath.getCanonicalPath(); if ("file".equals(this.confPath.getScheme())) {
if (persist.isFile()) System.setProperty(DataSources.DATASOURCE_CONFPATH, persist.getCanonicalPath()); File persist = new File(new File(confPath), "persistence.xml");
if (persist.isFile()) System.setProperty(DataSources.DATASOURCE_CONFPATH, persist.getCanonicalPath());
} else {
System.setProperty(DataSources.DATASOURCE_CONFPATH, confpath + (confpath.endsWith("/") ? "" : "/") + "persistence.xml");
}
String pidstr = ""; String pidstr = "";
try { //JDK 9+ try { //JDK 9+
Class phclass = Class.forName("java.lang.ProcessHandle"); Class phclass = Class.forName("java.lang.ProcessHandle");
@@ -410,7 +557,7 @@ public final class Application {
pidstr = "APP_PID = " + pid + "\r\n"; pidstr = "APP_PID = " + pid + "\r\n";
} catch (Throwable t) { } 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); String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? confpath : (lib + ";" + confpath); lib = lib.isEmpty() ? confpath : (lib + ";" + confpath);
Server.loadLib(classLoader, logger, lib); Server.loadLib(classLoader, logger, lib);
@@ -425,13 +572,17 @@ public final class Application {
if (dfloads != null) { if (dfloads != null) {
for (String dfload : dfloads.split(";")) { for (String dfload : dfloads.split(";")) {
if (dfload.trim().isEmpty()) continue; if (dfload.trim().isEmpty()) continue;
final File df = (dfload.indexOf('/') < 0) ? new File(confPath, "/" + dfload) : new File(dfload); final URI df = (dfload.indexOf('/') < 0) ? URI.create(confpath + (confpath.endsWith("/") ? "" : "/") + dfload) : new File(dfload).toURI();
if (df.isFile()) { if (!"file".equals(df.getScheme()) || new File(df).isFile()) {
Properties ps = new Properties(); Properties ps = new Properties();
InputStream in = new FileInputStream(df); try {
ps.load(in); InputStream in = df.toURL().openStream();
in.close(); ps.load(in);
ps.forEach((x, y) -> resourceFactory.register("property." + x, y.toString().replace("${APP_HOME}", homepath))); in.close();
ps.forEach((x, y) -> resourceFactory.register("property." + x, y.toString().replace("${APP_HOME}", homepath)));
} catch (Exception e) {
logger.log(Level.WARNING, "load properties(" + dfload + ") error", e);
}
} }
} }
} }
@@ -522,6 +673,41 @@ public final class Application {
}, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class); }, 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(); initResources();
} }
@@ -536,7 +722,7 @@ public final class Application {
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) { if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("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")) { for (AnyValue node : conf.getAnyValues("node")) {
final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port")); final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
ginfo.putAddress(addr); ginfo.putAddress(addr);
@@ -558,9 +744,10 @@ public final class Application {
} }
public void restoreConfig() throws IOException { public void restoreConfig() throws IOException {
if (!"file".equals(this.confPath.getScheme())) return;
synchronized (this) { synchronized (this) {
File confFile = new File(this.confPath, "application.xml"); File confFile = new File(this.confPath.toString(), "application.xml");
confFile.renameTo(new File(this.confPath, "application_" + String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", System.currentTimeMillis()) + ".xml")); confFile.renameTo(new File(this.confPath.toString(), "application_" + String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", System.currentTimeMillis()) + ".xml"));
final PrintStream ps = new PrintStream(new FileOutputStream(confFile)); final PrintStream ps = new PrintStream(new FileOutputStream(confFile));
ps.append(config.toXML("application")); ps.append(config.toXML("application"));
ps.close(); ps.close();
@@ -571,7 +758,7 @@ public final class Application {
final Application application = this; final Application application = this;
new Thread() { new Thread() {
{ {
setName("Application-Control-Thread"); setName("Redkale-Application-SelfServer-Thread");
} }
@Override @Override
@@ -589,7 +776,8 @@ public final class Application {
buffer.flip(); buffer.flip();
byte[] bytes = new byte[buffer.remaining()]; byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes); buffer.get(bytes);
if ("SHUTDOWN".equalsIgnoreCase(new String(bytes))) { final String cmd = new String(bytes);
if ("SHUTDOWN".equalsIgnoreCase(cmd)) {
try { try {
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
logger.info(application.getClass().getSimpleName() + " shutdowning"); logger.info(application.getClass().getSimpleName() + " shutdowning");
@@ -609,7 +797,7 @@ public final class Application {
buffer.flip(); buffer.flip();
channel.send(buffer, address); channel.send(buffer, address);
} }
} else if ("APIDOC".equalsIgnoreCase(new String(bytes))) { } else if ("APIDOC".equalsIgnoreCase(cmd)) {
try { try {
new ApiDocsService(application).run(); new ApiDocsService(application).run();
buffer.clear(); buffer.clear();
@@ -622,6 +810,16 @@ public final class Application {
buffer.flip(); buffer.flip();
channel.send(buffer, address); 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) { } catch (Exception e) {
@@ -632,10 +830,10 @@ public final class Application {
}.start(); }.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(); final DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true); 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); ByteBuffer buffer = ByteBuffer.allocate(128);
buffer.put(command.getBytes()); buffer.put(command.getBytes());
buffer.flip(); buffer.flip();
@@ -648,7 +846,7 @@ public final class Application {
byte[] bytes = new byte[buffer.remaining()]; byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes); buffer.get(bytes);
channel.close(); channel.close();
logger.info("Send: " + command + ", Reply: " + new String(bytes)); if (logger != null) logger.info("Send: " + command + ", Reply: " + new String(bytes));
Thread.sleep(1000); Thread.sleep(1000);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof PortUnreachableException) { if (e instanceof PortUnreachableException) {
@@ -657,7 +855,7 @@ public final class Application {
application.init(); application.init();
application.start(); application.start();
new ApiDocsService(application).run(); new ApiDocsService(application).run();
logger.info("APIDOC OK"); if (logger != null) logger.info("APIDOC OK");
return; return;
} }
} }
@@ -666,6 +864,9 @@ public final class Application {
} }
public void start() throws Exception { public void start() throws Exception {
if (!singletonrun && this.clusterAgent != null) {
this.clusterAgent.register(this);
}
final AnyValue[] entrys = config.getAnyValues("server"); final AnyValue[] entrys = config.getAnyValues("server");
CountDownLatch timecd = new CountDownLatch(entrys.length); CountDownLatch timecd = new CountDownLatch(entrys.length);
final List<AnyValue> sncps = new ArrayList<>(); final List<AnyValue> sncps = new ArrayList<>();
@@ -687,12 +888,40 @@ public final class Application {
runServers(timecd, others); runServers(timecd, others);
runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务 runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务
timecd.await(); 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) signalHandle();
//if (!singletonrun) clearPersistData(); //if (!singletonrun) clearPersistData();
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n"); logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n");
if (!singletonrun) this.serversLatch.await(); 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() { // private void clearPersistData() {
// File cachedir = new File(home, "cache"); // File cachedir = new File(home, "cache");
// if (!cachedir.isDirectory()) return; // if (!cachedir.isDirectory()) return;
@@ -742,7 +971,7 @@ public final class Application {
Thread thread = new Thread() { Thread thread = new Thread() {
{ {
String host = serconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "*"); String host = serconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "*");
setName(serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread"); setName("Redkale-" + serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread");
this.setDaemon(true); this.setDaemon(true);
} }
@@ -771,20 +1000,18 @@ public final class Application {
synchronized (nodeClasses) { synchronized (nodeClasses) {
if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS
ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null); 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(); final Set<FilterEntry<NodeServer>> entrys = profilter.getFilterEntrys();
for (FilterEntry<NodeServer> entry : entrys) { for (FilterEntry<NodeServer> entry : entrys) {
final Class<? extends NodeServer> type = entry.getType(); final Class<? extends NodeServer> type = entry.getType();
NodeProtocol pros = type.getAnnotation(NodeProtocol.class); NodeProtocol pros = type.getAnnotation(NodeProtocol.class);
for (String p : pros.value()) { String p = pros.value().toUpperCase();
p = p.toUpperCase(); if ("SNCP".equals(p) || "HTTP".equals(p)) continue;
if ("SNCP".equals(p) || "HTTP".equals(p)) continue; final Class<? extends NodeServer> old = nodeClasses.get(p);
final Class<? extends NodeServer> old = nodeClasses.get(p); if (old != null && old != type) {
if (old != null && old != type) { throw new RuntimeException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")");
throw new RuntimeException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")");
}
nodeClasses.put(p, type);
} }
nodeClasses.put(p, type);
} }
} }
} }
@@ -819,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 { 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"); if (serviceClass == null) throw new IllegalArgumentException("serviceClass is null");
final Application application = Application.create(true); 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) { if (extServiceClasses != null && extServiceClasses.length > 0) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (Class clazz : extServiceClasses) { for (Class clazz : extServiceClasses) {
if (sb.length() > 0) sb.append(','); if (sb.length() > 0) sb.append(',');
sb.append(clazz.getName()); sb.append(clazz.getName());
} }
System.setProperty("red" + "kale-singleton-extserviceclasses", sb.toString()); System.setProperty("red" + "kale.singleton.extserviceclasses", sb.toString());
} }
application.init(); application.init();
application.start(); application.start();
@@ -840,30 +1067,34 @@ public final class Application {
} }
public static Application create(final boolean singleton) throws IOException { 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('\\', '/'); final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/');
System.setProperty(RESNAME_APP_HOME, home); System.setProperty(RESNAME_APP_HOME, home);
String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf"); String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf");
File appfile; URI appconf;
if (confsubpath.charAt(0) == '/' || confsubpath.indexOf(':') > 0) { if (confsubpath.contains("://")) {
appfile = new File(confsubpath).getCanonicalFile(); appconf = URI.create(confsubpath + (confsubpath.endsWith("/") ? "" : "/") + "application.xml");
} else if (confsubpath.charAt(0) == '/' || confsubpath.indexOf(':') > 0) {
appconf = new File(confsubpath, "application.xml").toURI();
} else { } else {
appfile = new File(new File(home), confsubpath); appconf = new File(new File(home, confsubpath), "application.xml").toURI();
} }
File appconf = new File(appfile, "application.xml"); return load(appconf.toURL().openStream());
return new Application(singleton, load(new FileInputStream(appconf)));
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Utility.midnight(); //先初始化一下Utility Utility.midnight(); //先初始化一下Utility
Thread.currentThread().setName("Redkale-Application-Main-Thread");
//运行主程序 //运行主程序
final Application application = Application.create(false);
if (System.getProperty("CMD") != null) { if (System.getProperty("CMD") != null) {
application.sendCommand(System.getProperty("CMD")); AnyValue config = loadAppXml();
return; Application.sendCommand(null, config.getIntValue("port"), System.getProperty("CMD"));
} else if (System.getProperty("SHUTDOWN") != null) { //兼容旧接口
application.sendCommand("SHUTDOWN");
return; return;
} }
final Application application = Application.create(false);
application.init(); application.init();
application.startSelfServer(); application.startSelfServer();
try { try {
@@ -887,6 +1118,17 @@ public final class Application {
return null; 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 { public void shutdown() throws Exception {
for (ApplicationListener listener : this.listeners) { for (ApplicationListener listener : this.listeners) {
try { try {
@@ -895,8 +1137,26 @@ public final class Application {
logger.log(Level.WARNING, listener.getClass() + " preShutdown erroneous", e); logger.log(Level.WARNING, listener.getClass() + " preShutdown erroneous", e);
} }
} }
List<NodeServer> localServers = new ArrayList<>(servers); //顺序sncps, others, watchs
servers.stream().forEach((server) -> { 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 { try {
server.shutdown(); server.shutdown();
} catch (Exception t) { } catch (Exception t) {
@@ -905,7 +1165,16 @@ public final class Application {
serversLatch.countDown(); 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) { for (DataSource source : dataSources) {
if (source == null) continue; if (source == null) continue;
try { try {

View File

@@ -175,8 +175,9 @@ public final class ClassFilter<T> {
} }
if (!r && ors != null) { if (!r && ors != null) {
for (ClassFilter filter : ors) { for (ClassFilter filter : ors) {
if (filter.accept(property, clazzname)) { if (filter.accept(filter.conf, clazzname)) {
cf = filter; cf = filter;
property = cf.conf;
break; break;
} }
} }
@@ -189,11 +190,11 @@ public final class ClassFilter<T> {
if (property == null) { if (property == null) {
property = cf.conf; property = cf.conf;
} else if (property instanceof DefaultAnyValue) { } else if (property instanceof DefaultAnyValue) {
((DefaultAnyValue) property).addAll(cf.conf); ((DefaultAnyValue) property).addAllStringSet(cf.conf);
} else { } else {
DefaultAnyValue dav = new DefaultAnyValue(); DefaultAnyValue dav = new DefaultAnyValue();
dav.addAll(property); dav.addAllStringSet(property);
dav.addAll(cf.conf); dav.addAllStringSet(cf.conf);
property = dav; property = dav;
} }
} }
@@ -207,7 +208,9 @@ public final class ClassFilter<T> {
} catch (Throwable cfe) { } catch (Throwable cfe) {
if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.") if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
&& !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.") && !clazzname.startsWith("META-INF") && !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); 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) { if (!r && ors != null) {
for (ClassFilter filter : ors) { for (ClassFilter filter : ors) {
if (filter.accept(property, classname)) return true; if (filter.accept(filter.conf, classname)) return true;
} }
} }
return r; return r;
@@ -405,7 +408,7 @@ public final class ClassFilter<T> {
str = str.trim(); str = str.trim();
if (str.endsWith(";")) str = str.substring(0, str.length() - 1); 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.property = property;
this.autoload = autoload; this.autoload = autoload;
this.expect = expect; this.expect = expect;
@@ -415,7 +418,7 @@ public final class ClassFilter<T> {
@Override @Override
public String toString() { public String toString() {
return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName() 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 @Override
@@ -442,6 +445,10 @@ public final class ClassFilter<T> {
return property; return property;
} }
public boolean containsGroup(String group) {
return groups != null && groups.contains(group);
}
public boolean isEmptyGroups() { public boolean isEmptyGroups() {
return groups == null || groups.isEmpty(); return groups == null || groups.isEmpty();
} }
@@ -493,7 +500,7 @@ public final class ClassFilter<T> {
boolean skip = false; boolean skip = false;
for (Pattern p : excludePatterns) { for (Pattern p : excludePatterns) {
if (p.matcher(url.toString()).matches()) { if (p.matcher(url.toString()).matches()) {
skip = false; skip = true;
break; break;
} }
} }

View File

@@ -46,19 +46,19 @@ public class LogFileHandler extends Handler {
private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s\r\n%5$s%6$s\r\n"; private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s\r\n%5$s%6$s\r\n";
@Override @Override
public String format(LogRecord record) { public String format(LogRecord log) {
String source; String source;
if (record.getSourceClassName() != null) { if (log.getSourceClassName() != null) {
source = record.getSourceClassName(); source = log.getSourceClassName();
if (record.getSourceMethodName() != null) { if (log.getSourceMethodName() != null) {
source += " " + record.getSourceMethodName(); source += " " + log.getSourceMethodName();
} }
} else { } else {
source = record.getLoggerName(); source = log.getLoggerName();
} }
String message = formatMessage(record); String message = formatMessage(log);
String throwable = ""; String throwable = "";
if (record.getThrown() != null) { if (log.getThrown() != null) {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw) { PrintWriter pw = new PrintWriter(sw) {
@Override @Override
@@ -67,22 +67,22 @@ public class LogFileHandler extends Handler {
} }
}; };
pw.println(); pw.println();
record.getThrown().printStackTrace(pw); log.getThrown().printStackTrace(pw);
pw.close(); pw.close();
throwable = sw.toString(); throwable = sw.toString();
} }
return String.format(format, return String.format(format,
System.currentTimeMillis(), System.currentTimeMillis(),
source, source,
record.getLoggerName(), log.getLoggerName(),
record.getLevel().getName(), log.getLevel().getName(),
message, message,
throwable); throwable);
} }
} }
protected final LinkedBlockingQueue<LogRecord> records = new LinkedBlockingQueue(); protected final LinkedBlockingQueue<LogRecord> logqueue = new LinkedBlockingQueue();
private String pattern; private String pattern;
@@ -133,7 +133,7 @@ public class LogFileHandler extends Handler {
} }
private void open() { private void open() {
final String name = "Logging-" + getClass().getSimpleName() + "-Thread"; final String name = "Redkale-Logging-" + getClass().getSimpleName() + "-Thread";
new Thread() { new Thread() {
{ {
setName(name); setName(name);
@@ -144,9 +144,9 @@ public class LogFileHandler extends Handler {
public void run() { public void run() {
while (true) { while (true) {
try { try {
LogRecord record = records.take(); LogRecord log = logqueue.take();
final boolean bigger = (limit > 0 && limit <= loglength.get()); final boolean bigger = (limit > 0 && limit <= loglength.get());
final boolean changeday = tomorrow <= record.getMillis(); final boolean changeday = tomorrow <= log.getMillis();
if (bigger || changeday) { if (bigger || changeday) {
updateTomorrow(); updateTomorrow();
if (logstream != null) { if (logstream != null) {
@@ -193,12 +193,12 @@ public class LogFileHandler extends Handler {
logunusualstream = new FileOutputStream(logunusualfile, append); logunusualstream = new FileOutputStream(logunusualfile, append);
} }
//----------------------写日志------------------------- //----------------------写日志-------------------------
String message = getFormatter().format(record); String message = getFormatter().format(log);
String encoding = getEncoding(); String encoding = getEncoding();
byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding); byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding);
logstream.write(bytes); logstream.write(bytes);
loglength.addAndGet(bytes.length); loglength.addAndGet(bytes.length);
if (unusual != null && (record.getLevel() == Level.WARNING || record.getLevel() == Level.SEVERE)) { if (unusual != null && (log.getLevel() == Level.WARNING || log.getLevel() == Level.SEVERE)) {
logunusualstream.write(bytes); logunusualstream.write(bytes);
logunusuallength.addAndGet(bytes.length); logunusuallength.addAndGet(bytes.length);
} }
@@ -310,21 +310,21 @@ public class LogFileHandler extends Handler {
} }
@Override @Override
public void publish(LogRecord record) { public void publish(LogRecord log) {
final String sourceClassName = record.getSourceClassName(); final String sourceClassName = log.getSourceClassName();
if (sourceClassName == null || true) { if (sourceClassName == null || true) {
StackTraceElement[] ses = new Throwable().getStackTrace(); StackTraceElement[] ses = new Throwable().getStackTrace();
for (int i = 2; i < ses.length; i++) { for (int i = 2; i < ses.length; i++) {
if (ses[i].getClassName().startsWith("java.util.logging")) continue; if (ses[i].getClassName().startsWith("java.util.logging")) continue;
record.setSourceClassName('[' + Thread.currentThread().getName() + "] " + ses[i].getClassName()); log.setSourceClassName('[' + Thread.currentThread().getName() + "] " + ses[i].getClassName());
record.setSourceMethodName(ses[i].getMethodName()); log.setSourceMethodName(ses[i].getMethodName());
break; break;
} }
} else { } else {
record.setSourceClassName('[' + Thread.currentThread().getName() + "] " + sourceClassName); log.setSourceClassName('[' + Thread.currentThread().getName() + "] " + sourceClassName);
} }
if (denyreg != null && denyreg.matcher(record.getMessage()).find()) return; if (denyreg != null && denyreg.matcher(log.getMessage()).find()) return;
records.offer(record); logqueue.offer(log);
} }
@Override @Override

View File

@@ -14,6 +14,8 @@ import java.util.logging.Level;
import javax.annotation.*; import javax.annotation.*;
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR; import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.cluster.ClusterAgent;
import org.redkale.mq.MessageAgent;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.http.*; import org.redkale.net.http.*;
import org.redkale.net.sncp.Sncp; import org.redkale.net.sncp.Sncp;
@@ -30,7 +32,7 @@ import org.redkale.watch.*;
* *
* @author zhangjx * @author zhangjx
*/ */
@NodeProtocol({"HTTP"}) @NodeProtocol("HTTP")
public class NodeHttpServer extends NodeServer { public class NodeHttpServer extends NodeServer {
protected final boolean rest; //是否加载REST服务 为true加载rest节点信息并将所有可REST化的Service生成RestServlet 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)); resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class));
} }
if (nodeService == null) { 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); regFactory.register(resourceName, WebSocketNode.class, nodeService);
} }
resourceFactory.inject(nodeService, self); resourceFactory.inject(nodeService, self);
@@ -136,7 +146,7 @@ public class NodeHttpServer extends NodeServer {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter<? extends Filter> classFilter) throws Exception { protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter<? extends Filter> classFilter) throws Exception {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; 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()); List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
for (FilterEntry<? extends Filter> en : list) { for (FilterEntry<? extends Filter> en : list) {
Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType(); Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType();
@@ -145,7 +155,7 @@ public class NodeHttpServer extends NodeServer {
resourceFactory.inject(filter, this); resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty(); DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.httpServer.addHttpFilter(filter, filterConf); 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()); 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(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0; if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
final String prefix = 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<FilterEntry<? extends Servlet>> list = new ArrayList(servletFilter.getFilterEntrys());
list.sort((FilterEntry<? extends Servlet> o1, FilterEntry<? extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode list.sort((FilterEntry<? extends Servlet> o1, FilterEntry<? extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode
boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType()); boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType());
@@ -176,7 +186,11 @@ public class NodeHttpServer extends NodeServer {
Class<HttpServlet> clazz = (Class<HttpServlet>) en.getType(); Class<HttpServlet> clazz = (Class<HttpServlet>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue; if (Modifier.isAbstract(clazz.getModifiers())) continue;
WebServlet ws = clazz.getAnnotation(WebServlet.class); 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(); final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(servlet, this); resourceFactory.inject(servlet, this);
final String[] mappings = ws.value(); final String[] mappings = ws.value();
@@ -197,7 +211,7 @@ public class NodeHttpServer extends NodeServer {
if (as.getKey().length() > max) max = as.getKey().length(); if (as.getKey().length() > max) max = as.getKey().length();
} }
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { 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++) { for (int i = 0; i < max - as.getKey().length(); i++) {
sb.append(' '); sb.append(' ');
} }
@@ -222,11 +236,18 @@ public class NodeHttpServer extends NodeServer {
String prefix0 = restConf.getValue("path", ""); 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(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0; 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<>(); 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); final boolean autoload = restConf.getBoolValue("autoload", true);
{ //加载RestService { //加载RestService
String userTypeStr = restConf.getValue("usertype"); String userTypeStr = restConf.getValue("usertype");
@@ -269,7 +290,9 @@ public class NodeHttpServer extends NodeServer {
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = ""; if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this); 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) { if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) { for (int i = 0; i < mappings.length; i++) {
@@ -322,13 +345,13 @@ public class NodeHttpServer extends NodeServer {
return; return;
} }
restedObjects.add(stype); //避免重复创建Rest对象 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 if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
String prefix2 = prefix; String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = ""; if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this); 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) { if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) { 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) { if (ss != null && !ss.isEmpty() && sb != null) {
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey())); 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(); if (as.getKey().length() > max) max = as.getKey().length();
} }
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { 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++) { for (int i = 0; i < max - as.getKey().length(); i++) {
sb.append(' '); sb.append(' ');
} }
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR); 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 @Documented
public @interface NodeProtocol { public @interface NodeProtocol {
String[] value(); String value();
} }

View File

@@ -5,6 +5,8 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import org.redkale.cluster.ClusterAgent;
import org.redkale.mq.MessageAgent;
import org.redkale.util.RedkaleClassLoader; import org.redkale.util.RedkaleClassLoader;
import java.io.*; import java.io.*;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
@@ -27,7 +29,6 @@ import org.redkale.net.sncp.*;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.source.*; import org.redkale.source.*;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
/** /**
* Server节点的初始化配置类 * Server节点的初始化配置类
@@ -67,11 +68,13 @@ public abstract class NodeServer {
private InetSocketAddress sncpAddress; private InetSocketAddress sncpAddress;
//加载Service时的处理函数 //加载Service时的处理函数
protected Consumer<Service> consumer; protected BiConsumer<MessageAgent, Service> consumer;
//server节点的配置 //server节点的配置
protected AnyValue serverConf; protected AnyValue serverConf;
protected final String threadName;
//加载server节点后的拦截器 //加载server节点后的拦截器
protected NodeInterceptor interceptor; protected NodeInterceptor interceptor;
@@ -84,11 +87,20 @@ public abstract class NodeServer {
//远程模式的Service对象集合 //远程模式的Service对象集合
protected final Set<Service> remoteServices = new LinkedHashSet<>(); 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 maxClassNameLength = 0;
private volatile int maxNameLength = 0; private volatile int maxNameLength = 0;
public NodeServer(Application application, Server server) { public NodeServer(Application application, Server server) {
this.threadName = Thread.currentThread().getName();
this.application = application; this.application = application;
this.server = server; this.server = server;
this.resourceFactory = server.getResourceFactory(); this.resourceFactory = server.getResourceFactory();
@@ -111,7 +123,7 @@ public abstract class NodeServer {
this.serverConf = config == null ? AnyValue.create() : config; this.serverConf = config == null ? AnyValue.create() : config;
if (isSNCP()) { // SNCP协议 if (isSNCP()) { // SNCP协议
String host = this.serverConf.getValue("host", isWATCH() ? "127.0.0.1" : "0.0.0.0").replace("0.0.0.0", ""); 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); this.sncpGroup = application.sncpTransportFactory.findGroupName(this.sncpAddress);
//单向SNCP服务不需要对等group //单向SNCP服务不需要对等group
//if (this.sncpGroup == null) throw new RuntimeException("Server (" + String.valueOf(config).replaceAll("\\s+", " ") + ") not found <group> info"); //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 //必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
server.init(this.serverConf); server.init(this.serverConf);
//init之后才有Executor //init之后才有Executor
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getExecutor()); resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getExecutor()); resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getExecutor()); resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getWorkExecutor());
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。 initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
String interceptorClass = this.serverConf.getValue("interceptor", ""); String interceptorClass = this.serverConf.getValue("interceptor", "");
@@ -155,8 +167,8 @@ public abstract class NodeServer {
ClassFilter<Service> serviceFilter = createServiceClassFilter(); ClassFilter<Service> serviceFilter = createServiceClassFilter();
if (application.singletonrun) { //singleton模式下只加载指定的Service if (application.singletonrun) { //singleton模式下只加载指定的Service
final String ssc = System.getProperty("red" + "kale-singleton-serviceclass"); final String ssc = System.getProperty("red" + "kale.singleton.serviceclass");
final String extssc = System.getProperty("red" + "kale-singleton-extserviceclasses"); final String extssc = System.getProperty("red" + "kale.singleton.extserviceclasses");
if (ssc != null) { if (ssc != null) {
final List<String> sscList = new ArrayList<>(); final List<String> sscList = new ArrayList<>();
sscList.add(ssc); sscList.add(ssc);
@@ -172,13 +184,14 @@ public abstract class NodeServer {
ClassFilter<Servlet> servletFilter = createServletClassFilter(); ClassFilter<Servlet> servletFilter = createServletClassFilter();
ClassFilter otherFilter = createOtherClassFilter(); ClassFilter otherFilter = createOtherClassFilter();
long s = System.currentTimeMillis(); 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; long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms"); logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
loadService(serviceFilter, otherFilter); //必须在servlet之前 loadService(serviceFilter, otherFilter); //必须在servlet之前
if (!application.singletonrun) { //非singleton模式下才加载Filter、Servlet if (!application.singletonrun) { //非singleton模式下才加载Filter、Servlet
loadFilter(filterFilter, otherFilter); loadFilter(filterFilter, otherFilter);
loadServlet(servletFilter, otherFilter); loadServlet(servletFilter, otherFilter);
postLoadServlets();
} }
if (this.interceptor != null) this.resourceFactory.inject(this.interceptor); if (this.interceptor != null) this.resourceFactory.inject(this.interceptor);
} }
@@ -198,7 +211,20 @@ public abstract class NodeServer {
if (resources != null) { if (resources != null) {
for (AnyValue sourceConf : resources.getAnyValues("source")) { for (AnyValue sourceConf : resources.getAnyValues("source")) {
try { 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) { if (type == DataSource.class) {
type = DataMemorySource.class; type = DataMemorySource.class;
for (AnyValue itemConf : sourceConf.getAnyValues("property")) { for (AnyValue itemConf : sourceConf.getAnyValues("property")) {
@@ -271,9 +297,7 @@ public abstract class NodeServer {
SncpClient client = Sncp.getSncpClient(srcService); SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>(); final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup()); source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
} }
} }
} }
@@ -284,26 +308,12 @@ public abstract class NodeServer {
application.dataSources.add(source); application.dataSources.add(source);
appResFactory.register(resourceName, DataSource.class, 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); field.set(src, source);
rf.inject(source, self); // 给其可能包含@Resource的字段赋值; rf.inject(source, self); // 给其可能包含@Resource的字段赋值;
//NodeServer.this.watchFactory.inject(src); //NodeServer.this.watchFactory.inject(src);
if (source instanceof Service && needinit) ((Service) source).init(sourceConf); if (source instanceof Service && needinit) ((Service) source).init(sourceConf);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "DataSource inject error", e); logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject error", e);
} }
}, DataSource.class); }, DataSource.class);
@@ -318,20 +328,32 @@ public abstract class NodeServer {
final Service srcService = (Service) src; final Service srcService = (Service) src;
SncpClient client = Sncp.getSncpClient(srcService); SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); 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); SimpleEntry<Class, AnyValue> resEntry = cacheResource.get(resourceName);
AnyValue sourceConf = resEntry == null ? null : resEntry.getValue(); AnyValue sourceConf = resEntry == null ? null : resEntry.getValue();
if (sourceConf == null) { if (sourceConf == null) {
SimpleEntry<Class, AnyValue> resEntry2 = dataResources.get(resourceName); SimpleEntry<Class, AnyValue> resEntry2 = dataResources.get(resourceName);
sourceConf = resEntry2 == null ? null : resEntry2.getValue(); 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; Object source = null;
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource 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(); Type genericType = field.getGenericType();
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null; ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
Type valType = pt == null ? null : pt.getActualTypeArguments()[0]; Type valType = pt == null ? null : pt.getActualTypeArguments()[0];
@@ -376,18 +398,21 @@ public abstract class NodeServer {
if (nodeService == null) { if (nodeService == null) {
final HashSet<String> groups = new HashSet<>(); final HashSet<String> groups = new HashSet<>();
if (groups.isEmpty() && isSNCP() && NodeServer.this.sncpGroup != null) groups.add(NodeServer.this.sncpGroup); 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); (isSNCP() ? appResFactory : resourceFactory).register(resourceName, WebSocketNode.class, nodeService);
((WebSocketNodeService) nodeService).setName(resourceName);
} }
resourceFactory.inject(nodeService, self); 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); field.set(src, nodeService);
if (Sncp.isRemote(nodeService)) { if (Sncp.isRemote(nodeService)) {
remoteServices.add(nodeService); remoteServices.add(nodeService);
} else { } else {
if (field != null) rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源 rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源
localServices.add(nodeService); localServices.add(nodeService);
interceptorServices.add(nodeService); interceptorServices.add(nodeService);
if (consumer != null) consumer.accept(nodeService); if (consumer != null) consumer.accept(null, nodeService);
} }
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "WebSocketNode inject error", 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 { protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
if (serviceFilter == null) return; if (serviceFilter == null) return;
final long starts = System.currentTimeMillis(); 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(); final Set<FilterEntry<? extends Service>> entrys = (Set) serviceFilter.getAllFilterEntrys();
ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory; ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory;
final ResourceFactory appResourceFactory = application.getResourceFactory(); final ResourceFactory appResourceFactory = application.getResourceFactory();
@@ -418,12 +443,11 @@ public abstract class NodeServer {
if (Modifier.isAbstract(serviceImplClass.getModifiers())) continue; //修饰abstract的类跳过 if (Modifier.isAbstract(serviceImplClass.getModifiers())) continue; //修饰abstract的类跳过
if (DataSource.class.isAssignableFrom(serviceImplClass)) continue; if (DataSource.class.isAssignableFrom(serviceImplClass)) continue;
if (CacheSource.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()); if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
Service oldother = resourceFactory.find(entry.getName(), serviceImplClass); Service oldother = resourceFactory.find(entry.getName(), serviceImplClass);
if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。 if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。
interceptorServices.add(oldother); if (!Sncp.isRemote(oldother)) interceptorServices.add(oldother);
continue; continue;
} }
final HashSet<String> groups = entry.getGroups(); //groups.isEmpty()表示<services>没有配置groups属性。 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) -> { final ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
try { try {
if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) { //class没有可用的方法且没有标记启动优先级的 通常为BaseService 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; 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; Service service;
boolean ws = src instanceof WebSocketServlet; final boolean ws = src instanceof WebSocketServlet;
if (ws || localed) { //本地模式 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 { } 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); final Class restype = Sncp.getResourceType(service);
if (rf.find(resourceName, restype) == null) { if (rf.find(resourceName, restype) == null) {
@@ -456,11 +492,12 @@ public abstract class NodeServer {
} }
if (Sncp.isRemote(service)) { if (Sncp.isRemote(service)) {
remoteServices.add(service); remoteServices.add(service);
if (agent != null) sncpRemoteAgents.put(agent.getName(), agent);
} else { } else {
if (field != null) rf.inject(service); //动态加载的Service也存在按需加载的注入资源 if (field != null) rf.inject(service); //动态加载的Service也存在按需加载的注入资源
localServices.add(service); localServices.add(service);
interceptorServices.add(service); interceptorServices.add(service);
if (consumer != null) consumer.accept(service); if (consumer != null) consumer.accept(agent, service);
} }
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
throw ex; throw ex;
@@ -493,7 +530,13 @@ public abstract class NodeServer {
if (sb != null) { if (sb != null) {
remoteServices.forEach(y -> { 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 ----------------- //----------------- init -----------------
@@ -510,21 +553,25 @@ public abstract class NodeServer {
localServices.clear(); localServices.clear();
localServices.addAll(swlist); localServices.addAll(swlist);
//this.loadPersistData(); //this.loadPersistData();
long preinits = System.currentTimeMillis();
preInitServices(localServices, remoteServices);
long preinite = System.currentTimeMillis() - preinits;
final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>(); final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>();
localServices.stream().forEach(y -> { localServices.stream().forEach(y -> {
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
y.init(Sncp.getConf(y)); y.init(Sncp.getConf(y));
long e = System.currentTimeMillis() - s; long e = System.currentTimeMillis() - s;
String serstr = Sncp.toSimpleString(y, maxNameLength, maxClassNameLength); 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) { if (slist != null && sb != null) {
List<String> wlist = new ArrayList<>(slist); //直接使用CopyOnWriteArrayList偶尔会出现莫名的异常(CopyOnWriteArrayList源码1185行) List<String> wlist = new ArrayList<>(slist); //直接使用CopyOnWriteArrayList偶尔会出现莫名的异常(CopyOnWriteArrayList源码1185行)
for (String s : wlist) { for (String s : wlist) {
sb.append(s); 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()); 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); 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<Filter> createFilterClassFilter();
protected abstract ClassFilter<Servlet> createServletClassFilter(); protected abstract ClassFilter<Servlet> createServletClassFilter();
@@ -563,22 +648,24 @@ public abstract class NodeServer {
} }
cf = null; cf = null;
for (AnyValue list : proplist) { for (AnyValue list : proplist) {
DefaultAnyValue prop = null; AnyValue.DefaultAnyValue prop = null;
String sc = list.getValue("groups"); String sc = list.getValue("groups");
String mq = list.getValue("mq");
if (sc != null) { if (sc != null) {
sc = sc.trim(); sc = sc.trim();
if (sc.endsWith(";")) sc = sc.substring(0, sc.length() - 1); if (sc.endsWith(";")) sc = sc.substring(0, sc.length() - 1);
} }
if (sc == null) sc = localGroup; if (sc == null) sc = localGroup;
if (sc != null) { if (sc != null || mq != null) {
prop = new AnyValue.DefaultAnyValue(); 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); ClassFilter filter = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, prop);
for (AnyValue av : list.getAnyValues(property)) { // <service>、<filter>、<servlet> 节点 for (AnyValue av : list.getAnyValues(property)) { // <service>、<filter>、<servlet> 节点
final AnyValue[] items = av.getAnyValues("property"); final AnyValue[] items = av.getAnyValues("property");
if (av instanceof DefaultAnyValue && items.length > 0) { //存在 <property>节点 if (av instanceof AnyValue.DefaultAnyValue && items.length > 0) { //存在 <property>节点
DefaultAnyValue dav = DefaultAnyValue.create(); AnyValue.DefaultAnyValue dav = AnyValue.DefaultAnyValue.create();
final AnyValue.Entry<String>[] strings = av.getStringEntrys(); final AnyValue.Entry<String>[] strings = av.getStringEntrys();
if (strings != null) { //将<service>、<filter>、<servlet>节点的属性值传给dav if (strings != null) { //将<service>、<filter>、<servlet>节点的属性值传给dav
for (AnyValue.Entry<String> en : strings) { 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()); 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) { for (AnyValue item : items) {
ps.addValue(item.getValue("name"), item.getValue("value")); ps.addValue(item.getValue("name"), item.getValue("value"));
} }
@@ -627,6 +714,10 @@ public abstract class NodeServer {
return false; return false;
} }
public Application getApplication() {
return application;
}
public ResourceFactory getResourceFactory() { public ResourceFactory getResourceFactory() {
return resourceFactory; return resourceFactory;
} }
@@ -660,12 +751,14 @@ public abstract class NodeServer {
public void start() throws IOException { public void start() throws IOException {
if (interceptor != null) interceptor.preStart(this); if (interceptor != null) interceptor.preStart(this);
server.start(); server.start();
postStartServer(localServices, remoteServices);
} }
public void shutdown() throws IOException { public void shutdown() throws IOException {
if (interceptor != null) interceptor.preShutdown(this); if (interceptor != null) interceptor.preShutdown(this);
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final boolean finest = logger.isLoggable(Level.FINEST); final boolean finest = logger.isLoggable(Level.FINEST);
preDestroyServices(localServices, remoteServices);
localServices.forEach(y -> { localServices.forEach(y -> {
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
if (finest) logger.finest(y + " is destroying"); if (finest) logger.finest(y + " is destroying");
@@ -680,6 +773,42 @@ public abstract class NodeServer {
server.shutdown(); 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() { public <T extends Server> T getServer() {
return (T) server; return (T) server;
} }
@@ -696,4 +825,7 @@ public abstract class NodeServer {
return new LinkedHashSet<>(remoteServices); 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.*;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.mq.MessageAgent;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.sncp.*; import org.redkale.net.sncp.*;
import org.redkale.service.Service; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue; import org.redkale.util.AnyValue.DefaultAnyValue;
@@ -24,7 +25,7 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
* *
* @author zhangjx * @author zhangjx
*/ */
@NodeProtocol({"SNCP"}) @NodeProtocol("SNCP")
public class NodeSncpServer extends NodeServer { public class NodeSncpServer extends NodeServer {
protected final SncpServer sncpServer; protected final SncpServer sncpServer;
@@ -32,14 +33,15 @@ public class NodeSncpServer extends NodeServer {
private NodeSncpServer(Application application, AnyValue serconf) { private NodeSncpServer(Application application, AnyValue serconf) {
super(application, createServer(application, serconf)); super(application, createServer(application, serconf));
this.sncpServer = (SncpServer) this.server; 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) { 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); return new NodeSncpServer(application, serconf);
} }
@@ -52,8 +54,8 @@ public class NodeSncpServer extends NodeServer {
return sncpServer == null ? null : sncpServer.getSocketAddress(); return sncpServer == null ? null : sncpServer.getSocketAddress();
} }
public void consumerAccept(Service service) { public void consumerAccept(MessageAgent messageAgent, Service service) {
if (this.consumer != null) this.consumer.accept(service); if (this.consumer != null) this.consumer.accept(messageAgent, service);
} }
@Override @Override
@@ -62,11 +64,11 @@ public class NodeSncpServer extends NodeServer {
//------------------------------------------------------------------- //-------------------------------------------------------------------
if (sncpServer == null) return; //调试时server才可能为null if (sncpServer == null) return; //调试时server才可能为null
final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : 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(); List<SncpServlet> servlets = sncpServer.getSncpServlets();
Collections.sort(servlets); Collections.sort(servlets);
for (SncpServlet en : 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()); if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
} }
@@ -88,7 +90,7 @@ public class NodeSncpServer extends NodeServer {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void loadSncpFilter(final AnyValue servletsConf, final ClassFilter<? extends Filter> classFilter) throws Exception { protected void loadSncpFilter(final AnyValue servletsConf, final ClassFilter<? extends Filter> classFilter) throws Exception {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; 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()); List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
for (FilterEntry<? extends Filter> en : list) { for (FilterEntry<? extends Filter> en : list) {
Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType(); Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType();
@@ -97,7 +99,7 @@ public class NodeSncpServer extends NodeServer {
resourceFactory.inject(filter, this); resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty(); DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.sncpServer.addSncpFilter(filter, filterConf); 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()); if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
} }

View File

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

View File

@@ -35,7 +35,7 @@ public class FilterWatchService extends AbstractWatchService {
@Resource @Resource
protected Application application; protected Application application;
@RestMapping(name = "addfilter", auth = false, comment = "动态增加Filter") @RestMapping(name = "addFilter", auth = false, comment = "动态增加Filter")
public RetResult addFilter(@RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar, public RetResult addFilter(@RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar,
@RestParam(name = "server", comment = "Server节点名") final String serverName, @RestParam(name = "server", comment = "Server节点名") final String serverName,
@RestParam(name = "type", comment = "Filter类名") final String filterType) throws IOException { @RestParam(name = "type", comment = "Filter类名") final String filterType) throws IOException {

View File

@@ -50,7 +50,7 @@ public class ServerWatchService extends AbstractWatchService {
return new RetResult(rs); return new RetResult(rs);
} }
@RestMapping(name = "changeaddress", comment = "更改Server的监听地址和端口") @RestMapping(name = "changeAddress", comment = "更改Server的监听地址和端口")
public RetResult changeAddress(@RestParam(name = "#port:") final int oldport, public RetResult changeAddress(@RestParam(name = "#port:") final int oldport,
@RestParam(name = "#newhost:") final String newhost, @RestParam(name = "#newport:") final int newport) { @RestParam(name = "#newhost:") final String newhost, @RestParam(name = "#newport:") final int newport) {
if (oldport < 1) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `oldport`"); if (oldport < 1) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `oldport`");
@@ -81,7 +81,7 @@ public class ServerWatchService extends AbstractWatchService {
protocol += "/HTTP"; protocol += "/HTTP";
} else { } else {
NodeProtocol np = node.getClass().getAnnotation(NodeProtocol.class); 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("name", server.getName());
rs.put("protocol", protocol); rs.put("protocol", protocol);

View File

@@ -30,8 +30,8 @@ public class ServiceWatchService extends AbstractWatchService {
protected Application application; protected Application application;
@RestConvert(type = void.class) @RestConvert(type = void.class)
@RestMapping(name = "setfield", auth = false, comment = "设置Service中指定字段的内容") @RestMapping(name = "setField", auth = false, comment = "设置Service中指定字段的内容")
public RetResult setfield(@RestParam(name = "name", comment = "Service的资源名") String name, public RetResult setField(@RestParam(name = "name", comment = "Service的资源名") String name,
@RestParam(name = "type", comment = "Service的类名") String type, @RestParam(name = "type", comment = "Service的类名") String type,
@RestParam(name = "field", comment = "字段名") String field, @RestParam(name = "field", comment = "字段名") String field,
@RestParam(name = "value", comment = "字段值") String value) { @RestParam(name = "value", comment = "字段值") String value) {
@@ -65,8 +65,8 @@ public class ServiceWatchService extends AbstractWatchService {
} }
@RestConvert(type = void.class) @RestConvert(type = void.class)
@RestMapping(name = "getfield", auth = false, comment = "查询Service中指定字段的内容") @RestMapping(name = "getField", auth = false, comment = "查询Service中指定字段的内容")
public RetResult getfield(@RestParam(name = "name", comment = "Service的资源名") String name, public RetResult getField(@RestParam(name = "name", comment = "Service的资源名") String name,
@RestParam(name = "type", comment = "Service的类名") String type, @RestParam(name = "type", comment = "Service的类名") String type,
@RestParam(name = "field", comment = "字段名") String field) { @RestParam(name = "field", comment = "字段名") String field) {
if (name == null) name = ""; if (name == null) name = "";
@@ -98,8 +98,8 @@ public class ServiceWatchService extends AbstractWatchService {
} }
@RestConvert(type = void.class) @RestConvert(type = void.class)
@RestMapping(name = "runmethod", auth = false, comment = "调用Service中指定方法") @RestMapping(name = "runMethod", auth = false, comment = "调用Service中指定方法")
public RetResult runmethod(@RestParam(name = "name", comment = "Service的资源名") String name, public RetResult runMethod(@RestParam(name = "name", comment = "Service的资源名") String name,
@RestParam(name = "type", comment = "Service的类名") String type, @RestParam(name = "type", comment = "Service的类名") String type,
@RestParam(name = "method", comment = "Service的方法名") String method, @RestParam(name = "method", comment = "Service的方法名") String method,
@RestParam(name = "params", comment = "方法的参数值") List<String> params, @RestParam(name = "params", comment = "方法的参数值") List<String> params,
@@ -169,28 +169,28 @@ public class ServiceWatchService extends AbstractWatchService {
return dest; return dest;
} }
@RestMapping(name = "load", auth = false, comment = "动态增加Service") @RestMapping(name = "loadService", auth = false, comment = "动态增加Service")
public RetResult loadService(@RestParam(name = "type", comment = "Service的类名") String type, public RetResult loadService(@RestParam(name = "type", comment = "Service的类名") String type,
@RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) { @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
//待开发 //待开发
return RetResult.success(); return RetResult.success();
} }
@RestMapping(name = "reload", auth = false, comment = "重新加载Service") @RestMapping(name = "reloadService", auth = false, comment = "重新加载Service")
public RetResult reloadService(@RestParam(name = "name", comment = "Service的资源名") String name, public RetResult reloadService(@RestParam(name = "name", comment = "Service的资源名") String name,
@RestParam(name = "type", comment = "Service的类名") String type) { @RestParam(name = "type", comment = "Service的类名") String type) {
//待开发 //待开发
return RetResult.success(); return RetResult.success();
} }
@RestMapping(name = "stop", auth = false, comment = "动态停止Service") @RestMapping(name = "stopService", auth = false, comment = "动态停止Service")
public RetResult stopService(@RestParam(name = "name", comment = "Service的资源名") String name, public RetResult stopService(@RestParam(name = "name", comment = "Service的资源名") String name,
@RestParam(name = "type", comment = "Service的类名") String type) { @RestParam(name = "type", comment = "Service的类名") String type) {
//待开发 //待开发
return RetResult.success(); return RetResult.success();
} }
@RestMapping(name = "find", auth = false, comment = "查找Service") @RestMapping(name = "findService", auth = false, comment = "查找Service")
public RetResult find(@RestParam(name = "name", comment = "Service的资源名") String name, public RetResult find(@RestParam(name = "name", comment = "Service的资源名") String name,
@RestParam(name = "type", comment = "Service的类名") String type) { @RestParam(name = "type", comment = "Service的类名") String type) {
//待开发 //待开发

View File

@@ -25,13 +25,13 @@ public class ServletWatchService extends AbstractWatchService {
@Resource @Resource
protected TransportFactory transportFactory; protected TransportFactory transportFactory;
// //
// @RestMapping(name = "load", auth = false, comment = "动态增加Servlet") // @RestMapping(name = "loadServlet", auth = false, comment = "动态增加Servlet")
// public RetResult loadServlet(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) { // public RetResult loadServlet(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
// //待开发 // //待开发
// return RetResult.success(); // return RetResult.success();
// } // }
// //
// @RestMapping(name = "stop", auth = false, comment = "动态停止Servlet") // @RestMapping(name = "stopServlet", auth = false, comment = "动态停止Servlet")
// public RetResult stopServlet(String type) { // public RetResult stopServlet(String type) {
// //待开发 // //待开发
// return RetResult.success(); // return RetResult.success();

View File

@@ -72,15 +72,6 @@ public class TransportWatchService extends AbstractWatchService {
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) { if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
client.getRemoteGroupTransport().addRemoteAddresses(address); 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); 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)) { if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
client.getRemoteGroupTransport().removeRemoteAddresses(address); 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")) { 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

@@ -36,12 +36,15 @@ public abstract class Convert<R extends Reader, W extends Writer> {
return writer; return writer;
} }
protected <S extends W> S fieldFunc(S writer, BiFunction<Attribute, Object, Object> fieldFunc) { protected <S extends W> S fieldFunc(S writer, BiFunction<Attribute, Object, Object> objFieldFunc, Function<Object, ConvertField[]> objExtFunc) {
writer.fieldFunc = fieldFunc; writer.objFieldFunc = objFieldFunc;
writer.objExtFunc = objExtFunc;
return writer; return writer;
} }
public abstract Convert<R, W> newConvert(final BiFunction<Attribute, Object, Object> fieldFunc); public abstract Convert<R, W> newConvert(final BiFunction<Attribute, Object, Object> objFieldFunc);
public abstract Convert<R, W> newConvert(final BiFunction<Attribute, Object, Object> objFieldFunc, Function<Object, ConvertField[]> objExtFunc);
public abstract boolean isBinary(); public abstract boolean isBinary();
@@ -51,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 <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 Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value); public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value);

View File

@@ -133,6 +133,54 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
} }
}); });
try {
Class sqldateClass = Class.forName("java.sql.Date");
this.register(sqldateClass, new SimpledCoder<R, W, java.sql.Date>() {
@Override
public void convertTo(W out, java.sql.Date value) {
out.writeSmallString(value == null ? null : value.toString());
}
@Override
public java.sql.Date convertFrom(R in) {
String t = in.readSmallString();
return t == null ? null : java.sql.Date.valueOf(t);
}
});
Class sqltimeClass = Class.forName("java.sql.Time");
this.register(sqltimeClass, new SimpledCoder<R, W, java.sql.Time>() {
@Override
public void convertTo(W out, java.sql.Time value) {
out.writeSmallString(value == null ? null : value.toString());
}
@Override
public java.sql.Time convertFrom(R in) {
String t = in.readSmallString();
return t == null ? null : java.sql.Time.valueOf(t);
}
});
Class timestampClass = Class.forName("java.sql.Timestamp");
this.register(timestampClass, new SimpledCoder<R, W, java.sql.Timestamp>() {
@Override
public void convertTo(W out, java.sql.Timestamp value) {
out.writeSmallString(value == null ? null : value.toString());
}
@Override
public java.sql.Timestamp convertFrom(R in) {
String t = in.readSmallString();
return t == null ? null : java.sql.Timestamp.valueOf(t);
}
});
} catch (Throwable t) {
}
} }
} }
@@ -711,7 +759,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
encoder = new OptionalCoder(this, type); encoder = new OptionalCoder(this, type);
} else if (clazz == Object.class) { } else if (clazz == Object.class) {
return (Encodeable<W, E>) this.anyEncoder; return (Encodeable<W, E>) this.anyEncoder;
} else if (!clazz.getName().startsWith("java.") || java.net.HttpCookie.class == clazz || java.util.AbstractMap.SimpleEntry.class == clazz) { } else if (!clazz.getName().startsWith("java.") || java.net.HttpCookie.class == clazz
|| java.util.Map.Entry.class == clazz || java.util.AbstractMap.SimpleEntry.class == clazz) {
Encodeable simpleCoder = null; Encodeable simpleCoder = null;
for (final Method method : clazz.getDeclaredMethods()) { for (final Method method : clazz.getDeclaredMethods()) {
if (!Modifier.isStatic(method.getModifiers())) continue; if (!Modifier.isStatic(method.getModifiers())) continue;

View File

@@ -0,0 +1,102 @@
/*
* 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.convert;
import java.io.Serializable;
import java.lang.reflect.Type;
import org.redkale.convert.json.JsonConvert;
/**
* newConvert参数中的Function返回结果的数据类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class ConvertField implements Serializable {
protected String name;
protected Type type;
protected int position;
protected Object value;
public ConvertField() {
}
public ConvertField(String name, Object value) {
this.name = name;
this.value = value;
}
public ConvertField(String name, int position, Object value) {
this.name = name;
this.position = position;
this.value = value;
}
public ConvertField(String name, Type type, Object value) {
this.name = name;
this.type = type;
this.value = value;
}
public ConvertField(String name, Type type, int position, Object value) {
this.name = name;
this.type = type;
this.position = position;
this.value = value;
}
public static ConvertField[] ofArray(Object... items) {
int len = items.length / 2;
ConvertField[] rs = new ConvertField[len];
for (int i = 0; i < len; i++) {
rs[i] = new ConvertField(items[i * 2].toString(), items[i * 2 + 1]);
}
return rs;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -17,8 +17,10 @@ public enum ConvertType {
JSON(1), JSON(1),
BSON(2), BSON(2),
DIY(64), PROTOBUF(64),
ALL(127); PROTOBUF_JSON(64 + 1),
DIY(256),
ALL(1023);
private final int value; private final int value;
@@ -26,8 +28,19 @@ public enum ConvertType {
this.value = v; this.value = v;
} }
public int getValue() {
return value;
}
public boolean contains(ConvertType type) { public boolean contains(ConvertType type) {
if (type == null) return false; if (type == null) return false;
return this.value >= type.value && (this.value & type.value) > 0; 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); Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
fieldCoder = factory.loadDecoder(t); 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(); if (ref != null) member.index = ref.getIndex();
list.add(member); 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); Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type);
fieldCoder = factory.loadDecoder(t); 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(); if (ref != null) member.index = ref.getIndex();
list.add(member); list.add(member);
} }
@@ -149,7 +149,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
try { try {
Field f = clazz.getDeclaredField(constructorField); Field f = clazz.getDeclaredField(constructorField);
Type t = TypeToken.createClassType(f.getGenericType(), this.type); 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方法 } catch (NoSuchFieldException nsfe) { //不存在field 可能存在getter方法
char[] fs = constructorField.toCharArray(); char[] fs = constructorField.toCharArray();
fs[0] = Character.toUpperCase(fs[0]); 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); getter = clazz.getMethod("is" + mn);
} }
Type t = TypeToken.createClassType(TypeToken.getGenericType(getter.getGenericParameterTypes()[0], this.type), this.type); 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); Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
fieldCoder = factory.loadEncoder(t); 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(); if (ref != null) member.index = ref.getIndex();
list.add(member); 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); Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type);
fieldCoder = factory.loadEncoder(t); 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(); if (ref != null) member.index = ref.getIndex();
list.add(member); list.add(member);
} }
@@ -164,9 +164,22 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
return; return;
} }
if (out.writeObjectB(value) < 0) { if (out.writeObjectB(value) < 0) {
int maxPosition = 0;
for (EnMember member : members) { for (EnMember member : members) {
maxPosition = member.getPosition();
out.writeObjectField(member, value); out.writeObjectField(member, value);
} }
if (out.objExtFunc != null) {
ConvertField[] extFields = out.objExtFunc.apply(value);
if (extFields != null) {
Encodeable<W, ?> anyEncoder = factory.getAnyEncoder();
for (ConvertField en : extFields) {
if (en == null) continue;
maxPosition++;
out.writeObjectField(en.getName(), en.getType(), Math.max(en.getPosition(), maxPosition), anyEncoder, en.getValue());
}
}
}
} }
out.writeObjectE(value); out.writeObjectE(value);
} }
@@ -250,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; String fieldalias;
if (field != null) { // public field if (field != null) { // public field
ConvertColumnEntry ref = factory.findRef(clazz, field); ConvertColumnEntry ref = factory.findRef(clazz, field);
@@ -276,7 +289,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
} }
fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name(); 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>> { public class OptionalCoder<R extends Reader, W extends Writer, T> extends SimpledCoder<R, W, Optional<T>> {
private final Type type; protected final Type componentType;
private final Type componentType;
protected final Class componentClass; protected final Class componentClass;

View File

@@ -6,7 +6,7 @@
package org.redkale.convert; package org.redkale.convert;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.function.BiFunction; import java.util.function.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -26,7 +26,10 @@ public abstract class Writer {
protected Type specify; protected Type specify;
//对某个字段值进行动态处理 //对某个字段值进行动态处理
protected BiFunction<Attribute, Object, Object> fieldFunc; protected BiFunction<Attribute, Object, Object> objFieldFunc;
//对某个对象进行动态扩展字段值处理
protected Function<Object, ConvertField[]> objExtFunc;
/** /**
* 设置specify * 设置specify
@@ -44,7 +47,7 @@ public abstract class Writer {
} }
protected boolean recycle() { protected boolean recycle() {
this.fieldFunc = null; this.objFieldFunc = null;
return true; return true;
} }
@@ -116,10 +119,10 @@ public abstract class Writer {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void writeObjectField(final EnMember member, Object obj) { public void writeObjectField(final EnMember member, Object obj) {
Object value; Object value;
if (fieldFunc == null) { if (objFieldFunc == null) {
value = member.attribute.get(obj); value = member.attribute.get(obj);
} else { } else {
value = fieldFunc.apply(member.attribute, obj); value = objFieldFunc.apply(member.attribute, obj);
} }
if (value == null) return; if (value == null) return;
if (tiny()) { if (tiny()) {
@@ -129,11 +132,49 @@ public abstract class Writer {
if (!((Boolean) value)) return; if (!((Boolean) value)) return;
} }
} }
this.writeFieldName(member); Attribute attr = member.getAttribute();
this.writeFieldName(attr.field(), attr.genericType(), member.getPosition());
member.encoder.convertTo(this, value); member.encoder.convertTo(this, value);
this.comma = true; this.comma = true;
} }
/**
* 输出一个对象的某个扩展字段
*
*
* @param fieldName 字段名称
* @param fieldType 字段类型
* @param fieldPos 字段顺序
* @param anyEncoder Encoder
* @param value 写入的字段对象
*/
@SuppressWarnings("unchecked")
public void writeObjectField(final String fieldName, Type fieldType, int fieldPos, Encodeable anyEncoder, Object value) {
if (value == null) return;
if (fieldType == null) fieldType = value.getClass();
if (tiny() && fieldType instanceof Class) {
Class clazz = (Class) fieldType;
if (CharSequence.class.isAssignableFrom(clazz)) {
if (((CharSequence) value).length() == 0) return;
} else if (clazz == boolean.class || clazz == Boolean.class) {
if (!((Boolean) value)) return;
}
}
this.writeFieldName(fieldName, fieldType, fieldPos);
anyEncoder.convertTo(this, value);
this.comma = true;
}
/**
* 输出一个字段名
*
* @param member 字段
*/
public final void writeFieldName(final EnMember member) {
Attribute attr = member.getAttribute();
this.writeFieldName(attr.field(), attr.genericType(), member.getPosition());
}
/** /**
* 输出一个对象后的操作 * 输出一个对象后的操作
* *
@@ -191,9 +232,11 @@ public abstract class Writer {
/** /**
* 输出一个字段名 * 输出一个字段名
* *
* @param member 字段的EnMember对象 * @param fieldName 字段名称
* @param fieldType 字段类型
* @param fieldPos 字段顺序
*/ */
public abstract void writeFieldName(EnMember member); public abstract void writeFieldName(String fieldName, Type fieldType, int fieldPos);
/** /**
* 写入一个boolean值 * 写入一个boolean值

View File

@@ -61,10 +61,15 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override @Override
public BsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc) { public BsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc) {
return newConvert(fieldFunc, null);
}
@Override
public BsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc, Function<Object, ConvertField[]> objExtFunc) {
return new BsonConvert(getFactory(), tiny) { return new BsonConvert(getFactory(), tiny) {
@Override @Override
protected <S extends BsonWriter> S configWrite(S writer) { protected <S extends BsonWriter> S configWrite(S writer) {
return fieldFunc(writer, fieldFunc); return fieldFunc(writer, fieldFunc, objExtFunc);
} }
}; };
} }
@@ -172,6 +177,16 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
return result; 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 @Override
public byte[] convertMapTo(final Object... values) { public byte[] convertMapTo(final Object... values) {
if (values == null) return null; if (values == null) return null;

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.convert.bson; package org.redkale.convert.bson;
import java.lang.reflect.Type;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.ext.ByteSimpledCoder; import org.redkale.convert.ext.ByteSimpledCoder;
@@ -43,7 +44,7 @@ public class BsonWriter extends Writer {
} }
protected BsonWriter(byte[] bs) { protected BsonWriter(byte[] bs) {
this.content = bs; this.content = bs == null ? new byte[0] : bs;
} }
public BsonWriter() { public BsonWriter() {
@@ -102,7 +103,7 @@ public class BsonWriter extends Writer {
super.recycle(); super.recycle();
this.count = 0; this.count = 0;
this.specify = null; this.specify = null;
if (this.content.length > defaultSize) { if (this.content != null && this.content.length > defaultSize) {
this.content = new byte[defaultSize]; this.content = new byte[defaultSize];
} }
return true; return true;
@@ -200,11 +201,10 @@ public class BsonWriter extends Writer {
} }
@Override @Override
public final void writeFieldName(EnMember member) { public final void writeFieldName(String fieldName, Type fieldType, int fieldPos) {
Attribute attribute = member.getAttribute();
writeByte(BsonReader.SIGN_HASNEXT); writeByte(BsonReader.SIGN_HASNEXT);
writeSmallString(attribute.field()); writeSmallString(fieldName);
writeByte(BsonFactory.typeEnum(attribute.type())); writeByte(BsonFactory.typeEnum(fieldType));
} }
/** /**

View File

@@ -20,7 +20,7 @@ import org.redkale.convert.*;
*/ */
public class FileSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, File> { public class FileSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, File> {
public static final PatternSimpledCoder instance = new PatternSimpledCoder(); public static final FileSimpledCoder instance = new FileSimpledCoder();
@Override @Override
public void convertTo(W out, File value) { public void convertTo(W out, File value) {

View File

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

View File

@@ -115,7 +115,7 @@ public class JsonByteBufferWriter extends JsonWriter {
int byteLength = quote ? 2 : 0; int byteLength = quote ? 2 : 0;
ByteBuffer bb = null; ByteBuffer bb = null;
if (charset == null) { if (charset == null) {
byteLength += encodeUTF8Length(chs, start, len); byteLength += Utility.encodeUTF8Length(chs, start, len);
} else { } else {
bb = charset.encode(CharBuffer.wrap(chs, start, len)); bb = charset.encode(CharBuffer.wrap(chs, start, len));
byteLength += bb.remaining(); byteLength += bb.remaining();
@@ -134,6 +134,13 @@ public class JsonByteBufferWriter extends JsonWriter {
} else if (c < 0x800) { } else if (c < 0x800) {
buffer.put((byte) (0xc0 | (c >> 6))); buffer.put((byte) (0xc0 | (c >> 6)));
buffer.put((byte) (0x80 | (c & 0x3f))); buffer.put((byte) (0x80 | (c & 0x3f)));
} else if (Character.isSurrogate(c)) { //连取两个
int uc = Character.toCodePoint(c, chs[i + 1]);
buffer.put((byte) (0xf0 | ((uc >> 18))));
buffer.put((byte) (0x80 | ((uc >> 12) & 0x3f)));
buffer.put((byte) (0x80 | ((uc >> 6) & 0x3f)));
buffer.put((byte) (0x80 | (uc & 0x3f)));
i++;
} else { } else {
buffer.put((byte) (0xe0 | ((c >> 12)))); buffer.put((byte) (0xe0 | ((c >> 12))));
buffer.put((byte) (0x80 | ((c >> 6) & 0x3f))); buffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
@@ -155,7 +162,34 @@ public class JsonByteBufferWriter extends JsonWriter {
if (charset == null) { //UTF-8 if (charset == null) { //UTF-8
final int limit = start + len; final int limit = start + len;
for (int i = start; i < limit; i++) { for (int i = start; i < limit; i++) {
buffer = putUTF8Char(buffer, chs[i]); char c = chs[i];
if (c < 0x80) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) c);
} else if (c < 0x800) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0xc0 | (c >> 6)));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | (c & 0x3f)));
} else if (Character.isSurrogate(c)) { //连取两个
int uc = Character.toCodePoint(c, chs[i + 1]);
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0xf0 | ((uc >> 18))));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | ((uc >> 12) & 0x3f)));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | ((uc >> 6) & 0x3f)));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | (uc & 0x3f)));
i++;
} else {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0xe0 | ((c >> 12))));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | (c & 0x3f)));
}
} }
} else { } else {
while (bb.hasRemaining()) { while (bb.hasRemaining()) {
@@ -169,50 +203,18 @@ public class JsonByteBufferWriter extends JsonWriter {
} }
} }
private ByteBuffer putUTF8Char(ByteBuffer buffer, char c) {
if (c < 0x80) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) c);
} else if (c < 0x800) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0xc0 | (c >> 6)));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | (c & 0x3f)));
} else {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0xe0 | ((c >> 12))));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | (c & 0x3f)));
}
return buffer;
}
private ByteBuffer nextByteBuffer() { private ByteBuffer nextByteBuffer() {
this.buffers[this.index].flip(); this.buffers[this.index].flip();
return this.buffers[++this.index]; return this.buffers[++this.index];
} }
protected static int encodeUTF8Length(final char[] text, final int start, final int len) {
char c;
int size = 0;
final char[] chars = text;
final int limit = start + len;
for (int i = start; i < limit; i++) {
c = chars[i];
size += (c < 0x80 ? 1 : (c < 0x800 ? 2 : 3));
}
return size;
}
protected static int encodeEscapeUTF8Length(final char[] text, final int start, final int len) { protected static int encodeEscapeUTF8Length(final char[] text, final int start, final int len) {
char c; char c;
int size = 0; int size = 0;
final char[] chars = text; final char[] chs = text;
final int limit = start + len; final int limit = start + len;
for (int i = start; i < limit; i++) { for (int i = start; i < limit; i++) {
c = chars[i]; c = chs[i];
switch (c) { switch (c) {
case '\n': size += 2; case '\n': size += 2;
break; break;
@@ -225,7 +227,7 @@ public class JsonByteBufferWriter extends JsonWriter {
case '"': size += 2; case '"': size += 2;
break; break;
default: default:
size += (c < 0x80 ? 1 : (c < 0x800 ? 2 : 3)); size += (c < 0x80 ? 1 : (c < 0x800 || Character.isSurrogate(c) ? 2 : 3));
break; break;
} }
} }
@@ -239,19 +241,39 @@ public class JsonByteBufferWriter extends JsonWriter {
* @param value String值 * @param value String值
*/ */
@Override @Override
public void writeTo(final boolean quote, final String value) { public void writeLatin1To(final boolean quote, final String value) {
char[] chs = Utility.charArray(value); byte[] bs = Utility.byteArray(value);
writeTo(-1, quote, chs, 0, chs.length); int expandsize = expand(bs.length + (quote ? 2 : 0));
if (expandsize == 0) {// 只需要一个buffer
final ByteBuffer buffer = this.buffers[index];
if (quote) buffer.put((byte) '"');
buffer.put(bs);
if (quote) buffer.put((byte) '"');
} else {
ByteBuffer buffer = this.buffers[index];
if (quote) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) '"');
}
for (byte b : bs) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put(b);
}
if (quote) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) '"');
}
}
} }
@Override @Override
public void writeInt(int value) { public void writeInt(int value) {
writeTo(false, String.valueOf(value)); writeLatin1To(false, String.valueOf(value));
} }
@Override @Override
public void writeLong(long value) { public void writeLong(long value) {
writeTo(false, String.valueOf(value)); writeLatin1To(false, String.valueOf(value));
} }
@Override @Override
@@ -289,7 +311,8 @@ public class JsonByteBufferWriter extends JsonWriter {
if (expandsize == 0) { // 只需要一个buffer if (expandsize == 0) { // 只需要一个buffer
final ByteBuffer buffer = this.buffers[index]; final ByteBuffer buffer = this.buffers[index];
buffer.put((byte) '"'); buffer.put((byte) '"');
for (char c : chs) { for (int i = 0; i < chs.length; i++) {
char c = chs[i];
switch (c) { switch (c) {
case '\n': buffer.put((byte) '\\').put((byte) 'n'); case '\n': buffer.put((byte) '\\').put((byte) 'n');
break; break;
@@ -307,6 +330,13 @@ public class JsonByteBufferWriter extends JsonWriter {
} else if (c < 0x800) { } else if (c < 0x800) {
buffer.put((byte) (0xc0 | (c >> 6))); buffer.put((byte) (0xc0 | (c >> 6)));
buffer.put((byte) (0x80 | (c & 0x3f))); buffer.put((byte) (0x80 | (c & 0x3f)));
} else if (Character.isSurrogate(c)) { //连取两个
int uc = Character.toCodePoint(c, chs[i + 1]);
buffer.put((byte) (0xf0 | ((uc >> 18))));
buffer.put((byte) (0x80 | ((uc >> 12) & 0x3f)));
buffer.put((byte) (0x80 | ((uc >> 6) & 0x3f)));
buffer.put((byte) (0x80 | (uc & 0x3f)));
i++;
} else { } else {
buffer.put((byte) (0xe0 | ((c >> 12)))); buffer.put((byte) (0xe0 | ((c >> 12))));
buffer.put((byte) (0x80 | ((c >> 6) & 0x3f))); buffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));

View File

@@ -11,6 +11,7 @@ import java.nio.*;
import java.nio.charset.*; import java.nio.charset.*;
import java.util.function.*; import java.util.function.*;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.service.RetResult;
import org.redkale.util.*; 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>>() { public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.HashMap<String, String>>() {
}.getType(); }.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<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)); private static final ObjectPool<JsonWriter> writerPool = JsonWriter.createPool(Integer.getInteger("convert.json.pool.size", 16));
@@ -48,10 +52,15 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
@Override @Override
public JsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc) { public JsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc) {
return newConvert(fieldFunc, null);
}
@Override
public JsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc, Function<Object, ConvertField[]> objExtFunc) {
return new JsonConvert(getFactory(), tiny) { return new JsonConvert(getFactory(), tiny) {
@Override @Override
protected <S extends JsonWriter> S configWrite(S writer) { protected <S extends JsonWriter> S configWrite(S writer) {
return fieldFunc(writer, fieldFunc); return fieldFunc(writer, fieldFunc, objExtFunc);
} }
}; };
} }
@@ -209,6 +218,25 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
return result; 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 @Override
public String convertMapTo(final Object... values) { public String convertMapTo(final Object... values) {
if (values == null) return "null"; if (values == null) return "null";

View File

@@ -26,10 +26,7 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
private static final JsonFactory instance = new JsonFactory(null, Boolean.getBoolean("convert.json.tiny")); private static final JsonFactory instance = new JsonFactory(null, Boolean.getBoolean("convert.json.tiny"));
static { static {
instance.register(InetAddress.class, InetAddressSimpledCoder.InetAddressJsonSimpledCoder.instance);
instance.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressJsonSimpledCoder.instance);
instance.register(DLong.class, DLongSimpledCoder.DLongJsonSimpledCoder.instance);
instance.register(BigInteger.class, BigIntegerSimpledCoder.BigIntegerJsonSimpledCoder.instance);
instance.register(Serializable.class, instance.loadEncoder(Object.class)); instance.register(Serializable.class, instance.loadEncoder(Object.class));
instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class)); instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class));
@@ -38,6 +35,12 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
private JsonFactory(JsonFactory parent, boolean tiny) { private JsonFactory(JsonFactory parent, boolean tiny) {
super(parent, tiny); super(parent, tiny);
if (parent == null) {
this.register(InetAddress.class, InetAddressSimpledCoder.InetAddressJsonSimpledCoder.instance);
this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressJsonSimpledCoder.instance);
this.register(DLong.class, DLongSimpledCoder.DLongJsonSimpledCoder.instance);
this.register(BigInteger.class, BigIntegerSimpledCoder.BigIntegerJsonSimpledCoder.instance);
}
} }
@Override @Override

View File

@@ -87,19 +87,19 @@ class JsonStreamWriter extends JsonByteBufferWriter {
* @param value String值 * @param value String值
*/ */
@Override @Override
public void writeTo(final boolean quote, final String value) { public void writeLatin1To(final boolean quote, final String value) {
char[] chs = Utility.charArray(value); char[] chs = Utility.charArray(value);
writeTo(quote, chs, 0, chs.length); writeTo(quote, chs, 0, chs.length);
} }
@Override @Override
public void writeInt(int value) { public void writeInt(int value) {
writeTo(false, String.valueOf(value)); writeLatin1To(false, String.valueOf(value));
} }
@Override @Override
public void writeLong(long value) { public void writeLong(long value) {
writeTo(false, String.valueOf(value)); writeLatin1To(false, String.valueOf(value));
} }
@Override @Override

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.convert.json; package org.redkale.convert.json;
import java.lang.reflect.Type;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -88,7 +89,7 @@ public class JsonWriter extends Writer {
* @param quote 是否加双引号 * @param quote 是否加双引号
* @param value 非null且不含需要转义的字符的String值 * @param value 非null且不含需要转义的字符的String值
*/ */
public void writeTo(final boolean quote, final String value) { public void writeLatin1To(final boolean quote, final String value) {
int len = value.length(); int len = value.length();
expand(len + (quote ? 2 : 0)); expand(len + (quote ? 2 : 0));
if (quote) content[count++] = '"'; if (quote) content[count++] = '"';
@@ -102,7 +103,7 @@ public class JsonWriter extends Writer {
super.recycle(); super.recycle();
this.count = 0; this.count = 0;
this.specify = null; this.specify = null;
if (this.content.length > defaultSize) { if (this.content != null && this.content.length > defaultSize) {
this.content = new char[defaultSize]; this.content = new char[defaultSize];
} }
return true; return true;
@@ -159,15 +160,15 @@ public class JsonWriter extends Writer {
} }
@Override @Override
public final void writeFieldName(EnMember member) { public final void writeFieldName(String fieldName, Type fieldType, int fieldPos) {
if (this.comma) writeTo(','); if (this.comma) writeTo(',');
writeTo(true, member.getAttribute().field()); writeLatin1To(true, fieldName);
writeTo(':'); writeTo(':');
} }
@Override @Override
public final void writeSmallString(String value) { public final void writeSmallString(String value) {
writeTo(true, value); writeLatin1To(true, value);
} }
@Override @Override
@@ -313,17 +314,17 @@ public class JsonWriter extends Writer {
@Override @Override
public final void writeFloat(float value) { public final void writeFloat(float value) {
writeTo(false, String.valueOf(value)); writeLatin1To(false, String.valueOf(value));
} }
@Override @Override
public final void writeDouble(double value) { public final void writeDouble(double value) {
writeTo(false, String.valueOf(value)); writeLatin1To(false, String.valueOf(value));
} }
@Override @Override
public final void writeWrapper(StringWrapper value) { public final void writeWrapper(StringWrapper value) {
writeTo(false, String.valueOf(value)); writeLatin1To(false, String.valueOf(value));
} }
@Override @Override

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; package org.redkale.net;
import java.io.IOException; import java.io.*;
import java.net.*; import java.net.*;
import java.nio.*; import java.nio.*;
import java.nio.channels.*; import java.nio.channels.*;
@@ -14,7 +14,7 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import java.util.function.*; import java.util.function.*;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import org.redkale.util.ObjectPool; import org.redkale.util.*;
/** /**
* *
@@ -23,45 +23,49 @@ import org.redkale.util.ObjectPool;
* *
* @author zhangjx * @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 readtime;
protected volatile long writetime; 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表示没有事件 //关联的事件数, 小于1表示没有事件
protected final AtomicInteger eventing = new AtomicInteger(); private final AtomicInteger eventing = new AtomicInteger();
protected AsyncConnection(ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext) { protected AsyncConnection(ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext,
this(bufferPool, bufferPool, 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(bufferSupplier);
Objects.requireNonNull(bufferConsumer); Objects.requireNonNull(bufferConsumer);
this.bufferSupplier = bufferSupplier; this.bufferSupplier = bufferSupplier;
this.bufferConsumer = bufferConsumer; this.bufferConsumer = bufferConsumer;
this.sslContext = sslContext; this.sslContext = sslContext;
this.livingCounter = livingCounter;
this.closedCounter = closedCounter;
} }
public Supplier<ByteBuffer> getBufferSupplier() { public Supplier<ByteBuffer> getBufferSupplier() {
@@ -88,7 +92,6 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
return eventing.decrementAndGet(); return eventing.decrementAndGet();
} }
@Override
public abstract boolean isOpen(); public abstract boolean isOpen();
public abstract boolean isTCP(); public abstract boolean isTCP();
@@ -113,14 +116,11 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
public abstract void setWriteTimeoutSeconds(int writeTimeoutSeconds); public abstract void setWriteTimeoutSeconds(int writeTimeoutSeconds);
@Override public abstract ReadableByteChannel readableByteChannel();
public abstract int read(ByteBuffer dst) throws IOException;
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler); public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
public abstract WritableByteChannel writableByteChannel();
@Override
public abstract int write(ByteBuffer src) throws IOException;
public abstract <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler); public abstract <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler);
@@ -130,9 +130,9 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
public abstract <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler); public abstract <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler);
public void setReadBuffer(Buffer buffer) { public void setReadBuffer(ByteBuffer buffer) {
if (this.readBuffer != null) throw new RuntimeException("repeat AsyncConnection.setReadBuffer"); if (this.readBuffer != null) throw new RuntimeException("repeat AsyncConnection.setReadBuffer");
this.readBuffer = (ByteBuffer) buffer; this.readBuffer = buffer;
} }
public ByteBuffer pollReadBuffer() { public ByteBuffer pollReadBuffer() {
@@ -144,15 +144,16 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
return bufferSupplier.get(); return bufferSupplier.get();
} }
public void offerBuffer(Buffer buffer) { public void offerBuffer(ByteBuffer buffer) {
if (buffer == null) return; if (buffer == null) return;
bufferConsumer.accept((ByteBuffer) buffer); bufferConsumer.accept(buffer);
} }
public void offerBuffer(Buffer... buffers) { public void offerBuffer(ByteBuffer... buffers) {
if (buffers == null) return; if (buffers == null) return;
for (Buffer buffer : buffers) { Consumer<ByteBuffer> consumer = this.bufferConsumer;
bufferConsumer.accept((ByteBuffer) buffer); for (ByteBuffer buffer : buffers) {
consumer.accept(buffer);
} }
} }
@@ -189,7 +190,12 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
} }
} }
if (this.readBuffer != null) { if (this.readBuffer != null) {
bufferConsumer.accept(this.readBuffer); Consumer<ByteBuffer> consumer = this.bufferConsumer;
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// consumer = ((IOThread) thread).getBufferPool();
// }
consumer.accept(this.readBuffer);
} }
if (attributes == null) return; if (attributes == null) return;
try { try {

View File

@@ -60,7 +60,8 @@ public class PrepareRunner implements Runnable {
@Override @Override
public void completed(Integer count, ByteBuffer buffer) { public void completed(Integer count, ByteBuffer buffer) {
if (count < 1) { if (count < 1) {
response.request.offerReadBuffer(buffer); buffer.clear();
channel.setReadBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常 channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel(); response.removeChannel();
response.finish(true); response.finish(true);
@@ -84,7 +85,8 @@ public class PrepareRunner implements Runnable {
@Override @Override
public void failed(Throwable exc, ByteBuffer buffer) { public void failed(Throwable exc, ByteBuffer buffer) {
response.request.offerReadBuffer(buffer); buffer.clear();
channel.setReadBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常 channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel(); response.removeChannel();
response.finish(true); response.finish(true);
@@ -97,7 +99,7 @@ public class PrepareRunner implements Runnable {
channel.dispose();// response.init(channel); 在调用之前异常 channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel(); response.removeChannel();
response.finish(true); response.finish(true);
if (te != null && context.logger.isLoggable(Level.FINEST)) { if (context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te); context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te);
} }
} }
@@ -116,7 +118,8 @@ public class PrepareRunner implements Runnable {
if (buffer.hasRemaining()) { if (buffer.hasRemaining()) {
request.setMoredata(buffer); request.setMoredata(buffer);
} else { } else {
response.request.offerReadBuffer(buffer); buffer.clear();
channel.setReadBuffer(buffer);
} }
preparer.prepare(request, response); preparer.prepare(request, response);
} else { } else {
@@ -137,7 +140,8 @@ public class PrepareRunner implements Runnable {
if (attachment.hasRemaining()) { if (attachment.hasRemaining()) {
request.setMoredata(attachment); request.setMoredata(attachment);
} else { } else {
response.request.offerReadBuffer(attachment); attachment.clear();
channel.setReadBuffer(attachment);
} }
try { try {
preparer.prepare(request, response); preparer.prepare(request, response);
@@ -151,7 +155,8 @@ public class PrepareRunner implements Runnable {
@Override @Override
public void failed(Throwable exc, ByteBuffer attachment) { public void failed(Throwable exc, ByteBuffer attachment) {
preparer.illRequestCounter.incrementAndGet(); preparer.illRequestCounter.incrementAndGet();
response.request.offerReadBuffer(attachment); attachment.clear();
channel.setReadBuffer(attachment);
response.finish(true); response.finish(true);
if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, force to close channel ", exc); if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, force to close channel ", exc);
} }
@@ -175,19 +180,4 @@ public class PrepareRunner implements Runnable {
return response.removeChannel(); return response.removeChannel();
} }
protected ByteBuffer pollReadBuffer(Request request) {
return request.pollReadBuffer();
}
protected ByteBuffer pollReadBuffer(Response response) {
return response.request.pollReadBuffer();
}
protected void offerReadBuffer(Request request, ByteBuffer buffer) {
request.offerReadBuffer(buffer);
}
protected void offerReadBuffer(Response response, ByteBuffer buffer) {
response.request.offerReadBuffer(buffer);
}
} }

View File

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

View File

@@ -40,8 +40,6 @@ public abstract class Request<C extends Context> {
protected AsyncConnection channel; protected AsyncConnection channel;
protected ByteBuffer readBuffer;
/** /**
* properties 与 attributes 的区别在于调用recycle时 attributes会被清空而properties会保留; * properties 与 attributes 的区别在于调用recycle时 attributes会被清空而properties会保留;
* properties 通常存放需要永久绑定在request里的一些对象 * properties 通常存放需要永久绑定在request里的一些对象
@@ -67,23 +65,6 @@ public abstract class Request<C extends Context> {
return rs; return rs;
} }
protected ByteBuffer pollReadBuffer() {
ByteBuffer buffer = this.readBuffer;
this.readBuffer = null;
if (buffer == null) buffer = bufferPool.get();
return buffer;
}
protected void offerReadBuffer(ByteBuffer buffer) {
if (buffer == null) return;
if (this.readBuffer == null) {
buffer.clear();
this.readBuffer = buffer;
} else {
bufferPool.accept(buffer);
}
}
/** /**
* 返回值Integer.MIN_VALUE: 帧数据; -1数据不合法 0解析完毕 &gt;0: 需再读取的字节数。 * 返回值Integer.MIN_VALUE: 帧数据; -1数据不合法 0解析完毕 &gt;0: 需再读取的字节数。
* *

View File

@@ -27,18 +27,12 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected final C context; protected final C context;
protected final ObjectPool<ByteBuffer> bufferPool; protected final ObjectPool<Response> responsePool; //虚拟构建的Response可能不存在responsePool
protected final ObjectPool<Response> responsePool;
protected final R request; protected final R request;
protected AsyncConnection channel; protected AsyncConnection channel;
protected ByteBuffer writeHeadBuffer;
protected ByteBuffer writeBodyBuffer;
private volatile boolean inited = true; private volatile boolean inited = true;
protected Object output; //输出的结果对象 protected Object output; //输出的结果对象
@@ -49,8 +43,6 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected Servlet<C, R, ? extends Response<C, R>> servlet; protected Servlet<C, R, ? extends Response<C, R>> servlet;
private Supplier<ByteBuffer> bodyBufferSupplier;
private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() { private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() {
@Override @Override
@@ -58,31 +50,21 @@ public abstract class Response<C extends Context, R extends Request<C>> {
if (attachment.hasRemaining()) { if (attachment.hasRemaining()) {
channel.write(attachment, attachment, this); channel.write(attachment, attachment, this);
} else { } else {
offerResponseBuffer(attachment); channel.offerBuffer(attachment);
ByteBuffer data = request.removeMoredata();
final boolean more = data != null && request.keepAlive;
request.more = more;
finish(); finish();
if (more) new PrepareRunner(context, responsePool, request.channel, null, Response.this).run();
} }
} }
@Override @Override
public void failed(Throwable exc, ByteBuffer attachment) { public void failed(Throwable exc, ByteBuffer attachment) {
offerResponseBuffer(attachment); channel.offerBuffer(attachment);
finish(true); finish(true);
} }
private void offerResponseBuffer(ByteBuffer attachment) {
if (writeHeadBuffer == null) {
if (bufferPool.getRecyclerPredicate().test(attachment)) {
writeHeadBuffer = attachment;
}
} else if (writeBodyBuffer == null) {
if (bufferPool.getRecyclerPredicate().test(attachment)) {
writeBodyBuffer = attachment;
}
} else {
bufferPool.accept(attachment);
}
}
}; };
private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() { private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() {
@@ -99,73 +81,36 @@ public abstract class Response<C extends Context, R extends Request<C>> {
if (index >= 0) { if (index >= 0) {
channel.write(attachments, index, attachments.length - index, attachments, this); channel.write(attachments, index, attachments.length - index, attachments, this);
} else { } else {
offerResponseBuffer(attachments); for (ByteBuffer attachment : attachments) {
channel.offerBuffer(attachment);
}
ByteBuffer data = request.removeMoredata();
final boolean more = data != null && request.keepAlive;
request.more = more;
finish(); finish();
if (more) new PrepareRunner(context, responsePool, request.channel, null, Response.this).run();
} }
} }
@Override @Override
public void failed(Throwable exc, final ByteBuffer[] attachments) { public void failed(Throwable exc, final ByteBuffer[] attachments) {
offerResponseBuffer(attachments); for (ByteBuffer attachment : attachments) {
channel.offerBuffer(attachment);
}
finish(true); finish(true);
} }
private void offerResponseBuffer(ByteBuffer[] attachments) {
int start = 0;
if (writeHeadBuffer == null && attachments.length > start) {
if (bufferPool.getRecyclerPredicate().test(attachments[start])) {
writeHeadBuffer = attachments[start];
start++;
}
}
if (writeBodyBuffer == null && attachments.length > start) {
if (bufferPool.getRecyclerPredicate().test(attachments[start])) {
writeBodyBuffer = attachments[start];
start++;
}
}
for (int i = start; i < attachments.length; i++) {
bufferPool.accept(attachments[i]);
}
}
}; };
protected Response(C context, final R request, ObjectPool<Response> responsePool) { protected Response(C context, final R request, ObjectPool<Response> responsePool) {
this.context = context; this.context = context;
this.request = request; this.request = request;
this.bufferPool = request.bufferPool;
this.responsePool = responsePool; this.responsePool = responsePool;
this.writeHeadBuffer = bufferPool.get();
this.writeBodyBuffer = bufferPool.get();
this.bodyBufferSupplier = () -> {
ByteBuffer buffer = writeBodyBuffer;
if (buffer == null) return bufferPool.get();
writeBodyBuffer = null;
return buffer;
};
}
protected ByteBuffer pollWriteReadBuffer() {
ByteBuffer buffer = this.writeHeadBuffer;
this.writeHeadBuffer = null;
if (buffer == null) buffer = bufferPool.get();
return buffer;
}
protected ByteBuffer pollWriteBodyBuffer() {
ByteBuffer buffer = this.writeBodyBuffer;
this.writeBodyBuffer = null;
if (buffer == null) buffer = bufferPool.get();
return buffer;
}
protected Supplier<ByteBuffer> getBodyBufferSupplier() {
return bodyBufferSupplier;
} }
protected void offerBuffer(ByteBuffer... buffers) { protected void offerBuffer(ByteBuffer... buffers) {
for (ByteBuffer buffer : buffers) { for (ByteBuffer buffer : buffers) {
bufferPool.accept(buffer); channel.offerBuffer(buffer);
} }
} }
@@ -278,7 +223,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
public void finish(final byte[] bs) { public void finish(final byte[] bs) {
if (!this.inited) return; //避免重复关闭 if (!this.inited) return; //避免重复关闭
if (this.context.bufferCapacity == bs.length) { if (this.context.bufferCapacity == bs.length) {
ByteBuffer buffer = this.bufferPool.get(); ByteBuffer buffer = channel.getBufferSupplier().get();
buffer.put(bs); buffer.put(bs);
buffer.flip(); buffer.flip();
this.finish(buffer); this.finish(buffer);
@@ -289,33 +234,33 @@ public abstract class Response<C extends Context, R extends Request<C>> {
public void finish(ByteBuffer buffer) { public void finish(ByteBuffer buffer) {
if (!this.inited) return; //避免重复关闭 if (!this.inited) return; //避免重复关闭
ByteBuffer data = this.request.removeMoredata();
final AsyncConnection conn = this.channel; final AsyncConnection conn = this.channel;
final boolean more = data != null && this.request.keepAlive; // ByteBuffer data = this.request.removeMoredata();
this.request.more = more; // final boolean more = data != null && this.request.keepAlive;
// this.request.more = more;
conn.write(buffer, buffer, finishHandler); conn.write(buffer, buffer, finishHandler);
if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run(); // if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
} }
public void finish(boolean kill, ByteBuffer buffer) { public void finish(boolean kill, ByteBuffer buffer) {
if (!this.inited) return; //避免重复关闭 if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive(); if (kill) refuseAlive();
ByteBuffer data = this.request.removeMoredata();
final AsyncConnection conn = this.channel; final AsyncConnection conn = this.channel;
final boolean more = data != null && this.request.keepAlive; // ByteBuffer data = this.request.removeMoredata();
this.request.more = more; // final boolean more = data != null && this.request.keepAlive;
// this.request.more = more;
conn.write(buffer, buffer, finishHandler); conn.write(buffer, buffer, finishHandler);
if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run(); // if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
} }
public void finish(ByteBuffer... buffers) { public void finish(ByteBuffer... buffers) {
if (!this.inited) return; //避免重复关闭 if (!this.inited) return; //避免重复关闭
final AsyncConnection conn = this.channel; final AsyncConnection conn = this.channel;
ByteBuffer data = this.request.removeMoredata(); // ByteBuffer data = this.request.removeMoredata();
final boolean more = data != null && this.request.keepAlive; // final boolean more = data != null && this.request.keepAlive;
this.request.more = more; // this.request.more = more;
conn.write(buffers, buffers, finishHandler2); conn.write(buffers, buffers, finishHandler2);
if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run(); // if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
} }
public void finish(boolean kill, ByteBuffer... buffers) { public void finish(boolean kill, ByteBuffer... buffers) {
@@ -337,14 +282,14 @@ public abstract class Response<C extends Context, R extends Request<C>> {
if (buffer.hasRemaining()) { if (buffer.hasRemaining()) {
channel.write(buffer, attachment, this); channel.write(buffer, attachment, this);
} else { } else {
bufferPool.accept(buffer); channel.offerBuffer(buffer);
if (handler != null) handler.completed(result, attachment); if (handler != null) handler.completed(result, attachment);
} }
} }
@Override @Override
public void failed(Throwable exc, A attachment) { public void failed(Throwable exc, A attachment) {
bufferPool.accept(buffer); channel.offerBuffer(buffer);
if (handler != null) handler.failed(exc, attachment); if (handler != null) handler.failed(exc, attachment);
} }
@@ -362,7 +307,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
index = i; index = i;
break; break;
} }
bufferPool.accept(buffers[i]); channel.offerBuffer(buffers[i]);
} }
if (index == 0) { if (index == 0) {
channel.write(buffers, attachment, this); channel.write(buffers, attachment, this);
@@ -376,7 +321,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
@Override @Override
public void failed(Throwable exc, A attachment) { public void failed(Throwable exc, A attachment) {
for (ByteBuffer buffer : buffers) { for (ByteBuffer buffer : buffers) {
bufferPool.accept(buffer); channel.offerBuffer(buffer);
} }
if (handler != null) handler.failed(exc, attachment); if (handler != null) handler.failed(exc, attachment);
} }

View File

@@ -86,7 +86,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
protected int threads; protected int threads;
//线程池 //线程池
protected ThreadPoolExecutor executor; protected ThreadPoolExecutor workExecutor;
//ByteBuffer池大小 //ByteBuffer池大小
protected int bufferPoolSize; protected int bufferPoolSize;
@@ -125,11 +125,11 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.aliveTimeoutSeconds = config.getIntValue("aliveTimeoutSeconds", 30); this.aliveTimeoutSeconds = config.getIntValue("aliveTimeoutSeconds", 30);
this.readTimeoutSeconds = config.getIntValue("readTimeoutSeconds", 0); this.readTimeoutSeconds = config.getIntValue("readTimeoutSeconds", 0);
this.writeTimeoutSeconds = config.getIntValue("writeTimeoutSeconds", 0); this.writeTimeoutSeconds = config.getIntValue("writeTimeoutSeconds", 0);
this.backlog = parseLenth(config.getValue("backlog"), 8 * 1024); this.backlog = parseLenth(config.getValue("backlog"), 1024);
this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024); this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024);
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), "UDP".equalsIgnoreCase(protocol) ? 1350 : 32 * 1024); int bufCapacity = parseLenth(config.getValue("bufferCapacity"), "UDP".equalsIgnoreCase(protocol) ? 1350 : 32 * 1024);
this.bufferCapacity = "UDP".equalsIgnoreCase(protocol) ? bufCapacity : (bufCapacity < 8 * 1024 ? 8 * 1024 : bufCapacity); this.bufferCapacity = "UDP".equalsIgnoreCase(protocol) ? bufCapacity : (bufCapacity < 8 * 1024 ? 8 * 1024 : bufCapacity);
this.threads = config.getIntValue("threads", Runtime.getRuntime().availableProcessors() * 32); this.threads = config.getIntValue("threads", Math.max(8, Runtime.getRuntime().availableProcessors() * 2));
this.bufferPoolSize = config.getIntValue("bufferPoolSize", this.threads * 4); this.bufferPoolSize = config.getIntValue("bufferPoolSize", this.threads * 4);
this.responsePoolSize = config.getIntValue("responsePoolSize", this.threads * 2); this.responsePoolSize = config.getIntValue("responsePoolSize", this.threads * 2);
this.name = config.getValue("name", "Server-" + protocol + "-" + this.address.getPort()); this.name = config.getValue("name", "Server-" + protocol + "-" + this.address.getPort());
@@ -151,9 +151,9 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
final AtomicInteger counter = new AtomicInteger(); final AtomicInteger counter = new AtomicInteger();
final Format f = createFormat(); final Format f = createFormat();
final String n = name; final String n = name;
this.executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threads, (Runnable r) -> { this.workExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new WorkThread(executor, r); Thread t = new WorkThread(workExecutor, r);
t.setName(n + "-ServletThread-" + f.format(counter.incrementAndGet())); t.setName("Redkale-" + n + "-ServletThread-" + f.format(counter.incrementAndGet()));
return t; return t;
}); });
} }
@@ -187,8 +187,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return resourceFactory; return resourceFactory;
} }
public ThreadPoolExecutor getExecutor() { public ThreadPoolExecutor getWorkExecutor() {
return executor; return workExecutor;
} }
public InetSocketAddress getSocketAddress() { 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() + "] "; final String threadName = "[" + Thread.currentThread().getName() + "] ";
postStart(); postStart();
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address 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"); + ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
} }

View File

@@ -5,7 +5,7 @@
*/ */
package org.redkale.net; package org.redkale.net;
import java.io.IOException; import java.io.*;
import java.net.*; import java.net.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
@@ -22,10 +22,9 @@ import javax.net.ssl.SSLContext;
* *
* @author zhangjx * @author zhangjx
*/ */
public class TcpAioAsyncConnection extends AsyncConnection { class TcpAioAsyncConnection extends AsyncConnection {
private final Semaphore semaphore = new Semaphore(1);
//private final Semaphore semaphore = new Semaphore(1);
private int readTimeoutSeconds; private int readTimeoutSeconds;
private int writeTimeoutSeconds; private int writeTimeoutSeconds;
@@ -40,7 +39,7 @@ public class TcpAioAsyncConnection extends AsyncConnection {
final AsynchronousSocketChannel ch, final SSLContext sslContext, final SocketAddress addr0, final AsynchronousSocketChannel ch, final SSLContext sslContext, final SocketAddress addr0,
final int readTimeoutSeconds, final int writeTimeoutSeconds, final int readTimeoutSeconds, final int writeTimeoutSeconds,
final AtomicLong livingCounter, final AtomicLong closedCounter) { final AtomicLong livingCounter, final AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext); super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch; this.channel = ch;
this.readTimeoutSeconds = readTimeoutSeconds; this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSeconds = writeTimeoutSeconds; this.writeTimeoutSeconds = writeTimeoutSeconds;
@@ -53,8 +52,6 @@ public class TcpAioAsyncConnection extends AsyncConnection {
} }
} }
this.remoteAddress = addr; this.remoteAddress = addr;
this.livingCounter = livingCounter;
this.closedCounter = closedCounter;
} }
@Override @Override
@@ -103,53 +100,52 @@ public class TcpAioAsyncConnection extends AsyncConnection {
} }
} }
private <A> void nextWrite(Throwable exc, A attachment) { // private <A> void nextWrite(Throwable exc, A attachment) {
BlockingQueue<WriteEntry> queue = this.writeQueue; // BlockingQueue<WriteEntry> queue = this.writeQueue;
if (queue != null && exc != null && !isOpen()) { // if (queue != null && exc != null && !isOpen()) {
WriteEntry entry; // WriteEntry entry;
while ((entry = queue.poll()) != null) { // while ((entry = queue.poll()) != null) {
try { // try {
entry.writeHandler.failed(exc, entry.writeAttachment); // entry.writeHandler.failed(exc, entry.writeAttachment);
} catch (Throwable e) { // } catch (Throwable e) {
e.printStackTrace(System.err); // e.printStackTrace(System.err);
} // }
} // }
return; // return;
} // }
WriteEntry entry = queue == null ? null : queue.poll(); // WriteEntry entry = queue == null ? null : queue.poll();
//
if (entry != null) { // if (entry != null) {
try { // try {
if (entry.writeOneBuffer == null) { // if (entry.writeOneBuffer == null) {
write(false, entry.writeBuffers, entry.writeOffset, entry.writeLength, entry.writeAttachment, entry.writeHandler); // write(false, entry.writeBuffers, entry.writeOffset, entry.writeLength, entry.writeAttachment, entry.writeHandler);
} else { // } else {
write(false, entry.writeOneBuffer, entry.writeAttachment, entry.writeHandler); // write(false, entry.writeOneBuffer, entry.writeAttachment, entry.writeHandler);
} // }
} catch (Exception e) { // } catch (Exception e) {
entry.writeHandler.failed(e, entry.writeAttachment); // entry.writeHandler.failed(e, entry.writeAttachment);
} // }
} else { // } else {
semaphore.release(); // semaphore.release();
} // }
} // }
@Override @Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) { public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
write(true, src, attachment, handler); write(true, src, attachment, handler);
} }
private <A> void write(boolean acquire, ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) { private <A> void write(boolean acquire, ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
if (acquire && !semaphore.tryAcquire()) { // if (acquire && !semaphore.tryAcquire()) {
if (this.writeQueue == null) { // if (this.writeQueue == null) {
synchronized (semaphore) { // synchronized (semaphore) {
if (this.writeQueue == null) { // if (this.writeQueue == null) {
this.writeQueue = new LinkedBlockingDeque<>(); // this.writeQueue = new LinkedBlockingDeque<>();
} // }
} // }
} // }
this.writeQueue.add(new WriteEntry(src, attachment, handler)); // this.writeQueue.add(new WriteEntry(src, attachment, handler));
return; // return;
} // }
WriteOneCompletionHandler newHandler = new WriteOneCompletionHandler(src, handler); WriteOneCompletionHandler newHandler = new WriteOneCompletionHandler(src, handler);
if (!channel.isOpen()) { if (!channel.isOpen()) {
newHandler.failed(new ClosedChannelException(), attachment); newHandler.failed(new ClosedChannelException(), attachment);
@@ -173,17 +169,17 @@ public class TcpAioAsyncConnection extends AsyncConnection {
} }
private <A> void write(boolean acquire, ByteBuffer[] srcs, int offset, int length, A attachment, final CompletionHandler<Integer, ? super A> handler) { private <A> void write(boolean acquire, ByteBuffer[] srcs, int offset, int length, A attachment, final CompletionHandler<Integer, ? super A> handler) {
if (acquire && !semaphore.tryAcquire()) { // if (acquire && !semaphore.tryAcquire()) {
if (this.writeQueue == null) { // if (this.writeQueue == null) {
synchronized (semaphore) { // synchronized (semaphore) {
if (this.writeQueue == null) { // if (this.writeQueue == null) {
this.writeQueue = new LinkedBlockingDeque<>(); // this.writeQueue = new LinkedBlockingDeque<>();
} // }
} // }
} // }
this.writeQueue.add(new WriteEntry(srcs, offset, length, attachment, handler)); // this.writeQueue.add(new WriteEntry(srcs, offset, length, attachment, handler));
return; // return;
} // }
WriteMoreCompletionHandler newHandler = new WriteMoreCompletionHandler(srcs, offset, length, handler); WriteMoreCompletionHandler newHandler = new WriteMoreCompletionHandler(srcs, offset, length, handler);
if (!channel.isOpen()) { if (!channel.isOpen()) {
newHandler.failed(new ClosedChannelException(), attachment); newHandler.failed(new ClosedChannelException(), attachment);
@@ -232,21 +228,55 @@ public class TcpAioAsyncConnection extends AsyncConnection {
} }
@Override @Override
public final int read(ByteBuffer dst) throws IOException { public final ReadableByteChannel readableByteChannel() {
try { return new ReadableByteChannel() {
return channel.read(dst).get(); @Override
} catch (InterruptedException | ExecutionException e) { public int read(ByteBuffer dst) throws IOException {
throw new IOException(e); 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 @Override
public final int write(ByteBuffer src) throws IOException { public final WritableByteChannel writableByteChannel() {
try { return new WritableByteChannel() {
return channel.write(src).get(); @Override
} catch (InterruptedException | ExecutionException e) { public int write(ByteBuffer src) throws IOException {
throw new IOException(e); 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 @Override
@@ -300,16 +330,16 @@ public class TcpAioAsyncConnection extends AsyncConnection {
if (result >= 0) { if (result >= 0) {
writeCount += result; writeCount += result;
try { try {
int index = -1; int incre = -1;
for (int i = writeOffset; i < (writeOffset + writeLength); i++) { for (int i = writeOffset; i < (writeOffset + writeLength); i++) {
if (writeBuffers[i].hasRemaining()) { if (writeBuffers[i].hasRemaining()) {
index = i; incre = i - writeOffset;
break; break;
} }
} }
if (index >= 0) { if (incre >= 0) {
writeOffset += index; writeOffset += incre;
writeLength -= index; writeLength -= incre;
channel.write(writeBuffers, writeOffset, writeLength, writeTimeoutSeconds > 0 ? writeTimeoutSeconds : 60, TimeUnit.SECONDS, attachment, this); channel.write(writeBuffers, writeOffset, writeLength, writeTimeoutSeconds > 0 ? writeTimeoutSeconds : 60, TimeUnit.SECONDS, attachment, this);
return; return;
} }
@@ -317,27 +347,27 @@ public class TcpAioAsyncConnection extends AsyncConnection {
failed(e, attachment); failed(e, attachment);
return; return;
} }
try { // try {
writeHandler.completed(writeCount, attachment); writeHandler.completed(writeCount, attachment);
} finally { // } finally {
nextWrite(null, attachment); // nextWrite(null, attachment);
} // }
} else { } else {
try { // try {
writeHandler.completed(result.intValue(), attachment); writeHandler.completed(result.intValue(), attachment);
} finally { // } finally {
nextWrite(null, attachment); // nextWrite(null, attachment);
} // }
} }
} }
@Override @Override
public void failed(Throwable exc, A attachment) { public void failed(Throwable exc, A attachment) {
try { // try {
writeHandler.failed(exc, attachment); writeHandler.failed(exc, attachment);
} finally { // } finally {
nextWrite(exc, attachment); // nextWrite(exc, attachment);
} // }
} }
} }
@@ -364,21 +394,21 @@ public class TcpAioAsyncConnection extends AsyncConnection {
failed(e, attachment); failed(e, attachment);
return; return;
} }
try { // try {
writeHandler.completed(result, attachment); writeHandler.completed(result, attachment);
} finally { // } finally {
nextWrite(null, attachment); // nextWrite(null, attachment);
} // }
} }
@Override @Override
public void failed(Throwable exc, A attachment) { public void failed(Throwable exc, A attachment) {
try { // try {
writeHandler.failed(exc, attachment); writeHandler.failed(exc, attachment);
} finally { // } finally {
nextWrite(exc, attachment); // nextWrite(exc, attachment);
} // }
} }
} }

View File

@@ -104,7 +104,8 @@ public class TcpAioProtocolServer extends ProtocolServer {
AsyncConnection conn = new TcpAioAsyncConnection(bufferPool, bufferPool, channel, AsyncConnection conn = new TcpAioAsyncConnection(bufferPool, bufferPool, channel,
context.getSSLContext(), null, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter); context.getSSLContext(), null, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter);
context.runAsync(new PrepareRunner(context, responsePool, conn, null, null)); //context.runAsync(new PrepareRunner(context, responsePool, conn, null, null));
new PrepareRunner(context, responsePool, conn, null, null).run();
} catch (Throwable e) { } catch (Throwable e) {
context.logger.log(Level.INFO, channel + " accept error", e); context.logger.log(Level.INFO, channel + " accept error", e);
} }

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

View File

@@ -21,8 +21,8 @@ import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* System.getProperty("net.transport.pinginterval", "30") 心跳周期默认30秒 * System.getProperty("net.transport.ping.interval", "30") 心跳周期默认30秒
* System.getProperty("net.transport.checkinterval", "30") 检查不可用地址周期默认30秒 * System.getProperty("net.transport.check.interval", "30") 检查不可用地址周期默认30秒
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -65,10 +65,10 @@ public class TransportFactory {
protected final List<WeakReference<Transport>> transportReferences = new CopyOnWriteArrayList<>(); 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; protected int pinginterval;
@@ -119,7 +119,7 @@ public class TransportFactory {
if (this.checkinterval < 2) this.checkinterval = 2; if (this.checkinterval < 2) this.checkinterval = 2;
} }
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, this.getClass().getSimpleName() + "-TransportFactoryTask-Thread"); final Thread t = new Thread(r, "Redkale-" + this.getClass().getSimpleName() + "-Schedule-Thread");
t.setDaemon(true); t.setDaemon(true);
return t; return t;
}); });
@@ -162,7 +162,7 @@ public class TransportFactory {
ExecutorService transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> { ExecutorService transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r); Thread t = new Thread(r);
t.setDaemon(true); t.setDaemon(true);
t.setName("Transport-Thread-" + counter.incrementAndGet()); t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet());
return t; return t;
}); });
AsynchronousChannelGroup transportGroup = null; AsynchronousChannelGroup transportGroup = null;
@@ -203,16 +203,11 @@ public class TransportFactory {
} }
public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) { 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) { 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); 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);
} }
public String findGroupName(InetSocketAddress addr) { 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"); 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); 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.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) { for (InetSocketAddress addr : info.addresses) {
if (!groupAddrs.getOrDefault(addr, info.name).equals(info.name)) throw new RuntimeException(addr + " repeat but different group.name"); 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; return true;
} }
public Transport loadSameGroupTransport(InetSocketAddress sncpAddress) { public Transport loadTransport(InetSocketAddress sncpAddress, final Set<String> groups) {
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) {
if (groups == null) return null; if (groups == null) return null;
Set<InetSocketAddress> addresses = new HashSet<>(); Set<InetSocketAddress> addresses = new HashSet<>();
TransportGroupInfo info = null; TransportGroupInfo info = null;
@@ -293,14 +271,7 @@ public class TransportFactory {
} }
if (info == null) info = new TransportGroupInfo("TCP"); if (info == null) info = new TransportGroupInfo("TCP");
if (sncpAddress != null) addresses.remove(sncpAddress); 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); return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, 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);
} }
public ExecutorService getExecutor() { public ExecutorService getExecutor() {

View File

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

View File

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

View File

@@ -32,6 +32,7 @@ public class HttpContext extends Context {
public HttpContext(HttpContextConfig config) { public HttpContext(HttpContextConfig config) {
super(config); super(config);
this.remoteAddrHeader = config.remoteAddrHeader;
random.setSeed(Math.abs(System.nanoTime())); random.setSeed(Math.abs(System.nanoTime()));
} }

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -37,7 +37,7 @@ public class HttpResourceServlet extends HttpServlet {
public WatchThread(File root) throws IOException { public WatchThread(File root) throws IOException {
this.root = root; this.root = root;
this.setName("HttpResourceServlet-Watch-Thread"); this.setName("Redkale-HttpResourceServlet-Watch-Thread");
this.setDaemon(true); this.setDaemon(true);
this.watcher = this.root.toPath().getFileSystem().newWatchService(); this.watcher = this.root.toPath().getFileSystem().newWatchService();
} }

View File

@@ -50,6 +50,10 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
protected static final byte[] connectAliveBytes = "Connection: keep-alive\r\n".getBytes(); protected static final byte[] connectAliveBytes = "Connection: keep-alive\r\n".getBytes();
private static final byte[] fillContentLengthBytes = ("Content-Length: \r\n").getBytes();
private static final ZoneId ZONE_GMT = ZoneId.of("GMT");
private static final Set<OpenOption> options = new HashSet<>(); private static final Set<OpenOption> options = new HashSet<>();
private static final Map<Integer, String> httpCodes = new HashMap<>(); private static final Map<Integer, String> httpCodes = new HashMap<>();
@@ -103,8 +107,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
httpCodes.put(505, "HTTP Version Not Supported"); httpCodes.put(505, "HTTP Version Not Supported");
} }
private static final ZoneId ZONE_GMT = ZoneId.of("GMT");
private int status = 200; private int status = 200;
private String contentType = ""; private String contentType = "";
@@ -113,9 +115,17 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private HttpCookie[] cookies; private HttpCookie[] cookies;
private boolean headsended = false; private int headWritedSize = -1; //0表示跳过header正数表示header的字节长度。
private ByteBuffer headBuffer;
private int headLenPos = -1;
private BiFunction<HttpResponse, ByteBuffer[], ByteBuffer[]> bufferHandler; private BiFunction<HttpResponse, ByteBuffer[], ByteBuffer[]> bufferHandler;
private BiFunction<HttpRequest, org.redkale.service.RetResult, org.redkale.service.RetResult> retResultHandler;
private Supplier<ByteBuffer> bodyBufferSupplier;
//------------------------------------------------ //------------------------------------------------
private final String plainContentType; private final String plainContentType;
@@ -150,19 +160,24 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
public HttpResponse(HttpContext context, HttpRequest request, ObjectPool<Response> responsePool, HttpResponseConfig config) { public HttpResponse(HttpContext context, HttpRequest request, ObjectPool<Response> responsePool, HttpResponseConfig config) {
super(context, request, responsePool); super(context, request, responsePool);
this.plainContentType = config.plainContentType == null || config.plainContentType.isEmpty() ? "text/plain; charset=utf-8" : config.plainContentType; this.plainContentType = config == null || 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.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.plainContentTypeBytes = ("Content-Type: " + this.plainContentType + "\r\n").getBytes();
this.jsonContentTypeBytes = ("Content-Type: " + this.jsonContentType + "\r\n").getBytes(); this.jsonContentTypeBytes = ("Content-Type: " + this.jsonContentType + "\r\n").getBytes();
this.defaultAddHeaders = config.defaultAddHeaders; this.defaultAddHeaders = config == null ? null : config.defaultAddHeaders;
this.defaultSetHeaders = config.defaultSetHeaders; this.defaultSetHeaders = config == null ? null : config.defaultSetHeaders;
this.defaultCookie = config.defaultCookie; this.defaultCookie = config == null ? null : config.defaultCookie;
this.autoOptions = config.autoOptions; this.autoOptions = config == null ? false : config.autoOptions;
this.dateSupplier = config.dateSupplier; this.dateSupplier = config == null ? null : config.dateSupplier;
this.renders = config.renders; this.renders = config == null ? null : config.renders;
this.hasRender = renders != null && !renders.isEmpty(); this.hasRender = renders != null && !renders.isEmpty();
this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null; this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null;
this.contentType = this.plainContentType; this.contentType = this.plainContentType;
this.bodyBufferSupplier = () -> {
if (headWritedSize >= 0 || bufferHandler != null) return channel.pollWriteBuffer(); //bufferHandler 需要cached的请求不能带上header
if (contentLength < 0) contentLength = -2;
return createHeader();
};
} }
@Override @Override
@@ -185,12 +200,19 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.contentLength = -1; this.contentLength = -1;
this.contentType = null; this.contentType = null;
this.cookies = null; this.cookies = null;
this.headsended = false; this.headWritedSize = -1;
this.headBuffer = null;
this.headLenPos = -1;
this.header.clear(); this.header.clear();
this.bufferHandler = null; this.bufferHandler = null;
this.retResultHandler = null;
return super.recycle(); return super.recycle();
} }
protected Supplier<ByteBuffer> getBodyBufferSupplier() {
return bodyBufferSupplier;
}
@Override @Override
protected void init(AsyncConnection channel) { protected void init(AsyncConnection channel) {
super.init(channel); super.init(channel);
@@ -285,15 +307,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler()); return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler());
} }
/**
* 获取ByteBuffer生成器
*
* @return ByteBuffer生成器
*/
public Supplier<ByteBuffer> getBufferSupplier() {
return getBodyBufferSupplier();
}
/** /**
* 将对象以JSON格式输出 * 将对象以JSON格式输出
* *
@@ -383,14 +396,19 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* *
* @param ret RetResult输出对象 * @param ret RetResult输出对象
*/ */
public void finishJson(final org.redkale.service.RetResult ret) { public void finishJson(org.redkale.service.RetResult ret) {
this.contentType = this.jsonContentType; this.contentType = this.jsonContentType;
if (this.retResultHandler != null) {
ret = this.retResultHandler.apply(this.request, ret);
}
if (this.recycleListener != null) this.output = ret; if (this.recycleListener != null) this.output = ret;
if (ret != null && !ret.isSuccess()) { if (ret != null && !ret.isSuccess()) {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())); this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
this.header.addValue("retinfo", ret.getRetinfo()); this.header.addValue("retinfo", ret.getRetinfo());
} }
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), ret)); Convert convert = ret == null ? null : ret.convert();
if (convert == null || !(convert instanceof TextConvert)) convert = request.getJsonConvert();
finish(convert.convertTo(getBodyBufferSupplier(), ret));
} }
/** /**
@@ -399,8 +417,11 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param convert 指定的JsonConvert * @param convert 指定的JsonConvert
* @param ret RetResult输出对象 * @param ret RetResult输出对象
*/ */
public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) { public void finishJson(final JsonConvert convert, org.redkale.service.RetResult ret) {
this.contentType = this.jsonContentType; this.contentType = this.jsonContentType;
if (this.retResultHandler != null) {
ret = this.retResultHandler.apply(this.request, ret);
}
if (this.recycleListener != null) this.output = ret; if (this.recycleListener != null) this.output = ret;
if (ret != null && !ret.isSuccess()) { if (ret != null && !ret.isSuccess()) {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())); this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
@@ -409,6 +430,26 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
finish(convert.convertTo(getBodyBufferSupplier(), ret)); 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格式输出 * 将CompletableFuture的结果对象以JSON格式输出
* *
@@ -470,7 +511,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param obj 输出对象 * @param obj 输出对象
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void finish(final Convert convert, final Type type, final Object obj) { public void finish(final Convert convert, final Type type, Object obj) {
if (obj == null) { if (obj == null) {
finish("null"); finish("null");
} else if (obj instanceof CompletableFuture) { } else if (obj instanceof CompletableFuture) {
@@ -497,17 +538,10 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
context.getLogger().log(Level.WARNING, "HttpServlet finish File occur, force to close channel. request = " + getRequest(), e); context.getLogger().log(Level.WARNING, "HttpServlet finish File occur, force to close channel. request = " + getRequest(), e);
finish(500, null); finish(500, null);
} }
} else if (obj instanceof org.redkale.service.RetResult) {
finishJson((org.redkale.service.RetResult) obj);
} else if (obj instanceof HttpResult) { } else if (obj instanceof HttpResult) {
HttpResult result = (HttpResult) obj; finish(convert, (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 {
finish(result.getConvert() == null ? convert : result.getConvert(), result.getResult());
}
} else { } else {
if (hasRender) { if (hasRender) {
if (onlyoneHttpRender != null) { if (onlyoneHttpRender != null) {
@@ -533,10 +567,18 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (this.recycleListener != null) this.output = obj; if (this.recycleListener != null) this.output = obj;
if (obj instanceof org.redkale.service.RetResult) { if (obj instanceof org.redkale.service.RetResult) {
org.redkale.service.RetResult ret = (org.redkale.service.RetResult) obj; org.redkale.service.RetResult ret = (org.redkale.service.RetResult) obj;
if (this.retResultHandler != null) {
ret = this.retResultHandler.apply(this.request, ret);
obj = ret;
}
if (!ret.isSuccess()) { if (!ret.isSuccess()) {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())).addValue("retinfo", ret.getRetinfo()); 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) ByteBuffer[] buffers = type == null ? convert.convertTo(getBodyBufferSupplier(), obj)
: convert.convertTo(getBodyBufferSupplier(), type, obj); : convert.convertTo(getBodyBufferSupplier(), type, obj);
finish(buffers); finish(buffers);
@@ -631,7 +673,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
public void finish(final String contentType, final byte[] bs) { public void finish(final String contentType, final byte[] bs) {
if (isClosed()) return; //避免重复关闭 if (isClosed()) return; //避免重复关闭
final byte[] content = bs == null ? new byte[0] : bs; final byte[] content = bs == null ? new byte[0] : bs;
if (!this.headsended) { if (this.headWritedSize < 0) {
this.contentType = contentType; this.contentType = contentType;
this.contentLength = content.length; this.contentLength = content.length;
ByteBuffer headbuf = createHeader(); ByteBuffer headbuf = createHeader();
@@ -675,7 +717,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
@Override @Override
public void finish(boolean kill, ByteBuffer buffer) { public void finish(boolean kill, ByteBuffer buffer) {
if (isClosed()) return; //避免重复关闭 if (isClosed()) return; //避免重复关闭
if (!this.headsended) { if (this.headWritedSize < 0) {
this.contentLength = buffer == null ? 0 : buffer.remaining(); this.contentLength = buffer == null ? 0 : buffer.remaining();
ByteBuffer headbuf = createHeader(); ByteBuffer headbuf = createHeader();
headbuf.flip(); headbuf.flip();
@@ -713,7 +755,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (bufs != null) buffers = bufs; if (bufs != null) buffers = bufs;
} }
if (kill) refuseAlive(); if (kill) refuseAlive();
if (!this.headsended) { if (this.headWritedSize < 0) {
long len = 0; long len = 0;
for (ByteBuffer buf : buffers) { for (ByteBuffer buf : buffers) {
len += buf.remaining(); len += buf.remaining();
@@ -730,6 +772,17 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
super.finish(kill, newbuffers); super.finish(kill, newbuffers);
} }
} else { } else {
if (this.headLenPos > 0 && buffers[0] == headBuffer) {
long contentlen = -this.headWritedSize;
for (ByteBuffer buf : buffers) {
contentlen += buf.remaining();
}
byte[] lenBytes = String.valueOf(contentlen).getBytes();
int start = this.headLenPos - lenBytes.length;
for (int i = 0; i < lenBytes.length; i++) {
headBuffer.put(start + i, lenBytes[i]);
}
}
super.finish(kill, buffers); super.finish(kill, buffers);
} }
} }
@@ -743,7 +796,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param handler 异步回调函数 * @param handler 异步回调函数
*/ */
public <A> void sendBody(ByteBuffer buffer, A attachment, CompletionHandler<Integer, A> handler) { public <A> void sendBody(ByteBuffer buffer, A attachment, CompletionHandler<Integer, A> handler) {
if (!this.headsended) { if (this.headWritedSize < 0) {
if (this.contentLength < 0) this.contentLength = buffer == null ? 0 : buffer.remaining(); if (this.contentLength < 0) this.contentLength = buffer == null ? 0 : buffer.remaining();
ByteBuffer headbuf = createHeader(); ByteBuffer headbuf = createHeader();
headbuf.flip(); headbuf.flip();
@@ -766,7 +819,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param handler 异步回调函数 * @param handler 异步回调函数
*/ */
public <A> void sendBody(ByteBuffer[] buffers, A attachment, CompletionHandler<Integer, A> handler) { public <A> void sendBody(ByteBuffer[] buffers, A attachment, CompletionHandler<Integer, A> handler) {
if (!this.headsended) { if (this.headWritedSize < 0) {
if (this.contentLength < 0) { if (this.contentLength < 0) {
int len = 0; int len = 0;
if (buffers != null && buffers.length > 0) { if (buffers != null && buffers.length > 0) {
@@ -849,7 +902,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} }
this.contentLength = length; this.contentLength = length;
if (filename != null && !filename.isEmpty() && file != null) { if (filename != null && !filename.isEmpty() && file != null) {
addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); if (this.header.getValue("Content-Disposition") == null) {
addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
}
} }
this.contentType = MimeType.getByFilename(filename == null || filename.isEmpty() ? file.getName() : filename); this.contentType = MimeType.getByFilename(filename == null || filename.isEmpty() ? file.getName() : filename);
if (this.contentType == null) this.contentType = "application/octet-stream"; if (this.contentType == null) this.contentType = "application/octet-stream";
@@ -891,14 +946,19 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
//Header大小不能超过一个ByteBuffer的容量 //Header大小不能超过一个ByteBuffer的容量
protected ByteBuffer createHeader() { protected ByteBuffer createHeader() {
this.headsended = true; ByteBuffer buffer = this.channel.pollWriteBuffer();
ByteBuffer buffer = this.pollWriteReadBuffer(); int oldpos = buffer.position();
if (this.status == 200) { if (this.status == 200) {
buffer.put(status200Bytes); buffer.put(status200Bytes);
} else { } else {
buffer.put(("HTTP/1.1 " + this.status + " " + httpCodes.get(this.status) + "\r\n").getBytes()); buffer.put(("HTTP/1.1 " + this.status + " " + httpCodes.get(this.status) + "\r\n").getBytes());
} }
if (this.contentLength >= 0) buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes()); if (this.contentLength >= 0) {
buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
} else if (this.contentLength == -2) {
buffer.put(fillContentLengthBytes);
this.headLenPos = buffer.position() - 2; //去掉\r\n
}
if (!this.request.isWebSocket()) { if (!this.request.isWebSocket()) {
if (this.contentType == this.jsonContentType) { if (this.contentType == this.jsonContentType) {
buffer.put(this.jsonContentTypeBytes); buffer.put(this.jsonContentTypeBytes);
@@ -966,14 +1026,16 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (defaultCookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defaultCookie.getDomain()); if (defaultCookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defaultCookie.getDomain());
if (defaultCookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defaultCookie.getPath()); 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); buffer.put(LINE);
this.headWritedSize = buffer.position() - oldpos;
this.headBuffer = buffer;
return buffer; return buffer;
} }
private CharSequence genString(HttpCookie cookie) { private CharSequence cookieString(HttpCookie cookie) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(cookie.getName()).append("=").append(cookie.getValue()).append("; Version=1"); sb.append(cookie.getName()).append("=").append(cookie.getValue()).append("; Version=1");
if (cookie.getDomain() != null) sb.append("; Domain=").append(cookie.getDomain()); if (cookie.getDomain() != null) sb.append("; Domain=").append(cookie.getDomain());
@@ -995,7 +1057,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @return HttpResponse * @return HttpResponse
*/ */
public HttpResponse skipHeader() { public HttpResponse skipHeader() {
this.headsended = true; this.headWritedSize = 0;
return this; return this;
} }
@@ -1125,6 +1187,24 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.bufferHandler = bufferHandler; this.bufferHandler = bufferHandler;
} }
/**
* 获取输出RetResult时的拦截器
*
* @return 拦截器
*/
protected BiFunction<HttpRequest, org.redkale.service.RetResult, org.redkale.service.RetResult> getRetResultHandler() {
return retResultHandler;
}
/**
* 设置输出RetResult时的拦截器
*
* @param retResultHandler 拦截器
*/
public void retResultHandler(BiFunction<HttpRequest, org.redkale.service.RetResult, org.redkale.service.RetResult> retResultHandler) {
this.retResultHandler = retResultHandler;
}
protected final class TransferFileHandler implements CompletionHandler<Integer, ByteBuffer> { protected final class TransferFileHandler implements CompletionHandler<Integer, ByteBuffer> {
private final File file; private final File file;
@@ -1202,7 +1282,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
@Override @Override
public void failed(Throwable exc, ByteBuffer attachment) { public void failed(Throwable exc, ByteBuffer attachment) {
bufferPool.accept(attachment); channel.offerBuffer(attachment);
finish(true); finish(true);
try { try {
filechannel.close(); filechannel.close();

View File

@@ -7,7 +7,9 @@ package org.redkale.net.http;
import java.io.Serializable; import java.io.Serializable;
import java.net.HttpCookie; import java.net.HttpCookie;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
@@ -23,17 +25,20 @@ public class HttpResult<T> {
public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME; public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME;
protected Map<String, String> headers; @ConvertColumn(index = 1)
protected List<HttpCookie> cookies;
protected String contentType;
protected T result;
protected int status = 0; //不设置则为 200 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; protected Convert convert;
@@ -85,18 +90,29 @@ public class HttpResult<T> {
return this; return this;
} }
public HttpResult<T> message(String message) { public Convert convert() {
this.message = message;
return this;
}
@ConvertDisabled
public Convert getConvert() {
return convert; return convert;
} }
public void setConvert(Convert convert) { public HttpResult<T> convert(Convert convert) {
this.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() { public Map<String, String> getHeaders() {
@@ -139,16 +155,17 @@ public class HttpResult<T> {
this.status = status; this.status = status;
} }
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override @Override
public String toString() { 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); return JsonConvert.root().convertTo(this);
} }
} }

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