Compare commits

...

284 Commits

Author SHA1 Message Date
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
152 changed files with 20122 additions and 1240 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/

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

@ -8,6 +8,7 @@ import (
)
func TestConf(t *testing.T) {
// 外部导入 conf.yaml 需要导入 conf 包
// 每次迁移文件时, 使用 sudo make init-conf来将yam文件迁移到指定的文件夹下
// get 使用, 读取 public_conf 配置文件
@ -22,3 +23,13 @@ func TestConf(t *testing.T) {
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,19 +3,58 @@ version: 'v1.0'
env: 'dev'
storage:
host: '127.0.0.1'
host: '0.0.0.0'
port: 5890
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"
clearSize: "512mb"
maxSize: "1GB"
eventDriverSize: 2000
workTime: 1
detachNum: 300
logPrint:
stath: [
"debug",
"error"
]
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

@ -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,24 +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 gen-middleware
@make build
@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
@ -35,3 +47,12 @@ gen-middleware:
.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

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")
}

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,27 +4,30 @@ import (
"context"
)
type eventType int8
const (
defaultEventState = eventType(iota) //默认情况下的状态
waitEventState // 等待状态
workEventState //工作状态
closeEventState //事件关闭状态
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,146 +1,189 @@
package event
import (
"sync"
"sync/atomic"
"time"
"gitee.com/timedb/wheatCache/pkg/errorx"
"gitee.com/wheat-os/wheatCache/pkg/errorx"
)
type Active func() ([]string, error) // 事件带函数
// 事件 poll 降低 new 对象的频率
type eventPoll struct {
poll chan *event
maxSize int32
nowSize *int32
}
type Event struct {
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
WorkTime time.Duration // 工作时间
msg map[string]string // 消息
waitResult chan interface{} // 等待返回
err error
ru sync.RWMutex
muClose sync.Mutex //关闭锁
eventStatus eventType
eventStatus *int32
ttlManage *time.Timer
}
func (e *Event) SetMsg(key string, val string) {
e.ru.Lock()
defer e.ru.Unlock()
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]
}
func (e *Event) GetEventName() string {
func (e *event) GetEventName() string {
return e.eventName
}
// SetValue 写入 ctx 传递用参数
func (e *Event) SetValue(key string, value interface{}) {
e.ru.Lock()
defer e.ru.Unlock()
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) {
e.ru.RLock()
defer e.ru.RUnlock()
func (e *event) GetValue(key string) (interface{}, bool) {
val, ok := e.msgCtx[key]
return val, ok
}
// InitWaitEvent 初始化 wait event 必须调用才拥有等待特性
func (e *Event) InitWaitEvent() {
e.muClose.Lock()
defer e.muClose.Unlock()
e.waitResult = make(chan interface{})
e.eventStatus = waitEventState
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) {
t := time.NewTimer(ttl)
select {
case <-t.C:
e.muClose.Lock()
defer e.muClose.Unlock()
if e.eventStatus == workEventState {
return <-e.waitResult, e.err
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.eventStatus = closeEventState
return nil, errorx.TimeOutErr()
case result := <-e.waitResult:
return result, e.err
}
}
func (e *Event) ExecWorkAndSendResult(work EventWorkFunc) (interface{}, error) {
e.muClose.Lock()
defer e.muClose.Unlock()
if e.eventStatus != waitEventState {
func (e *event) ExecWorkAndSendResult(work EventWorkFunc) (interface{}, error) {
if !atomic.CompareAndSwapInt32(e.eventStatus, waitEventState, workEventState) {
return nil, errorx.New("not wait status, exec err")
}
e.eventStatus = workEventState
res, err := work()
e.err = err
e.waitResult <- res
close(e.waitResult)
e.eventStatus = closeEventState
return res, err
}
func (e *Event) SetResultErr(err error) {
e.muClose.Lock()
defer e.muClose.Unlock()
if e.eventStatus != waitEventState {
func (e *event) SetResultErr(err error) {
if !atomic.CompareAndSwapInt32(e.eventStatus, waitEventState, workEventState) {
return
}
e.eventStatus = workEventState
e.err = err
e.waitResult <- nil
close(e.waitResult)
e.eventStatus = closeEventState
}
func NewEvent(eventName string) *Event {
return &Event{
eventName: eventName,
eventStatus: defaultEventState,
}
}
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,7 +3,8 @@ package event
import (
"context"
"fmt"
"gitee.com/timedb/wheatCache/pkg/errorx"
"strconv"
"sync"
"testing"
"time"
@ -13,87 +14,76 @@ import (
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.SetValue("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.GetValue("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)
}
func waitProduce(t *testing.T, ctx context.Context, v ProduceInterface) {
for i := 0; i < 100; i++ {
event := NewEvent(waitTestEvent)
event.InitWaitEvent()
event.SetValue("test", i)
v.Call(ctx, event) // 推送给 consumer
res, err := event.StartWaitEvent(2 * time.Second) // 最多等待 consumer 回复 2s
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("test:%v", i), res)
}
}
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.GetValue("test")
require.True(t, ok)
require.Equal(t, res, i)
// 发送返回值给 produce
event.ExecWorkAndSendResult(func() (interface{}, error) {
return fmt.Sprintf("test:%v", res), nil
})
}
}
func TestEvent_SetResultErr(t *testing.T) {
ctx := context.Background()
event := NewEvent("dddd")
driver := NewDriver(100)
produce := NewProduce(driver)
consumer := NewConsumer(driver)
go func() {
event := consumer.Receive(ctx)
event.SetResultErr(errorx.New("err"))
for i := 0; i < 30000; i++ {
event := produce.NewEvent(testEvent)
event.SetMsg("k", strconv.Itoa(i))
produce.Call(ctx, event)
}
}()
event.InitWaitEvent()
produce.Call(ctx, event)
_, err := event.StartWaitEvent(2 * time.Second)
fmt.Println(err)
require.Error(t, err)
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)
}
// 双向 event
func Test_WaitEventDriver(t *testing.T) {
driver := NewDriver(200)
produce := NewProduce(driver)
consumer := NewConsumer(driver)
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

@ -2,9 +2,10 @@ package logx
import (
"context"
"gitee.com/timedb/wheatCache/pkg/event"
"github.com/spf13/viper"
"sync"
"gitee.com/wheat-os/wheatCache/pkg/event"
"github.com/spf13/viper"
)
type LogLevelState int8
@ -32,5 +33,11 @@ type logInterface 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{})
}

View File

@ -3,12 +3,13 @@ package logx
import (
"context"
"fmt"
"gitee.com/timedb/wheatCache/pkg/event"
middleMsg "gitee.com/timedb/wheatCache/pkg/middle-msg"
"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 {
@ -21,52 +22,108 @@ func With(ctx context.Context, p event.ProduceInterface) *upLogger {
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{}) {
Print("ERROR", format, msg...)
l.Print("ERROR", format, msg...)
os.Exit(-1)
}
func (l *upLogger) Print(level string, format string, msg ...interface{}) {
Print(level, format, msg...)
eventMiddle := event.NewEvent(middleMsg.EventNameLog)
eventMiddle.SetValue(middleMsg.EventKeyLog, middleMsg.LogContext{})
l.produce.Call(l.ctx, eventMiddle)
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 Debug(format string, msg ...interface{}) {
Print("DEBUG", format, msg...)
}
func Info(format string, msg ...interface{}) {
Print("INFO", format, msg...)
}
func Warn(format string, msg ...interface{}) {
Print("WARN", format, msg...)
}
func Error(format string, msg ...interface{}) {
Print("ERROR", format, msg...)
func (l *upLogger) Debugln(msg ...interface{}) {
l.Print("DEBUG", "%s", format(msg...))
}
func Panic(format string, msg ...interface{}) {
Print("ERROR", 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 Print(level string, format string, msg ...interface{}) {
place := findPlace()
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]
fmt.Println(level, datetime, fmt.Sprintf(format, msg...))
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) {
@ -76,11 +133,11 @@ func Print(level string, format string, msg ...interface{}) {
}
}
func findPlace() string {
func findPlace(floor int) string {
var (
place string
i = 0
i = floor
)
for {
@ -94,3 +151,11 @@ func findPlace() string {
return place
}
func format(message ...interface{}) (context string) {
for _, msg := range message {
context = fmt.Sprintf("%s\t%v", context, msg)
}
return context
}

View File

@ -2,9 +2,10 @@ package logx
import (
"context"
_ "gitee.com/timedb/wheatCache/conf"
"gitee.com/timedb/wheatCache/pkg/event"
"testing"
_ "gitee.com/wheat-os/wheatCache/conf"
"gitee.com/wheat-os/wheatCache/pkg/event"
)
func TestStd(t *testing.T) {
@ -12,7 +13,13 @@ func TestStd(t *testing.T) {
Debug("%d%s", 11, "Debug")
Warn("%d%s", 11, "Warn")
Error("%d%s", 11, "Error")
Panic("%d%s", 11, "Panic")
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)))
@ -21,6 +28,11 @@ func TestStd(t *testing.T) {
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.Panic("%d%s", 11, "Panic")
logger.Infoln(11, "Info")
logger.Debugln(11, "Debug")
logger.Warnln(11, "Warn")
logger.Errorln(11, "Error")
}

View File

@ -1,17 +1,19 @@
package lru
import (
"gitee.com/timedb/wheatCache/pkg/proto"
"gitee.com/timedb/wheatCache/pkg/structure"
"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"
WorkFuncEventKey = "workFunc"
OptionEventName = "operateEvent"
CleanEventName = "clearEvent"
TtlEventName = "ttlEvent"
)
var (
@ -20,15 +22,25 @@ var (
)
const (
lruMaxSize = 1 * 1024 * 1024 * 1024 * 8
lruClearSize = 0.5 * 1024 * 1024 * 1024 * 8
lruEventDriver = 2000
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)
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
)

View File

@ -2,31 +2,39 @@ package lru
import (
"container/list"
_ "gitee.com/timedb/wheatCache/conf"
"gitee.com/timedb/wheatCache/pkg/errorx"
"gitee.com/timedb/wheatCache/pkg/event"
"gitee.com/timedb/wheatCache/pkg/proto"
"gitee.com/timedb/wheatCache/pkg/structure"
"gitee.com/timedb/wheatCache/pkg/util"
"github.com/spf13/viper"
"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
key string
val structure.KeyBaseInterface
expire int64 // 过期时间戳
}
type SingleCache struct {
maxsize int64 //最大的长度
clearSize int64 // 清理长度
nowSize int64 // 现在的长度
li *list.List
lruMap map[string]*list.Element
maxsize int64 //最大的长度
clearSize int64 // 清理长度
nowSize int64 // 现在的长度
li *list.List
lruMap map[string]*list.Element
lruMaxDiverSize int
lruTtlManage *lruTTl // 定时清理器
lruDriver event.DriverInterface
lruConsumer event.ConsumerInterface
lruCleanProduce event.ProduceInterface // 发送清理事件
lruDriver event2.DriverInterface
lruConsumer event2.ConsumerInterface
lruCleanProduce event2.ProduceInterface // 发送清理事件
middleProduce event.ProduceInterface // 中间件驱动
}
// UpdateLruSize 更新现在的长度
@ -34,36 +42,42 @@ func (lru *SingleCache) UpdateLruSize(length structure.UpdateLength) {
atomic.AddInt64(&lru.nowSize, int64(length))
}
func cacheInit() (int64, int64, int) {
func cacheInit() (int64, int64, int, int) {
maxSize := viper.GetString("lruCache.maxSize")
retMaxSize, maxErr := util.ParseSizeToBit(maxSize)
if maxErr != nil {
return 0, 0, 0
return 0, 0, 0, 0
}
if retMaxSize == 0 {
retMaxSize = lruMaxSize
retMaxSize = defaultLruMaxSize
}
clearSize := viper.GetString("lruCache.clearSize")
retClearSize, clearErr := util.ParseSizeToBit(clearSize)
if clearErr != nil {
return 0, 0, 0
return 0, 0, 0, 0
}
if retClearSize == 0 {
retClearSize = lruClearSize
retClearSize = defaultLruClearSize
}
maxDriver := viper.GetInt("lruCache.eventDriverSize")
if maxDriver == 0 {
maxDriver = lruEventDriver
maxDriver = defaultLruEventDriver
}
return retMaxSize, retClearSize, maxDriver
detachNum := viper.GetInt("lruCache.detachNum")
if detachNum == 0 {
detachNum = defaultDetachNum
}
return retMaxSize, retClearSize, maxDriver, detachNum
}
// NewLRUCache lru初始化
func NewLRUCache() *SingleCache {
maxSize, clearSize, maxDriverSize := cacheInit()
lruDriver := event.NewDriver(maxDriverSize)
maxSize, clearSize, maxDriverSize, detachNum := cacheInit()
lruDriver := event2.NewDriver(maxDriverSize)
lruCacheOnce.Do(func() {
lru := &SingleCache{
maxsize: maxSize,
@ -71,45 +85,64 @@ func NewLRUCache() *SingleCache {
nowSize: 0,
li: list.New(),
lruMap: make(map[string]*list.Element),
lruMaxDiverSize: maxDriverSize,
lruDriver: lruDriver,
lruConsumer: event.NewConsumer(lruDriver),
lruCleanProduce: event.NewProduce(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() event.DriverInterface {
func (lru *SingleCache) GetDriver() event2.DriverInterface {
return lru.lruDriver
}
//Add 增加
func (lru *SingleCache) Add(key *proto.BaseKey, val structure.KeyBaseInterface) {
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,
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
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 lru.lruMap == nil {
if key == nil {
return nil, false
}
if elVal, ok := lru.lruMap[key.Key]; ok {
lru.li.MoveToFront(elVal)
return elVal.Value.(*keyBaseValue).val, true
@ -131,13 +164,62 @@ func (lru *SingleCache) Del() error {
}
//DelByKey 根据key删除
func (lru *SingleCache)DelByKey(key *proto.BaseKey) error {
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 _, ok := lru.lruMap[key.Key]; ok {
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)
}

View File

@ -2,10 +2,12 @@ package lru
import (
"fmt"
"gitee.com/timedb/wheatCache/pkg/proto"
"gitee.com/timedb/wheatCache/pkg/structure/stringx"
"github.com/stretchr/testify/require"
"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) {
@ -55,4 +57,47 @@ func TestNewLRUCache2(t *testing.T) {
_, 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))
}

View File

@ -2,20 +2,22 @@ package lru
import (
"context"
"gitee.com/timedb/wheatCache/pkg/event"
"gitee.com/timedb/wheatCache/pkg/proto"
"gitee.com/timedb/wheatCache/pkg/structure/stringx"
"github.com/stretchr/testify/require"
"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 := event.NewProduce(lru.GetDriver())
workEvent := event.NewEvent(OptionEventName)
workEvent.SetValue(WorkFuncEventKey, event.EventWorkFunc(func() (interface{}, error) {
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",
@ -30,3 +32,31 @@ func TestWorker(t *testing.T) {
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)
}

View File

@ -2,29 +2,134 @@ package lru
import (
"context"
"gitee.com/timedb/wheatCache/pkg/errorx"
"gitee.com/timedb/wheatCache/pkg/event"
"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() interface{} {
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:
workFunc, ok := workEvent.GetValue(WorkFuncEventKey)
if !ok {
workEvent.ExecWorkAndSendResult(func() (interface{}, error) {
return nil, errorx.New("the event haven't work of function")
})
continue
}
if work, ok := workFunc.(event.EventWorkFunc); ok {
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
}

View File

@ -1,11 +1,9 @@
package middle_msg
package middlemsg
import "time"
var (
EventNameLog = "LogContext"
EventKeyLog = "LogContext"
LogContextName = "log-context"
)
type LogContext struct {

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,7 +1,15 @@
package middle
import getMiddlewareMap "gitee.com/timedb/wheatCache/plugins/config"
import (
"sync"
)
func Init() {
getMiddlewareMap.GetMiddlewareMap()
}
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,39 +0,0 @@
// Code generated by gen-struct. DO NOT EDIT.
// make gen-struct generated
package structure
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

@ -14,3 +14,64 @@ const (
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. DO NOT EDIT.
// make gen-struct generated
package structure
import "gitee.com/timedb/wheatCache/pkg/proto"
type KeyBaseInterface interface {
SizeByte() int64
// RollBack TODO 事务相关, 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) (*proto.{{val}}Response, error)
{% endfor %}
}
{% endfor -%}
{%- endfor %}

View File

@ -1,10 +0,0 @@
# 这里定义结构体的支持的命令, 以后也许会添加结构体的命令验证
# 定义结构体名称以及方法使, 方法全部小写,结构体名称全部大写且结构体名称需要加上 _X, 如 LIST_X
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,24 +0,0 @@
package structure
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(val string) (string, UpdateLength)
Get() string
Add(renewal int32) (string, error)
Reduce(renewal int32) (string, error)
Setbit(offer int32, val bool) UpdateLength
Getbit(offer int32) (bool, 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,9 +1,7 @@
package stringx
import (
"gitee.com/timedb/wheatCache/pkg/errorx"
"gitee.com/timedb/wheatCache/pkg/structure"
"strconv"
"gitee.com/wheat-os/wheatCache/pkg/structure"
)
type StringSingle struct {
@ -17,7 +15,7 @@ func NewStringSingle() structure.StringXInterface {
}
func (s *StringSingle) SizeByte() int64 {
return int64(s.val.GetLength())
return int64(s.val.GetSize())
}
// RollBack TODO 事务相关, V2 实现
@ -50,32 +48,8 @@ func (s *StringSingle) Get() string {
return s.val.ToString()
}
func updateValueNotString(s *StringSingle, val int32) (string, error) {
switch s.val.GetDynamicType() {
case structure.DynamicNull:
s.val.SetInt(int64(val))
return strconv.Itoa(int(val)), nil
case structure.DynamicFloat:
f, err := s.val.ToFloat64()
if err != nil {
return "", err
}
s.val.SetFloat64(f + float64(val))
return strconv.FormatFloat(f+1, 'f', 2, 64), nil
case structure.DynamicInt:
i, err := s.val.ToInt()
if err != nil {
return "", err
}
s.val.SetInt(int64(val) + i)
return strconv.Itoa(int(i + int64(val))), nil
default:
return "", errorx.New("string cannot perform add operations")
}
}
func (s *StringSingle) Add(renewal int32) (string, error) {
result, err := updateValueNotString(s, renewal)
result, err := s.val.Incr(renewal)
if err != nil {
return "", err
}
@ -83,7 +57,7 @@ func (s *StringSingle) Add(renewal int32) (string, error) {
}
func (s *StringSingle) Reduce(renewal int32) (string, error) {
result, err := updateValueNotString(s, -1*renewal)
result, err := s.val.Incr(-1 * renewal)
if err != nil {
return "", err
}
@ -105,3 +79,15 @@ func (s *StringSingle) Getbit(offer int32) (bool, error) {
return b, err
}
func (s *StringSingle) Getrange(start, end int32) (string, error) {
b, err := s.val.SliceByString(int(start), int(end))
if err != nil {
return "", err
}
return string(b), nil
}
func (s *StringSingle) GetLength() int {
return s.val.GetLength()
}

View File

@ -1,9 +1,12 @@
package stringx
import (
"gitee.com/timedb/wheatCache/pkg/structure"
"github.com/stretchr/testify/require"
"fmt"
"testing"
"unsafe"
"gitee.com/wheat-os/wheatCache/pkg/structure"
"github.com/stretchr/testify/require"
)
func TestStringSingle_Set(t *testing.T) {
@ -80,3 +83,16 @@ func TestStringSingle_Getbit(t *testing.T) {
require.NoError(t, err)
require.Equal(t, res, false)
}
func TestStringSingle_Getrange(t *testing.T) {
s := NewStringSingle()
s.Set("abcdefg")
k, err := s.Getrange(0, 3)
require.NoError(t, err)
require.Equal(t, "abc", k)
}
func TestPointSize(t *testing.T) {
var a *int32
fmt.Println(unsafe.Sizeof(a))
}

View File

@ -3,9 +3,11 @@ package structure
import (
"bytes"
"encoding/binary"
"gitee.com/timedb/wheatCache/pkg/errorx"
"fmt"
"math"
"strconv"
"gitee.com/wheat-os/wheatCache/pkg/errorx"
)
// Value 提供一个基础的 动态类型
@ -16,16 +18,26 @@ type Value struct {
onType DynamicType
}
func NewValue() *Value {
return &Value{
func NewValue(val ...string) *Value {
stcValue := &Value{
val: make([]byte, defaultLen),
length: 0,
onType: DynamicNull,
}
if len(val) > 0 {
stcValue.InferValue(val[0])
}
return stcValue
}
func (v *Value) GetLength() int {
return len(v.val)
return v.length
}
func (v *Value) GetSize() int {
return len(v.val) + 16
}
func (v *Value) GetDynamicType() DynamicType {
@ -136,27 +148,90 @@ func (v *Value) ChangeValueLength(f func()) UpdateLength {
func (v *Value) SetByte(offset int, val bool) {
v.onType = DynamicNull // 位图使用无类型
b := byte(0)
if val {
b = byte(1)
}
if v.length >= offset {
v.val[offset] = b
// 扩容
if len(v.val) <= offset/8 {
newByte := make([]byte, (offset/8)+1)
copy(newByte, v.val[:len(v.val)])
v.val = newByte
v.length = len(v.val)
}
if val {
// true 位
v.val[offset/8] |= (0b1 << (offset % 8))
return
}
newByte := make([]byte, offset+1)
newByte[offset] = b
copy(newByte, v.val[:v.length])
v.val = newByte
v.length = len(newByte)
// false 位
v.val[offset/8] ^= (0b1 << (offset % 8))
}
func (v *Value) GetByte(offset int) (bool, error) {
if v.length >= offset {
return v.val[offset] == byte(1), nil
if len(v.val) >= offset/8 {
// 采用 & 来运算 是否为 true
return v.val[offset/8]&(0b1<<(offset%8)) != 0, nil
}
return false, errorx.New("the maximum length is exceeded")
}
func (v *Value) SliceByString(start, end int) ([]byte, error) {
if start > end {
return nil, errorx.New("the end cannot be greater than the beginning")
}
if v.onType == DynamicInt {
ret, err := v.ToInt()
if err != nil {
return nil, err
}
value := strconv.Itoa(int(ret))
if end > len(value) {
return nil, errorx.New("the maximum index is exceeded, max index: %d", len(value))
}
return []byte(value[start:end]), nil
}
if v.onType == DynamicFloat {
ret, err := v.ToFloat64()
if err != nil {
return nil, err
}
value := fmt.Sprintf("%.2f", ret)
if end > len(value) {
return nil, errorx.New("the maximum index is exceeded, max index: %d", len(value))
}
return []byte(value[start:end]), nil
}
if end > v.length {
return nil, errorx.New("the maximum index is exceeded, max index: %d", v.length)
}
return v.val[start:end], nil
}
// 自增
func (v *Value) Incr(renewal int32) (string, error) {
switch v.GetDynamicType() {
case DynamicNull:
v.SetInt(int64(renewal))
return strconv.Itoa(int(renewal)), nil
case DynamicFloat:
f, err := v.ToFloat64()
if err != nil {
return "", err
}
v.SetFloat64(f + float64(renewal))
return strconv.FormatFloat(f+float64(renewal), 'f', 2, 64), nil
case DynamicInt:
i, err := v.ToInt()
if err != nil {
return "", err
}
v.SetInt(int64(renewal) + i)
return strconv.Itoa(int(i + int64(renewal))), nil
default:
return "", errorx.New("string cannot perform add operations")
}
}

View File

@ -1,6 +1,7 @@
package structure
import (
"fmt"
"strconv"
"testing"
@ -91,7 +92,7 @@ func TestValue_ChangeValue(t *testing.T) {
chanageLen := value.ChangeValueLength(func() {
value.SetString("小葵花课堂开课了")
})
require.Equal(t, chanageLen, int64(value.GetLength()-oldLen))
require.Equal(t, int64(chanageLen), int64(value.GetLength()-oldLen))
lens := value.ChangeValueLength(func() {
value.SetInt(100)
@ -126,4 +127,20 @@ func TestValue_SetByte(t *testing.T) {
v, err = value.GetByte(10001)
require.NoError(t, err)
require.Equal(t, v, true)
require.Equal(t, value.GetSize(), (10001/8)+1+16)
}
func TestValue_SetByteWei(t *testing.T) {
k := make([]byte, 100)
offset := 700
k[offset/8] = 0b00000001
k[offset/8] |= 0b1 << (offset % 8)
fmt.Printf("%b\n", k[offset/8])
fmt.Printf("%v", (k[offset/8]&(0b1<<(offset%8))) != 0)
k[offset/8] ^= 0b1 << (offset % 8)
fmt.Printf("%v", (k[offset/8]&(0b1<<(offset%8))) != 0)
}

View File

@ -1,10 +1,11 @@
package util
import (
"gitee.com/timedb/wheatCache/pkg/errorx"
"regexp"
"strconv"
"strings"
"gitee.com/wheat-os/wheatCache/pkg/errorx"
)
// ParseSizeToBit
@ -17,16 +18,14 @@ func ParseSizeToBit(size string) (int64, error) {
sizeType := strings.ToUpper(unit[1])
switch {
case sizeType == "BIT" || sizeType == "B":
return Res * 8, nil
return Res, nil
case sizeType == "KB":
return Res * 1024 * 8, nil
case sizeType =="MB":
return Res * 1024 * 1024 * 8, nil
return Res * 1024, nil
case sizeType == "MB":
return Res * 1024 * 1024, nil
case sizeType == "GB":
return Res * 1024 *1024 * 1024 * 8, nil
return Res * 1024 * 1024 * 1024, nil
default:
return 0, errorx.New("your size is wrong")
}
}

29
pkg/util/server/os.go Normal file
View File

@ -0,0 +1,29 @@
package server
import (
"os"
"os/signal"
"syscall"
"gitee.com/wheat-os/wheatCache/pkg/logx"
"google.golang.org/grpc"
)
func ElegantExitServer(s *grpc.Server) {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGHUP, syscall.SIGINT)
go func() {
select {
case <-c:
s.Stop()
msg := `
|-------Wheat tools---------|
| see you next time |
|thank you for your efforts |
|---------------------------|
`
logx.Infoln(msg)
}
}()
}

View File

@ -0,0 +1,400 @@
package skiplist
import (
"crypto/rand"
"fmt"
"math"
"math/big"
)
type skipListNode struct {
score float64
val interface{}
next *skipListNode
down *skipListNode
up *skipListNode
pre *skipListNode
}
func newSkipListNode(score float64, val interface{}) *skipListNode {
return &skipListNode{
score: score,
val: val,
}
}
type SkipList struct {
head *skipListNode
tail *skipListNode
length int
levels int // 单前层级
maxLevels int // 最大层级
}
func NewSkipList(maxLevel int) *SkipList {
skl := new(SkipList)
skl.head = &skipListNode{score: math.MinInt64}
skl.tail = &skipListNode{score: math.MaxInt64}
skl.head.next = skl.tail
skl.tail.pre = skl.head
skl.length = 0
skl.levels = 1
skl.maxLevels = maxLevel
return skl
}
func (s *SkipList) Length() int {
return s.length
}
func (s *SkipList) Levels() int {
return s.levels
}
func (s *SkipList) MaxLength() int {
return s.maxLevels
}
// Insert 插入数据
func (s *SkipList) Insert(score float64, val interface{}) {
node := newSkipListNode(score, val)
f := s.searchNode(score)
s.insertAfter(f, node)
// 随机上升
for newLevel := 1; ; newLevel++ {
rander, _ := rand.Int(rand.Reader, big.NewInt(2))
if rander.Int64() == 0 || newLevel >= s.maxLevels {
break
}
// 上升层级
if newLevel >= s.levels && s.levels < s.maxLevels {
s.newLevels()
}
for f.up == nil {
f = f.pre
}
f = f.up
tmpNode := &skipListNode{score: score}
node.up = tmpNode
tmpNode.down = node
s.insertAfter(f, tmpNode)
node = tmpNode
}
s.length += 1
}
// Pop 弹出随机一个 score 值的 val
func (s *SkipList) Pop(score float64) interface{} {
f := s.searchNode(score)
if f.score == score {
return nil
}
v := f.val
s.delSkipListNode(f)
return v
}
// Get 获取 随机一个 score 值的 val
func (s *SkipList) Get(score float64) interface{} {
node := s.searchNode(score)
if node != nil && node.score == score {
return node.val
}
return nil
}
// GetAll 获取全部的 ALL
func (s *SkipList) GetAll(score float64) []interface{} {
node := s.searchNode(score)
values := make([]interface{}, 0)
p := node
// pre
for p.score == score {
values = append(values, p.val)
p = p.pre
}
// next
p = node.next
for p.score == score {
values = append(values, p.val)
p = p.next
}
return values
}
// RemoveAll 删除全部
func (s *SkipList) RemoveAll(score float64) {
node := s.searchNode(score)
p := node
delNode := make([]*skipListNode, 0)
// pre
for p.score == score {
delNode = append(delNode, p)
p = p.pre
}
// next
p = node.next
for p.score == score {
delNode = append(delNode, p)
p = p.next
}
s.delSkipListNode(delNode...)
}
func (s *SkipList) PopLeft(score float64) []interface{} {
endPoint := s.tail
head := s.head
for endPoint.score > score {
endPoint = endPoint.pre
}
var valuePoint *skipListNode
for endPoint != nil {
// 检查 end 后的节点情况
for endPoint.next.score <= score {
endPoint = endPoint.next
}
head.next = endPoint.next
endPoint.next.pre = head
valuePoint = endPoint
endPoint = endPoint.down
head = head.down
}
values := make([]interface{}, 0, 100)
for valuePoint != nil && valuePoint.score > s.head.score {
values = append(values, valuePoint.val)
valuePoint = valuePoint.pre
}
return values
}
func (s *SkipList) PopRight(score float64) []interface{} {
startPoint := s.head
tail := s.tail
for startPoint.score < score {
startPoint = startPoint.next
}
var valuePoint *skipListNode
for startPoint != nil {
// 检查 end 后的节点情况
for startPoint.pre.score >= score {
startPoint = startPoint.pre
}
tail.pre = startPoint.pre
startPoint.pre.next = tail
valuePoint = startPoint
startPoint = startPoint.down
tail = tail.down
}
values := make([]interface{}, 0, 100)
for valuePoint != nil && valuePoint.score < s.tail.score {
values = append(values, valuePoint.val)
valuePoint = valuePoint.next
}
return values
}
// PopRangeByScore 删除包括 start 到 end 的 score 的全部 value
func (s *SkipList) PopRangeByScore(startScore float64, endScore float64) []interface{} {
if startScore >= endScore {
return nil
}
startPoint := s.head
endPoint := s.tail
// 定位删除索引最高级
for startPoint.score < startScore {
startPoint = startPoint.next
}
for endPoint.score > endScore {
endPoint = endPoint.pre
}
var valuePoint *skipListNode
for startPoint != nil {
// 检查 start 前的节点情况
for startPoint.pre.score >= startScore {
startPoint = startPoint.pre
}
// 检查 end 后的节点情况
for endPoint.next.score <= endScore {
endPoint = endPoint.next
}
// 删除节点
valuePoint = startPoint
startPoint.pre.next = endPoint.next
endPoint.next.pre = startPoint.pre
// 下一级处理
startPoint = startPoint.down
endPoint = endPoint.down
}
values := make([]interface{}, 0, 100)
for valuePoint != nil && valuePoint.score <= endScore {
values = append(values, valuePoint.val)
valuePoint = valuePoint.next
}
return values
}
// 查找节点位置
func (s *SkipList) searchNode(score float64) *skipListNode {
const (
exitStatus = iota
initStatus
downStatus
nextStatus
)
status := initStatus
p := s.head
for {
switch status {
case exitStatus:
return p
case initStatus:
if p.score == score || p.next.score > score {
status = downStatus
continue
}
status = nextStatus
case downStatus:
if p.down == nil {
status = exitStatus
continue
}
p = p.down
status = initStatus
case nextStatus:
// 不知名的 bug 保护一下
if p.next.score != math.MaxInt64 {
p = p.next
}
status = initStatus
}
}
}
// 在节点后插入新节点
func (s *SkipList) insertAfter(pNode *skipListNode, newNode *skipListNode) {
if pNode == nil || newNode == nil {
return
}
newNode.next = pNode.next
newNode.pre = pNode
pNode.next.pre = newNode
pNode.next = newNode
}
// 添加 一个新的 levels 层
func (s *SkipList) newLevels() {
newHead := &skipListNode{score: math.MinInt64}
newTail := &skipListNode{score: math.MaxInt64}
newHead.next = newTail
newTail.pre = newHead
s.head.up = newHead
newHead.down = s.head
s.tail.up = newTail
newTail.down = s.tail
s.head = newHead
s.tail = newTail
s.levels++
}
func (s *SkipList) debugPrint() {
mapScore := make(map[float64]int)
p := s.head
for p.down != nil {
p = p.down
}
index := 0
for p != nil {
mapScore[p.score] = index
p = p.next
index++
}
p = s.head
for i := 0; i < s.levels; i++ {
q := p
preIndex := 0
for q != nil {
s := q.score
if s == math.MinInt64 {
fmt.Printf("%s", "BEGIN")
q = q.next
continue
}
index := mapScore[s]
c := (index - preIndex - 1) * 6
for m := 0; m < c; m++ {
fmt.Print("-")
}
if s == math.MaxInt64 {
fmt.Printf("-->%s\n", "END")
} else {
fmt.Printf("-->%3d", int(s))
preIndex = index
}
q = q.next
}
p = p.down
}
}
func (s *SkipList) delSkipListNode(sKm ...*skipListNode) {
for _, f := range sKm {
for f != nil {
f.pre.next = f.next
f.next.pre = f.pre
f = f.up
}
}
}

View File

@ -0,0 +1,116 @@
package skiplist
import (
"fmt"
"math/rand"
"testing"
"time"
"gitee.com/wheat-os/wheatCache/pkg/logx"
"github.com/stretchr/testify/require"
)
// 时间测试
func TestSkiplist_InsertTimeB(t *testing.T) {
// 测试 达到 maxLevel = 18 时性能 OK
list := NewSkipList(18)
for i := 0; i < 100; i++ {
list.Insert(float64(i), i)
}
list.debugPrint()
}
func TestSkiplist_InsertTimeA(t *testing.T) {
// 测试 达到 maxLevel = 18 时性能 OK
list := NewSkipList(20)
for i := 0; i < 200; i++ {
list.Insert(float64(rand.Intn(100)), i)
}
list.debugPrint()
start := time.Now()
list.Insert(7890, 1)
end := time.Now()
fmt.Println(end.UnixNano() - start.UnixNano())
}
func TestSkipList_Insert(t *testing.T) {
s := NewSkipList(18)
s.Insert(20, 1)
s.Insert(30, 2)
s.Insert(11, 3)
s.Insert(20, 11)
s.debugPrint()
val := s.Get(30)
require.Equal(t, 2, val)
val = s.Get(11)
require.Equal(t, 3, val)
require.Len(t, s.GetAll(20), 2)
s.RemoveAll(20)
require.Equal(t, s.GetAll(20), []interface{}{})
}
func Test_skipList_ClearLeft(t *testing.T) {
s := NewSkipList(18)
for i := 0; i < 100; i++ {
s.Insert(float64(rand.Intn(100)), i)
}
val := s.PopLeft(50)
logx.Debug("val:%v", val)
s.debugPrint()
require.Equal(t, s.Get(20), nil)
}
func TestSkipList_PopRangeByScore(t *testing.T) {
startNum := 5
endNum := 79
result := make([]interface{}, 0)
for i := startNum; i <= endNum; i++ {
result = append(result, i)
}
for i := 0; i < 100; i++ {
s := NewSkipList(14)
for i := 0; i < 100; i++ {
s.Insert(float64(i), i)
}
res := s.PopRangeByScore(float64(startNum), float64(endNum))
require.Equal(t, res, result)
}
}
func TestSkipList_PopRight(t *testing.T) {
score := 33
result := make([]interface{}, 0)
for i := score; i < 100; i++ {
result = append(result, i)
}
for i := 0; i < 100; i++ {
s := NewSkipList(14)
for i := 0; i < 100; i++ {
s.Insert(float64(i), i)
}
res := s.PopRight(float64(score))
require.Equal(t, res, result)
s.debugPrint()
}
}
func Test_skipList_InsertTime(t *testing.T) {
now := time.Now()
s := NewSkipList(18)
for i := 0; i < 20; i++ {
s.Insert(float64(now.UnixMicro()), now)
}
s.debugPrint()
}

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