Compare commits

..

381 Commits

Author SHA1 Message Date
innov a1fa8b22de ADD file via upload 2022-03-09 16:19:43 +08:00
bandl a9fac2572b fix(doc): fix redme image err 2022-02-23 18:08:05 +08:00
bandl 57caa66ef8
!103 更新文档
Merge pull request !103 from bandl/feat-doc-update
2022-02-23 05:40:30 +00:00
bandl d254a71560 feat(doc): update doc 2022-02-23 13:39:27 +08:00
bandl 07dd0f8874
!102 修复 dao 的测试错误
Merge pull request !102 from bandl/fix-dao-test
2022-02-23 05:36:52 +00:00
bandl dafffdde2c fix(dao): fix dao test err 2022-02-23 13:34:10 +08:00
bandl c6b14ced27 !101 feat-channex
Merge pull request !101 from bandl/feat-storage-channelx
2021-11-30 10:55:07 +00:00
bandl b87799a88e feat(README): update Readme 2021-11-30 18:54:32 +08:00
bandl dcfae2353b test(dao-channelX): add dao channelX test 2021-11-30 18:54:32 +08:00
bandl bf0c03a77f feat(dao-channelx): add dao channelx 2021-11-30 18:54:32 +08:00
bandl a57eab1184 test(channex): add channelx test 2021-11-30 18:54:32 +08:00
bandl ef4cbb0cee f 2021-11-30 18:54:32 +08:00
bandl f62a59b551 feat(structure): add channelx 2021-11-29 23:35:18 +08:00
bandl a949ab2edc chore(mock): update mock 2021-11-29 23:34:14 +08:00
bandl 7ac9651ef3 fix(lru): add key update length 2021-11-29 16:11:28 +08:00
bandl 19e1bb59a3 test(channelx): add channelx test 2021-11-29 00:16:17 +08:00
bandl c381c57374 feat(structure-channelx): add channelx 2021-11-29 00:16:04 +08:00
bandl 3f337dba9b chore(dao-service): update dao service 2021-11-28 20:11:21 +08:00
bandl a37097b3dd !100 修复 stringx 的一些 bug
Merge pull request !100 from bandl/fix-dao-stringx
2021-11-28 11:50:29 +00:00
bandl d90c05296a test(dao-stringx): update GetSet 2021-11-28 19:47:23 +08:00
bandl 2a556a9db6 fix(structure-stringx): fix GetSet set init nil key 2021-11-28 19:47:08 +08:00
bandl c0da22ef6f test(dao-stringx): add test TestDao_GetRange 2021-11-28 19:33:25 +08:00
bandl cf4b24ea86 fix(value): add fix value get range slice 2021-11-28 19:32:59 +08:00
bandl 953daca82c !99 修复 Stringx 的测试中的 warning
Merge pull request !99 from Pyroo/dao-stringx-test
2021-11-27 11:23:49 +00:00
Pyroo 5974dadd3e fix(storage-stringx): fix warning 2021-11-25 20:50:57 +08:00
bandl 1baac521fa !98 添加 Stringx 的测试
Merge pull request !98 from Pyroo/test-dao-stringx
2021-11-25 12:37:25 +00:00
Pyroo d6792444a7 test(storage-stringx): add stringx test 2021-11-25 20:28:53 +08:00
bandl 64a02b642e !97 完成 setx 测试
Merge pull request !97 from bandl/feat-dao-setx
2021-11-21 15:11:37 +00:00
bandl a5ef559e14 feat(dao-external): add external mock 2021-11-21 23:10:14 +08:00
bandl 0c8a17b21d test(setx): add setx test 2021-11-21 23:09:55 +08:00
bandl 0207878507 fix(setx): fix make cap 2021-11-21 23:09:40 +08:00
bandl 813b48b650 !96 添加 hashx 测试
Merge pull request !96 from bandl/test-dao-hashx
2021-11-18 11:03:32 +00:00
bandl cdd26736a5 feat(dao-hashx): add dao hashx test 2021-11-18 18:59:15 +08:00
bandl 40afd8057d fix(hashx): fix HIncrBy err catch 2021-11-18 18:58:35 +08:00
bandl 08bc1892df chore(proto): update proto 2021-11-18 17:05:03 +08:00
bandl 5e89f37e40 !95 add dao test stringx
Merge pull request !95 from bandl/test-dao-mock
2021-11-18 07:36:48 +00:00
bandl 6b2f3fb3be test(dao-stringx): add dao stringx test 2021-11-18 15:35:21 +08:00
bandl 499322c080 chore(mock): update mock 2021-11-18 14:54:42 +08:00
bandl e5f217b3b0 teat(storage-gateway): add gateway mock 2021-11-18 14:54:27 +08:00
bandl 113185d17c feat(storage-gateway): add storage gateway mock 2021-11-18 14:54:03 +08:00
bandl 981a3341ef !94 添加 setx 接口
Merge pull request !94 from bandl/feat-storage-setx
2021-11-16 06:44:29 +00:00
bandl af6426f83b feay(storage-dao): add setx type dao 2021-11-16 00:19:18 +08:00
bandl d68209c94c feat(external-gateway): add external gateway call 2021-11-16 00:19:18 +08:00
bandl 60cf8f5eb1 test(structure-hashx): add hashx range test 2021-11-16 00:19:18 +08:00
bandl 594f8acf32 feat(structure-hashx): add hashx range 2021-11-16 00:19:18 +08:00
bandl 366793d955 chore(proto): update proto 2021-11-16 00:19:18 +08:00
bandl 5105a62bca test(hashx): add unsafe.Point test 2021-11-13 20:04:21 +08:00
bandl 400d620aa4 test(lru-worker): update lru worker test 2021-11-11 23:00:21 +08:00
bandl 25be4a1509 test(hash-endpoint): update hash endpoint test 2021-11-11 23:00:04 +08:00
bandl 60de1a6ae9 chore(service): update service 2021-11-11 22:46:39 +08:00
bandl 55204575d7 feat(temp): update service temp to event2 2021-11-11 22:46:20 +08:00
bandl 2f81eab981 feat(lru, single service): update event2 2021-11-11 22:39:50 +08:00
bandl 3eb515325d feat(lru): update lru event2 2021-11-11 22:32:14 +08:00
bandl 3f3b208db1 test(event2): add event2 test 2021-11-11 22:24:28 +08:00
bandl 8b8fd58c09 feat(event2): add event2 2021-11-11 22:24:17 +08:00
bandl 62d3105273 doc(event): add event2.0 doc 2021-11-10 17:38:20 +08:00
bandl 51bd9b242f fix(hash-endpoint): tranport to endpoint 2021-11-07 21:57:22 +08:00
bandl f6eaae71b9 chore(service): update service 2021-11-07 21:49:34 +08:00
bandl 921078c61d chore(proto): update proto 2021-11-07 20:46:22 +08:00
bandl c8ef4c6e46 !93 doc(storage): update storage
Merge pull request !93 from bandl/auto-5261189-master-1636186822154
2021-11-06 08:20:46 +00:00
bandl c6273d084e doc(storage): update storage 2021-11-06 16:20:15 +08:00
bandl fd605d125f !92 doc(storage): add storage icon
Merge pull request !92 from bandl/auto-5261189-master-1636177701995
2021-11-06 05:49:01 +00:00
bandl c9dcb41cad doc(storage): add storage icon 2021-11-06 13:48:17 +08:00
bandl f7bb695e82 !91 doc(storage): fix url
Merge pull request !91 from bandl/auto-5261189-master-1636124592092
2021-11-05 15:03:34 +00:00
bandl 4e6c2ed22a doc(storage): fix url 2021-11-05 23:03:08 +08:00
bandl 0c08e40667 !90 doc(Redeme): fix url !
Merge pull request !90 from bandl/auto-5261189-master-1636124488904
2021-11-05 15:01:51 +00:00
bandl 7fa66abbf0 doc(Redeme): fix url ! 2021-11-05 23:01:24 +08:00
bandl a910e4af21 !89 添加开发者文档
Merge pull request !89 from bandl/doc-developer-doc
2021-11-05 14:59:50 +00:00
bandl 51914aea8a doc(storage): add developer doc 2021-11-05 22:58:59 +08:00
bandl bbfd32cebc !88 完成
Merge pull request !88 from bandl/feat-structure-hashx
2021-11-05 12:51:13 +00:00
bandl 44bd5bc7cd doc(readme): over hashx 2021-11-05 20:48:34 +08:00
bandl fcdb2310fe feat(storage-dao): add hashx 2021-11-05 20:44:09 +08:00
bandl 222bcc4eea chore(service): update service 2021-11-05 20:43:56 +08:00
bandl c3cff03b2b test(structure-hashx): add hashx test 2021-11-05 16:36:18 +08:00
bandl 06baf3993d feat(hashx-interface): add hash interface 2021-11-05 16:36:00 +08:00
bandl 976215ee10 feat(structure-hashx): add hashx 2021-11-05 16:34:50 +08:00
bandl 778d5152c8 perf(incr): option incr to values 2021-11-05 14:41:39 +08:00
bandl 5ce350f115 fix(linces): update license 2021-11-04 21:35:09 +08:00
bandl 047c910278 chore(service): update service 2021-11-04 20:36:42 +08:00
bandl dcada2f2fe feat(shell): add parse proto map 2021-11-04 20:33:37 +08:00
bandl 88774daede chore(proto): update proto 2021-11-04 20:28:42 +08:00
bandl 344c136586 feat(yaml): add hashx yaml 2021-11-04 19:18:56 +08:00
bandl 4c9ab82123 !87 test(structure-val): add perf test
Merge pull request !87 from bandl/auto-5261189-master-1636014762802
2021-11-04 08:33:07 +00:00
bandl 2ed75832ba test(structure-val): add perf test 2021-11-04 16:32:17 +08:00
bandl 0f1142d434 perf(structure-value): perf set bit 2021-11-04 16:31:55 +08:00
bandl b6dedfa384 !86 添加 docker 安装,以及描述
Merge pull request !86 from bandl/docker-build
2021-11-03 16:03:03 +00:00
bandl 0f2b73e932 doc(readme): add v1 readme 2021-11-04 00:01:14 +08:00
bandl 2f46dfaa2e feat(icon): add icon 2021-11-04 00:01:14 +08:00
bandl c867665b60 feat(conf): update conf 2021-11-04 00:01:14 +08:00
bandl b91966709b feat(dockerfile): add docker file
f
2021-11-04 00:00:17 +08:00
bandl 47cdf0859b !85 fix(proto): fix proto key err
Merge pull request !85 from bandl/auto-5261189-master-1635925021767
2021-11-03 07:37:57 +00:00
bandl 0b9230094a fix(proto): fix proto key err 2021-11-03 15:36:55 +08:00
bandl eccfd6a439 !84 aof
Merge pull request !84 from bandl/feat-recover-aof
2021-11-03 04:42:21 +00:00
bandl f5c3b0e1db fat(ttl): add permanent storage 2021-11-03 12:40:58 +08:00
bandl 5ee502021d feat(eooorx): update errorx key base 2021-11-02 21:34:23 +08:00
bandl 9fed552380 feat(storage): recver lru by aof 2021-11-02 21:19:35 +08:00
bandl 7d9081ce8e chore(service): updater dao service 2021-11-02 20:35:45 +08:00
bandl 4ca09febb4 feat(shell): add recover aof 2021-11-02 20:35:28 +08:00
bandl be528cbb6d !83 更新 mod 号为 wheat-os
Merge pull request !83 from bandl/refactor-mod
2021-11-02 06:46:09 +00:00
bandl 70b23849bc refactor(mod): gitee.com/wheat-os 2021-11-02 14:45:08 +08:00
bandl 99133bf7ea !82 fix-aof
Merge pull request !82 from bandl/fix-aof-nil
2021-11-02 05:03:07 +00:00
bandl 1ca43cde6c fix(aof): fix aof is nil 2021-11-02 13:02:02 +08:00
bandl de59f13234 !81 add-AOF
Merge pull request !81 from bandl/feat-storage-aof
2021-11-01 16:00:57 +00:00
bandl 72add38cee chore(service): update service 2021-11-01 23:55:12 +08:00
bandl 3b61f55fe3 feat(service): add aof service 2021-11-01 23:48:48 +08:00
bandl 6fc2417de2 feat(conf): add aof conf 2021-11-01 23:48:09 +08:00
bandl 7aa39979f0 test(aof): update test 2021-11-01 22:51:43 +08:00
bandl 4ffa91ac6a feat(aof): add aof log 2021-11-01 22:51:43 +08:00
bandl 170ba44d2d chore(aof): update aof codec 2021-11-01 21:55:50 +08:00
bandl ce888a1d0e test(aof): add codec test 2021-11-01 17:38:54 +08:00
bandl 787003d95a chore(service): update gen service 2021-11-01 17:36:35 +08:00
bandl 27542cf898 feat(shell): add gen aof codec 2021-11-01 17:35:59 +08:00
bandl 3bcd154177 feat(proto): add new key base 2021-11-01 15:28:52 +08:00
bandl bed3f1893a !80 修复 skiplist 的 bug
Merge pull request !80 from bandl/fix-skiplist
2021-10-28 06:52:59 +00:00
bandl 200623bd29 test(client): add storage client 2021-10-28 12:00:08 +08:00
bandl dce8739514 fix(work): storage worker 2021-10-28 12:00:08 +08:00
bandl 686c032f34 fix(lru-worker, skiplist): fix skiplist insert err, perf lru 2021-10-28 12:00:08 +08:00
bandl 41e282de5d !79 feat-storage-template
Merge pull request !79 from bandl/feat-make-service
2021-10-27 16:28:58 +00:00
bandl 23b13afc86 doc(storage): update upgrate storage doc 2021-10-28 00:24:39 +08:00
bandl b4c3cc5a86 test(dao): remove dao test 2021-10-27 23:55:36 +08:00
bandl 54520cb033 fix(storage): fix storage service 2021-10-27 23:53:25 +08:00
bandl c67e0c863c feat(shell): add gen service shell 2021-10-27 23:51:54 +08:00
bandl ea8e10fcbf feat(single-service): add single service 2021-10-27 23:51:17 +08:00
bandl 19a0259f58 feat(listx): update dao listx 2021-10-27 23:30:45 +08:00
bandl 511a66bd98 feat(dao): add interface 2021-10-27 23:15:48 +08:00
bandl 5642a00f61 feat(stringx): update dao stringx 2021-10-27 23:15:26 +08:00
bandl 1f85847243 feat(dao): update interface 2021-10-27 23:15:07 +08:00
bandl dab21a4ebc feat(dao): add dao template 2021-10-27 21:38:31 +08:00
bandl 14c52da1ed feat(dao): add gen dao interface tools 2021-10-27 21:38:21 +08:00
bandl 5f188eaddf feat(dao): update dao interface 2021-10-27 21:38:01 +08:00
bandl bbc4c27027 !78 优化 事件驱动
Merge pull request !78 from bandl/perf-event
2021-10-26 09:13:08 +00:00
bandl b6348f5992 feat(test): update lru test 2021-10-26 16:35:14 +08:00
bandl 404dc1fbbc feat(event): update event 2021-10-26 16:01:11 +08:00
bandl db615609cd feat(lru-worker): event recovery 2021-10-26 15:41:16 +08:00
bandl 46b029b339 test(event): add poll test 2021-10-26 14:39:50 +08:00
bandl ac4fdd7309 feat(event): add event poll 2021-10-26 14:39:34 +08:00
bandl bff937700e feat(event-pool): add event poll 2021-10-26 09:29:39 +08:00
bandl 2a69f393e5 !77 ppeof
Merge pull request !77 from bandl/feat-pporf-plugins
2021-10-25 07:49:21 +00:00
bandl 417ddf1ccf feat(plugins): add plugins 2021-10-25 15:48:21 +08:00
bandl d78cb874ed feat(conf): add conf 2021-10-25 15:47:21 +08:00
bandl 06042b778c feat(conf): ones load conf 2021-10-25 15:34:23 +08:00
bandl 19562221f9 chore(pluigin): init pluugins 2021-10-25 15:24:34 +08:00
bandl 5d7df024e4 !76 修复 内存计算错误问题
Merge pull request !76 from bandl/fix-length
2021-10-25 03:21:05 +00:00
bandl 9298b77c7c fix(lru): fix length byte 2021-10-25 11:08:18 +08:00
bandl 3f4607fb58 !75 gateway 添加 一致性 hash 环算法实现 分布式集群
Merge pull request !75 from bandl/feat-gateway-transpoart
2021-10-24 12:07:30 +00:00
bandl 09730e9ebf feat(gateway): add hash ring gateway 2021-10-24 20:03:35 +08:00
bandl cd1ba29f42 feat(conf): update conf 2021-10-24 20:03:14 +08:00
bandl cf9dc1da38 feat(conf): add target addr 2021-10-24 19:50:59 +08:00
bandl fc60b2a779 test(transport): add test hash 2021-10-24 19:12:26 +08:00
bandl e81124c7e6 feat(transport): add hash transport 2021-10-24 19:12:07 +08:00
bandl ebfa948421 !74 ref
Merge pull request !74 from bandl/refactor-struct-proto
2021-10-23 16:42:16 +00:00
bandl b32606ee15 chore(proto): update proto 2021-10-24 00:39:07 +08:00
bandl 3c8d4fd89b fix(shell): update gen proto 2021-10-24 00:35:26 +08:00
bandl 9a1f1fe0ee fix(storage): fix storage method name 2021-10-24 00:35:26 +08:00
bandl e894734418 chore(proto): update proto 2021-10-24 00:35:26 +08:00
bandl e404f670a0 feat(shell): update const gen 2021-10-24 00:35:16 +08:00
bandl 4459beba61 feat(sturcture temp): update temp 2021-10-23 23:31:42 +08:00
bandl ab277fb66e !73 storage 的 listx
Merge pull request !73 from bandl/feat-listx-option
2021-10-23 08:19:41 +00:00
bandl 0b781a178e feat(listx): add storage listx option 2021-10-23 16:17:29 +08:00
bandl e96f21399c chore(proto): update proto 2021-10-23 16:04:35 +08:00
bandl 6a63bdca84 fix(stringx): ctx 2021-10-23 16:02:52 +08:00
bandl f15792032a feat(dao): add listx dao 2021-10-23 15:18:21 +08:00
bandl 7ce1a55d0e test(listx): add listx remove 2021-10-23 15:18:10 +08:00
bandl 4f6fb64ecc feat(listx): add listx 2021-10-23 15:17:57 +08:00
bandl 74e42e78ac test(listx): test range 2021-10-22 22:01:58 +08:00
bandl 8883abd461 feat(listx): add range func 2021-10-22 22:01:48 +08:00
bandl 8c5c594ac5 feat(proto): update proto 2021-10-22 18:15:26 +08:00
bandl 77988052ee doc(make): add make doc 2021-10-22 16:26:17 +08:00
bandl e6eb86de05 doc(middle): update doc 2021-10-22 16:12:13 +08:00
bandl f022ae9cbe doc(structure): add doc 2021-10-22 16:11:59 +08:00
bandl d42b01ff77 feat(structure): update interface view 2021-10-22 15:56:44 +08:00
bandl a9dbb5169c test(listx): add listx test 2021-10-22 15:37:25 +08:00
bandl 15e1bb06af feat(listx): add listx structure 2021-10-22 15:36:25 +08:00
bandl 41ef546d95 test(stringx): val test 2021-10-22 15:35:48 +08:00
bandl 70a246f84c feat(listx): add listx interface 2021-10-21 16:18:30 +08:00
bandl 2295fbcf40 chore(proto): update proto 2021-10-21 15:57:31 +08:00
bandl 3a4fc48f46 !72 feat-stringx-option
Merge pull request !72 from bandl/feat-stringx-option
2021-10-20 13:45:46 +00:00
bandl 5b0a636e02 test(stringx): test Getrange 2021-10-20 21:42:58 +08:00
bandl fe096c9054 feat(storage): add storage opt 2021-10-20 21:35:56 +08:00
bandl a374638758 feat(dao): add dao 2021-10-20 21:35:27 +08:00
bandl 56d69f63af feat(string): add stringx option 2021-10-20 21:35:15 +08:00
bandl aed507fc9a feat(proto): update proto 2021-10-20 20:30:51 +08:00
bandl 739cb56243 feat(stringx): add string option 2021-10-20 20:30:02 +08:00
bandl 1a884ce9f0 !71 feat-middle-worker
Merge pull request !71 from bandl/feat-middle-worker
2021-10-19 08:40:06 +00:00
bandl 576a7bbd41 test(middle): add test worker 2021-10-19 16:38:31 +08:00
bandl 623373f7ac feat(middle-msg): update push middle-msg 2021-10-19 16:38:07 +08:00
bandl 4413ee6f92 feat(conf): update conf 2021-10-19 16:34:32 +08:00
bandl 4a887b0e58 feat(logx): update send msg mode 2021-10-19 16:05:24 +08:00
bandl 74a85e518d feat(conf): update conf 2021-10-19 16:02:05 +08:00
bandl cb1e555986 feat(plugins): update gen plugins 2021-10-19 15:33:03 +08:00
bandl 780cf7d276 feat(middle-msg): update middle msg 2021-10-19 15:17:43 +08:00
bandl e94a41ec73 test(client): update client test 2021-10-19 14:52:23 +08:00
bandl 6ef4654c63 !61 middle_msg
Merge pull request !61 from Sodesnei/feat-plugin-install
2021-10-19 06:52:06 +00:00
bandl 6bd16eec06 !70 优化 lru
Merge pull request !70 from bandl/perf-lru
2021-10-18 16:25:35 +00:00
bandl 21cac4639c fear(dao): update dao api 2021-10-19 00:24:36 +08:00
bandl d17f8e243c perf(lru): perf lru keybase err 2021-10-19 00:24:16 +08:00
bandl 95d3fdbd17 test(skiplist, ttl): update ttl 2021-10-19 00:06:42 +08:00
bandl ac8f748356 perf(skiplist): perf list pop left, pop range 2021-10-18 23:58:11 +08:00
bandl 064ef3e50e !69 doc-init
Merge pull request !69 from bandl/doc-tidy-up
2021-10-17 13:08:16 +00:00
bandl eb0014437b doc(init-doc): refactor doc 2021-10-17 21:06:24 +08:00
bandl 310e11c65b !68 feat-dao-test
Merge pull request !68 from bandl/feat-alone-storage
2021-10-17 12:14:08 +00:00
bandl 019322eb8f fix(stringx): fix reduce float 2021-10-17 20:04:26 +08:00
bandl a00f10c70e test(dao): add dao test 2021-10-17 20:03:13 +08:00
bandl 3c68787ff4 test(client): add client 2021-10-17 20:02:48 +08:00
bandl 57e84b6dee feat(gateway): add gateway refault 2021-10-17 19:27:00 +08:00
bandl 5fe201ca32 feat(client): add cache client 2021-10-17 17:20:01 +08:00
bandl aade8e8edb test(gateway): remove test 2021-10-17 14:49:55 +08:00
bandl 6fdf250833 !66 gateway 添加 keybase 检查
Merge pull request !66 from bandl/fix-lru-panic
2021-10-17 02:52:41 +00:00
bandl d963c06576 test(dao): add set test dao 2021-10-17 10:50:13 +08:00
bandl d6f1631d7b feat(gateway): add get keybase ctx 2021-10-17 10:49:14 +08:00
bandl 477613f0de feat(mod): update mod 2021-10-17 08:23:14 +08:00
bandl bd0e598f1a feat(makefile): add no-print-directory 2021-10-16 23:54:17 +08:00
bandl 98787c8f03 !65 更新包名
Merge pull request !65 from bandl/fix-package-name
2021-10-16 15:27:37 +00:00
bandl 1e4d611c77 feat(pro): update package name in upper 2021-10-16 23:25:34 +08:00
bandl b97edc4cf1 !64 添加 实现 网关的转发功能
Merge pull request !64 from bandl/feat-getway
2021-10-16 14:55:54 +00:00
bandl 91d93bf5be test(gateway): add gateway test 2021-10-16 22:53:27 +08:00
bandl ebdc5bd5b0 feat(gateway-reset): add director mock 2021-10-16 22:53:11 +08:00
bandl 43392236b6 feat(gateway): add gateway root 2021-10-16 22:52:37 +08:00
bandl 52c9b166f3 feat(storage): update storage 2021-10-16 22:51:51 +08:00
bandl 40374333fa feat(uitl): grace exit 2021-10-16 22:51:28 +08:00
bandl 3adeac539b chore(conf): update conf and makefule 2021-10-16 22:50:38 +08:00
bandl ec8a126581 doc(feat-gateway-doc): add gateway doc 2021-10-16 20:44:27 +08:00
bandl 4e78d08e2c feat(conf): addgateway conf 2021-10-16 18:29:31 +08:00
bandl 2c854deb52 feat(gateway): add a forwarding rules to gateway 2021-10-16 16:54:35 +08:00
bandl 62eaa8edd0 feat(gateway-codce): update codce option 2021-10-16 16:53:21 +08:00
bandl cbf7ba1933 feat(gateway): add codec mode 2021-10-15 22:36:49 +08:00
bandl 32936576d3 feat(gateway): update gateway cmd 2021-10-15 22:36:29 +08:00
bandl 57a655a708 feat(storage): update conf 2021-10-15 22:35:16 +08:00
bandl 554f7feef9 chore(proto): update proto 2021-10-12 21:22:46 +08:00
bandl 101059126c fix(single-server): fix timeOut 2021-10-12 21:22:46 +08:00
bandl ccf7179713 doc(getway): add getway doc 2021-10-12 21:22:46 +08:00
bandl 5934e88e03 test(storage): add single server test 2021-10-12 21:22:46 +08:00
bandl f7d4cee102 feat(getway): init getway 2021-10-12 21:22:46 +08:00
bandl ef5c088a49 !63 完成 全部 single lru
Merge pull request !63 from bandl/feat-lru-ttl
2021-10-12 13:18:32 +00:00
bandl c8f23d1357 test(lru): add lru test process 2021-10-12 21:15:21 +08:00
bandl 85e9a3b5f8 chore(proto): update proto 2021-10-12 21:15:21 +08:00
bandl 02bd151381 test(lru): test ttl and worker 2021-10-12 21:15:21 +08:00
bandl 758f1bfec2 feat(lru): update lru ttl and worker 2021-10-12 21:15:09 +08:00
bandl e3c7546023 fix(middle): fix middleware name msg 2021-10-12 15:10:53 +08:00
bandl b61794d74b test(util): test skiplist 2021-10-12 15:10:10 +08:00
bandl 5776e0ba6e feat(util): add skiplist 2021-10-12 15:10:00 +08:00
bandl c4161e77d0 doc(config): update wheat-cache.yaml 2021-10-12 15:09:12 +08:00
bandl e826ff4569 !62 增加清理事件
Merge pull request !62 from K-on/add-Clear-Event
2021-10-11 12:40:42 +00:00
K-on d2866256dd update pkg/lru/define.go. 2021-10-11 11:19:11 +00:00
K-on 5459c7710c 删除文件 pkg/lru/cleanwork.go 2021-10-11 11:09:59 +00:00
HuangJiaLuo aebab3b3d7 Merge remote-tracking branch 'origin/add-Clear-Event' into add-Clear-Event
# Conflicts:
#	pkg/lru/define.go
#	pkg/lru/lru.go
2021-10-11 19:06:48 +08:00
HuangJiaLuo b39899e5d6 feat(middle-msg): update lru msg 2021-10-11 19:02:01 +08:00
HuangJiaLuo 922fae0f78 feat(lru): update lru clean work 2021-10-11 19:01:24 +08:00
Sodesnei 44dbb04f0b feat(middle-msg): add PluginsInfo msg 2021-10-10 22:28:00 +08:00
Sodesnei e2f00fa2ae feat(middle-msg): add SendMiddleMsg function 2021-10-10 22:28:00 +08:00
Sodesnei ec8cea59a4 feat(doc): middleware doc 2021-10-10 22:28:00 +08:00
bandl ab257b2009 !60 日志
Merge pull request !60 from 黎白南/feat-logx-middle
2021-10-10 13:23:43 +00:00
黎白南 e5ac4d1cf9 feat(logx) add print without format, color print 2021-10-10 21:07:23 +08:00
黎白南 51517da519 feat(logx) add print without format 2021-10-10 21:07:23 +08:00
HuangJiaLuo 73e91b3ff0 test 2021-10-10 20:54:17 +08:00
HuangJiaLuo dcce00a32d feat(lru): feat the function of launching the cleanWorkFunc to driver 2021-10-10 20:54:17 +08:00
HuangJiaLuo 2500251699 feat(lru): feat the function of cleanEvent 2021-10-10 20:54:17 +08:00
HuangJiaLuo 29b9c8f3b6 feat(event): feat the function of getting the queue length 2021-10-10 20:54:17 +08:00
HuangJiaLuo f119557eec feat(errorx): feat the LruNotWorkFuncEvent error 2021-10-10 20:54:17 +08:00
HuangJiaLuo 5275be2b8e feat(event): feat the io of judgement function 2021-10-10 20:54:17 +08:00
HuangJiaLuo 83bf49ec17 feat(event): feat the get the io length 2021-10-10 20:54:17 +08:00
HuangJiaLuo cf1c90442b feat(worker): feat get the io 2021-10-10 20:54:16 +08:00
Sodesnei aac12f93f4 !58 middle driver
Merge pull request !58 from Sodesnei/fix-middleware-driver
2021-10-10 06:14:02 +00:00
Sodesnei debde8a66a feat(middle): update middle define var 2021-10-10 14:10:21 +08:00
Sodesnei 95f165fb55 feat(middle): update middle driver 2021-10-10 14:09:09 +08:00
Sodesnei febd5501c4 feat(conf): updata conf test 2021-10-10 14:05:01 +08:00
HuangJiaLuo 8a6db79fd6 feat(lru): feat the function of launching the cleanWorkFunc to driver 2021-10-09 22:08:09 +08:00
HuangJiaLuo 64b38044c8 feat(lru): feat the function of cleanEvent 2021-10-09 22:06:38 +08:00
HuangJiaLuo 843fcd27ca feat(event): feat the function of getting the queue length 2021-10-09 22:05:39 +08:00
HuangJiaLuo a251cdb844 feat(errorx): feat the LruNotWorkFuncEvent error 2021-10-09 22:04:47 +08:00
Sodesnei 4d9558c21f feat(conf): add conf single test 2021-10-09 21:56:43 +08:00
Sodesnei b182d7602d feat(plugins): updata plugins 2021-10-09 21:55:39 +08:00
Sodesnei 99c3c56a5b feat(middle-msg): add pluginsInfo 2021-10-09 21:55:16 +08:00
Sodesnei 81d3bb8434 feat(middle-msg): update base parameter 2021-10-09 21:54:26 +08:00
Sodesnei ce8f72040b feat(middleware): add middleware work function 2021-10-09 21:51:52 +08:00
Sodesnei 329513bd98 feat(middle-msg): updatae logx 2021-10-09 21:49:55 +08:00
Sodesnei 2e04517066 feat(conf): update wheat-cache.yaml 2021-10-09 21:49:16 +08:00
HuangJiaLuo f1d2cbb0ee feat(event): feat the io of judgement function 2021-10-09 10:55:12 +08:00
HuangJiaLuo c45c2682e0 feat(event): feat the get the io length 2021-10-09 10:47:17 +08:00
HuangJiaLuo 929f931cd6 feat(worker): feat get the io 2021-10-09 10:35:42 +08:00
黎白南 b168eecbb4 !56 日志
Merge pull request !56 from 黎白南/feat-logx-middle
2021-10-08 13:35:19 +00:00
黎白南 e98731c1b3 fix(log) fix produce msg, remove more route 2021-10-08 21:19:04 +08:00
黎白南 77d4b88c71 fix(log) fix produce msg, remove more route 2021-10-08 21:04:54 +08:00
Sodesnei 1540651864 !55 修改middle代码自动生成模板
Merge pull request !55 from Sodesnei/middle-template
2021-10-08 12:14:07 +00:00
Sodesnei 166d1f20c1 fix(middle-template): update middle template 2021-10-08 20:11:13 +08:00
bandl 96c8a6d53e !53 更新driver, 重构了worker
Merge pull request !53 from K-on/fix-driver
2021-10-07 08:35:26 +00:00
HuangJiaLuo 83febdd482 refactor(worker): refactor 2021-10-07 16:31:46 +08:00
HuangJiaLuo e3171a8b3e feat(event): Add SetResultErr func 2021-10-07 16:31:46 +08:00
bandl 60abd336b1 !52 增加了根据key删除value, 将key的类型修改成 KeyBase
Merge pull request !52 from K-on/feat-Lru-delete-key
2021-10-06 12:47:04 +00:00
HuangJiaLuo 428a8fa8f8 feat(worker): change string to proto.KeyBase 2021-10-06 20:39:52 +08:00
HuangJiaLuo 97833ed150 feat(single): change string to proto.KeyBase 2021-10-06 20:39:38 +08:00
HuangJiaLuo f17cab2016 feat(dao): change string to proto.KeyBase 2021-10-06 20:39:20 +08:00
HuangJiaLuo d4d93a0e51 feat(lru): change string to proto.KeyBase 2021-10-06 20:39:07 +08:00
HuangJiaLuo f9ad4d914e feat(lru): change string to proto.KeyBase 2021-10-06 20:38:56 +08:00
HuangJiaLuo d08e50f4ce feat(lru): change string to proto.KeyBase 2021-10-06 20:38:37 +08:00
HuangJiaLuo 90b021bee2 feat(lru): del by key 2021-10-06 19:56:32 +08:00
bandl 63bd44da44 !50 日志
Merge pull request !50 from 黎白南/feat-logx-middle
2021-10-06 11:14:52 +00:00
黎白南 d8dc56d95a fix(log) fix panic, remove fmt.Print 2021-10-06 19:14:16 +08:00
bandl c36659da26 !51 增加了开始的提示
Merge pull request !51 from K-on/fix-conf-test
2021-10-06 11:09:51 +00:00
黎白南 87ada5f981 doc(doc) add log param 2021-10-06 19:02:37 +08:00
黎白南 880aba64d0 test(log) add text func 2021-10-06 19:01:15 +08:00
黎白南 34dd0fe94f feat(doc) add panic, init 2021-10-06 19:00:48 +08:00
HuangJiaLuo 36e8efd682 fix(conf): add warning about the file when we want to use the yaml 2021-10-06 18:55:23 +08:00
黎白南 692ad28380 fix(doc) logx to log 2021-10-06 14:54:52 +08:00
黎白南 0e2db14d32 fix(log) fix log with context 2021-10-06 14:47:58 +08:00
黎白南 5061b2e3da feat(middle-msg) make log msg standard 2021-10-05 21:36:57 +08:00
黎白南 b75aa1d141 feat(logx) make struct without new 2021-10-05 21:36:06 +08:00
黎白南 a706d57370 feat(logx) add log function 2021-10-05 20:53:11 +08:00
bandl 5292163b14 !48 feat-storage-server-v2
Merge pull request !48 from bandl/fix-storage-structure
2021-10-05 10:24:11 +00:00
bandl c9cfa04725 test(lru): update worker 2021-10-05 16:53:28 +08:00
bandl 4afe3dabb6 feat(lru): update lru 2021-10-05 16:53:16 +08:00
bandl 212e025b23 feat(storage): update single storage 2021-10-05 16:41:17 +08:00
bandl 125b137ef6 feat(dao): add dao server 2021-10-05 16:40:29 +08:00
bandl 67da63ac03 feat(errorx): add err 2021-10-05 16:39:31 +08:00
bandl 74ab13c756 feat(shell): update proto 2021-10-05 16:39:00 +08:00
bandl 26226eca28 f 2021-10-04 22:37:21 +08:00
bandl 1de8e1b141 feat(lru): update sizebyte 2021-10-04 22:37:04 +08:00
bandl 8bcac6f739 test(lru): rename SingleCache 2021-10-04 22:20:58 +08:00
bandl e6987546ae test(stringx): update stringx test 2021-10-04 21:56:49 +08:00
bandl 8e6f18f887 chore(proto): update dcgen 2021-10-04 21:20:03 +08:00
bandl 30c34cf30f feat(structure): update structure stringx 2021-10-04 21:20:03 +08:00
bandl c27328ced4 feat(shell): update shell storage 2021-10-04 21:20:03 +08:00
Sodesnei 7c6c26c5fa !44 update middlewareFile name
Merge pull request !44 from Sodesnei/feat-middleware-python
2021-10-04 13:17:28 +00:00
Sodesnei 49496cf63c fix(middle): fix middle.template 2021-10-04 21:15:30 +08:00
Sodesnei bd804e7105 fix(middle): update define 2021-10-04 21:15:30 +08:00
Sodesnei 1c9c437a0f fix(middleware): add middlewareInterface function 2021-10-04 21:15:30 +08:00
Sodesnei 0bdbe06376 fix(middle): update middleware 2021-10-04 21:15:30 +08:00
Sodesnei ea36d370a7 fix(middle-driver): update file name 2021-10-04 21:15:30 +08:00
Sodesnei 373de8b282 fix(middle): rollback 2021-10-04 21:15:30 +08:00
K-on 15cede5780 !45 fix util
Merge pull request !45 from K-on/fix-util
2021-10-04 13:14:27 +00:00
HuangJiaLuo e637cccaed fix(util): fix the bug of memory 2021-10-04 21:06:40 +08:00
bandl f7508d67a3 !42 lru fix bug
Merge pull request !42 from K-on/feat-lru-driver
2021-10-04 12:48:25 +00:00
HuangJiaLuo b95fb85ad6 perf(util): use switch to change size 2021-10-04 20:33:38 +08:00
HuangJiaLuo 1774c9f230 feat(makefile): add init-conf 2021-10-04 20:32:55 +08:00
HuangJiaLuo 477adec1bb fix(lru): fix the bug of lru 2021-10-04 20:32:20 +08:00
HuangJiaLuo ca04ba1686 test(lru): add the test of worker 2021-10-04 16:25:52 +08:00
HuangJiaLuo 71ddeb4772 perf(lru): add the sync.Once to define 2021-10-04 16:25:09 +08:00
HuangJiaLuo 75e1251d22 feat(lru): feat the init cache 2021-10-04 16:23:37 +08:00
HuangJiaLuo dbc0467de4 fix(util): fix the bug of ParseSizeToBit 2021-10-04 16:21:48 +08:00
HuangJiaLuo 29c5aca40d test(lru): update test 2021-10-04 11:22:35 +08:00
HuangJiaLuo 99dd878bb3 feat(shell): add init_conf and update makefile 2021-10-04 11:22:34 +08:00
HuangJiaLuo 96d5a9acc9 feat(lru): fix lru function and memory function 2021-10-04 11:22:34 +08:00
HuangJiaLuo 47a49b1fdb feat(lru): update lru function 2021-10-04 11:22:34 +08:00
HuangJiaLuo 768f3df70e lru driver-test 2021-10-04 11:22:34 +08:00
bandl a556840f9b !43 修复 degen 错误问题
Merge pull request !43 from bandl/fix-structure-gen
2021-10-04 01:22:38 +00:00
bandl 2c11d500fd fix(gen-proto): update gen-proto shell 2021-10-03 16:22:34 +08:00
bandl a323f7e8a9 fix(template): update template 2021-10-03 16:22:06 +08:00
bandl 5888e2e15a !41 fix-middle-tools
Merge pull request !41 from bandl/fix-middle-tools
2021-09-29 13:23:39 +00:00
bandl 57bdec3c1d feat(middleware): update middleware 2021-09-29 21:21:45 +08:00
bandl ae5a0531d8 chore(middleware): update middle 2021-09-29 21:18:00 +08:00
bandl 04cb2a1ed1 !40 中间件自动化生成代码
Merge pull request !40 from Sodesnei/feat-middleware-python
2021-09-29 13:12:43 +00:00
Sodesnei 2c6a6a6848 fix(middleware):add middleware format 2021-09-29 20:25:06 +08:00
Sodesnei 488d84580e feat(middle): Automated code generation 2021-09-29 20:25:06 +08:00
bandl 26f3f9f663 !39 修改 lru的define文件
Merge pull request !39 from Sodesnei/fix-lru-single-work
2021-09-29 08:31:38 +00:00
Sodesnei 82e358e268 fix(lru):delete function SingleworkFunc 2021-09-29 13:38:25 +08:00
bandl c11d10d6e2 !38 feat-storage-template
Merge pull request !38 from bandl/feat-storage-tempalte
2021-09-28 15:07:49 +00:00
bandl 600ecb644e chore(structure): update structure 2021-09-28 23:00:22 +08:00
bandl 187280bc2c feat(shell): add storage shell 2021-09-28 23:00:22 +08:00
bandl ad0cdedba9 feat(conf): update const conf 2021-09-28 23:00:22 +08:00
bandl 0903477c22 !35 方案分支
Merge pull request !35 from bandl/fd-middle-tools
2021-09-28 14:58:15 +00:00
bandl 0677c56492 !36 修复 event 的一些 bug
Merge pull request !36 from bandl/fix-event-bug
2021-09-28 13:09:40 +00:00
bandl f53eea9c22 doc(event): update event doc 2021-09-28 20:59:54 +08:00
bandl 0b8e2a104f test(event): update test 2021-09-28 20:55:26 +08:00
bandl 748f61830b feat(event): fix event wait err 2021-09-28 20:55:09 +08:00
bandl 05bd410900 feat(errorx): add time out err 2021-09-28 20:29:57 +08:00
bandl d4a73b93d3 awd 2021-09-27 09:52:27 +08:00
bandl 70319ae2b1 !34 add middleware interface
Merge pull request !34 from Sodesnei/feat-middle-tools-interface
2021-09-27 01:22:31 +00:00
Sodesnei ea9a660740 feat(middleware): add middleware interface 2021-09-26 22:22:40 +08:00
bandl 0458ed3d21 !32 feat-stringx-struct
Merge pull request !32 from bandl/feat-stringx-struct
2021-09-26 12:46:24 +00:00
bandl f4327aa5b6 !33 添加 事件驱动文档
Merge pull request !33 from bandl/doc-event
2021-09-26 12:46:11 +00:00
bandl 8dd5879b2a doc(event): add event doc 2021-09-26 20:01:41 +08:00
bandl 060c1d2bfb test(structure): test stringx type 2021-09-26 16:48:13 +08:00
bandl 8d663fe920 feat(structure): add stringx type 2021-09-26 16:47:54 +08:00
bandl a8bbbc70a4 chore(structure): update template 2021-09-25 23:38:19 +08:00
bandl 175bddf527 feat(shell): update shell 2021-09-25 23:37:24 +08:00
bandl eb53a976e2 test(value): add test value 2021-09-25 16:22:33 +08:00
bandl bcacc83df6 feat(value): update value 2021-09-25 16:21:28 +08:00
bandl 3d7df7149a !31 更新 模板, 添加基础类型
Merge pull request !31 from bandl/feat-gen-storage
2021-09-25 07:43:12 +00:00
bandl ec9d3a1a71 chore(structure): update model 2021-09-25 14:52:42 +08:00
bandl f0835b3344 test(structure): test base value 2021-09-24 22:48:41 +08:00
bandl fc47f3eca0 feat(structure): add base value 2021-09-24 22:48:08 +08:00
157 changed files with 21768 additions and 901 deletions

3
.gitignore vendored
View File

@ -17,4 +17,5 @@
# build file
/bin/storage
/bin/storage
/bin/gateway

7
Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM ubuntu:18.04
WORKDIR /home/src/gitee.com/wheat-os/wheat-cache
ADD . /home/src/gitee.com/wheat-os/wheat-cache
RUN mkdir /etc/wheat-cache
RUN mv /home/src/gitee.com/wheat-os/wheat-cache/conf/wheat-cache.yaml /etc/wheat-cache/

Binary file not shown.

147
README.md

File diff suppressed because one or more lines are too long

22
client/client.go Normal file
View File

@ -0,0 +1,22 @@
package client
import (
"gitee.com/wheat-os/wheatCache/client/middle"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"google.golang.org/grpc"
)
func newWheatClient(targer string, opt ...middle.ClientMiddle) (proto.CommServerClient, error) {
interceptor := middle.GetUnaryInterceptor(opt...)
comm, err := grpc.Dial(targer, grpc.WithInsecure(), grpc.WithUnaryInterceptor(interceptor))
if err != nil {
return nil, err
}
return proto.NewCommServerClient(comm), nil
}
func NewWheatClient(targer string, opt ...middle.ClientMiddle) (proto.CommServerClient, error) {
return newWheatClient(targer, opt...)
}

47
client/client_test.go Normal file
View File

@ -0,0 +1,47 @@
package client
import (
"context"
"testing"
"gitee.com/wheat-os/wheatCache/client/middle"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"github.com/stretchr/testify/require"
)
func TestClient(t *testing.T) {
cli, err := NewWheatClient("127.0.0.1:5891", middle.WithUnaryColonyClient)
require.NoError(t, err)
ctx := context.Background()
bKey := proto.NewBaseKey("apple")
resp, err := cli.Set(ctx, &proto.SetRequest{
Key: bKey,
Val: "yyyy",
})
require.NoError(t, err)
require.Equal(t, resp.Result, "yyyy")
getResp, err := cli.Get(ctx, &proto.GetRequest{
Key: bKey,
})
require.NoError(t, err)
require.Equal(t, getResp.Result, "yyyy")
}
func TestClientGet(t *testing.T) {
cli, err := NewWheatClient("127.0.0.1:5891", middle.WithUnaryColonyClient)
require.NoError(t, err)
ctx := context.Background()
bKey := &proto.BaseKey{
Key: "apple",
}
getResp, err := cli.Get(ctx, &proto.GetRequest{
Key: bKey,
})
require.NoError(t, err)
require.Equal(t, getResp.Result, "yyyy")
}

6
client/middle/define.go Normal file
View File

@ -0,0 +1,6 @@
package middle
import "context"
// type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
type ClientMiddle func(ctx context.Context, method string, req, reply interface{}, header map[string]string) error

53
client/middle/middle.go Normal file
View File

@ -0,0 +1,53 @@
package middle
import (
"context"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
func WithUnaryColonyClient(ctx context.Context, method string, req, reply interface{}, header map[string]string) error {
key, ok := req.(proto.GetKeyBaseInterface)
if !ok {
return status.Errorf(codes.Unknown, "key base err")
}
if header == nil {
return nil
}
// meta 解析会出现 全部小写问题
header[proto.BaseKeyMethodKey] = key.GetKey().Key
return nil
}
func getKeyByKeyMapvalue(m map[string]string) []string {
l := make([]string, 0)
for key, value := range m {
l = append(l, key, value)
}
return l
}
func GetUnaryInterceptor(middleOpts ...ClientMiddle) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{},
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
// 加载中间件
header := make(map[string]string)
for _, mid := range middleOpts {
err := mid(ctx, method, req, reply, header)
if err != nil {
return err
}
}
lm := getKeyByKeyMapvalue(header)
headerData := metadata.Pairs(lm...)
ctxH := metadata.NewOutgoingContext(ctx, headerData)
return invoker(ctxH, method, req, reply, cc, opts...)
}
}

View File

@ -2,34 +2,38 @@ package conf
import (
"log"
"sync"
"github.com/spf13/viper"
)
const (
linuxPath = "/etc/wheat-cache/"
devPath = "./conf"
devPathBin = "../conf"
)
func init() {
setDefaultConfValue()
err := LoadConf("")
switch err.(type) {
case nil:
case viper.ConfigFileNotFoundError:
formatPath := []string{linuxPath, devPath, devPath}
log.Fatalf("the profile could not be read, read path:%v", formatPath)
default:
log.Fatalf("the resolution of the profile failed, err: %v", err)
var confLock sync.Once
}
func init() {
confLock.Do(func() {
setDefaultConfValue()
err := LoadConf("")
switch err.(type) {
case nil:
case viper.ConfigFileNotFoundError:
formatPath := []string{linuxPath}
log.Fatalf("the profile could not be read, read path:%v", formatPath)
default:
log.Fatalf("the resolution of the profile failed, err: %v", err)
}
},
)
}
func setDefaultConfValue() {
// 设置一些默认值
viper.SetDefault("version", "base-01")
defaultStorage()
}
func LoadConf(path string) error {
@ -42,10 +46,6 @@ func LoadConf(path string) error {
// linux
viper.AddConfigPath(linuxPath)
// 开发环境
viper.AddConfigPath(devPath)
viper.AddConfigPath(devPathBin)
viper.SetConfigType("yaml")
err := viper.ReadInConfig()

View File

@ -7,30 +7,29 @@ import (
"github.com/stretchr/testify/require"
)
func TestLoadConf(t *testing.T) {
type args struct {
path string
}
tests := []struct {
name string
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := LoadConf(tt.args.path); (err != nil) != tt.wantErr {
t.Errorf("LoadConf() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestConf(t *testing.T) {
// 外部导入 conf.yaml 需要导入 conf 包
// 每次迁移文件时, 使用 sudo make init-conf来将yam文件迁移到指定的文件夹下
// get 使用, 读取 public_conf 配置文件
h := viper.Get("storage.host")
require.Equal(t, h, "127.0.0.1")
h = viper.Get("env")
require.Equal(t, h, "dev")
// set 使用
viper.Set("host", "1222")
host := viper.GetString("host")
require.Equal(t, host, "1222")
}
func TestMiddleConf(t *testing.T) {
ct := viper.GetStringSlice("plugins-control.logcontext")
require.Equal(t, ct, []string{"logMiddle"})
d := viper.GetInt("middleware-driver.driverCount")
require.Equal(t, d, 1000)
c := viper.GetInt("middleware-driver.middleConsumerCount")
require.Equal(t, c, 5)
}

11
conf/storage.go Normal file
View File

@ -0,0 +1,11 @@
package conf
import "github.com/spf13/viper"
func defaultStorage() {
// aof
viper.SetDefault("storage.aof-path", "/etc/wheat-cache/wheat.aof")
viper.SetDefault("storage.aof-flush-time", 5)
viper.SetDefault("storage.aof-check-time", 1)
viper.SetDefault("storage.aof-check-freq", 20)
}

View File

@ -3,10 +3,58 @@ version: 'v1.0'
env: 'dev'
storage:
host: '127.0.0.1'
host: '0.0.0.0'
port: 5890
lru-cache:
mode: "single" # thread
timeOut: 2 # second
aof-codec: "b16" # 目前只实现了 b16 编码方案。
aof-path: "/etc/wheat-cache/wheat.aof"
aof-flush-time: 5 # second , 每 5 秒刷新缓冲区的内容到磁盘。
aof-check-time: 1 # 每 1 second 执行一次 io 检查
aof-check-freq: 20 # 在一个 aof-check-time 周期内,出现超过 aof-check-freq 的 IO 操作会刷新磁盘
# clearSize and maxSize must be Int
lruCache:
clearSize: "512mb"
maxSize: "1GB"
eventDriverSize: 2000
workTime: 1
detachNum: 300
logPrint:
stath: ["error"]
middleware-driver:
driverCount: 1000
middleConsumerCount: 5
# Register the message push type
# 在这里注册消息推送类型,
plugins-control:
# log-context: Logs generated by storage or gateway are pushed through this message
# log-context: storage 或者 gateway 产生的日志通过这个消息推送
log-context: [ "mock-plugins" ]
# lru-clean-context: Lru is pushed through this message when data cleansing occurs
# lru-clean-context: Lru 发生数据清理时通过这个消息推送
lru-clean-context: ["mock-plugins"]
# lru-ttl-context: Lru is pushed through this message when data expires
# lru-ttl-context: Lru 发生数据过期时通过这个消息推送
lru-ttl-context: ["mock-plugins"]
# plugins-info-contextAll plugins information for the current project
# plugins-info-context: 当前项目全部的插件信息
plugins-infos-context: ["mock-plugins"]
gateway:
host: '0.0.0.0'
port: 5891
target: ["127.0.0.1:5890"]
mock-plugin:
pprof-addr: "127.0.0.1:8000"

2
doc/_icon/alf.svg Normal file
View File

@ -0,0 +1,2 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="94" height="20" role="img" aria-label="license: AFL3.0"><title>license: AFL3.0</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="94" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="47" height="20" fill="#555"/><rect x="47" width="47" height="20" fill="#97ca00"/><rect width="94" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="245" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="370">license</text><text x="245" y="140" transform="scale(.1)" fill="#fff" textLength="370">license</text><text aria-hidden="true" x="695" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="370">AFL3.0</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="370">AFL3.0</text></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 37 KiB

2
doc/_icon/cache.svg Normal file
View File

@ -0,0 +1,2 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="88" height="20" role="img" aria-label="Wheat: Cache"><title>Wheat: Cache</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="88" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="45" height="20" fill="#555"/><rect x="45" width="43" height="20" fill="#a4a61d"/><rect width="88" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="235" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">Wheat</text><text x="235" y="140" transform="scale(.1)" fill="#fff" textLength="350">Wheat</text><text aria-hidden="true" x="655" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="330">Cache</text><text x="655" y="140" transform="scale(.1)" fill="#fff" textLength="330">Cache</text></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

3
doc/_icon/event.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.9 KiB

2
doc/_icon/version.svg Normal file
View File

@ -0,0 +1,2 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="86" height="20" role="img" aria-label="version: v1.1"><title>version: v1.1</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="86" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="51" height="20" fill="#555"/><rect x="51" width="35" height="20" fill="#007ec6"/><rect width="86" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="265" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">version</text><text x="265" y="140" transform="scale(.1)" fill="#fff" textLength="410">version</text><text aria-hidden="true" x="675" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="250">v1.1</text><text x="675" y="140" transform="scale(.1)" fill="#fff" textLength="250">v1.1</text></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,11 @@
### Cache 分布式方案-Getway
![getway方案](https://gitee.com/timedb/img/raw/master/images/getway方案.svg)
1. single 集群分布式方案中,使用 getway 方向代理客户端的 grpc 请求, 通过 hash 环实现 分布式。
2. 集群模式中, 通过主从来 实现 cache 的备份问题,提高容灾性。

32
doc/make.md Normal file
View File

@ -0,0 +1,32 @@
### 构建工具文档
#### dcgen
1. 根据结构体接口模板生成 proto 文件
2. 迁移 proto 到 pkg/proto 下
3. 更新结构体常量
>PS : 开发一个 storage 的新接口时一般有以下步骤
>1. 修改 storage 接口配置文件
>2. make dcgen
>3. 修改生成的 proto 文件
>4. make dcgen
>5. 添加 storage 的操作接口
#### build-storage
编译并且生成 /bin/storage
#### build-gateway
编译并且生成 /bin/gateway
#### install
1. 安装项目,需要 sudo
#### storage
根据配置文件启动 storage
#### gateway
根据配置文件启动 gateway
#### init-conf
根据配置文件文档初始化配置文件到 /etc/wheat-cache/wheat-cache.yaml

View File

@ -1,17 +0,0 @@
## 构建工具文档
### 构建 proto grpc-go
```shell
make dcgen
```
### 编译全部的 go 项目
```shell
make build
```
### 启动 storage 服务
```shell
make dev
```
### 根据配置生成 结构体命令

View File

@ -0,0 +1,13 @@
### 事件驱动 2.0
### event 1.0 存在的问题
事件驱动 1.0 在 相互关联访问时,会发生 死锁问题, 导致一个事件执行周期失败
### event 2.0 新特性
- 异步事件支持
- 挂起操作
### event 2.0 设计图
![](../../_icon/event.svg)

View File

@ -0,0 +1,150 @@
### 事件驱动使用文档
- 包目录: pkg/event
- 使用场景, 异步推送数据
#### 事件驱动基本概念
- event
event 是事件驱动的事件部分, 主要负责一些信息的传递, 一共分为,有等待和无等待的事件, 无等待事件 consumer 消费事件后,不会给 produce 回复, 有等待事件, 支持事件消费后, 向 produce 发送一个回复。
- driver
driver 是事件的驱动, 主要复制连接 produce 以及 consumer 其中维护了一个 event 的队列。
- produce
produce 主要负责 推送 事件, 每个 produce 都需要维护一个 事件驱动通过 driver 来进行数据的推送。
- consumer
consumer 负责接收事件, 每个 consumer 都需要注册一个 事件驱动 来获取 event。
####
#### 应用场景
事件驱动主要用于 异步消息的推送(目前的实现也支持 同步的消息实现), 一般来说 produce 发送 event 后 event 被消费过程会完全于 produce 无关。
#### 无返回 event 使用例子
我们使用 借书为例子使用, 假设 A 要把 书 S 交还给 B 他们约定, A 把书 S 放到图书馆, B 在空闲时去图书馆取出书 S。
```go
func TestNewConsumer(t *testing.T) {
// 定义一个图书馆
type Library struct {
driver DriverInterface
}
library := &Library{
driver: NewDriver(100),
}
ctx := context.Background()
// 定义 A
type A struct {
produce ProduceInterface
}
a := &A{
produce: NewProduce(library.driver),
}
// 定义 B
type B struct {
consumer ConsumerInterface
}
b := &B{
consumer: NewConsumer(library.driver),
}
// 定义书 S 并且添加一些描述
book := NewEvent("S")
book.SetMsg("title", "hello world")
book.SetCtxValue("pages", 120)
// A 把书 S 放到图书馆
go func() {
a.produce.Call(ctx, book)
}()
// 模拟 B 去图书馆拿书
book = b.consumer.Receive(ctx)
fmt.Println(book.GetMsg("title"))
}
```
#### 有返回event的使用例子
在上述流程的情景下, 我们添加一些条件, B 拿到书以后检查书是否损坏,如果损坏, 需要告诉 A 书损坏情况。
```go
func TestNewConsumer(t *testing.T) {
// 定义一个图书馆
type Library struct {
driver DriverInterface
}
library := &Library{
driver: NewDriver(100),
}
ctx := context.Background()
// 定义 A
type A struct {
produce ProduceInterface
}
a := &A{
produce: NewProduce(library.driver),
}
// 定义 B
type B struct {
consumer ConsumerInterface
}
b := &B{
consumer: NewConsumer(library.driver),
}
// 定义书 S 并且添加一些描述
book := NewEvent("S")
book.SetMsg("title", "hello world")
book.SetValue("pages", 120)
// A 把书 S 放到图书馆
go func() {
book.InitWaitEvent()
a.produce.Call(ctx, book)
// A 等待 B 的回复, 但是他最多只会等待 B 2个小时
res, err := book.StartWaitEvent(2 * time.Hour)
require.NoError(t, err)
fmt.Println(res)
}()
// 模拟 B 去图书馆拿书
book = b.consumer.Receive(ctx)
fmt.Println(book.GetMsg("title"))
// 书完好
book.ExecWorkAndSendResult(func() (interface{}, error) {
// b 检查书
return "OK", nil
})
time.Sleep(50 * time.Millisecond)
}
```

View File

@ -2,6 +2,6 @@
EventP : 生产事件
EventQ : 队列事件
EventQ : 事件队列
Event CP : 清理事件

View File

@ -1,5 +1,5 @@
### RDB + AOF 的事务解决方案
### RDB + AOF 的事务解决方案
![事务RDB方案](https://gitee.com/timedb/img/raw/master/images/事务RDB方案.svg)

47
doc/pkg/middle/middle.md Normal file
View File

@ -0,0 +1,47 @@
### 中间件调用
### 创建事件
event := event.NewEvent("logcontext")
### 创建驱动
middleware := NewMiddleWare()
### 将事件推入驱动
middleware.eventProduce.Call(ctx, event)
### 获取驱动的事件
middleware.eventConsumer.Reciver(ctx)
### 插件接口
type MiddleToolsInterface interface {
Init() // 初始化
Exec(interface{}) (interface{}, error) // 处理用户发送事件
Name() string // 获取中间件名称
Describe() string // 描述
}
### 插件的New方法规定为 NewMiddleWare()
每个插件都要定义 NewMiddleWare()
### 将插件名 “logMiddle” 注册到配置文件wheat-cache.yaml,其他插件注册
plugins-control:
logcontext: ["logMiddle"]

View File

@ -0,0 +1,60 @@
### 结构体开发文档
#### 基础结构体开发
- 基础结构体放到 /pkg/structure。
- 每个结构 类型都以 x 结尾,如 listx stringx。
- 在 pkg/structure/define.go 文件中添加对应结构体的接口,主要为 了保证项目可以扩展线程安全结构体, 以及结构体接口的实现, 目前 lru 采用 single 模式, 可以只开发 single 的结构体,如 stringxSingle。
- 请在结构体包中 补充单元测试。
#### 目前实现结构体接口说明
> ps: 所有的详细用法都可以查看单元测试文件。
```go
// stringx
type StringXInterface interface {
KeyBaseInterface
// 重新设置一个 值
Set(string) (string, UpdateLength)
// 获取值的 string 形式
Get() string
// 值自动增加 一个值,只对 float 和 string 有效
Add(int32) (string, error)
// 值自动增加 减少值,只对 float 和 string 有效
Reduce(int32) (string, error)
// 使用位图类型
Setbit(int32, bool) UpdateLength
Getbit(int32) (bool, error)
// 获取字符串的切片
Getrange(start, end int32) (string, error)
GetLength() int
}
// listx
type ListXInterface interface {
KeyBaseInterface
LPush(...string) UpdateLength
RPush(...string) UpdateLength
LPop(int) ([]string, UpdateLength)
RPop(int) ([]string, UpdateLength)
Index(int) (string, error)
// 插入一组数据, bool 类型表示是否右插尾插false 时采用左插(头插)
Insert(int, bool, ...string) (UpdateLength, error)
Length() int
// 切片, O(n)复杂度
Slice(start, end int) (UpdateLength, error)
}
```
### structure.Value 类型
- 结构体的数据区域全部使用 structure.Value 类型存储。
- structure.Value 主要实现了 sting, int64, float64 的存储接口。
- structure.Value 非常容易计算内存占用。
- structure.UpdateLength 指的是在进行某一个操作以后,内存大小的变化。
- 为了保证 lru 的存储效率lru 不会去遍历全部的 key 来重新计算大小,而是根据 UpdateLength 来动态更新 lru 的大小,具体实现在 dao 中。
- structure.Value 类型的使用方法可以在 pkg/structure/value_test.go 中获取。

46
doc/storage/storage.md Normal file
View File

@ -0,0 +1,46 @@
### 快速进行 storage 开发
#### 开发环境
- ubuntu18, 可以使用 wsl
- go1.15+, python3
- jinja2
- go mod
- protobuf 3.17.3
- protoc-gen-go v1.26.0
#### storage 执行流程
![](../_icon/storage-dao.svg)
#### 分层,简介
```sh
.
├── cmd # storage 启动函数
│ └── root.go
├── dao # 实际处理层,接口实现全部再 dao 层里实现
│ ├── dao.go
│ ├── dao_test.go
│ ├── interface.gen.go
│ ├── listx.go # listx 相关功能
│ └── stringx.go
├── main.go
├── service # 接口层,由 gen-service 自动生成
│ ├── define.go
│ ├── single.go
│ └── single_service.gen.go
└── temp # 开发模板层
├── const.gen.go
├── const.template
├── dao.template
├── service.template
└── tem.yaml
```
#### 快速开发接口
> [快速开发视频 blibli](https://www.bilibili.com/video/BV1HL4y1v7ps)
1. 修改 temp/tem.yaml 文件,添加新接口
2. 在项目根目录执行 `make dcgen` 生成 proto 原始结构
3. 修改对应新添加接口的 proto 文件,再次执行 `make dcgen` 完成 proto 迁移
4. 执行 `make gen-service` 生成 dao 接口
5. 完成 新 dao 层接口, 根据需要添加单元测试。
6. 使用 make install 编译并且安装项目

View File

View File

@ -0,0 +1,54 @@
### 单元测试文档
#### 样例
```go
package dao
import (
"testing"
_ "gitee.com/wheat-os/wheatCache/conf"
"gitee.com/wheat-os/wheatCache/pkg/lru"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"github.com/stretchr/testify/require"
)
// 规范1. 每个 package 应该都至少有一个单测
// 规范2. 包里有公用的 mock 数据应该分离出来。
func mockData(t *testing.T, d *Dao) {
values := []string{"1", "1.3", "abcdefg"}
for _, val := range values {
key := &proto.BaseKey{
Key: val,
}
_, err := d.Set(key, val)
// 规范3. 使用 require 包来完成单测
require.NoError(t, err)
}
}
// 规范4. 单元测试应该尽可能覆盖全部情况,不要使用依赖注入。
func TestDao_Reduce(t *testing.T) {
lruCache := lru.NewLRUCache()
dao := NewDao(lruCache)
mockData(t, dao)
resp, err := dao.Reduce(&proto.BaseKey{Key: "1"}, 2)
require.NoError(t, err)
require.Equal(t, resp, "-1")
resp, err = dao.Reduce(&proto.BaseKey{Key: "1.3"}, 2)
require.NoError(t, err)
require.Equal(t, resp, "-0.70")
_, err = dao.Reduce(&proto.BaseKey{Key: "abcdefg"}, 2)
require.Error(t, err)
}
```

69
gateway/cmd/root.go Normal file
View File

@ -0,0 +1,69 @@
package cmd
import (
"fmt"
"net"
_ "gitee.com/wheat-os/wheatCache/conf"
wheatCodec "gitee.com/wheat-os/wheatCache/gateway/codec"
"gitee.com/wheat-os/wheatCache/gateway/endpoint"
"gitee.com/wheat-os/wheatCache/gateway/proxy"
"gitee.com/wheat-os/wheatCache/pkg/logx"
"gitee.com/wheat-os/wheatCache/pkg/util/server"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"google.golang.org/grpc"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "getway",
Short: "getway",
Long: `start getway server`,
Run: func(cmd *cobra.Command, args []string) {
host := viper.GetString("gateway.host")
port := viper.GetInt("gateway.port")
tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil {
logx.Panic("get gateway addr err:%v", err)
}
listen, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
logx.Panic("get gateway tcp conn err:%v", err)
}
gatewayServer := GetGatewayServer()
server.ElegantExitServer(gatewayServer)
logx.Info("start gateway in addr: %s", tcpAddr.String())
if err := gatewayServer.Serve(listen); err != nil {
logx.Errorln(err)
}
},
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
cobra.CheckErr(rootCmd.Execute())
}
func GetGatewayServer() *grpc.Server {
targets := viper.GetStringSlice("gateway.target")
logx.Debug("service target in %v", targets)
stream := proxy.GetDirectorByServiceHash()
endpoint := endpoint.NewHashEndpoint(endpoint.HashReplicasDefault, nil, targets...)
opts := make([]grpc.ServerOption, 0)
opts = append(
opts,
grpc.ForceServerCodec(wheatCodec.Codec()),
grpc.UnknownServiceHandler(proxy.TransparentHandler(stream, endpoint)),
)
return grpc.NewServer(opts...)
}

60
gateway/codec/codce.go Normal file
View File

@ -0,0 +1,60 @@
package codec
import (
"google.golang.org/grpc/encoding"
"google.golang.org/protobuf/proto"
)
// protoCodec 用于 gateway 解析全部的 grpc 类型的消息
type protoCodec struct{}
func (protoCodec) Name() string {
return "wheat-cache-proto"
}
func (protoCodec) Marshal(v interface{}) ([]byte, error) {
return proto.Marshal(v.(proto.Message))
}
func (protoCodec) Unmarshal(data []byte, v interface{}) error {
return proto.Unmarshal(data, v.(proto.Message))
}
type Frame struct {
payload []byte
}
type proxyCodec struct {
baseCodec encoding.Codec
}
func (p *proxyCodec) Name() string {
return "wheat-cache-proxy"
}
func (p *proxyCodec) Marshal(v interface{}) ([]byte, error) {
out, ok := v.(*Frame)
if !ok {
return p.Marshal(v)
}
return out.payload, nil
}
func (p *proxyCodec) Unmarshal(data []byte, v interface{}) error {
dst, ok := v.(*Frame)
if !ok {
return p.Unmarshal(data, v)
}
dst.payload = data
return nil
}
// CodeWithParent 生成基于 proto 的解码器
func CodeWithParent(parent encoding.Codec) encoding.Codec {
return &proxyCodec{parent}
}
func Codec() encoding.Codec {
return CodeWithParent(protoCodec{})
}

View File

@ -0,0 +1,11 @@
package endpoint
type EndpointInterface interface {
GetTargetAddr(...string) (string, error)
IsEmpty() bool
AddTarget(targets ...string)
}
const (
HashReplicasDefault = 3
)

85
gateway/endpoint/hash.go Normal file
View File

@ -0,0 +1,85 @@
package endpoint
import (
"hash/crc32"
"sort"
"strconv"
"gitee.com/wheat-os/wheatCache/pkg/errorx"
)
type HashFunc func(data []byte) uint32
// 实现 sort
type UInt32Slice []uint32
func (s UInt32Slice) Len() int {
return len(s)
}
func (s UInt32Slice) Less(i, j int) bool {
return s[i] < s[j]
}
func (s UInt32Slice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
type HashEndpoint struct {
hash HashFunc
replicas int // 复制因子
keys UInt32Slice
hashMap map[uint32]string // taraget 隐射
}
func NewHashEndpoint(replicas int, fn HashFunc, target ...string) EndpointInterface {
endpoint := &HashEndpoint{
replicas: replicas,
hash: fn,
hashMap: make(map[uint32]string, len(target)),
}
if endpoint.hash == nil {
endpoint.hash = crc32.ChecksumIEEE // 默认使用 CRC32 算法
}
endpoint.AddTarget(target...)
return endpoint
}
func (h *HashEndpoint) IsEmpty() bool {
return len(h.keys) == 0
}
func (h *HashEndpoint) AddTarget(targets ...string) {
for _, tar := range targets {
for i := 0; i < h.replicas; i++ {
hash := h.hash([]byte(strconv.Itoa(i) + tar))
h.keys = append(h.keys, hash)
h.hashMap[hash] = tar
}
}
// 虚拟值排序,方便查找
sort.Sort(h.keys)
}
func (h *HashEndpoint) GetTargetAddr(str ...string) (string, error) {
if h.IsEmpty() {
return "", errorx.New("gateway not register transport")
}
if len(str) != 1 {
return "", errorx.New("must give key")
}
hash := h.hash([]byte(str[0]))
idx := sort.Search(len(h.keys), func(i int) bool { return h.keys[i] >= hash })
if idx == len(h.keys) {
return h.hashMap[h.keys[0]], nil
}
return h.hashMap[h.keys[idx]], nil
}

View File

@ -0,0 +1,17 @@
package endpoint
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestHashTransport_GetTargetAddr(t *testing.T) {
tran := NewHashEndpoint(3, nil, "127.0.0.1:5581", "127.0.0.1:5582", "127.0.0.1:5583")
key := "test"
target, err := tran.GetTargetAddr(key)
require.NoError(t, err)
require.Equal(t, target, "127.0.0.1:5582")
}

7
gateway/main.go Normal file
View File

@ -0,0 +1,7 @@
package main
import "gitee.com/wheat-os/wheatCache/gateway/cmd"
func main() {
cmd.Execute()
}

17
gateway/proxy/define.go Normal file
View File

@ -0,0 +1,17 @@
package proxy
import (
"context"
"gitee.com/wheat-os/wheatCache/gateway/endpoint"
"google.golang.org/grpc"
)
type StreamDirector func(ctx context.Context, fullMethodName string, endpoint endpoint.EndpointInterface) (context.Context, *grpc.ClientConn, error)
var (
clientStreamDescForProxying = &grpc.StreamDesc{
ServerStreams: true,
ClientStreams: true,
}
)

37
gateway/proxy/director.go Normal file
View File

@ -0,0 +1,37 @@
package proxy
import (
"context"
"gitee.com/wheat-os/wheatCache/gateway/codec"
"gitee.com/wheat-os/wheatCache/gateway/endpoint"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
func GetDirectorByServiceHash() StreamDirector {
return func(ctx context.Context, fullMethodName string, endpoint endpoint.EndpointInterface) (context.Context, *grpc.ClientConn, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, nil, status.Errorf(codes.Unknown, "from FromIncomingContext err")
}
baseKey, ok := md[proto.BaseKeyMethodKey]
if !ok {
return nil, nil, status.Errorf(codes.Unknown,
"grpc header is not found %s, please check the client interceptor", proto.BaseKeyMethodKey)
}
target, err := endpoint.GetTargetAddr(baseKey...)
if err != nil {
return nil, nil, status.Errorf(codes.Unknown, "get transport err, err:%v", err)
}
cli, err := grpc.DialContext(ctx, target, grpc.WithInsecure(), grpc.WithDefaultCallOptions(grpc.ForceCodec(codec.Codec())))
return ctx, cli, err
}
}

131
gateway/proxy/proxy.go Normal file
View File

@ -0,0 +1,131 @@
package proxy
import (
"context"
"io"
wheatCodec "gitee.com/wheat-os/wheatCache/gateway/codec"
"gitee.com/wheat-os/wheatCache/gateway/endpoint"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// TransparentHandler returns a handler that attempts to proxy all requests that are not registered in the server.
// The indented use here is as a transparent proxy, where the server doesn't know about the services implemented by the
// backends. It should be used as a `grpc.UnknownServiceHandler`.
//
// This can *only* be used if the `server` also uses grpcproxy.CodecForServer() ServerOption.
func TransparentHandler(director StreamDirector, endpoint endpoint.EndpointInterface) grpc.StreamHandler {
streamer := &handler{
director,
endpoint,
}
return streamer.handler
}
type handler struct {
director StreamDirector
endpoint endpoint.EndpointInterface
}
// handler is where the real magic of proxying happens.
// It is invoked like any gRPC server stream and uses the gRPC server framing to get and receive bytes from the wire,
// forwarding it to a ClientStream established against the relevant ClientConn.
func (s *handler) handler(srv interface{}, serverStream grpc.ServerStream) error {
fullMethodName, ok := grpc.MethodFromServerStream(serverStream)
if !ok {
return status.Errorf(codes.Internal, "lowLevelServerStream not exists in context")
}
outgoingCtx, backendConn, err := s.director(serverStream.Context(), fullMethodName, s.endpoint)
if err != nil {
return err
}
clientCtx, clientCancel := context.WithCancel(outgoingCtx)
defer clientCancel()
clientStream, err := grpc.NewClientStream(clientCtx, clientStreamDescForProxying, backendConn, fullMethodName)
if err != nil {
return err
}
s2cErrChan := s.forwardServerToClient(serverStream, clientStream)
c2sErrChan := s.forwardClientToServer(clientStream, serverStream)
for i := 0; i < 2; i++ {
select {
case s2cErr := <-s2cErrChan:
if s2cErr == io.EOF {
// 客户端流发送完毕正常关闭结束, Proxy 关闭对 Backend 的连接
clientStream.CloseSend()
break
}
clientCancel()
return status.Errorf(codes.Internal, "failed proxying s2c: %v", s2cErr)
case c2sErr := <-c2sErrChan:
// 服务的没用在提供数据触发这个分支
serverStream.SetTrailer(clientStream.Trailer())
if c2sErr != io.EOF {
return c2sErr
}
return nil
}
}
return status.Errorf(codes.Internal, "gRPC proxying should never reach this stage.")
}
func (s *handler) forwardClientToServer(src grpc.ClientStream, dst grpc.ServerStream) chan error {
ret := make(chan error, 1)
go func() {
f := &wheatCodec.Frame{}
for i := 0; ; i++ {
if err := src.RecvMsg(f); err != nil {
ret <- err // this can be io.EOF which is happy case
break
}
if i == 0 {
// This is a bit of a hack, but client to server headers are only readable after first client msg is
// received but must be written to server stream before the first msg is flushed.
// This is the only place to do it nicely.
md, err := src.Header()
if err != nil {
ret <- err
break
}
if err := dst.SendHeader(md); err != nil {
ret <- err
break
}
}
if err := dst.SendMsg(f); err != nil {
ret <- err
break
}
}
}()
return ret
}
func (s *handler) forwardServerToClient(src grpc.ServerStream, dst grpc.ClientStream) chan error {
ret := make(chan error, 1)
go func() {
f := &wheatCodec.Frame{}
for i := 0; ; i++ {
if err := src.RecvMsg(f); err != nil {
ret <- err // this can be io.EOF which is happy case
break
}
if err := dst.SendMsg(f); err != nil {
ret <- err
break
}
}
}()
return ret
}

5
go.mod
View File

@ -1,11 +1,12 @@
module gitee.com/timedb/wheatCache
module gitee.com/wheat-os/wheatCache
go 1.16
require (
github.com/golang/mock v1.5.0
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.0
google.golang.org/grpc v1.38.0
google.golang.org/grpc v1.41.0
google.golang.org/protobuf v1.26.0
)

8
go.sum
View File

@ -46,6 +46,7 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -53,6 +54,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@ -65,6 +67,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
@ -86,6 +89,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -252,6 +256,7 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
@ -557,8 +562,9 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=

View File

@ -2,23 +2,36 @@ BASE_PATH = $(shell pwd)
STORAGE_PATH = $(BASE_PATH)/storage
BASE_OUT = $(BASE_PATH)/bin
MAKEFLAGS+= --no-print-directory
dcgen:
@python3 ./shell/gen_protobuf.py
@python3 ./shell/proto.py
@python3 ./shell/make-struct.py
.PHONY : build
build:
.PHONY: build-storage
build-storage:
@cd storage && go build -o $(BASE_OUT)/storage
.PHONY: build-gateway
build-gateway:
@cd gateway && go build -o $(BASE_OUT)/gateway
.PHONY: install
install:
@make build
@make gen-middleware
@make build-storage
@make build-gateway
@sudo python3 ./shell/init_conf.py
.PHONY: dev
dev:
.PHONY: storage
storage:
@./bin/storage storage
.PHONY: gateway
gateway:
@./bin/gateway gateway
.PHONY: gen-struct
gen-struct:
@python3 ./shell/make-struct.py
@ -26,3 +39,20 @@ gen-struct:
.PHONY: gen-protobuf
gen-protobuf:
@python3 ./shell/gen_protobuf.py
.PHONY: gen-middleware
gen-middleware:
@python3 ./shell/gen_middleware.py
.PHONY: init-conf
init-conf:
@python3 ./shell/init_conf.py
.PHONY: gen-service
gen-service:
@python3 ./shell/make_service.py
.PHONY: gen-mock
gen-mock:
@mockgen -source=./pkg/proto/storage.pb.go CommServerClient > ./mock/storage/mock_client.gen.go

File diff suppressed because it is too large Load Diff

9
pkg/errorx/dao.go Normal file
View File

@ -0,0 +1,9 @@
package errorx
func DaoTypeErr(typ string) error {
return New("the type is not: %s", typ)
}
func NotKeyErr(key string) error {
return New("the key is not exist, key:%s", key)
}

View File

@ -6,7 +6,7 @@ import (
)
// New TODO 添加链路追踪等 @bandl @lgq
func New(msg string, opt ...interface{}) error {
msg = fmt.Sprintf(msg, opt...)
func New(msg string, format ...interface{}) error {
msg = fmt.Sprintf(msg, format...)
return errors.New(msg)
}

5
pkg/errorx/event.go Normal file
View File

@ -0,0 +1,5 @@
package errorx
func EventRecoveryErr() error {
return New("this event has been recycled")
}

9
pkg/errorx/lru.go Normal file
View File

@ -0,0 +1,9 @@
package errorx
func LruNotWorkFuncEventErr() error {
return New("the event haven't work of function")
}
func KeyBaseIsNilErr() error {
return New("key base can't be nil")
}

5
pkg/errorx/time.go Normal file
View File

@ -0,0 +1,5 @@
package errorx
func TimeOutErr() error {
return New("time out err")
}

View File

@ -6,10 +6,18 @@ type Consumer struct {
driver DriverInterface
}
func (c *Consumer) Receive(ctx context.Context) *Event {
func (c *Consumer) Receive(ctx context.Context) *event {
return c.driver.Get()
}
func (c *Consumer) NewEvent(name string) *event {
return c.driver.NewEvent(name)
}
func (c *Consumer) Recovery(e *event) {
c.driver.Recovery(e)
}
func NewConsumer(driver DriverInterface) ConsumerInterface {
return &Consumer{
driver: driver,

View File

@ -4,22 +4,30 @@ import (
"context"
)
type eventType int8
const (
normalEvent = eventType(iota)
waitEvent
defaultEventState = int32(iota) //默认情况下的状态
waitEventState // 等待状态
workEventState //工作状态
closeEventState //事件关闭状态
)
type EventWorkFunc func() (interface{}, error)
type DriverInterface interface {
Get() *Event
Put(event *Event)
Get() *event
Put(*event)
GetLength() int
NewEvent(string) *event
Recovery(*event)
}
type ProduceInterface interface {
Call(ctx context.Context, event *Event)
Call(context.Context, *event)
NewEvent(string) *event
Recovery(*event)
}
type ConsumerInterface interface {
Receive(ctx context.Context) *Event
Receive(ctx context.Context) *event
Recovery(*event)
}

View File

@ -1,121 +1,189 @@
package event
import (
"gitee.com/timedb/wheatCache/pkg/errorx"
"sync"
"sync/atomic"
"time"
"gitee.com/wheat-os/wheatCache/pkg/errorx"
)
type Active func() ([]string, error) // 事件带函数
type Event struct {
msgCtx map[string]interface{}
eventName string
WorkTime time.Duration // 工作时间
msg map[string]string // 消息
waitResult chan interface{} // 等待返回
ru sync.RWMutex
eventOnType eventType
// 事件 poll 降低 new 对象的频率
type eventPoll struct {
poll chan *event
maxSize int32
nowSize *int32
}
func (e *Event) SetMsg(key string, val string) {
e.ru.Lock()
defer e.ru.Unlock()
func (e *eventPoll) getEvent() *event {
issSize := atomic.LoadInt32(e.nowSize)
if issSize < e.maxSize {
atomic.AddInt32(e.nowSize, 1)
return newEvent()
}
return <-e.poll
}
func (e *eventPoll) recovery(rEvent *event) {
rEvent.Reset()
e.poll <- rEvent
}
func newEventPoll(maxSize int) *eventPoll {
return &eventPoll{
poll: make(chan *event, maxSize),
maxSize: int32(maxSize),
nowSize: new(int32),
}
}
type event struct {
msgCtx map[string]interface{}
eventName string
msg map[string]string // 消息
waitResult chan interface{} // 等待返回
err error
eventStatus *int32
ttlManage *time.Timer
}
func newEvent() *event {
status := defaultEventState
return &event{
eventStatus: &status,
}
}
func (e *event) Reset() {
if e.ttlManage != nil {
e.ttlManage.Stop()
}
e.err = nil
atomic.SwapInt32(e.eventStatus, defaultEventState)
}
func (e *event) SetMsg(key string, val string) {
if e.msg == nil {
e.msg = make(map[string]string)
}
e.msg[key] = val
}
func (e *Event) GetMsg(key string) string {
e.ru.RLock()
defer e.ru.RUnlock()
func (e *event) GetMsg(key string) string {
return e.msg[key]
}
// SetCtxValue 写入 ctx 传递用参数
func (e *Event) SetCtxValue(key string, value interface{}) {
e.ru.Lock()
defer e.ru.Unlock()
func (e *event) GetEventName() string {
return e.eventName
}
// SetValue 写入 ctx 传递用参数
func (e *event) SetValue(key string, value interface{}) {
if e.msgCtx == nil {
e.msgCtx = make(map[string]interface{})
}
e.msgCtx[key] = value
}
func (e *Event) GetCtxValue(key string) (interface{}, bool) {
e.ru.RLock()
defer e.ru.RUnlock()
func (e *event) GetValue(key string) (interface{}, bool) {
val, ok := e.msgCtx[key]
return val, ok
}
func (e *Event) SetWaitResult(val interface{}) (err error) {
defer func() {
if errChan := recover(); errChan != nil {
err = errorx.New("channel err:%v", errChan)
// InitWaitEvent 初始化 wait event 必须调用才拥有等待特性
func (e *event) InitWaitEvent() {
if e.waitResult == nil || len(e.waitResult) > 0 {
e.waitResult = make(chan interface{})
}
// 清理残留
if e.ttlManage == nil {
e.ttlManage = time.NewTimer(0)
}
e.ttlManage.Stop()
if len(e.ttlManage.C) > 0 {
<-e.ttlManage.C
}
atomic.CompareAndSwapInt32(e.eventStatus, defaultEventState, waitEventState)
}
// StartWaitEvent 开始一个等待任务
func (e *event) StartWaitEvent(ttl time.Duration) (interface{}, error) {
e.ttlManage.Reset(ttl)
for {
select {
case <-e.ttlManage.C:
if atomic.CompareAndSwapInt32(e.eventStatus, waitEventState, closeEventState) {
return nil, errorx.TimeOutErr()
}
continue
case result := <-e.waitResult:
atomic.CompareAndSwapInt32(e.eventStatus, workEventState, closeEventState)
return result, e.err
}
}()
e.waitResult <- val
return nil
}
// UpdateWaitEvent 升级到 wait Event
func (e *Event) UpdateWaitEvent(ttl time.Duration) (<-chan interface{}, error) {
if e.eventOnType == waitEvent {
return nil, errorx.New("the upgrade cannot be repeated")
}
ttl = 1 * time.Second
if ttl > 0 {
e.WorkTime = ttl
}
e.waitResult = make(chan interface{})
e.eventOnType = waitEvent
return e.waitResult, nil
}
// GetTtlTimer 只对 wait Event 有效
func (e *Event) GetTtlTimer() (*time.Timer, error) {
if e.eventOnType != waitEvent {
return nil, errorx.New("cannot be called in normalEvent")
}
timer := time.NewTimer(e.WorkTime)
return timer, nil
}
func NewEvent(eventName string) *Event {
return &Event{
eventName: eventName,
eventOnType: normalEvent,
}
}
// Close 关闭结束事件
func (e *Event) Close() {
close(e.waitResult)
func (e *event) ExecWorkAndSendResult(work EventWorkFunc) (interface{}, error) {
if !atomic.CompareAndSwapInt32(e.eventStatus, waitEventState, workEventState) {
return nil, errorx.New("not wait status, exec err")
}
res, err := work()
e.err = err
e.waitResult <- res
return res, err
}
func (e *event) SetResultErr(err error) {
if !atomic.CompareAndSwapInt32(e.eventStatus, waitEventState, workEventState) {
return
}
e.err = err
e.waitResult <- nil
}
type Driver struct {
maxQueueSize int
queue chan *Event
queue chan *event
poll *eventPoll
}
// Get 获取驱动
func (d *Driver) Get() *Event {
func (d *Driver) Get() *event {
return <-d.queue
}
func (d *Driver) Put(event *Event) {
func (d *Driver) Put(event *event) {
d.queue <- event
}
func (d *Driver) GetLength() int {
return len(d.queue)
}
func (d *Driver) NewEvent(name string) *event {
event := d.poll.getEvent()
event.eventName = name
return event
}
// 任何时候回收事件都应该由 最后使用者回收
func (d *Driver) Recovery(e *event) {
d.poll.recovery(e)
}
// NewDriver 新建 Driver
func NewDriver(maxSize int) DriverInterface {
return &Driver{
maxQueueSize: maxSize,
queue: make(chan *Event, maxSize),
queue: make(chan *event, maxSize),
poll: newEventPoll(maxSize),
}
}

View File

@ -3,110 +3,87 @@ package event
import (
"context"
"fmt"
"github.com/stretchr/testify/require"
"strconv"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestEvent_SetWaitResult(t *testing.T) {
event := &Event{
waitResult: make(chan interface{}),
}
c, err := event.UpdateWaitEvent(2 * time.Second)
require.NoError(t, err)
go getValueByChannel(c)
err = event.SetWaitResult(1)
require.NoError(t, err)
event.Close()
err = event.SetWaitResult(1)
require.Error(t, err)
}
func getValueByChannel(c <-chan interface{}) {
for {
if i, isClose := <-c; !isClose {
break
} else {
fmt.Println(i)
}
}
}
const testEvent = "1001"
const waitTestEvent = "1002"
// 简单 非等待响应模式, 使用 event driver
func TestEvent_DriverEventTest(t *testing.T) {
ctx := context.Background()
driver := NewDriver(500)
// 简单的 单向 event 使用
func Test_EventDriver(t *testing.T) {
driver := NewDriver(2000)
produce := NewProduce(driver)
consumer := NewConsumer(driver)
go produceEvent(t, ctx, produce)
consumerEvent(t, ctx, consumer)
}
func produceEvent(t *testing.T, ctx context.Context, v ProduceInterface) {
for i := 0; i < 100; i++ {
event := NewEvent(testEvent)
event.SetCtxValue("test", i)
v.Call(ctx, event)
}
}
func consumerEvent(t *testing.T, ctx context.Context, v ConsumerInterface) {
for i := 0; i < 100; i++ {
event := v.Receive(ctx)
res, ok := event.GetCtxValue("test")
require.True(t, ok)
fmt.Println(res)
require.Equal(t, res, i)
}
}
// 响应等待用法
func TestEvent_SpanWaitEvent(t *testing.T) {
ctx := context.Background()
driver := NewDriver(500)
produce := NewProduce(driver)
consumer := NewConsumer(driver)
go waitConsumer(t, ctx, consumer)
wait := sync.WaitGroup{}
wait.Add(30000)
waitProduce(t, ctx, produce)
}
go func() {
for i := 0; i < 30000; i++ {
event := produce.NewEvent(testEvent)
event.SetMsg("k", strconv.Itoa(i))
produce.Call(ctx, event)
func waitProduce(t *testing.T, ctx context.Context, v ProduceInterface) {
for i := 0; i < 100; i++ {
event := NewEvent(waitTestEvent)
waitChan, err := event.UpdateWaitEvent(2 * time.Second) // 设置事件过期时间, 获取结果等待对象
require.NoError(t, err)
event.SetCtxValue("test", i)
v.Call(ctx, event) // 推送给 consumer
timer, err := event.GetTtlTimer()
require.NoError(t, err)
select {
case result := <-waitChan:
require.Equal(t, result, fmt.Sprintf("test:%v", i))
fmt.Println(result)
case <-timer.C:
panic("time out")
}
}
}()
go func() {
for {
event := consumer.Receive(ctx)
fmt.Println(event.GetMsg("k"))
consumer.Recovery(event)
wait.Done()
}
}()
wait.Wait()
fmt.Println(*driver.(*Driver).poll.nowSize)
}
func waitConsumer(t *testing.T, ctx context.Context, v ConsumerInterface) {
for i := 0; i < 100; i++ {
event := v.Receive(ctx) // 接受 produce 的 event
res, ok := event.GetCtxValue("test")
require.True(t, ok)
require.Equal(t, res, i)
// 双向 event
func Test_WaitEventDriver(t *testing.T) {
driver := NewDriver(200)
produce := NewProduce(driver)
consumer := NewConsumer(driver)
err := event.SetWaitResult(fmt.Sprintf("test:%v", res)) // 发送返回值给 produce
require.NoError(t, err)
}
ctx := context.Background()
wait := sync.WaitGroup{}
wait.Add(300000)
go func() {
for i := 0; i < 300000; i++ {
event := produce.NewEvent(testEvent)
event.SetMsg("k", strconv.Itoa(i))
event.InitWaitEvent()
produce.Call(ctx, event)
val, err := event.StartWaitEvent(2 * time.Second)
require.NoError(t, err)
fmt.Println(val)
produce.Recovery(event)
wait.Done()
}
}()
go func() {
for {
event := consumer.Receive(ctx)
event.ExecWorkAndSendResult(func() (interface{}, error) {
msg := event.GetMsg("k")
return "hello: " + msg, nil
})
}
}()
wait.Wait()
fmt.Println(*driver.(*Driver).poll.nowSize)
}

View File

@ -6,8 +6,16 @@ type Produce struct {
driver DriverInterface
}
func (p *Produce) Call(ctx context.Context, event *Event) {
p.driver.Put(event)
func (p *Produce) NewEvent(name string) *event {
return p.driver.NewEvent(name)
}
func (p *Produce) Recovery(e *event) {
p.driver.Recovery(e)
}
func (p *Produce) Call(ctx context.Context, e *event) {
p.driver.Put(e)
}
func NewProduce(driver DriverInterface) ProduceInterface {

21
pkg/event2/consumer.go Normal file
View File

@ -0,0 +1,21 @@
package event2
import "context"
type Consumer struct {
driver DriverInterface
}
func (c *Consumer) Receive(ctx context.Context) *event {
return c.driver.Get()
}
func (c *Consumer) NewEvent(name string) *event {
return c.driver.NewEvent(name)
}
func NewConsumer(driver DriverInterface) ConsumerInterface {
return &Consumer{
driver: driver,
}
}

49
pkg/event2/define.go Normal file
View File

@ -0,0 +1,49 @@
package event2
import (
"context"
)
const (
initEventState = int32(iota) // 初始化状态
waitEventState // 等待状态
workEventState // 工作状态
closeEventState // 事件关闭状态
recoveryEventState // 事件回收状态
)
const (
awaitThread = 3
)
const (
WorkFuncEventKey = "workFunc"
)
// 线程安全
type EventWorkFunc func() (interface{}, error)
// 挂起事件, 线程不安全
type EventAwaitFunc func() (interface{}, error)
// 实际操作
type awaitFunc func() (*event, interface{}, error)
type DriverInterface interface {
Get() *event
Put(*event)
GetLength() int
NewEvent(string) *event
await(awaitFunc)
recovery(e *event)
}
type ProduceInterface interface {
Call(context.Context, *event)
NewEvent(string) *event
}
type ConsumerInterface interface {
Receive(ctx context.Context) *event
}

221
pkg/event2/driver.go Normal file
View File

@ -0,0 +1,221 @@
package event2
import (
"sync/atomic"
"time"
"gitee.com/wheat-os/wheatCache/pkg/errorx"
)
type event struct {
msgCtx map[string]interface{}
eventName string
msg map[string]string // 消息
waitResult chan interface{} // 等待返回
err error
eventStatus int32
ttlManage *time.Timer
parentDriver DriverInterface
}
func (e *event) reset() {
if e.ttlManage != nil {
e.ttlManage.Stop()
if len(e.ttlManage.C) > 0 {
<-e.ttlManage.C
}
}
e.err = nil
// 清空结果
if len(e.waitResult) != 0 {
<-e.waitResult
}
}
func (e *event) Recovery() {
e.parentDriver.recovery(e)
}
func (e *event) SetMsg(key string, val string) {
if e.msg == nil {
e.msg = make(map[string]string)
}
e.msg[key] = val
}
func (e *event) GetMsg(key string) string {
if e.msg == nil {
return ""
}
return e.msg[key]
}
func (e *event) GetEventName() string {
return e.eventName
}
// SetValue 写入 ctx 传递用参数
func (e *event) SetValue(key string, value interface{}) {
if e.msgCtx == nil {
e.msgCtx = make(map[string]interface{})
}
e.msgCtx[key] = value
}
func (e *event) GetValue(key string) (interface{}, bool) {
if e.msgCtx == nil {
return nil, false
}
val, ok := e.msgCtx[key]
return val, ok
}
func (e *event) InitWaitEvent() {
e.reset()
if e.waitResult == nil {
e.waitResult = make(chan interface{})
}
atomic.SwapInt32(&e.eventStatus, waitEventState)
}
func (e *event) SetResultErr(err error) {
if !atomic.CompareAndSwapInt32(&e.eventStatus, waitEventState, workEventState) {
return
}
e.err = err
e.waitResult <- nil
}
// StartWaitEvent 开始一个等待任务
func (e *event) StartWaitEvent(ttl time.Duration) (interface{}, error) {
if e.ttlManage == nil {
e.ttlManage = time.NewTimer(ttl)
} else {
e.ttlManage.Reset(ttl)
}
for {
select {
case <-e.ttlManage.C:
if atomic.CompareAndSwapInt32(&e.eventStatus, waitEventState, closeEventState) {
return nil, errorx.TimeOutErr()
}
continue
case result := <-e.waitResult:
atomic.SwapInt32(&e.eventStatus, closeEventState)
return result, e.err
}
}
}
// 实际执行推送
func (e *event) execWorker(res interface{}, err error) {
switch work := res.(type) {
case EventAwaitFunc:
await := func() (*event, interface{}, error) {
result, err := work()
return e, result, err
}
e.parentDriver.await(await)
case EventWorkFunc:
e.InitWaitEvent()
e.SetValue(WorkFuncEventKey, work)
e.parentDriver.Put(e)
default:
e.err = err
e.waitResult <- res
}
}
func (e *event) ExecWorkAndSendResult(work EventWorkFunc) (interface{}, error) {
if !atomic.CompareAndSwapInt32(&e.eventStatus, waitEventState, workEventState) {
return nil, errorx.New("not wait status, exec err")
}
res, err := work()
e.execWorker(res, err)
return res, err
}
type driver struct {
waitQueue chan awaitFunc
eventQueue chan *event
levelQueue chan *event
// event 池的实现
poll chan *event
maxPoolSize int32
nowPoolSize int32
}
func NewDriver(maxSize int) DriverInterface {
d := &driver{
// pool
maxPoolSize: int32(maxSize),
nowPoolSize: 0,
poll: make(chan *event, maxSize),
// waitQueue 1/3 的挂起指标
waitQueue: make(chan awaitFunc, maxSize/3),
levelQueue: make(chan *event, maxSize/3),
eventQueue: make(chan *event, maxSize),
}
d.awaitWorker()
return d
}
func (d *driver) NewEvent(name string) *event {
issSize := atomic.LoadInt32(&d.nowPoolSize)
if issSize < d.maxPoolSize {
atomic.AddInt32(&d.nowPoolSize, 1)
return d.newEvent(name)
}
e := <-d.poll
e.eventName = name
return e
}
func (d *driver) newEvent(name string) *event {
status := initEventState
return &event{
eventStatus: status,
parentDriver: d,
eventName: name,
}
}
// 先尝试 level
func (d *driver) Get() *event {
if len(d.levelQueue) > 0 {
return <-d.levelQueue
}
return <-d.eventQueue
}
func (d *driver) Put(e *event) {
d.eventQueue <- e
}
func (d *driver) GetLength() int {
return len(d.eventQueue) + len(d.levelQueue)
}
func (d *driver) recovery(e *event) {
atomic.SwapInt32(&e.eventStatus, recoveryEventState)
e.reset()
d.poll <- e
}
// 挂起操作相关
func (d *driver) await(a awaitFunc) {
d.waitQueue <- a
}

152
pkg/event2/driver_test.go Normal file
View File

@ -0,0 +1,152 @@
package event2
import (
"context"
"fmt"
"strconv"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
)
const testEvent = "1001"
const waitTestEvent = "1002"
// 简单的 单向 event 使用
func Test_EventDriver(t *testing.T) {
driver := NewDriver(2000)
produce := NewProduce(driver)
consumer := NewConsumer(driver)
ctx := context.Background()
wait := sync.WaitGroup{}
wait.Add(30000)
go func() {
for i := 0; i < 30000; i++ {
event := produce.NewEvent(testEvent)
event.SetMsg("k", strconv.Itoa(i))
produce.Call(ctx, event)
}
}()
go func() {
for {
event := consumer.Receive(ctx)
fmt.Println(event.GetMsg("k"))
event.Recovery()
wait.Done()
}
}()
wait.Wait()
}
// 双向 event
func Test_EventDriver_Tow_way(t *testing.T) {
ctx := context.Background()
driver := NewDriver(2000)
produce := NewProduce(driver)
consumer := NewConsumer(driver)
go func() {
for {
event := consumer.Receive(ctx)
work, ok := event.GetValue(WorkFuncEventKey)
if !ok {
panic("get work key err")
}
workFunc, ok := work.(EventWorkFunc)
if !ok {
panic("work func err")
}
_, err := event.ExecWorkAndSendResult(workFunc)
require.NoError(t, err)
}
}()
// 一般的 two-way 模式
for i := 0; i < 10000; i++ {
event := produce.NewEvent(waitTestEvent)
event.InitWaitEvent()
event.SetValue(WorkFuncEventKey, EventWorkFunc(func() (interface{}, error) {
return i + 1, nil
}))
produce.Call(ctx, event)
res, err := event.StartWaitEvent(2 * time.Second)
require.NoError(t, err)
require.Equal(t, res, i+1)
event.Recovery()
}
// 挂起模式2 秒左右的执行时间
group := sync.WaitGroup{}
group.Add(5)
for i := 0; i < 5; i++ {
go func(i int) {
event := produce.NewEvent(waitTestEvent)
event.InitWaitEvent()
event.SetValue(WorkFuncEventKey, EventWorkFunc(func() (interface{}, error) {
// 访问 await Work 来发起一个 异步请求操作
return EventAwaitFunc(func() (interface{}, error) {
time.Sleep(time.Second)
return i + 1, nil
}), nil
}))
produce.Call(ctx, event)
res, err := event.StartWaitEvent(2 * time.Second)
require.NoError(t, err)
require.Equal(t, res, i+1)
event.Recovery()
group.Done()
}(i)
}
// 挂起成功不发生超时
for i := 0; i < 10000; i++ {
event := produce.NewEvent(waitTestEvent)
event.InitWaitEvent()
event.SetValue(WorkFuncEventKey, EventWorkFunc(func() (interface{}, error) {
return i + 1, nil
}))
produce.Call(ctx, event)
res, err := event.StartWaitEvent(500 * time.Millisecond)
require.NoError(t, err)
require.Equal(t, res, i+1)
event.Recovery()
}
group.Wait()
// 挂起一个高延迟操作, 保证局部操作还在事件中
group = sync.WaitGroup{}
group.Add(5)
for i := 0; i < 5; i++ {
event := produce.NewEvent(waitTestEvent)
event.InitWaitEvent()
event.SetValue(WorkFuncEventKey, EventWorkFunc(func() (interface{}, error) {
return EventAwaitFunc(func() (interface{}, error) {
// 返回值为 EventWorkFunc 时, 会重新加入末端队列
return EventWorkFunc(func() (interface{}, error) {
return i + 1, nil
}), nil
}), nil
}))
produce.Call(ctx, event)
res, err := event.StartWaitEvent(2 * time.Second)
require.NoError(t, err)
require.Equal(t, res, i+1)
event.Recovery()
group.Done()
fmt.Println(i)
}
group.Wait()
}

21
pkg/event2/produce.go Normal file
View File

@ -0,0 +1,21 @@
package event2
import "context"
type Produce struct {
driver DriverInterface
}
func (p *Produce) NewEvent(name string) *event {
return p.driver.NewEvent(name)
}
func (p *Produce) Call(ctx context.Context, e *event) {
p.driver.Put(e)
}
func NewProduce(driver DriverInterface) ProduceInterface {
return &Produce{
driver: driver,
}
}

13
pkg/event2/worker.go Normal file
View File

@ -0,0 +1,13 @@
package event2
func (d *driver) awaitWorker() {
for i := 0; i < awaitThread; i++ {
go func() {
for {
awaitFunc := <-d.waitQueue
e, res, err := awaitFunc()
e.execWorker(res, err)
}
}()
}
}

View File

@ -1 +0,0 @@
package log

43
pkg/logx/define.go Normal file
View File

@ -0,0 +1,43 @@
package logx
import (
"context"
"sync"
"gitee.com/wheat-os/wheatCache/pkg/event"
"github.com/spf13/viper"
)
type LogLevelState int8
var (
once sync.Once
stath []string
)
type upLogger struct {
ctx context.Context
produce event.ProduceInterface
}
func init() {
once.Do(func() {
stath = viper.GetStringSlice("logPrint.stath")
})
}
type logInterface interface {
Debug(format string, msg ...interface{})
Info(format string, msg ...interface{})
Warn(format string, msg ...interface{})
Error(format string, msg ...interface{})
Panic(format string, msg ...interface{})
Debugln(msg ...interface{})
Infoln(msg ...interface{})
Warnln(msg ...interface{})
Errorln(msg ...interface{})
Panicln(msg ...interface{})
Print(level string, format string, msg ...interface{})
}

161
pkg/logx/logx.go Normal file
View File

@ -0,0 +1,161 @@
package logx
import (
"context"
"fmt"
"os"
"runtime"
"strings"
"time"
"gitee.com/wheat-os/wheatCache/pkg/event"
middleMsg "gitee.com/wheat-os/wheatCache/pkg/middle-msg"
)
func With(ctx context.Context, p event.ProduceInterface) *upLogger {
return &upLogger{
ctx: ctx,
produce: p,
}
}
func (l *upLogger) Debug(format string, msg ...interface{}) {
l.Print("DEBUG", format, msg...)
}
func (l *upLogger) Info(format string, msg ...interface{}) {
l.Print("INFO", format, msg...)
}
func (l *upLogger) Warn(format string, msg ...interface{}) {
l.Print("WARN", format, msg...)
}
func (l *upLogger) Error(format string, msg ...interface{}) {
l.Print("ERROR", format, msg...)
}
func (l *upLogger) Panic(format string, msg ...interface{}) {
l.Print("ERROR", format, msg...)
os.Exit(-1)
}
func (l *upLogger) Print(level string, format string, msg ...interface{}) {
logPrint(4, level, format, msg...)
sendMsg := &middleMsg.LogContext{
Level: level,
Data: time.Now(),
Msg: fmt.Sprintf(format, msg...),
Route: findPlace(4),
}
middleMsg.SendMiddleMsg(l.ctx, l.produce, sendMsg)
}
func (l *upLogger) Debugln(msg ...interface{}) {
l.Print("DEBUG", "%s", format(msg...))
}
func (l *upLogger) Infoln(msg ...interface{}) {
l.Print("INFO", "%s", format(msg...))
}
func (l *upLogger) Warnln(msg ...interface{}) {
l.Print("WARN", "%s", format(msg...))
}
func (l *upLogger) Errorln(msg ...interface{}) {
l.Print("ERROR", "%s", format(msg...))
}
func (l *upLogger) Panicln(msg ...interface{}) {
l.Print("ERROR", "%s", format(msg...))
os.Exit(-1)
}
func Debug(format string, msg ...interface{}) {
logPrint(3, "DEBUG", format, msg...)
}
func Info(format string, msg ...interface{}) {
logPrint(3, "INFO", format, msg...)
}
func Warn(format string, msg ...interface{}) {
logPrint(3, "WARN", format, msg...)
}
func Error(format string, msg ...interface{}) {
logPrint(3, "ERROR", format, msg...)
}
func Panic(format string, msg ...interface{}) {
logPrint(3, "PANIC", format, msg...)
os.Exit(-1)
}
func Debugln(msg ...interface{}) {
logPrint(3, "DEBUG", "%s", format(msg...))
}
func Infoln(msg ...interface{}) {
logPrint(3, "INFO", "%s", format(msg...))
}
func Warnln(msg ...interface{}) {
logPrint(3, "WARN", "%s", format(msg...))
}
func Errorln(msg ...interface{}) {
logPrint(3, "ERROR", "%s", format(msg...))
}
func Panicln(msg ...interface{}) {
logPrint(3, "PANIC", "%s", format(msg...))
os.Exit(-1)
}
func logPrint(floor int, level string, format string, msg ...interface{}) {
place := findPlace(floor)
datetime := fmt.Sprintf("%s", time.Now())[0:19]
switch level {
case "DEBUG":
fmt.Printf("\033[1;37;40m")
case "INFO":
fmt.Printf("\033[1;32;40m")
case "WARN":
fmt.Printf("\033[1;33;40m")
default:
fmt.Printf("\033[1;31;40m")
}
//fmt.Println(level, datetime, fmt.Sprintf(format, msg...))
fmt.Printf("%s\t%v\t%s\n", level, datetime, fmt.Sprintf(format, msg...))
for _, lv := range stath {
if strings.ToUpper(lv) == strings.ToUpper(level) {
fmt.Println(place)
break
}
}
}
func findPlace(floor int) string {
var (
place string
i = floor
)
for {
_, file, line, _ := runtime.Caller(i)
if line == 0 {
break
}
i++
place = fmt.Sprintf("%s:%d\n%s", file, line, place)
}
return place
}
func format(message ...interface{}) (context string) {
for _, msg := range message {
context = fmt.Sprintf("%s\t%v", context, msg)
}
return context
}

38
pkg/logx/logx_test.go Normal file
View File

@ -0,0 +1,38 @@
package logx
import (
"context"
"testing"
_ "gitee.com/wheat-os/wheatCache/conf"
"gitee.com/wheat-os/wheatCache/pkg/event"
)
func TestStd(t *testing.T) {
Info("%d%s", 11, "Info")
Debug("%d%s", 11, "Debug")
Warn("%d%s", 11, "Warn")
Error("%d%s", 11, "Error")
Infoln(1321, "dwad", 0x9812933)
Debugln(1321, "dwad", 0x9812933)
Warnln(1321, "dwad", 0x9812933)
Errorln(1321, "dwad", 0x9812933)
//Panic("%d%s", 11, "Panic")
logger := With(context.Background(),
event.NewProduce(event.NewDriver(100)))
logger.Info("%d%s", 11, "Info")
logger.Debug("%d%s", 11, "Debug")
logger.Warn("%d%s", 11, "Warn")
logger.Error("%d%s", 11, "Error")
//logger.Panic("%d%s", 11, "Panic")
logger.Infoln(11, "Info")
logger.Debugln(11, "Debug")
logger.Warnln(11, "Warn")
logger.Errorln(11, "Error")
}

View File

@ -1,101 +0,0 @@
package lru
import (
"container/list"
"errors"
"gitee.com/timedb/wheatCache/pkg/structure"
)
// feat
/*
1. cleanProduce
2. 定义 LRUSingle Work 函数
*/
type Node struct {
Key interface{}
value structure.KeyBaseInterface
}
func (*Node) NewCacheNode(k interface{}, v structure.KeyBaseInterface) *Node {
return &Node{Key: k, value: v}
}
type LRUCache struct {
maxsize int64 //最大的长度
clearsize int64 // 清理长度
nowsize int64 // 现在的长度
li *list.List
LruMap map[interface{}]*list.Element // TODO LRU 对外暴露
}
// NewLRUCache lru初始化 TODO 单例模式
func NewLRUCache(msize, csize int64) *LRUCache {
return &LRUCache{
maxsize: msize,
clearsize: csize,
nowsize: 0,
li: list.New(),
LruMap: make(map[interface{}]*list.Element)}
}
// Add 增 TODO 大问题
func (lru *LRUCache) Add(k interface{}, v structure.KeyBaseInterface) error {
if lru.li == nil {
return nil // TODO err
}
// TODO 封装 Key 迁移方法
if PreEle, ok := lru.LruMap[k]; ok {
lru.li.MoveToFront(PreEle)
PreEle.Value.(*Node).value = v
lru.nowsize += PreEle.Value.(*Node).value.SizeByte()
return nil
}
// TODO 增加的情况下, 应该只维护 maxSize
if lru.nowsize >= lru.maxsize*1/3 {
lru.nowsize -= lru.maxsize
if PreEle, ok := lru.LruMap[k]; ok {
cacheNode := PreEle.Value.(*Node).value
delete(lru.LruMap, cacheNode.SizeByte())
lru.li.Remove(PreEle)
}
}
newEle := lru.li.PushFront(&Node{k, v})
lru.LruMap[k] = newEle // TODO nil 指针
return nil
}
// Get 查 // TODO err
func (lru *LRUCache) Get(k interface{}) (v structure.KeyBaseInterface, ret bool) {
if lru.LruMap == nil {
return v, false
}
if PreEle, ok := lru.LruMap[k]; ok {
// 移到最前面
lru.li.MoveToFront(PreEle)
return PreEle.Value.(*Node).value, true
}
return v, false
}
// Clear 删除 // TODO 方案不对
func (lru *LRUCache) Clear(k interface{}) error {
if lru.LruMap == nil {
return errors.New("cache 为空")
}
for lru.nowsize >= lru.clearsize*1/3 {
if PreEle, ok := lru.LruMap[k]; ok {
cacheNode := PreEle.Value.(*Node).value
delete(lru.LruMap, cacheNode)
lru.li.Remove(PreEle)
return nil
}
}
return nil
}

View File

@ -1,3 +1,46 @@
package lru
type SingleWorkFunc func()
import (
"sync"
"time"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"gitee.com/wheat-os/wheatCache/pkg/structure"
)
type SingleWorkFunc func() interface{}
const (
OptionEventName = "operateEvent"
CleanEventName = "clearEvent"
TtlEventName = "ttlEvent"
)
var (
lruCacheOnce sync.Once
lruCache *SingleCache
)
const (
defaultLruMaxSize = 1 * 1024 * 1024 * 1024
defaultLruClearSize = 0.5 * 1024 * 1024 * 1024
defaultLruEventDriver = 2000
)
const (
defaultWaitTime = 20 * time.Minute
)
type CacheInterface interface {
Del() error
Get(key *proto.BaseKey) (structure.KeyBaseInterface, bool)
Add(key *proto.BaseKey, val structure.KeyBaseInterface) error
UpdateLruSize(length structure.UpdateLength)
DelByKey(key *proto.BaseKey) error
DelToClearSize() error
}
// TTL
const (
defaultDetachNum = 300
defaultTtlMaxLevel = 18
)

225
pkg/lru/lru.go Normal file
View File

@ -0,0 +1,225 @@
package lru
import (
"container/list"
"sync/atomic"
_ "gitee.com/wheat-os/wheatCache/conf"
"gitee.com/wheat-os/wheatCache/pkg/errorx"
"gitee.com/wheat-os/wheatCache/pkg/event"
"gitee.com/wheat-os/wheatCache/pkg/event2"
"gitee.com/wheat-os/wheatCache/pkg/middle"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"gitee.com/wheat-os/wheatCache/pkg/structure"
"gitee.com/wheat-os/wheatCache/pkg/util"
"github.com/spf13/viper"
)
type keyBaseValue struct {
key string
val structure.KeyBaseInterface
expire int64 // 过期时间戳
}
type SingleCache struct {
maxsize int64 //最大的长度
clearSize int64 // 清理长度
nowSize int64 // 现在的长度
li *list.List
lruMap map[string]*list.Element
lruMaxDiverSize int
lruTtlManage *lruTTl // 定时清理器
lruDriver event2.DriverInterface
lruConsumer event2.ConsumerInterface
lruCleanProduce event2.ProduceInterface // 发送清理事件
middleProduce event.ProduceInterface // 中间件驱动
}
// UpdateLruSize 更新现在的长度
func (lru *SingleCache) UpdateLruSize(length structure.UpdateLength) {
atomic.AddInt64(&lru.nowSize, int64(length))
}
func cacheInit() (int64, int64, int, int) {
maxSize := viper.GetString("lruCache.maxSize")
retMaxSize, maxErr := util.ParseSizeToBit(maxSize)
if maxErr != nil {
return 0, 0, 0, 0
}
if retMaxSize == 0 {
retMaxSize = defaultLruMaxSize
}
clearSize := viper.GetString("lruCache.clearSize")
retClearSize, clearErr := util.ParseSizeToBit(clearSize)
if clearErr != nil {
return 0, 0, 0, 0
}
if retClearSize == 0 {
retClearSize = defaultLruClearSize
}
maxDriver := viper.GetInt("lruCache.eventDriverSize")
if maxDriver == 0 {
maxDriver = defaultLruEventDriver
}
detachNum := viper.GetInt("lruCache.detachNum")
if detachNum == 0 {
detachNum = defaultDetachNum
}
return retMaxSize, retClearSize, maxDriver, detachNum
}
// NewLRUCache lru初始化
func NewLRUCache() *SingleCache {
maxSize, clearSize, maxDriverSize, detachNum := cacheInit()
lruDriver := event2.NewDriver(maxDriverSize)
lruCacheOnce.Do(func() {
lru := &SingleCache{
maxsize: maxSize,
clearSize: clearSize,
nowSize: 0,
li: list.New(),
lruMap: make(map[string]*list.Element),
lruMaxDiverSize: maxDriverSize,
lruDriver: lruDriver,
lruConsumer: event2.NewConsumer(lruDriver),
lruCleanProduce: event2.NewProduce(lruDriver),
middleProduce: event.NewProduce(middle.NewMiddleWare().GetEventDriver()),
lruTtlManage: newLruTTl(detachNum),
}
lruCache = lru
// 启动 lru 事件驱动
go lru.lruSingleWork()
go lru.lruTtlWork()
go lru.cleanWork()
})
return lruCache
}
// GetDriver 获取驱动
func (lru *SingleCache) GetDriver() event2.DriverInterface {
return lru.lruDriver
}
//Add 增加
func (lru *SingleCache) Add(key *proto.BaseKey, val structure.KeyBaseInterface) error {
if key == nil {
return errorx.KeyBaseIsNilErr()
}
exp := lru.lruTtlManage.setKeys(key)
keyBaseVal := &keyBaseValue{
key: key.Key,
val: val,
expire: exp,
}
if elVal, ok := lru.lruMap[key.Key]; ok {
lru.li.MoveToFront(elVal)
oldSize := elVal.Value.(structure.KeyBaseInterface).SizeByte()
lru.UpdateLruSize(structure.UpdateLength(val.SizeByte() - oldSize))
elVal.Value = keyBaseVal
return nil
}
valEl := lru.li.PushFront(keyBaseVal)
lru.lruMap[key.Key] = valEl
//增加大小
lru.UpdateLruSize(structure.UpdateLength(valEl.Value.(*keyBaseValue).val.SizeByte()))
return nil
}
// Get 查找key对应的value
func (lru *SingleCache) Get(key *proto.BaseKey) (structure.KeyBaseInterface, bool) {
if key == nil {
return nil, false
}
if elVal, ok := lru.lruMap[key.Key]; ok {
lru.li.MoveToFront(elVal)
return elVal.Value.(*keyBaseValue).val, true
}
return nil, false
}
//Del 删除机制
func (lru *SingleCache) Del() error {
if lru.lruMap == nil {
return errorx.New("lru is nil")
}
data := lru.li.Back()
delete(lru.lruMap, data.Value.(*keyBaseValue).key)
//删除大小
lru.UpdateLruSize(structure.UpdateLength(-1 * data.Value.(*keyBaseValue).val.SizeByte()))
lru.li.Remove(data)
return nil
}
//DelByKey 根据key删除
func (lru *SingleCache) DelByKey(key *proto.BaseKey) error {
if key == nil {
return errorx.KeyBaseIsNilErr()
}
if lru.lruMap == nil {
return errorx.New("lru is nil")
}
if el, ok := lru.lruMap[key.Key]; ok {
delete(lru.lruMap, key.Key)
lru.li.Remove(el)
lru.UpdateLruSize(structure.UpdateLength(-1 * el.Value.(*keyBaseValue).val.SizeByte()))
return nil
}
return errorx.New("lru no this key")
}
//DelByKeyAndExTtl 根据key(string)删除已经过期的 key
func (lru *SingleCache) delByKeyAndExTtl(key string, beforeTime int64) {
if elVal, ok := lru.lruMap[key]; ok {
exp := elVal.Value.(*keyBaseValue).expire
if exp <= beforeTime {
delete(lru.lruMap, key)
lru.li.Remove(elVal)
lru.UpdateLruSize(structure.UpdateLength(-1 * elVal.Value.(*keyBaseValue).val.SizeByte()))
}
}
}
func (lru *SingleCache) DelToClearSize() error {
if lru.lruMap == nil {
return errorx.New("lru is nil")
}
for lru.nowSize > lru.clearSize {
//del自动给nowSize进行大小的改变
err := lru.Del()
if err != nil {
return err
}
}
return nil
}
// 更新过期时间
func (lru *SingleCache) UpdateTTl(key *proto.BaseKey) error {
if key == nil {
return errorx.KeyBaseIsNilErr()
}
if elVal, ok := lru.lruMap[key.Key]; ok {
expire := lru.lruTtlManage.setKeys(key)
elVal.Value.(*keyBaseValue).expire = expire
}
return errorx.New("the key is not in lru cache, key:%s", key.Key)
}

103
pkg/lru/lru_test.go Normal file
View File

@ -0,0 +1,103 @@
package lru
import (
"fmt"
"testing"
"time"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"gitee.com/wheat-os/wheatCache/pkg/structure/stringx"
"github.com/stretchr/testify/require"
)
func TestNewLRUCache(t *testing.T) {
cache := NewLRUCache()
v1 := stringx.NewStringSingle()
v2 := stringx.NewStringSingle()
v3 := stringx.NewStringSingle()
key1 := proto.BaseKey{
Key: "1",
}
key2 := proto.BaseKey{
Key: "2",
}
key3 := proto.BaseKey{
Key: "3",
}
cache.Add(&key1, v1)
cache.Add(&key2, v2)
cache.Add(&key3, v3)
cache.Add(&key1, v1)
fmt.Println(cache.nowSize)
cache.Del()
fmt.Println(cache.nowSize)
_, isTrue := cache.Get(&key1)
require.Equal(t, isTrue, true)
}
func TestNewLRUCache2(t *testing.T) {
//根据key删除
cache := NewLRUCache()
v1 := stringx.NewStringSingle()
v2 := stringx.NewStringSingle()
v3 := stringx.NewStringSingle()
key1 := proto.BaseKey{
Key: "1",
}
key2 := proto.BaseKey{
Key: "2",
}
key3 := proto.BaseKey{
Key: "3",
}
cache.Add(&key1, v1)
cache.Add(&key2, v2)
cache.Add(&key3, v3)
cache.DelByKey(&key1)
_, ok := cache.Get(&key1)
require.Equal(t, ok, false)
require.Error(t, cache.DelByKey(&key1))
}
func TestLruProcess(t *testing.T) {
lru := NewLRUCache()
lru.clearSize = 3600
for i := 100; i < 200; i++ {
lru.Add(&proto.BaseKey{
Key: fmt.Sprint(i),
Ttl: 20 << 2,
}, stringx.NewStringSingle())
}
// mock LruKey
for i := 0; i < 100; i++ {
lru.Add(&proto.BaseKey{
Key: fmt.Sprint(i),
Ttl: 4,
}, stringx.NewStringSingle())
}
require.Equal(t, lru.nowSize, int64(200*24))
// 自动清理测试
fmt.Println(lru.clearSize)
require.Equal(t, lru.li.Len(), 200)
time.Sleep(3 * time.Second)
require.Less(t, lru.nowSize, lru.clearSize+1)
// TTL 测试, 100-200 key 发生自动清理 留下 50-100 共 1000-100 + 20个 key5s 后,前 0-100的 key 过期,剩下
time.Sleep(2 * time.Second)
require.Equal(t, lru.li.Len(), 50)
// 过期全部的 Key
for i := 100; i < 200; i++ {
lru.UpdateTTl(&proto.BaseKey{
Key: fmt.Sprint(i),
Ttl: -1,
})
}
time.Sleep(2 * time.Second)
require.Equal(t, lru.nowSize, int64(0))
}

56
pkg/lru/ttl.go Normal file
View File

@ -0,0 +1,56 @@
package lru
import (
"sync"
"time"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"gitee.com/wheat-os/wheatCache/pkg/util/skiplist"
)
// lru 的 ttl 管理器
type lruTTl struct {
sk *skiplist.SkipList
memoryKey chan string // 缓存过期的 key
detachNum int // 每次移除的数量
mu sync.Mutex
}
func (l *lruTTl) setKeys(key *proto.BaseKey) int64 {
l.mu.Lock()
defer l.mu.Unlock()
// 永久存储
if key.Expire == nil && key.Ttl == 0 {
return 0
}
ttlTime := time.Now().Unix()
if key.Expire != nil {
ttlTime = key.Expire.GetSeconds()
}
ttlTime += key.GetTtl()
l.sk.Insert(float64(ttlTime), key.GetKey())
return ttlTime
}
// 加载过期的 Key 到 Memory
func (l *lruTTl) ttlKeyToMemoryBySecond() {
t := time.Now()
values := l.sk.PopLeft(float64(t.Unix()))
for _, val := range values {
l.memoryKey <- val.(string)
}
}
func newLruTTl(detachNum int) *lruTTl {
return &lruTTl{
sk: skiplist.NewSkipList(defaultTtlMaxLevel),
// 默认 10000 个 Key
memoryKey: make(chan string, 10000),
detachNum: detachNum,
}
}

36
pkg/lru/ttl_test.go Normal file
View File

@ -0,0 +1,36 @@
package lru
import (
"fmt"
"testing"
"time"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"gitee.com/wheat-os/wheatCache/pkg/structure/stringx"
"github.com/stretchr/testify/require"
)
func TestTTlCup(t *testing.T) {
k := make([]string, 100, 3000)
fmt.Println(cap(k))
p := k[:50]
fmt.Println(cap(p))
}
func Test_LruTTl(t *testing.T) {
lru := NewLRUCache()
s := stringx.NewStringSingle()
lru.Add(&proto.BaseKey{
Key: "k8s",
Ttl: 1,
}, s)
lru.Add(&proto.BaseKey{
Key: "990",
Ttl: 10,
}, s)
require.Equal(t, lru.nowSize, int64(48))
time.Sleep(4 * time.Second)
require.Equal(t, lru.nowSize, int64(24))
}

62
pkg/lru/woker_test.go Normal file
View File

@ -0,0 +1,62 @@
package lru
import (
"context"
"testing"
"time"
"gitee.com/wheat-os/wheatCache/pkg/event2"
"gitee.com/wheat-os/wheatCache/pkg/logx"
"gitee.com/wheat-os/wheatCache/pkg/proto"
"gitee.com/wheat-os/wheatCache/pkg/structure/stringx"
"github.com/stretchr/testify/require"
)
func TestWorker(t *testing.T) {
ctx := context.Background()
lru := NewLRUCache()
produce := event2.NewProduce(lru.GetDriver())
workEvent := produce.NewEvent(OptionEventName)
workEvent.SetValue(event2.WorkFuncEventKey, event2.EventWorkFunc(func() (interface{}, error) {
v1 := stringx.NewStringSingle()
key := proto.BaseKey{
Key: "v1",
}
res, _ := v1.Set("123")
lru.Add(&key, v1)
return res, nil
}))
workEvent.InitWaitEvent()
produce.Call(ctx, workEvent)
res, err := workEvent.StartWaitEvent(2 * time.Second)
require.NoError(t, err)
require.Equal(t, res, "123")
}
func TestSingleCache_DelToClearSize(t *testing.T) {
ctx := context.Background()
lru := NewLRUCache()
produce := event2.NewProduce(lru.GetDriver())
for i := int32(20000); i > 0; i-- {
workEvent := produce.NewEvent(OptionEventName)
workEvent.SetValue(event2.WorkFuncEventKey, event2.EventWorkFunc(func() (interface{}, error) {
v1 := stringx.NewStringSingle()
key := proto.BaseKey{
Key: string(i),
Ttl: 1,
}
u := v1.Setbit(i, true)
lru.Add(&key, v1)
return u, nil
}))
workEvent.InitWaitEvent()
produce.Call(ctx, workEvent)
workEvent.StartWaitEvent(2 * time.Second)
workEvent.Recovery()
}
logx.Info("start size is %d", lru.nowSize)
time.Sleep(10 * time.Second)
logx.Info("end size is %d", lru.nowSize)
}

135
pkg/lru/worker.go Normal file
View File

@ -0,0 +1,135 @@
package lru
import (
"context"
"time"
"gitee.com/wheat-os/wheatCache/pkg/errorx"
"gitee.com/wheat-os/wheatCache/pkg/event"
"gitee.com/wheat-os/wheatCache/pkg/event2"
"gitee.com/wheat-os/wheatCache/pkg/logx"
mMsg "gitee.com/wheat-os/wheatCache/pkg/middle-msg"
)
func (lru *SingleCache) lruSingleWork() {
ctx := context.Background()
for {
workEvent := lru.lruConsumer.Receive(ctx)
workFunc, ok := workEvent.GetValue(event2.WorkFuncEventKey)
if !ok {
workEvent.SetResultErr(errorx.LruNotWorkFuncEventErr())
continue
}
switch workEvent.GetEventName() {
case OptionEventName:
if work, ok := workFunc.(event2.EventWorkFunc); ok {
workEvent.ExecWorkAndSendResult(work)
}
case CleanEventName:
// 对当前的io数量进行判断
ioNum := lru.GetDriver().GetLength()
if ioNum > lru.lruMaxDiverSize/2 {
lru.lruCleanProduce.Call(ctx, workEvent)
continue
}
if work, ok := workFunc.(event2.EventWorkFunc); ok {
workEvent.ExecWorkAndSendResult(work)
}
case TtlEventName:
if work, ok := workFunc.(event2.EventWorkFunc); ok {
workEvent.ExecWorkAndSendResult(work)
}
}
}
}
// 执行过期事件
func (lru *SingleCache) lruTtlWork() {
ctx := context.Background()
// 清理事件
go func() {
work := event2.EventWorkFunc(func() (interface{}, error) {
beforeTime := time.Now().Unix()
cle := lru.lruTtlManage.detachNum
if cle > len(lru.lruTtlManage.memoryKey) {
cle = len(lru.lruTtlManage.memoryKey)
}
keys := make([]string, 0)
for i := 0; i < cle; i++ {
key := <-lru.lruTtlManage.memoryKey
keys = append(keys, key)
lru.delByKeyAndExTtl(key, beforeTime)
}
return keys, nil
})
cleanTTlTicker := time.NewTicker(500 * time.Millisecond)
defer cleanTTlTicker.Stop()
for {
// 清理事件
<-cleanTTlTicker.C
if len(lru.lruTtlManage.memoryKey) == 0 {
continue
}
ttlEvent := lru.lruCleanProduce.NewEvent(TtlEventName)
ttlEvent.SetValue(event2.WorkFuncEventKey, work)
ttlEvent.InitWaitEvent()
lru.lruCleanProduce.Call(ctx, ttlEvent)
keys, err := ttlEvent.StartWaitEvent(time.Second * 2)
ttlEvent.Recovery()
mMsg.SendMiddleMsg(ctx, lru.middleProduce, mMsg.LruTTlContext{
Keys: keys.([]string),
CleanTime: time.Now(),
})
if err != nil {
logx.With(ctx, lru.middleProduce).Errorln(err)
}
}
}()
// 收集事件
for {
time.Sleep(1 * time.Second)
lru.lruTtlManage.ttlKeyToMemoryBySecond()
}
}
func (lru *SingleCache) cleanWork() {
cxt := context.Background()
work := event.EventWorkFunc(func() (interface{}, error) {
err := lru.DelToClearSize()
return nil, err
})
for {
time.Sleep(2 * time.Second)
if lru.clearSize < lru.nowSize {
lruCleanEvent := lru.lruCleanProduce.NewEvent(CleanEventName)
lruCleanEvent.SetValue(event2.WorkFuncEventKey, work)
lruCleanEvent.InitWaitEvent()
lru.lruCleanProduce.Call(cxt, lruCleanEvent)
_, err := lruCleanEvent.StartWaitEvent(defaultWaitTime)
if err != nil {
logx.With(cxt, lru.middleProduce).Errorln(err)
}
// 归还
lruCleanEvent.Recovery()
}
}
}

40
pkg/middle-msg/define.go Normal file
View File

@ -0,0 +1,40 @@
package middlemsg
import (
"context"
"gitee.com/wheat-os/wheatCache/pkg/errorx"
"gitee.com/wheat-os/wheatCache/pkg/event"
)
const (
MiddleMsgKey = "middleMsgKey"
)
func SendMiddleMsg(
ctx context.Context,
middleProduce event.ProduceInterface,
val interface{},
) error {
if middleProduce == nil {
return errorx.New("middleProduce not is nil")
}
var eventName string
switch val.(type) {
case *LogContext:
eventName = LogContextName
case *LruCleanContext:
eventName = LruCleanContextName
case *LruTTlContext:
eventName = LruTTlContextName
case *PulginsInfos:
eventName = PulginsInfosName
}
msgEvent := middleProduce.NewEvent(eventName)
msgEvent.SetValue(MiddleMsgKey, val)
middleProduce.Call(ctx, msgEvent)
return nil
}

14
pkg/middle-msg/logx.go Normal file
View File

@ -0,0 +1,14 @@
package middlemsg
import "time"
var (
LogContextName = "log-context"
)
type LogContext struct {
Level string
Data time.Time
Msg string
Route string
}

21
pkg/middle-msg/lru.go Normal file
View File

@ -0,0 +1,21 @@
package middlemsg
import "time"
const LruCleanContextName = "lru-clean-context"
type LruCleanContext struct {
Keys []string
BeforeCleanSize int64
BehindCleanSize int64
StartTime time.Time
EndTime time.Time
}
const LruTTlContextName = "lru-ttl-context"
type LruTTlContext struct {
Keys []string
CleanTime time.Time
}

21
pkg/middle-msg/plugins.go Normal file
View File

@ -0,0 +1,21 @@
package middlemsg
import (
"time"
)
const (
PulginsInfosName = "plugins-infos-context"
)
type PulginsInfo struct {
Version string
Desc string
Name string
Statux string
Time time.Duration
}
type PulginsInfos struct {
Infos []*PulginsInfo
}

View File

@ -1 +1,15 @@
package middle
import (
"sync"
)
var (
oneMiddle sync.Once
middleWareDriver *MiddleWare
)
const (
defaultConsumerCount = 5
defaultDriverCount = 1000
)

74
pkg/middle/middleware.go Normal file
View File

@ -0,0 +1,74 @@
package middle
import (
_ "gitee.com/wheat-os/wheatCache/conf"
"gitee.com/wheat-os/wheatCache/pkg/event"
"gitee.com/wheat-os/wheatCache/plugins"
"gitee.com/wheat-os/wheatCache/plugins/config"
"github.com/spf13/viper"
)
type MiddleWare struct {
eventDriver event.DriverInterface
eventConsumer event.ConsumerInterface
eventProduce event.ProduceInterface
plugins map[string][]plugins.PluginInterface
consumerCount int
driverCount int
}
func NewMiddleWare() *MiddleWare {
oneMiddle.Do(func() {
consumerCount, driverCount := loadConfigAndDefault()
driver := event.NewDriver(driverCount)
middleWareDriver = &MiddleWare{
eventDriver: driver,
eventConsumer: event.NewConsumer(driver),
eventProduce: event.NewProduce(driver),
driverCount: driverCount,
consumerCount: consumerCount,
}
middleWareDriver.loadPlugins()
// 多消费 middle
middleWareDriver.startWork()
})
return middleWareDriver
}
func (m *MiddleWare) GetEventDriver() event.DriverInterface {
return m.eventDriver
}
func (m *MiddleWare) loadPlugins() {
plug := viper.GetStringMapStringSlice("plugins-control")
pluginsMap := config.GetMiddlewareMap()
pluginsContext := make(map[string][]plugins.PluginInterface)
for msg, pluNames := range plug {
pulgSingle := make([]plugins.PluginInterface, 0)
for _, name := range pluNames {
pulgSingle = append(pulgSingle, pluginsMap[name])
}
pluginsContext[msg] = pulgSingle
}
m.plugins = pluginsContext
}
func loadConfigAndDefault() (int, int) {
// 加载 consumerCount
consumerCount := viper.GetInt("middle-driver.middleConsumerCount")
if consumerCount == 0 {
consumerCount = defaultConsumerCount
}
driverCount := viper.GetInt("middle-driver.driverCount")
if driverCount == 0 {
driverCount = defaultDriverCount
}
return consumerCount, driverCount
}

36
pkg/middle/worker.go Normal file
View File

@ -0,0 +1,36 @@
package middle
import (
"context"
"gitee.com/wheat-os/wheatCache/pkg/logx"
middleMsg "gitee.com/wheat-os/wheatCache/pkg/middle-msg"
)
func (m *MiddleWare) startWork() {
for i := 0; i < m.consumerCount; i++ {
go func() {
ctx := context.Background()
for {
workEvent := m.eventConsumer.Receive(ctx)
plugs := m.plugins[workEvent.GetEventName()]
msg, ok := workEvent.GetValue(middleMsg.MiddleMsgKey)
m.eventConsumer.Recovery(workEvent)
if !ok {
logx.With(ctx, m.eventProduce).Error("get event value errnot key:%s", middleMsg.MiddleMsgKey)
continue
}
// 发送事件到 全部的 plugs 里
for _, val := range plugs {
_, err := val.Exec(msg)
if err != nil {
logx.With(ctx, m.eventProduce).Errorln(err)
}
}
}
}()
}
}

42
pkg/middle/worker_test.go Normal file
View File

@ -0,0 +1,42 @@
package middle
import (
"context"
"fmt"
"testing"
"time"
"gitee.com/wheat-os/wheatCache/pkg/event"
middleMsg "gitee.com/wheat-os/wheatCache/pkg/middle-msg"
)
func Test_middleware_loadPlugins(t *testing.T) {
m := NewMiddleWare()
m.loadPlugins()
fmt.Println(m.plugins)
}
func TestWorker(t *testing.T) {
ctx := context.Background()
m := NewMiddleWare()
product := event.NewProduce(m.GetEventDriver())
middleMsg.SendMiddleMsg(ctx, product, &middleMsg.LogContext{
Msg: "debug msg",
})
middleMsg.SendMiddleMsg(ctx, product, &middleMsg.PulginsInfos{
Infos: []*middleMsg.PulginsInfo{
{
Desc: "miss",
},
},
})
middleMsg.SendMiddleMsg(ctx, product, &middleMsg.LruTTlContext{
Keys: []string{"1", "2", "3"},
})
time.Sleep(1 * time.Second)
}

View File

@ -84,6 +84,44 @@ func (x *BaseKey) GetExpire() *timestamppb.Timestamp {
return nil
}
type External struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *External) Reset() {
*x = External{}
if protoimpl.UnsafeEnabled {
mi := &file_base_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *External) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*External) ProtoMessage() {}
func (x *External) ProtoReflect() protoreflect.Message {
mi := &file_base_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use External.ProtoReflect.Descriptor instead.
func (*External) Descriptor() ([]byte, []int) {
return file_base_proto_rawDescGZIP(), []int{1}
}
var File_base_proto protoreflect.FileDescriptor
var file_base_proto_rawDesc = []byte{
@ -96,8 +134,9 @@ var file_base_proto_rawDesc = []byte{
0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65,
0x42, 0x0b, 0x5a, 0x09, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
0x22, 0x0a, 0x0a, 0x08, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x42, 0x0b, 0x5a, 0x09,
0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
@ -112,13 +151,14 @@ func file_base_proto_rawDescGZIP() []byte {
return file_base_proto_rawDescData
}
var file_base_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_base_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_base_proto_goTypes = []interface{}{
(*BaseKey)(nil), // 0: BaseKey
(*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp
(*External)(nil), // 1: External
(*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp
}
var file_base_proto_depIdxs = []int32{
1, // 0: BaseKey.expire:type_name -> google.protobuf.Timestamp
2, // 0: BaseKey.expire:type_name -> google.protobuf.Timestamp
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
@ -144,6 +184,18 @@ func file_base_proto_init() {
return nil
}
}
file_base_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*External); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@ -151,7 +203,7 @@ func file_base_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_base_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},

733
pkg/proto/channelx.pb.go Normal file
View File

@ -0,0 +1,733 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.17.3
// source: channelx.proto
package proto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type CPushRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key *BaseKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Value []string `protobuf:"bytes,2,rep,name=value,proto3" json:"value,omitempty"`
}
func (x *CPushRequest) Reset() {
*x = CPushRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_channelx_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CPushRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CPushRequest) ProtoMessage() {}
func (x *CPushRequest) ProtoReflect() protoreflect.Message {
mi := &file_channelx_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CPushRequest.ProtoReflect.Descriptor instead.
func (*CPushRequest) Descriptor() ([]byte, []int) {
return file_channelx_proto_rawDescGZIP(), []int{0}
}
func (x *CPushRequest) GetKey() *BaseKey {
if x != nil {
return x.Key
}
return nil
}
func (x *CPushRequest) GetValue() []string {
if x != nil {
return x.Value
}
return nil
}
type CPushResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
E *External `protobuf:"bytes,1,opt,name=e,proto3" json:"e,omitempty"`
}
func (x *CPushResponse) Reset() {
*x = CPushResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_channelx_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CPushResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CPushResponse) ProtoMessage() {}
func (x *CPushResponse) ProtoReflect() protoreflect.Message {
mi := &file_channelx_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CPushResponse.ProtoReflect.Descriptor instead.
func (*CPushResponse) Descriptor() ([]byte, []int) {
return file_channelx_proto_rawDescGZIP(), []int{1}
}
func (x *CPushResponse) GetE() *External {
if x != nil {
return x.E
}
return nil
}
type CPopRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key *BaseKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Count int32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
}
func (x *CPopRequest) Reset() {
*x = CPopRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_channelx_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CPopRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CPopRequest) ProtoMessage() {}
func (x *CPopRequest) ProtoReflect() protoreflect.Message {
mi := &file_channelx_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CPopRequest.ProtoReflect.Descriptor instead.
func (*CPopRequest) Descriptor() ([]byte, []int) {
return file_channelx_proto_rawDescGZIP(), []int{2}
}
func (x *CPopRequest) GetKey() *BaseKey {
if x != nil {
return x.Key
}
return nil
}
func (x *CPopRequest) GetCount() int32 {
if x != nil {
return x.Count
}
return 0
}
type CPopResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
E *External `protobuf:"bytes,1,opt,name=e,proto3" json:"e,omitempty"`
Result []string `protobuf:"bytes,2,rep,name=result,proto3" json:"result,omitempty"`
}
func (x *CPopResponse) Reset() {
*x = CPopResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_channelx_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CPopResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CPopResponse) ProtoMessage() {}
func (x *CPopResponse) ProtoReflect() protoreflect.Message {
mi := &file_channelx_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CPopResponse.ProtoReflect.Descriptor instead.
func (*CPopResponse) Descriptor() ([]byte, []int) {
return file_channelx_proto_rawDescGZIP(), []int{3}
}
func (x *CPopResponse) GetE() *External {
if x != nil {
return x.E
}
return nil
}
func (x *CPopResponse) GetResult() []string {
if x != nil {
return x.Result
}
return nil
}
type CMakeRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key *BaseKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Length int32 `protobuf:"varint,2,opt,name=length,proto3" json:"length,omitempty"`
}
func (x *CMakeRequest) Reset() {
*x = CMakeRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_channelx_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CMakeRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CMakeRequest) ProtoMessage() {}
func (x *CMakeRequest) ProtoReflect() protoreflect.Message {
mi := &file_channelx_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CMakeRequest.ProtoReflect.Descriptor instead.
func (*CMakeRequest) Descriptor() ([]byte, []int) {
return file_channelx_proto_rawDescGZIP(), []int{4}
}
func (x *CMakeRequest) GetKey() *BaseKey {
if x != nil {
return x.Key
}
return nil
}
func (x *CMakeRequest) GetLength() int32 {
if x != nil {
return x.Length
}
return 0
}
type CMakeResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *CMakeResponse) Reset() {
*x = CMakeResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_channelx_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CMakeResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CMakeResponse) ProtoMessage() {}
func (x *CMakeResponse) ProtoReflect() protoreflect.Message {
mi := &file_channelx_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CMakeResponse.ProtoReflect.Descriptor instead.
func (*CMakeResponse) Descriptor() ([]byte, []int) {
return file_channelx_proto_rawDescGZIP(), []int{5}
}
type CLenRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key *BaseKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
}
func (x *CLenRequest) Reset() {
*x = CLenRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_channelx_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CLenRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CLenRequest) ProtoMessage() {}
func (x *CLenRequest) ProtoReflect() protoreflect.Message {
mi := &file_channelx_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CLenRequest.ProtoReflect.Descriptor instead.
func (*CLenRequest) Descriptor() ([]byte, []int) {
return file_channelx_proto_rawDescGZIP(), []int{6}
}
func (x *CLenRequest) GetKey() *BaseKey {
if x != nil {
return x.Key
}
return nil
}
type CLenResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Length int32 `protobuf:"varint,2,opt,name=length,proto3" json:"length,omitempty"`
}
func (x *CLenResponse) Reset() {
*x = CLenResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_channelx_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CLenResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CLenResponse) ProtoMessage() {}
func (x *CLenResponse) ProtoReflect() protoreflect.Message {
mi := &file_channelx_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CLenResponse.ProtoReflect.Descriptor instead.
func (*CLenResponse) Descriptor() ([]byte, []int) {
return file_channelx_proto_rawDescGZIP(), []int{7}
}
func (x *CLenResponse) GetLength() int32 {
if x != nil {
return x.Length
}
return 0
}
type CCleanRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key *BaseKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
}
func (x *CCleanRequest) Reset() {
*x = CCleanRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_channelx_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CCleanRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CCleanRequest) ProtoMessage() {}
func (x *CCleanRequest) ProtoReflect() protoreflect.Message {
mi := &file_channelx_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CCleanRequest.ProtoReflect.Descriptor instead.
func (*CCleanRequest) Descriptor() ([]byte, []int) {
return file_channelx_proto_rawDescGZIP(), []int{8}
}
func (x *CCleanRequest) GetKey() *BaseKey {
if x != nil {
return x.Key
}
return nil
}
type CCleanResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *CCleanResponse) Reset() {
*x = CCleanResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_channelx_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CCleanResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CCleanResponse) ProtoMessage() {}
func (x *CCleanResponse) ProtoReflect() protoreflect.Message {
mi := &file_channelx_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CCleanResponse.ProtoReflect.Descriptor instead.
func (*CCleanResponse) Descriptor() ([]byte, []int) {
return file_channelx_proto_rawDescGZIP(), []int{9}
}
var File_channelx_proto protoreflect.FileDescriptor
var file_channelx_proto_rawDesc = []byte{
0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x1a, 0x0a, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x40, 0x0a, 0x0c,
0x43, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x03,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x42, 0x61, 0x73, 0x65,
0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x28,
0x0a, 0x0d, 0x43, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x17, 0x0a, 0x01, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x45, 0x78, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x52, 0x01, 0x65, 0x22, 0x3f, 0x0a, 0x0b, 0x43, 0x50, 0x6f, 0x70,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03,
0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01,
0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3f, 0x0a, 0x0c, 0x43, 0x50, 0x6f,
0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x01, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x52,
0x01, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x03,
0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x42, 0x0a, 0x0c, 0x43, 0x4d,
0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x03, 0x6b, 0x65,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65,
0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x0f,
0x0a, 0x0d, 0x43, 0x4d, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x29, 0x0a, 0x0b, 0x43, 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a,
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x42, 0x61,
0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x26, 0x0a, 0x0c, 0x43, 0x4c,
0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65,
0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67,
0x74, 0x68, 0x22, 0x2b, 0x0a, 0x0d, 0x43, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x08, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22,
0x10, 0x0a, 0x0e, 0x43, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x42, 0x0b, 0x5a, 0x09, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_channelx_proto_rawDescOnce sync.Once
file_channelx_proto_rawDescData = file_channelx_proto_rawDesc
)
func file_channelx_proto_rawDescGZIP() []byte {
file_channelx_proto_rawDescOnce.Do(func() {
file_channelx_proto_rawDescData = protoimpl.X.CompressGZIP(file_channelx_proto_rawDescData)
})
return file_channelx_proto_rawDescData
}
var file_channelx_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_channelx_proto_goTypes = []interface{}{
(*CPushRequest)(nil), // 0: CPushRequest
(*CPushResponse)(nil), // 1: CPushResponse
(*CPopRequest)(nil), // 2: CPopRequest
(*CPopResponse)(nil), // 3: CPopResponse
(*CMakeRequest)(nil), // 4: CMakeRequest
(*CMakeResponse)(nil), // 5: CMakeResponse
(*CLenRequest)(nil), // 6: CLenRequest
(*CLenResponse)(nil), // 7: CLenResponse
(*CCleanRequest)(nil), // 8: CCleanRequest
(*CCleanResponse)(nil), // 9: CCleanResponse
(*BaseKey)(nil), // 10: BaseKey
(*External)(nil), // 11: External
}
var file_channelx_proto_depIdxs = []int32{
10, // 0: CPushRequest.key:type_name -> BaseKey
11, // 1: CPushResponse.e:type_name -> External
10, // 2: CPopRequest.key:type_name -> BaseKey
11, // 3: CPopResponse.e:type_name -> External
10, // 4: CMakeRequest.key:type_name -> BaseKey
10, // 5: CLenRequest.key:type_name -> BaseKey
10, // 6: CCleanRequest.key:type_name -> BaseKey
7, // [7:7] is the sub-list for method output_type
7, // [7:7] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension type_name
7, // [7:7] is the sub-list for extension extendee
0, // [0:7] is the sub-list for field type_name
}
func init() { file_channelx_proto_init() }
func file_channelx_proto_init() {
if File_channelx_proto != nil {
return
}
file_base_proto_init()
if !protoimpl.UnsafeEnabled {
file_channelx_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CPushRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_channelx_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CPushResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_channelx_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CPopRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_channelx_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CPopResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_channelx_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CMakeRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_channelx_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CMakeResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_channelx_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CLenRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_channelx_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CLenResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_channelx_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CCleanRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_channelx_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CCleanResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_channelx_proto_rawDesc,
NumEnums: 0,
NumMessages: 10,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_channelx_proto_goTypes,
DependencyIndexes: file_channelx_proto_depIdxs,
MessageInfos: file_channelx_proto_msgTypes,
}.Build()
File_channelx_proto = out.File
file_channelx_proto_rawDesc = nil
file_channelx_proto_goTypes = nil
file_channelx_proto_depIdxs = nil
}

35
pkg/proto/define.go Normal file
View File

@ -0,0 +1,35 @@
package proto
import (
"google.golang.org/protobuf/types/known/timestamppb"
)
type GetKeyBaseInterface interface {
GetKey() *BaseKey
}
const (
BaseKeyMethodKey = "basekey"
)
// NewBaseKey
// keyttlexpire
func NewBaseKey(key string, t ...int64) *BaseKey {
var expire *timestamppb.Timestamp = nil
var ttl int64
if len(t) > 1 {
expire = &timestamppb.Timestamp{
Seconds: t[1],
}
ttl = t[0]
} else if len(t) == 1 {
ttl = t[0]
}
return &BaseKey{
Key: key,
Expire: expire,
Ttl: ttl,
}
}

1301
pkg/proto/hashx.pb.go Normal file

File diff suppressed because it is too large Load Diff

1676
pkg/proto/listx.pb.go Normal file

File diff suppressed because it is too large Load Diff

1913
pkg/proto/setx.pb.go Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,66 @@
package channelx
import (
"sync/atomic"
"gitee.com/wheat-os/wheatCache/pkg/structure"
)
type ChannelX struct {
channel chan *structure.Value
sizeByte int64
}
func MakeChannelX(length int) structure.ChannelXInterface {
return &ChannelX{
channel: make(chan *structure.Value, length),
sizeByte: 0,
}
}
func (c *ChannelX) SizeByte() int64 {
return c.sizeByte
}
// RollBack TODO 事务相关, V2 实现
func (c *ChannelX) RollBack() error {
panic("not implemented") // TODO: Implement
}
// Begin 事务相关, V2 实现
func (c *ChannelX) Begin() error {
panic("not implemented") // TODO: Implement
}
// Comment 事务相关, V2 实现
func (c *ChannelX) Comment() error {
panic("not implemented") // TODO: Implement
}
func (c *ChannelX) Encode() ([]byte, error) {
panic("not implemented") // TODO: Implement
}
func (c *ChannelX) Push(value string) structure.UpdateLength {
val := structure.NewValue(value)
up := val.GetSize()
c.channel <- val
atomic.AddInt64(&c.sizeByte, int64(up))
return structure.UpdateLength(up)
}
func (c *ChannelX) Pop() (string, structure.UpdateLength) {
val := <-c.channel
return val.ToString(), structure.UpdateLength(val.GetSize()) * -1
}
func (c *ChannelX) Length() int {
return len(c.channel)
}
func (c *ChannelX) Clean() structure.UpdateLength {
c.channel = make(chan *structure.Value, cap(c.channel))
up := c.sizeByte
c.sizeByte = 0
return structure.UpdateLength(up) * -1
}

View File

@ -0,0 +1,24 @@
package channelx
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestChannelX_Push(t *testing.T) {
c := MakeChannelX(10)
require.Equal(t, c.Length(), 0)
up := c.Push("111")
require.Equal(t, 24, int(up))
res, up := c.Pop()
require.Equal(t, -24, int(up))
require.Equal(t, res, "111")
up = c.Push("111")
c.Clean()
require.Equal(t, c.Length(), 0)
}

View File

@ -1,3 +1,77 @@
package structure
const (
defaultLen = 8 // 默认创建的 value 大小
)
type DynamicType int8
type UpdateLength int64
const (
DynamicNull = DynamicType(iota)
DynamicInt
DynamicFloat
DynamicString
)
type KeyBaseInterface interface {
SizeByte() int64
// RollBack TODO 事务相关, V2 实现
RollBack() error
// Begin 事务相关, V2 实现
Begin() error
// Comment 事务相关, V2 实现
Comment() error
Encode() ([]byte, error)
}
type StringXInterface interface {
KeyBaseInterface
Set(string) (string, UpdateLength)
Get() string
Add(int32) (string, error)
Reduce(int32) (string, error)
Setbit(int32, bool) UpdateLength
Getbit(int32) (bool, error)
Getrange(start, end int32) (string, error)
GetLength() int
}
type ListXInterface interface {
KeyBaseInterface
LPush(...string) UpdateLength
RPush(...string) UpdateLength
LPop(int) ([]string, UpdateLength)
RPop(int) ([]string, UpdateLength)
Index(int) (string, error)
Insert(int, bool, ...string) (UpdateLength, error)
Length() int
Slice(start, end int) (UpdateLength, error) // 切片, O(n)复杂度
Range(start, end int) ([]string, error)
Remove(value string, count int) (int, UpdateLength)
}
type HashXInterface interface {
KeyBaseInterface
Set(key string, val string) UpdateLength
Get(key string) (string, error)
Del(key string) (UpdateLength, error)
Key() []string
Value() []string
Item() map[string]string
Add(renewal int, key ...string) (int, []string, error) // 访问影响成功的结果
SetX(key string, val string) (bool, UpdateLength) // 不存在才插入
Length() int
Range(consur, count int, regex string) []string
}
type ChannelXInterface interface {
KeyBaseInterface
Push(value string) UpdateLength
Pop() (string, UpdateLength)
Length() int
Clean() UpdateLength
}

View File

@ -1,29 +0,0 @@
// Code generated by gen-struct-const. DO NOT EDIT.
// make gen-struct generated
package structure
import "gitee.com/timedb/wheatCache/pkg/proto"
type KeyBaseInterface interface {
SizeByte() int64
// TODO RollBack 事务相关, V2 实现
RollBack() error
// Begin 事务相关, V2 实现
Begin() error
// Comment 事务相关, V2 实现
Comment() error
Encode() ([]byte, error)
}
{% for dict_item in Data %}
{% for key, value in dict_item.items() %}
type {{key}}Interface interface{
KeyBaseInterface
{% for val in value -%}
{{val}}(*proto.{{val}}Request) ([]string, error)
{% endfor %}
}
{% endfor -%}
{%- endfor %}

View File

@ -1,35 +0,0 @@
// Code generated by gen-struct-const. DO NOT EDIT.
// make gen-struct generated
package generate
const (
DEFAULT_KEY = iota
STRING_X
)
const (
DEFAULT_COMM = iota
SET
GET
ADD
REDUCE
SETBIT
GETBIT
)
var CommKeyString = map[string]int{"set": STRING_X,
"get": STRING_X,
"add": STRING_X,
"reduce": STRING_X,
"setbit": STRING_X,
"getbit": STRING_X,
}
var CommKey = map[int]int{SET: STRING_X,
GET: STRING_X,
ADD: STRING_X,
REDUCE: STRING_X,
SETBIT: STRING_X,
GETBIT: STRING_X,
}

View File

@ -1,9 +0,0 @@
# 这里定义结构体的支持的命令, 以后也许会添加结构体的命令验证
STRING_X:
- set
- get
- add
- reduce
- setbit
- getbit

View File

@ -0,0 +1,157 @@
package hashx
import (
"regexp"
"gitee.com/wheat-os/wheatCache/pkg/errorx"
"gitee.com/wheat-os/wheatCache/pkg/structure"
)
type HashX map[string]*structure.Value
func NewHashXSingle() structure.HashXInterface {
return make(HashX)
}
func (h HashX) SizeByte() int64 {
var size int
for _, val := range h {
size += val.GetSize()
}
return int64(size)
}
// RollBack TODO 事务相关, V2 实现
func (h HashX) RollBack() error {
panic("not implemented") // TODO: Implement
}
// Begin 事务相关, V2 实现
func (h HashX) Begin() error {
panic("not implemented") // TODO: Implement
}
// Comment 事务相关, V2 实现
func (h HashX) Comment() error {
panic("not implemented") // TODO: Implement
}
func (h HashX) Encode() ([]byte, error) {
panic("not implemented") // TODO: Implement
}
func (h HashX) Set(key string, val string) structure.UpdateLength {
var Length structure.UpdateLength
if v, ok := h[key]; ok {
Length -= structure.UpdateLength(v.GetSize())
}
strVal := structure.NewValue(val)
h[key] = strVal
return Length + structure.UpdateLength(strVal.GetSize())
}
func (h HashX) Get(key string) (string, error) {
if v, ok := h[key]; ok {
return v.ToString(), nil
}
return "", errorx.New("this key does not exist in hashx, key:%s", key)
}
func (h HashX) Del(key string) (structure.UpdateLength, error) {
if v, ok := h[key]; ok {
delete(h, key)
return structure.UpdateLength(v.GetSize()), nil
}
return 0, errorx.New("this key does not exist in hashx, key:%s", key)
}
func (h HashX) Key() []string {
result := make([]string, 0, len(h))
for key := range h {
result = append(result, key)
}
return result
}
func (h HashX) Value() []string {
result := make([]string, 0, len(h))
for _, val := range h {
result = append(result, val.ToString())
}
return result
}
func (h HashX) Item() map[string]string {
result := make(map[string]string, len(h))
for key, val := range h {
result[key] = val.ToString()
}
return result
}
func (h HashX) Add(renewal int, keys ...string) (count int, result []string, err error) {
for _, key := range keys {
if v, ok := h[key]; ok {
res, err := v.Incr(int32(renewal))
if err != nil {
continue
}
count += 1
result = append(result, res)
}
}
return count, result, nil
}
func (h HashX) SetX(key string, val string) (bool, structure.UpdateLength) {
if _, ok := h[key]; ok {
return false, 0
}
strVal := structure.NewValue(val)
h[key] = strVal
return true, structure.UpdateLength(strVal.GetSize())
}
func (h HashX) Length() int {
return len(h)
}
func (h HashX) Range(consur, count int, regex string) []string {
var reComp *regexp.Regexp
if regex == "" {
reComp = nil
} else {
reComp = regexp.MustCompile(regex)
}
result := make([]string, 0)
for _, val := range h {
if consur > 0 {
consur--
continue
}
if count == 0 && count != -1 {
break
}
s := val.ToString()
if reComp == nil {
count--
result = append(result, s)
continue
}
if reComp.MatchString(s) {
count--
result = append(result, s)
}
}
return result
}

View File

@ -0,0 +1,113 @@
package hashx
import (
"fmt"
"reflect"
"regexp"
"testing"
"unsafe"
"github.com/stretchr/testify/require"
)
func TestHashX_Set_SetX(t *testing.T) {
h := NewHashXSingle()
h.Set("key", "opq")
res, err := h.Get("key")
require.NoError(t, err)
require.Equal(t, res, "opq")
b, _ := h.SetX("key", "opq")
require.Equal(t, b, false)
b, _ = h.SetX("key1", "opq")
require.Equal(t, b, true)
res, err = h.Get("key1")
require.NoError(t, err)
require.Equal(t, res, "opq")
}
func TestHashX_Del(t *testing.T) {
h := NewHashXSingle()
up := h.Set("key", "opq")
upu, err := h.Del("key")
require.NoError(t, err)
require.Equal(t, up, upu)
}
func TestHashX_Key(t *testing.T) {
h := NewHashXSingle()
h.Set("key", "opq")
h.Set("key1", "opq")
h.Set("key2", "opq")
require.Equal(t, h.Key(), []string{"key", "key1", "key2"})
}
func TestHashX_Value(t *testing.T) {
h := NewHashXSingle()
h.Set("key", "opq")
h.Set("key1", "opq")
h.Set("key2", "opq")
require.Equal(t, h.Value(), []string{"opq", "opq", "opq"})
}
func TestHashX_Add(t *testing.T) {
h := NewHashXSingle()
h.Set("1", "1")
c, res, err := h.Add(1, "1")
require.NoError(t, err)
require.Equal(t, c, 1)
require.Equal(t, res, []string{"2"})
s, err := h.Get("1")
require.NoError(t, err)
require.Equal(t, s, "2")
}
func Test_Pointer(t *testing.T) {
s := make([]int, 9, 20)
lens := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)))
fmt.Println(lens, len(s))
mp := make(map[string]int)
mp["qcrao"] = 100
mp["stefno"] = 18
count := **(**uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&mp)) + uintptr(16)))
fmt.Println(count, len(mp)) // 2
}
func string2bytes(s string) []byte {
stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&s))
result := reflect.SliceHeader{
Data: stringHeader.Data,
Len: stringHeader.Len,
Cap: stringHeader.Len,
}
return *(*[]byte)(unsafe.Pointer(&result))
}
func TestHashX_Range(t *testing.T) {
reComp := regexp.MustCompile("a.+")
require.True(t, reComp.MatchString("abbs"))
h := NewHashXSingle()
h.Set("abbs", "abbs")
h.Set("ppp", "ppp")
h.Set("accs", "accs")
result := h.Range(0, 3, "")
require.Len(t, result, 3)
result = h.Range(0, -1, "")
require.Len(t, result, 3)
result = h.Range(0, -1, "a.+")
require.Len(t, result, 2)
result = h.Range(1, -1, "a.+")
require.Len(t, result, 1)
}

View File

@ -1,29 +0,0 @@
// Code generated by gen-struct-const. DO NOT EDIT.
// make gen-struct generated
package structure
import "gitee.com/timedb/wheatCache/pkg/proto"
type KeyBaseInterface interface {
SizeByte() int64
// TODO RollBack 事务相关, V2 实现
RollBack() error
// Begin 事务相关, V2 实现
Begin() error
// Comment 事务相关, V2 实现
Comment() error
Encode() ([]byte, error)
}
type StringXInterface interface {
KeyBaseInterface
Set(*proto.SetRequest) ([]string, error)
Get(*proto.GetRequest) ([]string, error)
Add(*proto.AddRequest) ([]string, error)
Reduce(*proto.ReduceRequest) ([]string, error)
Setbit(*proto.SetbitRequest) ([]string, error)
Getbit(*proto.GetbitRequest) ([]string, error)
}

View File

@ -0,0 +1,428 @@
package listx
import (
"gitee.com/wheat-os/wheatCache/pkg/errorx"
"gitee.com/wheat-os/wheatCache/pkg/structure"
)
/*
1. 双向链表
2. 支持头尾操作
3. 支持索引
4. 支持切片
*/
type ListxNode struct {
next *ListxNode
pre *ListxNode
val *structure.Value
}
type Listx struct {
head *ListxNode
tail *ListxNode
length int
}
func NewListXSingle() structure.ListXInterface {
return &Listx{
head: nil,
tail: nil,
length: 0,
}
}
func (l *Listx) initByValue(val string) int {
if l.head == nil && l.length == 0 {
node := &ListxNode{
val: structure.NewValue(val),
}
l.head = node
l.tail = node
l.length = 1
return node.val.GetSize()
}
return 0
}
// 定位到 list 的元素, 支持反向索引
func (l *Listx) location(index int) (*ListxNode, error) {
// 正定位
if index >= 0 {
node := l.head
for ; index != 0 && node != nil; index -= 1 {
node = node.next
}
if node == nil {
return nil, errorx.New("index crosses the line")
}
return node, nil
}
node := l.tail
for index = (-index) - 1; index != 0 && node != nil; {
node = node.pre
index -= 1
}
if node == nil {
return nil, errorx.New("index crosses the line")
}
return node, nil
}
// 转换为左索引,负数索引
func (l *Listx) leftIndex(index int) (int, error) {
if index < 0 && l.length+index > 0 {
return index, nil
}
if index >= 0 && index < l.length {
return index - l.length, nil
}
return 0, errorx.New("the index is not valid, index:%d", index)
}
// 转换为右索引,正数索引
func (l *Listx) rightIndex(index int) (int, error) {
if index >= 0 && index < l.length {
return index, nil
}
if index < 0 && l.length+index >= 0 {
return l.length + index, nil
}
return 0, errorx.New("the index is not valid, index:%d", index)
}
func (l *Listx) remove(node *ListxNode) {
if node == nil {
return
}
l.length -= 1
if node == l.head {
l.head = node.next
node.next.pre = nil
return
}
if node == l.tail {
l.tail = node.pre
node.pre.next = nil
return
}
node.pre.next = node.next
node.next.pre = node.pre
}
func (l *Listx) SizeByte() int64 {
bytes := 0
r := l.head
for r != nil {
bytes += 16 + r.val.GetSize()
r = r.next
}
return int64(bytes)
}
// RollBack TODO 事务相关, V2 实现
func (l *Listx) RollBack() error {
panic("not implemented") // TODO: Implement
}
// Begin 事务相关, V2 实现
func (l *Listx) Begin() error {
panic("not implemented") // TODO: Implement
}
// Comment 事务相关, V2 实现
func (l *Listx) Comment() error {
panic("not implemented") // TODO: Implement
}
func (l *Listx) Encode() ([]byte, error) {
panic("not implemented") // TODO: Implement
}
func (l *Listx) LPush(valueStr ...string) structure.UpdateLength {
if len(valueStr) == 0 {
return 0
}
// 使用第一个元素尝试初始化列表
updateLength := l.initByValue(valueStr[0])
for i := 1; i < len(valueStr); i++ {
head := l.head
val := structure.NewValue(valueStr[i])
node := &ListxNode{val: val}
node.next = head
head.pre = node
l.head = node
l.length += 1
updateLength += val.GetSize()
}
return structure.UpdateLength(updateLength)
}
func (l *Listx) RPush(valueStr ...string) structure.UpdateLength {
if len(valueStr) == 0 {
return 0
}
updateLength := l.initByValue(valueStr[0])
for i := 1; i < len(valueStr); i++ {
tail := l.tail
val := structure.NewValue(valueStr[i])
node := &ListxNode{val: val}
tail.next = node
node.pre = tail
l.tail = node
l.length += 1
updateLength += val.GetSize()
}
return structure.UpdateLength(updateLength)
}
func (l *Listx) LPop(count int) ([]string, structure.UpdateLength) {
values := make([]string, 0, count)
head := l.head
// 动态变化长度
updateLength := 0
for nodePoint := 0; head != nil && nodePoint < count; nodePoint++ {
values = append(values, head.val.ToString())
updateLength += head.val.GetSize()
head = head.next
l.length -= 1
}
if head != nil {
head.pre = nil
} else {
l.tail = nil
}
l.head = head
return values, structure.UpdateLength(updateLength)
}
func (l *Listx) RPop(count int) ([]string, structure.UpdateLength) {
values := make([]string, 0, count)
tail := l.tail
updateLength := 0
for nodePoint := 0; tail != nil && nodePoint < count; nodePoint++ {
values = append(values, tail.val.ToString())
updateLength += tail.val.GetSize()
tail = tail.pre
l.length -= 1
}
if tail != nil {
tail.next = nil
} else {
l.head = nil
}
l.tail = tail
return values, structure.UpdateLength(updateLength)
}
func (l *Listx) Index(index int) (string, error) {
node, err := l.location(index)
if err != nil {
return "", nil
}
return node.val.ToString(), nil
}
// Insert, right 为 true 右添加,否则左添加
func (l *Listx) Insert(index int, right bool, valueStr ...string) (structure.UpdateLength, error) {
targetNode, err := l.location(index)
if err != nil {
return 0, err
}
updateLength := 0
for _, valStr := range valueStr {
val := structure.NewValue(valStr)
node := &ListxNode{val: val}
// 右插
if right {
node.pre = targetNode
node.next = targetNode.next
targetNode.next = node
targetNode.next.pre = node
// 更新尾部
if targetNode == l.tail {
l.tail = node
}
targetNode = node
} else {
// 左插
node.pre = targetNode.pre
targetNode.pre.next = node
node.next = targetNode
targetNode.pre = node
// 更新头部
if targetNode == l.head {
l.head = node
}
targetNode = node
}
updateLength += val.GetSize()
l.length += 1
}
return structure.UpdateLength(updateLength), nil
}
func (l *Listx) Length() int {
return l.length
}
// Slice 切片
func (l *Listx) Slice(start int, end int) (structure.UpdateLength, error) {
startOffset, err := l.rightIndex(start)
if err != nil {
return 0, err
}
endRightOffset, err := l.rightIndex(end)
if err != nil && end != l.length {
return 0, errorx.New("index overstep the boundary, index: %d", end)
}
if startOffset >= endRightOffset && endRightOffset != 0 {
return 0, errorx.New("the start index must be larger than the end index")
}
// 计算左偏移
var endOffset int
if end == l.length {
endOffset = 0
} else {
endOffset, err = l.leftIndex(end)
if err != nil {
return 0, err
}
}
updateLength := 0
// 右切片
head := l.head
for nodePoint := 0; head != nil && nodePoint < startOffset; nodePoint++ {
updateLength += head.val.GetSize()
head = head.next
l.length -= 1
}
l.head = head
head.pre = nil
tail := l.tail
for nodePoint := 0; tail != nil && nodePoint < -endOffset; nodePoint++ {
updateLength += tail.val.GetSize()
tail = tail.pre
l.length -= 1
}
l.tail = tail
tail.next = nil
return structure.UpdateLength(updateLength), nil
}
// Range 遍历
func (l *Listx) Range(start, end int) ([]string, error) {
startOffset, err := l.rightIndex(start)
if err != nil {
return nil, err
}
endRightOffset, err := l.rightIndex(end)
if err != nil && end != l.length {
return nil, errorx.New("index overstep the boundary, index: %d", end)
}
if startOffset >= endRightOffset && endRightOffset != 0 {
return nil, errorx.New("the start index must be larger than the end index")
}
head := l.head
for nodePoint := 0; head != nil && nodePoint < startOffset; nodePoint++ {
head = head.next
}
values := make([]string, 0)
for i := 0; i < endRightOffset-startOffset && head != nil; i++ {
values = append(values, head.val.ToString())
head = head.next
}
return values, nil
}
func (l *Listx) Remove(value string, count int) (int, structure.UpdateLength) {
if count == 0 {
return 0, 0
}
updateLength := 0
remCount := count
// 头删除
if count > 0 {
node := l.head
for node != nil && remCount > 0 {
if node.val.ToString() == value {
l.remove(node)
remCount--
updateLength += node.val.GetSize()
}
node = node.next
}
return count - remCount, structure.UpdateLength(updateLength)
}
// 尾删除
node := l.tail
for node != nil && remCount < 0 {
if node.val.ToString() == value {
l.remove(node)
remCount++
updateLength += node.val.GetSize()
}
node = node.pre
}
return remCount - count, structure.UpdateLength(updateLength)
}

View File

@ -0,0 +1,228 @@
package listx
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestListx_LPush_And_Pop(t *testing.T) {
list := NewListXSingle()
up := list.LPush("1", "2", "3")
require.Equal(t, list.Length(), 3)
values, updateLength := list.LPop(3)
require.Equal(t, values, []string{"3", "2", "1"})
require.Equal(t, up, updateLength)
list.LPush("1", "2", "3")
values, updateLength = list.LPop(1)
require.Equal(t, values, []string{"3"})
require.Equal(t, int(updateLength), 24)
}
func TestListx_RPush_Pop(t *testing.T) {
list := NewListXSingle()
up := list.RPush("1", "2", "3")
require.Equal(t, list.Length(), 3)
values, updateLength := list.LPop(3)
require.Equal(t, values, []string{"1", "2", "3"})
require.Equal(t, up, updateLength)
list.RPush("1", "2", "3")
values, updateLength = list.RPop(2)
require.Equal(t, values, []string{"3", "2"})
require.Equal(t, int(updateLength), 48)
}
func TestListx_location(t *testing.T) {
list := &Listx{
head: nil,
tail: nil,
length: 0,
}
list.RPush("1", "2", "3")
node, err := list.location(1)
require.NoError(t, err)
require.Equal(t, node.val.ToString(), "2")
node, err = list.location(0)
require.NoError(t, err)
require.Equal(t, node.val.ToString(), "1")
node, err = list.location(2)
require.NoError(t, err)
require.Equal(t, node.val.ToString(), "3")
_, err = list.location(3)
require.Error(t, err)
node, err = list.location(-1)
require.NoError(t, err)
require.Equal(t, node.val.ToString(), "3")
node, err = list.location(-2)
require.NoError(t, err)
require.Equal(t, node.val.ToString(), "2")
node, err = list.location(-3)
require.NoError(t, err)
require.Equal(t, node.val.ToString(), "1")
_, err = list.location(-4)
require.Error(t, err)
}
func TestListx_Insert(t *testing.T) {
list := NewListXSingle()
list.LPush("a", "b", "c", "d", "e")
val, _ := list.LPop(1)
require.Equal(t, val, []string{"e"})
res, err := list.Index(1)
require.NoError(t, err)
require.Equal(t, res, "c")
_, err = list.Insert(1, false, "1", "2", "3")
require.NoError(t, err)
// 全部取出
val, _ = list.LPop(list.Length())
require.Equal(t, val, []string{"d", "3", "2", "1", "c", "b", "a"})
list.RPush("1", "2", "3", "4", "5")
res, err = list.Index(-2)
require.NoError(t, err)
require.Equal(t, res, "4")
_, err = list.Insert(-1, true, "6", "7")
require.NoError(t, err)
val, _ = list.LPop(list.Length())
require.Equal(t, val, []string{"1", "2", "3", "4", "5", "6", "7"})
}
func TestListx_Index(t *testing.T) {
list := &Listx{
head: nil,
tail: nil,
length: 0,
}
list.RPush("a", "b", "c", "d", "e")
index := 2
leftIndex, err := list.leftIndex(index)
require.NoError(t, err)
rightIndex, err := list.rightIndex(index)
require.NoError(t, err)
v1, err := list.Index(leftIndex)
require.NoError(t, err)
v2, err := list.Index(rightIndex)
require.NoError(t, err)
require.Equal(t, v1, v2)
}
func TestListx_Slice(t *testing.T) {
list := NewListXSingle()
list.RPush("a", "b", "c", "d", "e")
// 主流程测试
list.Slice(1, 2)
values, _ := list.LPop(list.Length())
require.Equal(t, values, []string{"b"})
list2 := NewListXSingle()
list2.RPush("1", "2", "3", "4", "5")
list2.Slice(0, 4)
values, _ = list2.LPop(list2.Length())
require.Equal(t, values, []string{"1", "2", "3", "4"})
list3 := NewListXSingle()
list3.RPush("1", "2", "3", "4", "5")
list3.Slice(2, list3.Length())
values, _ = list3.LPop(list3.Length())
require.Equal(t, values, []string{"3", "4", "5"})
// 测试负数索引
list3 = NewListXSingle()
list3.RPush("1", "2", "3", "4", "5")
_, err := list3.Slice(0, -2)
require.NoError(t, err)
values, _ = list3.LPop(list3.Length())
require.Equal(t, values, []string{"1", "2", "3"})
// 测试负数双边际
list3 = NewListXSingle()
list3.RPush("1", "2", "3", "4", "5")
_, err = list3.Slice(-3, -2)
require.NoError(t, err)
values, _ = list3.LPop(list3.Length())
require.Equal(t, values, []string{"3"})
// 测试负数边际
list3 = NewListXSingle()
list3.RPush("1", "2", "3", "4", "5")
_, err = list3.Slice(-5, 4)
require.NoError(t, err)
values, _ = list3.LPop(list3.Length())
require.Equal(t, values, []string{"1", "2", "3", "4"})
}
func TestListx_Range(t *testing.T) {
list := NewListXSingle()
list.RPush("a", "b", "c", "d", "e")
val, err := list.Range(0, 2)
require.NoError(t, err)
require.Equal(t, val, []string{"a", "b"})
val, err = list.Range(0, -1)
require.NoError(t, err)
require.Equal(t, val, []string{"a", "b", "c", "d"})
val, err = list.Range(-3, 3)
require.NoError(t, err)
require.Equal(t, val, []string{"c"})
val, err = list.Range(-1, list.Length())
require.NoError(t, err)
require.Equal(t, val, []string{})
_, err = list.Range(6, -1)
require.Error(t, err)
}
func TestListx_Remove(t *testing.T) {
list := NewListXSingle()
list.RPush("a", "b", "c", "c", "e")
count, up := list.Remove("c", -2)
require.Equal(t, count, 2)
require.Equal(t, int(up), 34)
res, _ := list.LPop(list.Length())
require.Equal(t, res, []string{"a", "b", "e"})
list = NewListXSingle()
list.RPush("a", "b", "c", "c", "e")
count, up = list.Remove("b", 1)
require.Equal(t, count, 1)
require.Equal(t, int(up), 17)
res, _ = list.LPop(list.Length())
require.Equal(t, res, []string{"a", "c", "c", "e"})
}

View File

@ -1 +0,0 @@
package stringx

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