Compare commits
450 Commits
cache-midd
...
master
Author | SHA1 | Date |
---|---|---|
innov | a1fa8b22de | |
bandl | a9fac2572b | |
bandl | 57caa66ef8 | |
bandl | d254a71560 | |
bandl | 07dd0f8874 | |
bandl | dafffdde2c | |
bandl | c6b14ced27 | |
bandl | b87799a88e | |
bandl | dcfae2353b | |
bandl | bf0c03a77f | |
bandl | a57eab1184 | |
bandl | ef4cbb0cee | |
bandl | f62a59b551 | |
bandl | a949ab2edc | |
bandl | 7ac9651ef3 | |
bandl | 19e1bb59a3 | |
bandl | c381c57374 | |
bandl | 3f337dba9b | |
bandl | a37097b3dd | |
bandl | d90c05296a | |
bandl | 2a556a9db6 | |
bandl | c0da22ef6f | |
bandl | cf4b24ea86 | |
bandl | 953daca82c | |
Pyroo | 5974dadd3e | |
bandl | 1baac521fa | |
Pyroo | d6792444a7 | |
bandl | 64a02b642e | |
bandl | a5ef559e14 | |
bandl | 0c8a17b21d | |
bandl | 0207878507 | |
bandl | 813b48b650 | |
bandl | cdd26736a5 | |
bandl | 40afd8057d | |
bandl | 08bc1892df | |
bandl | 5e89f37e40 | |
bandl | 6b2f3fb3be | |
bandl | 499322c080 | |
bandl | e5f217b3b0 | |
bandl | 113185d17c | |
bandl | 981a3341ef | |
bandl | af6426f83b | |
bandl | d68209c94c | |
bandl | 60cf8f5eb1 | |
bandl | 594f8acf32 | |
bandl | 366793d955 | |
bandl | 5105a62bca | |
bandl | 400d620aa4 | |
bandl | 25be4a1509 | |
bandl | 60de1a6ae9 | |
bandl | 55204575d7 | |
bandl | 2f81eab981 | |
bandl | 3eb515325d | |
bandl | 3f3b208db1 | |
bandl | 8b8fd58c09 | |
bandl | 62d3105273 | |
bandl | 51bd9b242f | |
bandl | f6eaae71b9 | |
bandl | 921078c61d | |
bandl | c8ef4c6e46 | |
bandl | c6273d084e | |
bandl | fd605d125f | |
bandl | c9dcb41cad | |
bandl | f7bb695e82 | |
bandl | 4e6c2ed22a | |
bandl | 0c08e40667 | |
bandl | 7fa66abbf0 | |
bandl | a910e4af21 | |
bandl | 51914aea8a | |
bandl | bbfd32cebc | |
bandl | 44bd5bc7cd | |
bandl | fcdb2310fe | |
bandl | 222bcc4eea | |
bandl | c3cff03b2b | |
bandl | 06baf3993d | |
bandl | 976215ee10 | |
bandl | 778d5152c8 | |
bandl | 5ce350f115 | |
bandl | 047c910278 | |
bandl | dcada2f2fe | |
bandl | 88774daede | |
bandl | 344c136586 | |
bandl | 4c9ab82123 | |
bandl | 2ed75832ba | |
bandl | 0f1142d434 | |
bandl | b6dedfa384 | |
bandl | 0f2b73e932 | |
bandl | 2f46dfaa2e | |
bandl | c867665b60 | |
bandl | b91966709b | |
bandl | 47cdf0859b | |
bandl | 0b9230094a | |
bandl | eccfd6a439 | |
bandl | f5c3b0e1db | |
bandl | 5ee502021d | |
bandl | 9fed552380 | |
bandl | 7d9081ce8e | |
bandl | 4ca09febb4 | |
bandl | be528cbb6d | |
bandl | 70b23849bc | |
bandl | 99133bf7ea | |
bandl | 1ca43cde6c | |
bandl | de59f13234 | |
bandl | 72add38cee | |
bandl | 3b61f55fe3 | |
bandl | 6fc2417de2 | |
bandl | 7aa39979f0 | |
bandl | 4ffa91ac6a | |
bandl | 170ba44d2d | |
bandl | ce888a1d0e | |
bandl | 787003d95a | |
bandl | 27542cf898 | |
bandl | 3bcd154177 | |
bandl | bed3f1893a | |
bandl | 200623bd29 | |
bandl | dce8739514 | |
bandl | 686c032f34 | |
bandl | 41e282de5d | |
bandl | 23b13afc86 | |
bandl | b4c3cc5a86 | |
bandl | 54520cb033 | |
bandl | c67e0c863c | |
bandl | ea8e10fcbf | |
bandl | 19a0259f58 | |
bandl | 511a66bd98 | |
bandl | 5642a00f61 | |
bandl | 1f85847243 | |
bandl | dab21a4ebc | |
bandl | 14c52da1ed | |
bandl | 5f188eaddf | |
bandl | bbc4c27027 | |
bandl | b6348f5992 | |
bandl | 404dc1fbbc | |
bandl | db615609cd | |
bandl | 46b029b339 | |
bandl | ac4fdd7309 | |
bandl | bff937700e | |
bandl | 2a69f393e5 | |
bandl | 417ddf1ccf | |
bandl | d78cb874ed | |
bandl | 06042b778c | |
bandl | 19562221f9 | |
bandl | 5d7df024e4 | |
bandl | 9298b77c7c | |
bandl | 3f4607fb58 | |
bandl | 09730e9ebf | |
bandl | cd1ba29f42 | |
bandl | cf9dc1da38 | |
bandl | fc60b2a779 | |
bandl | e81124c7e6 | |
bandl | ebfa948421 | |
bandl | b32606ee15 | |
bandl | 3c8d4fd89b | |
bandl | 9a1f1fe0ee | |
bandl | e894734418 | |
bandl | e404f670a0 | |
bandl | 4459beba61 | |
bandl | ab277fb66e | |
bandl | 0b781a178e | |
bandl | e96f21399c | |
bandl | 6a63bdca84 | |
bandl | f15792032a | |
bandl | 7ce1a55d0e | |
bandl | 4f6fb64ecc | |
bandl | 74e42e78ac | |
bandl | 8883abd461 | |
bandl | 8c5c594ac5 | |
bandl | 77988052ee | |
bandl | e6eb86de05 | |
bandl | f022ae9cbe | |
bandl | d42b01ff77 | |
bandl | a9dbb5169c | |
bandl | 15e1bb06af | |
bandl | 41ef546d95 | |
bandl | 70a246f84c | |
bandl | 2295fbcf40 | |
bandl | 3a4fc48f46 | |
bandl | 5b0a636e02 | |
bandl | fe096c9054 | |
bandl | a374638758 | |
bandl | 56d69f63af | |
bandl | aed507fc9a | |
bandl | 739cb56243 | |
bandl | 1a884ce9f0 | |
bandl | 576a7bbd41 | |
bandl | 623373f7ac | |
bandl | 4413ee6f92 | |
bandl | 4a887b0e58 | |
bandl | 74a85e518d | |
bandl | cb1e555986 | |
bandl | 780cf7d276 | |
bandl | e94a41ec73 | |
bandl | 6ef4654c63 | |
bandl | 6bd16eec06 | |
bandl | 21cac4639c | |
bandl | d17f8e243c | |
bandl | 95d3fdbd17 | |
bandl | ac8f748356 | |
bandl | 064ef3e50e | |
bandl | eb0014437b | |
bandl | 310e11c65b | |
bandl | 019322eb8f | |
bandl | a00f10c70e | |
bandl | 3c68787ff4 | |
bandl | 57e84b6dee | |
bandl | 5fe201ca32 | |
bandl | aade8e8edb | |
bandl | 6fdf250833 | |
bandl | d963c06576 | |
bandl | d6f1631d7b | |
bandl | 477613f0de | |
bandl | bd0e598f1a | |
bandl | 98787c8f03 | |
bandl | 1e4d611c77 | |
bandl | b97edc4cf1 | |
bandl | 91d93bf5be | |
bandl | ebdc5bd5b0 | |
bandl | 43392236b6 | |
bandl | 52c9b166f3 | |
bandl | 40374333fa | |
bandl | 3adeac539b | |
bandl | ec8a126581 | |
bandl | 4e78d08e2c | |
bandl | 2c854deb52 | |
bandl | 62eaa8edd0 | |
bandl | cbf7ba1933 | |
bandl | 32936576d3 | |
bandl | 57a655a708 | |
bandl | 554f7feef9 | |
bandl | 101059126c | |
bandl | ccf7179713 | |
bandl | 5934e88e03 | |
bandl | f7d4cee102 | |
bandl | ef5c088a49 | |
bandl | c8f23d1357 | |
bandl | 85e9a3b5f8 | |
bandl | 02bd151381 | |
bandl | 758f1bfec2 | |
bandl | e3c7546023 | |
bandl | b61794d74b | |
bandl | 5776e0ba6e | |
bandl | c4161e77d0 | |
bandl | e826ff4569 | |
K-on | d2866256dd | |
K-on | 5459c7710c | |
HuangJiaLuo | aebab3b3d7 | |
HuangJiaLuo | b39899e5d6 | |
HuangJiaLuo | 922fae0f78 | |
Sodesnei | 44dbb04f0b | |
Sodesnei | e2f00fa2ae | |
Sodesnei | ec8cea59a4 | |
bandl | ab257b2009 | |
黎白南 | e5ac4d1cf9 | |
黎白南 | 51517da519 | |
HuangJiaLuo | 73e91b3ff0 | |
HuangJiaLuo | dcce00a32d | |
HuangJiaLuo | 2500251699 | |
HuangJiaLuo | 29b9c8f3b6 | |
HuangJiaLuo | f119557eec | |
HuangJiaLuo | 5275be2b8e | |
HuangJiaLuo | 83bf49ec17 | |
HuangJiaLuo | cf1c90442b | |
Sodesnei | aac12f93f4 | |
Sodesnei | debde8a66a | |
Sodesnei | 95f165fb55 | |
Sodesnei | febd5501c4 | |
HuangJiaLuo | 8a6db79fd6 | |
HuangJiaLuo | 64b38044c8 | |
HuangJiaLuo | 843fcd27ca | |
HuangJiaLuo | a251cdb844 | |
Sodesnei | 4d9558c21f | |
Sodesnei | b182d7602d | |
Sodesnei | 99c3c56a5b | |
Sodesnei | 81d3bb8434 | |
Sodesnei | ce8f72040b | |
Sodesnei | 329513bd98 | |
Sodesnei | 2e04517066 | |
HuangJiaLuo | f1d2cbb0ee | |
HuangJiaLuo | c45c2682e0 | |
HuangJiaLuo | 929f931cd6 | |
黎白南 | b168eecbb4 | |
黎白南 | e98731c1b3 | |
黎白南 | 77d4b88c71 | |
Sodesnei | 1540651864 | |
Sodesnei | 166d1f20c1 | |
bandl | 96c8a6d53e | |
HuangJiaLuo | 83febdd482 | |
HuangJiaLuo | e3171a8b3e | |
bandl | 60abd336b1 | |
HuangJiaLuo | 428a8fa8f8 | |
HuangJiaLuo | 97833ed150 | |
HuangJiaLuo | f17cab2016 | |
HuangJiaLuo | d4d93a0e51 | |
HuangJiaLuo | f9ad4d914e | |
HuangJiaLuo | d08e50f4ce | |
HuangJiaLuo | 90b021bee2 | |
bandl | 63bd44da44 | |
黎白南 | d8dc56d95a | |
bandl | c36659da26 | |
黎白南 | 87ada5f981 | |
黎白南 | 880aba64d0 | |
黎白南 | 34dd0fe94f | |
HuangJiaLuo | 36e8efd682 | |
黎白南 | 692ad28380 | |
黎白南 | 0e2db14d32 | |
黎白南 | 5061b2e3da | |
黎白南 | b75aa1d141 | |
黎白南 | a706d57370 | |
bandl | 5292163b14 | |
bandl | c9cfa04725 | |
bandl | 4afe3dabb6 | |
bandl | 212e025b23 | |
bandl | 125b137ef6 | |
bandl | 67da63ac03 | |
bandl | 74ab13c756 | |
bandl | 26226eca28 | |
bandl | 1de8e1b141 | |
bandl | 8bcac6f739 | |
bandl | e6987546ae | |
bandl | 8e6f18f887 | |
bandl | 30c34cf30f | |
bandl | c27328ced4 | |
Sodesnei | 7c6c26c5fa | |
Sodesnei | 49496cf63c | |
Sodesnei | bd804e7105 | |
Sodesnei | 1c9c437a0f | |
Sodesnei | 0bdbe06376 | |
Sodesnei | ea36d370a7 | |
Sodesnei | 373de8b282 | |
K-on | 15cede5780 | |
HuangJiaLuo | e637cccaed | |
bandl | f7508d67a3 | |
HuangJiaLuo | b95fb85ad6 | |
HuangJiaLuo | 1774c9f230 | |
HuangJiaLuo | 477adec1bb | |
HuangJiaLuo | ca04ba1686 | |
HuangJiaLuo | 71ddeb4772 | |
HuangJiaLuo | 75e1251d22 | |
HuangJiaLuo | dbc0467de4 | |
HuangJiaLuo | 29c5aca40d | |
HuangJiaLuo | 99dd878bb3 | |
HuangJiaLuo | 96d5a9acc9 | |
HuangJiaLuo | 47a49b1fdb | |
HuangJiaLuo | 768f3df70e | |
bandl | a556840f9b | |
bandl | 2c11d500fd | |
bandl | a323f7e8a9 | |
bandl | 5888e2e15a | |
bandl | 57bdec3c1d | |
bandl | ae5a0531d8 | |
bandl | 04cb2a1ed1 | |
Sodesnei | 2c6a6a6848 | |
Sodesnei | 488d84580e | |
bandl | 26f3f9f663 | |
Sodesnei | 82e358e268 | |
bandl | c11d10d6e2 | |
bandl | 600ecb644e | |
bandl | 187280bc2c | |
bandl | ad0cdedba9 | |
bandl | 0903477c22 | |
bandl | 0677c56492 | |
bandl | f53eea9c22 | |
bandl | 0b8e2a104f | |
bandl | 748f61830b | |
bandl | 05bd410900 | |
bandl | d4a73b93d3 | |
bandl | 70319ae2b1 | |
Sodesnei | ea9a660740 | |
bandl | 0458ed3d21 | |
bandl | f4327aa5b6 | |
bandl | 8dd5879b2a | |
bandl | 060c1d2bfb | |
bandl | 8d663fe920 | |
bandl | a8bbbc70a4 | |
bandl | 175bddf527 | |
bandl | eb53a976e2 | |
bandl | bcacc83df6 | |
bandl | 3d7df7149a | |
bandl | ec9d3a1a71 | |
bandl | f0835b3344 | |
bandl | fc47f3eca0 | |
bandl | 98ef750a67 | |
bandl | d1113821c9 | |
bandl | 6d26969cae | |
bandl | 36d18c1368 | |
bandl | ea83b319de | |
bandl | 24d0ef2611 | |
HuangJiaLuo | 5d66e47e74 | |
bandl | 6637f4c77e | |
bandl | cc57179c11 | |
bandl | a9ab92c16a | |
bandl | ddd7a8a5ed | |
bandl | e3acfcd0fe | |
bandl | 6eec180306 | |
bandl | 76c203c37a | |
yu_lang | f3e894a068 | |
yu_lang | 53fe26db48 | |
yu_lang | f410738b27 | |
bandl | b9060a64d5 | |
yu_lang | cf4b5cd63c | |
yu_lang | 8bc30d3b85 | |
bandl | 2e9216d894 | |
HuangJiaLuo | 77cab55402 | |
bandl | 23baebe2d7 | |
HuangJiaLuo | 09b8907085 | |
bandl | b03d096b53 | |
bandl | 20b2272e7c | |
bandl | 4b27125092 | |
bandl | 63d6f66770 | |
yu_lang | 1de6231a17 | |
yu_lang | 27fbb69034 | |
HuangJiaLuo | d9a9da6dee | |
bandl | fa2c67d140 | |
bandl | 81f85009ab | |
bandl | c1ca0a499f | |
yu_lang | 4653e6f27c | |
bandl | 8714fc8b8f | |
bandl | 9b9ece8f54 | |
bandl | 9cbd06227d | |
bandl | 7837edb92e | |
bandl | f910e2c503 | |
bandl | 4405512540 | |
bandl | 221dbdd6d4 | |
bandl | b902c8cc1f | |
bandl | 40dd0dd91a | |
bandl | 414cd6d9a7 | |
bandl | c84873208b | |
bandl | 8cfed8b325 | |
bandl | aadb43cfd9 | |
bandl | 6b80b30f31 | |
bandl | 0b88178df0 | |
bandl | 136fc48206 | |
bandl | fd80e3ddde | |
bandl | 6817eccff6 | |
bandl | 30e182d018 | |
bandl | 8c114bcb9a | |
bandl | 6453b2a0ce | |
bandl | a5a01b8e33 | |
bandl | 13a3291e9e | |
bandl | 781fc4b6b7 | |
bandl | 087bec4861 | |
yu_lang | c321d6865d | |
yu_lang | 78ee204877 | |
yu_lang | 9fd9be29a3 | |
bandl | f768f361c1 | |
bandl | c699fa0527 | |
bandl | 2c9e6784b1 | |
bandl | 9262c6e8c5 | |
bandl | 7e8108bc73 | |
bandl | 2c0eeb89df |
|
@ -7,7 +7,7 @@
|
|||
### 描述(做了什么,变更了什么)
|
||||
|
||||
|
||||
### 测试用例(新增、改动、可能影响的功能)
|
||||
### 影响到的模块
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -5,8 +5,17 @@
|
|||
*.so
|
||||
*.dylib
|
||||
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
|
||||
# build file
|
||||
/bin/storage
|
||||
/bin/gateway
|
|
@ -1,8 +0,0 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/wheat-cache.iml" filepath="$PROJECT_DIR$/.idea/wheat-cache.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,7 @@
|
|||
FROM ubuntu:18.04
|
||||
|
||||
WORKDIR /home/src/gitee.com/wheat-os/wheat-cache
|
||||
ADD . /home/src/gitee.com/wheat-os/wheat-cache
|
||||
|
||||
RUN mkdir /etc/wheat-cache
|
||||
RUN mv /home/src/gitee.com/wheat-os/wheat-cache/conf/wheat-cache.yaml /etc/wheat-cache/
|
Binary file not shown.
|
@ -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...)
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
|
@ -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...)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package conf
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
linuxPath = "/etc/wheat-cache/"
|
||||
)
|
||||
|
||||
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 {
|
||||
if path != "" {
|
||||
viper.AddConfigPath(path)
|
||||
}
|
||||
viper.SetConfigName("wheat-cache")
|
||||
|
||||
// 添加默认读取地址
|
||||
// linux
|
||||
viper.AddConfigPath(linuxPath)
|
||||
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
err := viper.ReadInConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package conf
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConf(t *testing.T) {
|
||||
|
||||
// 外部导入 conf.yaml 需要导入 conf 包
|
||||
// 每次迁移文件时, 使用 sudo make init-conf来将yam文件迁移到指定的文件夹下
|
||||
// get 使用, 读取 public_conf 配置文件
|
||||
h := viper.Get("storage.host")
|
||||
require.Equal(t, h, "127.0.0.1")
|
||||
|
||||
h = viper.Get("env")
|
||||
require.Equal(t, h, "dev")
|
||||
|
||||
// set 使用
|
||||
viper.Set("host", "1222")
|
||||
host := viper.GetString("host")
|
||||
require.Equal(t, host, "1222")
|
||||
}
|
||||
|
||||
func TestMiddleConf(t *testing.T) {
|
||||
ct := viper.GetStringSlice("plugins-control.logcontext")
|
||||
require.Equal(t, ct, []string{"logMiddle"})
|
||||
|
||||
d := viper.GetInt("middleware-driver.driverCount")
|
||||
require.Equal(t, d, 1000)
|
||||
c := viper.GetInt("middleware-driver.middleConsumerCount")
|
||||
require.Equal(t, c, 5)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
version: 'v1.0'
|
||||
|
||||
env: 'dev'
|
||||
|
||||
storage:
|
||||
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"
|
||||
maxSize: "1GB"
|
||||
eventDriverSize: 2000
|
||||
workTime: 1
|
||||
detachNum: 300
|
||||
|
||||
|
||||
logPrint:
|
||||
stath: ["error"]
|
||||
|
||||
|
||||
middleware-driver:
|
||||
driverCount: 1000
|
||||
middleConsumerCount: 5
|
||||
|
||||
# Register the message push type
|
||||
# 在这里注册消息推送类型,
|
||||
plugins-control:
|
||||
|
||||
# log-context: Logs generated by storage or gateway are pushed through this message
|
||||
# log-context: storage 或者 gateway 产生的日志通过这个消息推送
|
||||
log-context: [ "mock-plugins" ]
|
||||
|
||||
# lru-clean-context: Lru is pushed through this message when data cleansing occurs
|
||||
# lru-clean-context: Lru 发生数据清理时通过这个消息推送
|
||||
lru-clean-context: ["mock-plugins"]
|
||||
|
||||
# lru-ttl-context: Lru is pushed through this message when data expires
|
||||
# lru-ttl-context: Lru 发生数据过期时通过这个消息推送
|
||||
lru-ttl-context: ["mock-plugins"]
|
||||
|
||||
# plugins-info-context:All 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"
|
|
@ -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 |
|
@ -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 |
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 |
|
@ -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 |
|
@ -0,0 +1,11 @@
|
|||
### Cache 分布式方案-Getway
|
||||
|
||||
|
||||
|
||||
![getway方案](https://gitee.com/timedb/img/raw/master/images/getway方案.svg)
|
||||
|
||||
|
||||
|
||||
1. single 集群分布式方案中,使用 getway 方向代理客户端的 grpc 请求, 通过 hash 环实现 分布式。
|
||||
2. 集群模式中, 通过主从来 实现 cache 的备份问题,提高容灾性。
|
||||
|
|
@ -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
|
|
@ -0,0 +1,13 @@
|
|||
### 事件驱动 2.0
|
||||
|
||||
### event 1.0 存在的问题
|
||||
事件驱动 1.0 在 相互关联访问时,会发生 死锁问题, 导致一个事件执行周期失败
|
||||
|
||||
### event 2.0 新特性
|
||||
- 异步事件支持
|
||||
- 挂起操作
|
||||
|
||||
|
||||
### event 2.0 设计图
|
||||
![](../../_icon/event.svg)
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
### 事件驱动使用文档
|
||||
|
||||
|
||||
|
||||
- 包目录: pkg/event
|
||||
- 使用场景, 异步推送数据
|
||||
|
||||
|
||||
|
||||
#### 事件驱动基本概念
|
||||
|
||||
- event
|
||||
|
||||
event 是事件驱动的事件部分, 主要负责一些信息的传递, 一共分为,有等待和无等待的事件, 无等待事件 consumer 消费事件后,不会给 produce 回复, 有等待事件, 支持事件消费后, 向 produce 发送一个回复。
|
||||
|
||||
- driver
|
||||
|
||||
driver 是事件的驱动, 主要复制连接 produce, 以及 consumer, 其中维护了一个 event 的队列。
|
||||
|
||||
- produce
|
||||
|
||||
produce 主要负责 推送 事件, 每个 produce 都需要维护一个 事件驱动通过 driver 来进行数据的推送。
|
||||
|
||||
- consumer
|
||||
|
||||
consumer 负责接收事件, 每个 consumer 都需要注册一个 事件驱动 来获取 event。
|
||||
|
||||
####
|
||||
|
||||
#### 应用场景
|
||||
|
||||
事件驱动主要用于 异步消息的推送(目前的实现也支持 同步的消息实现), 一般来说 produce 发送 event 后 event 被消费过程会完全于 produce 无关。
|
||||
|
||||
|
||||
|
||||
#### 无返回 event 使用例子
|
||||
|
||||
我们使用 借书为例子使用, 假设 A 要把 书 S 交还给 B, 他们约定, A 把书 S 放到图书馆, B 在空闲时去图书馆取出书 S。
|
||||
|
||||
```go
|
||||
func TestNewConsumer(t *testing.T) {
|
||||
// 定义一个图书馆
|
||||
type Library struct {
|
||||
driver DriverInterface
|
||||
}
|
||||
library := &Library{
|
||||
driver: NewDriver(100),
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
// 定义 A
|
||||
type A struct {
|
||||
produce ProduceInterface
|
||||
}
|
||||
|
||||
a := &A{
|
||||
produce: NewProduce(library.driver),
|
||||
}
|
||||
|
||||
// 定义 B
|
||||
type B struct {
|
||||
consumer ConsumerInterface
|
||||
}
|
||||
|
||||
b := &B{
|
||||
consumer: NewConsumer(library.driver),
|
||||
}
|
||||
|
||||
// 定义书 S 并且添加一些描述
|
||||
book := NewEvent("S")
|
||||
book.SetMsg("title", "hello world")
|
||||
book.SetCtxValue("pages", 120)
|
||||
|
||||
// A 把书 S 放到图书馆
|
||||
go func() {
|
||||
a.produce.Call(ctx, book)
|
||||
}()
|
||||
|
||||
// 模拟 B 去图书馆拿书
|
||||
book = b.consumer.Receive(ctx)
|
||||
fmt.Println(book.GetMsg("title"))
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 有返回event的使用例子
|
||||
|
||||
在上述流程的情景下, 我们添加一些条件, B 拿到书以后检查书是否损坏,如果损坏, 需要告诉 A 书损坏情况。
|
||||
|
||||
```go
|
||||
func TestNewConsumer(t *testing.T) {
|
||||
// 定义一个图书馆
|
||||
type Library struct {
|
||||
driver DriverInterface
|
||||
}
|
||||
library := &Library{
|
||||
driver: NewDriver(100),
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
// 定义 A
|
||||
type A struct {
|
||||
produce ProduceInterface
|
||||
}
|
||||
|
||||
a := &A{
|
||||
produce: NewProduce(library.driver),
|
||||
}
|
||||
|
||||
// 定义 B
|
||||
type B struct {
|
||||
consumer ConsumerInterface
|
||||
}
|
||||
|
||||
b := &B{
|
||||
consumer: NewConsumer(library.driver),
|
||||
}
|
||||
|
||||
// 定义书 S 并且添加一些描述
|
||||
book := NewEvent("S")
|
||||
book.SetMsg("title", "hello world")
|
||||
book.SetValue("pages", 120)
|
||||
|
||||
// A 把书 S 放到图书馆
|
||||
go func() {
|
||||
book.InitWaitEvent()
|
||||
a.produce.Call(ctx, book)
|
||||
|
||||
// A 等待 B 的回复, 但是他最多只会等待 B 2个小时
|
||||
res, err := book.StartWaitEvent(2 * time.Hour)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(res)
|
||||
}()
|
||||
|
||||
// 模拟 B 去图书馆拿书
|
||||
book = b.consumer.Receive(ctx)
|
||||
fmt.Println(book.GetMsg("title"))
|
||||
|
||||
// 书完好
|
||||
book.ExecWorkAndSendResult(func() (interface{}, error) {
|
||||
// b 检查书
|
||||
return "OK", nil
|
||||
})
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
![](https://gitee.com/HuangJiaLuo/imgbed/raw/master/LRUdesign.svg)
|
||||
|
||||
EventP : 生产事件
|
||||
|
||||
EventQ : 事件队列
|
||||
|
||||
Event CP : 清理事件
|
|
@ -0,0 +1,5 @@
|
|||
### RDB + AOF 的事务解决方案
|
||||
|
||||
|
||||
|
||||
![事务RDB方案](https://gitee.com/timedb/img/raw/master/images/事务RDB方案.svg)
|
|
@ -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"]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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 中获取。
|
|
@ -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 编译并且安装项目
|
|
@ -0,0 +1,6 @@
|
|||
### 前端技术选择
|
||||
- react框架
|
||||
- 组件(antd或React-bootstrap或其他css组件)
|
||||
- 图表库(BizCharts)
|
||||
- protobufjs
|
||||
- grafana
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
```
|
|
@ -0,0 +1,119 @@
|
|||
### 开发流程
|
||||
|
||||
|
||||
|
||||
- 拉取 master 到本地
|
||||
|
||||
```
|
||||
git fetch origin master && git rebase origin/master
|
||||
```
|
||||
|
||||
- 查看本地更改 commit
|
||||
```
|
||||
git log
|
||||
```
|
||||
|
||||
- 添加分支
|
||||
|
||||
```sh
|
||||
# name-(feat, fix, doc)-(mode)-(task)
|
||||
git checkout -b doc-pro-git-doc
|
||||
```
|
||||
|
||||
- do task
|
||||
|
||||
- 提交 检查
|
||||
|
||||
```
|
||||
git push origin doc-pro-git-doc
|
||||
```
|
||||
|
||||
- 监听文件
|
||||
|
||||
```
|
||||
git add .
|
||||
```
|
||||
|
||||
- 添加 commit
|
||||
|
||||
```
|
||||
git commit -m "init doc"
|
||||
```
|
||||
|
||||
>**commit message格式**
|
||||
>
|
||||
>```text
|
||||
><type>(<scope>): <subject>
|
||||
>```
|
||||
>
|
||||
>**type(必须)**
|
||||
>
|
||||
>用于说明git commit的类别,只允许使用下面的标识。
|
||||
>
|
||||
>feat:新功能(feature)。
|
||||
>
|
||||
>fix:修复bug,可以是QA发现的BUG,也可以是研发自己发现的BUG。
|
||||
>
|
||||
>doc:文档(documentation)。
|
||||
>
|
||||
>style:格式(不影响代码运行的变动)。
|
||||
>
|
||||
>refactor:重构(即不是新增功能,也不是修改bug的代码变动)。
|
||||
>
|
||||
>perf:优化相关,比如提升性能、体验。
|
||||
>
|
||||
>test:增加测试。
|
||||
>
|
||||
>chore:构建过程或辅助工具的变动。
|
||||
>
|
||||
>revert:回滚到上一个版本。
|
||||
>
|
||||
>merge:代码合并。
|
||||
|
||||
|
||||
|
||||
- 提交PR (remove)
|
||||
|
||||
```
|
||||
添加描述, 提交PR
|
||||
```
|
||||
|
||||
- 解决问题
|
||||
|
||||
- 整理 commit
|
||||
|
||||
- 整理
|
||||
|
||||
```shell
|
||||
git rebase -i origin/master
|
||||
f:
|
||||
# 回调
|
||||
git rebase --abort
|
||||
|
||||
# 继续
|
||||
|
||||
```
|
||||
|
||||
- 强行推送
|
||||
|
||||
```
|
||||
git push -f origin doc-pro-git-doc
|
||||
```
|
||||
|
||||
- 重做
|
||||
|
||||
```sh
|
||||
git log
|
||||
# 7e8108bc7347ef6e2f01d6bc1d1f249afc82a486
|
||||
|
||||
git reset 7e8108bc7347ef6e2f01d6bc1d1f249afc82a486
|
||||
```
|
||||
|
||||
- 合并冲突
|
||||
|
||||
```sh
|
||||
# B 分支名称
|
||||
git pull origin B
|
||||
```
|
||||
|
||||
-
|
|
@ -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...)
|
||||
}
|
|
@ -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{})
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package endpoint
|
||||
|
||||
type EndpointInterface interface {
|
||||
GetTargetAddr(...string) (string, error)
|
||||
IsEmpty() bool
|
||||
AddTarget(targets ...string)
|
||||
}
|
||||
|
||||
const (
|
||||
HashReplicasDefault = 3
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package main
|
||||
|
||||
import "gitee.com/wheat-os/wheatCache/gateway/cmd"
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
)
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
6
go.mod
6
go.mod
|
@ -1,8 +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.41.0
|
||||
google.golang.org/protobuf v1.26.0
|
||||
)
|
||||
|
|
13
go.sum
13
go.sum
|
@ -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=
|
||||
|
@ -103,6 +107,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
|||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
|
@ -116,6 +121,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
|
@ -250,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=
|
||||
|
@ -330,6 +337,7 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -462,6 +470,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
|
@ -532,6 +541,7 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
|
|||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
|
@ -553,6 +563,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
|
|||
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/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=
|
||||
|
@ -564,6 +576,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
|||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
|
|
58
makefile
58
makefile
|
@ -0,0 +1,58 @@
|
|||
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-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-storage
|
||||
@make build-gateway
|
||||
@sudo python3 ./shell/init_conf.py
|
||||
|
||||
.PHONY: storage
|
||||
storage:
|
||||
@./bin/storage storage
|
||||
|
||||
.PHONY: gateway
|
||||
gateway:
|
||||
@./bin/gateway gateway
|
||||
|
||||
.PHONY: gen-struct
|
||||
gen-struct:
|
||||
@python3 ./shell/make-struct.py
|
||||
|
||||
.PHONY: gen-protobuf
|
||||
gen-protobuf:
|
||||
@python3 ./shell/gen_protobuf.py
|
||||
|
||||
.PHONY: gen-middleware
|
||||
gen-middleware:
|
||||
@python3 ./shell/gen_middleware.py
|
||||
|
||||
.PHONY: init-conf
|
||||
init-conf:
|
||||
@python3 ./shell/init_conf.py
|
||||
|
||||
|
||||
.PHONY: gen-service
|
||||
gen-service:
|
||||
@python3 ./shell/make_service.py
|
||||
|
||||
.PHONY: gen-mock
|
||||
gen-mock:
|
||||
@mockgen -source=./pkg/proto/storage.pb.go CommServerClient > ./mock/storage/mock_client.gen.go
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
package errorx
|
||||
|
||||
func DaoTypeErr(typ string) error {
|
||||
return New("the type is not: %s", typ)
|
||||
}
|
||||
|
||||
func NotKeyErr(key string) error {
|
||||
return New("the key is not exist, key:%s", key)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package errorx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// New TODO 添加链路追踪等 @bandl @lgq
|
||||
func New(msg string, format ...interface{}) error {
|
||||
msg = fmt.Sprintf(msg, format...)
|
||||
return errors.New(msg)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package errorx
|
||||
|
||||
func EventRecoveryErr() error {
|
||||
return New("this event has been recycled")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package errorx
|
||||
|
||||
func TimeOutErr() error {
|
||||
return New("time out err")
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package event
|
||||
|
||||
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 (c *Consumer) Recovery(e *event) {
|
||||
c.driver.Recovery(e)
|
||||
}
|
||||
|
||||
func NewConsumer(driver DriverInterface) ConsumerInterface {
|
||||
return &Consumer{
|
||||
driver: driver,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package event
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultEventState = int32(iota) //默认情况下的状态
|
||||
waitEventState // 等待状态
|
||||
workEventState //工作状态
|
||||
closeEventState //事件关闭状态
|
||||
)
|
||||
|
||||
type EventWorkFunc func() (interface{}, error)
|
||||
|
||||
type DriverInterface interface {
|
||||
Get() *event
|
||||
Put(*event)
|
||||
GetLength() int
|
||||
NewEvent(string) *event
|
||||
Recovery(*event)
|
||||
}
|
||||
|
||||
type ProduceInterface interface {
|
||||
Call(context.Context, *event)
|
||||
NewEvent(string) *event
|
||||
Recovery(*event)
|
||||
}
|
||||
|
||||
type ConsumerInterface interface {
|
||||
Receive(ctx context.Context) *event
|
||||
Recovery(*event)
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
package event
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"gitee.com/wheat-os/wheatCache/pkg/errorx"
|
||||
)
|
||||
|
||||
// 事件 poll 降低 new 对象的频率
|
||||
type eventPoll struct {
|
||||
poll chan *event
|
||||
maxSize int32
|
||||
nowSize *int32
|
||||
}
|
||||
|
||||
func (e *eventPoll) getEvent() *event {
|
||||
issSize := atomic.LoadInt32(e.nowSize)
|
||||
if issSize < e.maxSize {
|
||||
atomic.AddInt32(e.nowSize, 1)
|
||||
return newEvent()
|
||||
}
|
||||
|
||||
return <-e.poll
|
||||
}
|
||||
|
||||
func (e *eventPoll) recovery(rEvent *event) {
|
||||
rEvent.Reset()
|
||||
e.poll <- rEvent
|
||||
}
|
||||
|
||||
func newEventPoll(maxSize int) *eventPoll {
|
||||
return &eventPoll{
|
||||
poll: make(chan *event, maxSize),
|
||||
maxSize: int32(maxSize),
|
||||
nowSize: new(int32),
|
||||
}
|
||||
}
|
||||
|
||||
type event struct {
|
||||
msgCtx map[string]interface{}
|
||||
eventName string
|
||||
msg map[string]string // 消息
|
||||
waitResult chan interface{} // 等待返回
|
||||
err error
|
||||
eventStatus *int32
|
||||
ttlManage *time.Timer
|
||||
}
|
||||
|
||||
func newEvent() *event {
|
||||
status := defaultEventState
|
||||
return &event{
|
||||
eventStatus: &status,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *event) Reset() {
|
||||
if e.ttlManage != nil {
|
||||
e.ttlManage.Stop()
|
||||
}
|
||||
|
||||
e.err = nil
|
||||
|
||||
atomic.SwapInt32(e.eventStatus, defaultEventState)
|
||||
}
|
||||
|
||||
func (e *event) SetMsg(key string, val string) {
|
||||
if e.msg == nil {
|
||||
e.msg = make(map[string]string)
|
||||
}
|
||||
e.msg[key] = val
|
||||
}
|
||||
|
||||
func (e *event) GetMsg(key string) string {
|
||||
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) {
|
||||
val, ok := e.msgCtx[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// InitWaitEvent 初始化 wait event 必须调用才拥有等待特性
|
||||
func (e *event) InitWaitEvent() {
|
||||
if e.waitResult == nil || len(e.waitResult) > 0 {
|
||||
e.waitResult = make(chan interface{})
|
||||
}
|
||||
|
||||
// 清理残留
|
||||
if e.ttlManage == nil {
|
||||
e.ttlManage = time.NewTimer(0)
|
||||
}
|
||||
e.ttlManage.Stop()
|
||||
if len(e.ttlManage.C) > 0 {
|
||||
<-e.ttlManage.C
|
||||
}
|
||||
|
||||
atomic.CompareAndSwapInt32(e.eventStatus, defaultEventState, waitEventState)
|
||||
}
|
||||
|
||||
// StartWaitEvent 开始一个等待任务
|
||||
func (e *event) StartWaitEvent(ttl time.Duration) (interface{}, error) {
|
||||
e.ttlManage.Reset(ttl)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-e.ttlManage.C:
|
||||
if atomic.CompareAndSwapInt32(e.eventStatus, waitEventState, closeEventState) {
|
||||
return nil, errorx.TimeOutErr()
|
||||
}
|
||||
continue
|
||||
|
||||
case result := <-e.waitResult:
|
||||
atomic.CompareAndSwapInt32(e.eventStatus, workEventState, closeEventState)
|
||||
return result, e.err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *event) ExecWorkAndSendResult(work EventWorkFunc) (interface{}, error) {
|
||||
if !atomic.CompareAndSwapInt32(e.eventStatus, waitEventState, workEventState) {
|
||||
return nil, errorx.New("not wait status, exec err")
|
||||
}
|
||||
|
||||
res, err := work()
|
||||
e.err = err
|
||||
e.waitResult <- res
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (e *event) SetResultErr(err error) {
|
||||
if !atomic.CompareAndSwapInt32(e.eventStatus, waitEventState, workEventState) {
|
||||
return
|
||||
}
|
||||
|
||||
e.err = err
|
||||
e.waitResult <- nil
|
||||
}
|
||||
|
||||
type Driver struct {
|
||||
maxQueueSize int
|
||||
queue chan *event
|
||||
poll *eventPoll
|
||||
}
|
||||
|
||||
// Get 获取驱动
|
||||
func (d *Driver) Get() *event {
|
||||
return <-d.queue
|
||||
}
|
||||
|
||||
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),
|
||||
poll: newEventPoll(maxSize),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package event
|
||||
|
||||
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"))
|
||||
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)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package event
|
||||
|
||||
import "context"
|
||||
|
||||
type Produce struct {
|
||||
driver DriverInterface
|
||||
}
|
||||
|
||||
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 {
|
||||
return &Produce{
|
||||
driver: driver,
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package define
|
|
@ -0,0 +1,43 @@
|
|||
package logx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"gitee.com/wheat-os/wheatCache/pkg/event"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type LogLevelState int8
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
stath []string
|
||||
)
|
||||
|
||||
type upLogger struct {
|
||||
ctx context.Context
|
||||
produce event.ProduceInterface
|
||||
}
|
||||
|
||||
func init() {
|
||||
once.Do(func() {
|
||||
stath = viper.GetStringSlice("logPrint.stath")
|
||||
})
|
||||
}
|
||||
|
||||
type logInterface interface {
|
||||
Debug(format string, msg ...interface{})
|
||||
Info(format string, msg ...interface{})
|
||||
Warn(format string, msg ...interface{})
|
||||
Error(format string, msg ...interface{})
|
||||
Panic(format string, msg ...interface{})
|
||||
|
||||
Debugln(msg ...interface{})
|
||||
Infoln(msg ...interface{})
|
||||
Warnln(msg ...interface{})
|
||||
Errorln(msg ...interface{})
|
||||
Panicln(msg ...interface{})
|
||||
|
||||
Print(level string, format string, msg ...interface{})
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package logx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitee.com/wheat-os/wheatCache/pkg/event"
|
||||
middleMsg "gitee.com/wheat-os/wheatCache/pkg/middle-msg"
|
||||
)
|
||||
|
||||
func With(ctx context.Context, p event.ProduceInterface) *upLogger {
|
||||
return &upLogger{
|
||||
ctx: ctx,
|
||||
produce: p,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *upLogger) Debug(format string, msg ...interface{}) {
|
||||
l.Print("DEBUG", format, msg...)
|
||||
}
|
||||
|
||||
func (l *upLogger) Info(format string, msg ...interface{}) {
|
||||
l.Print("INFO", format, msg...)
|
||||
}
|
||||
|
||||
func (l *upLogger) Warn(format string, msg ...interface{}) {
|
||||
l.Print("WARN", format, msg...)
|
||||
}
|
||||
|
||||
func (l *upLogger) Error(format string, msg ...interface{}) {
|
||||
l.Print("ERROR", format, msg...)
|
||||
}
|
||||
|
||||
func (l *upLogger) Panic(format string, msg ...interface{}) {
|
||||
l.Print("ERROR", format, msg...)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
func (l *upLogger) Print(level string, format string, msg ...interface{}) {
|
||||
logPrint(4, level, format, msg...)
|
||||
sendMsg := &middleMsg.LogContext{
|
||||
Level: level,
|
||||
Data: time.Now(),
|
||||
Msg: fmt.Sprintf(format, msg...),
|
||||
Route: findPlace(4),
|
||||
}
|
||||
middleMsg.SendMiddleMsg(l.ctx, l.produce, sendMsg)
|
||||
}
|
||||
|
||||
func (l *upLogger) Debugln(msg ...interface{}) {
|
||||
l.Print("DEBUG", "%s", format(msg...))
|
||||
}
|
||||
|
||||
func (l *upLogger) Infoln(msg ...interface{}) {
|
||||
l.Print("INFO", "%s", format(msg...))
|
||||
}
|
||||
|
||||
func (l *upLogger) Warnln(msg ...interface{}) {
|
||||
l.Print("WARN", "%s", format(msg...))
|
||||
}
|
||||
|
||||
func (l *upLogger) Errorln(msg ...interface{}) {
|
||||
l.Print("ERROR", "%s", format(msg...))
|
||||
}
|
||||
|
||||
func (l *upLogger) Panicln(msg ...interface{}) {
|
||||
l.Print("ERROR", "%s", format(msg...))
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
func Debug(format string, msg ...interface{}) {
|
||||
logPrint(3, "DEBUG", format, msg...)
|
||||
}
|
||||
func Info(format string, msg ...interface{}) {
|
||||
logPrint(3, "INFO", format, msg...)
|
||||
}
|
||||
func Warn(format string, msg ...interface{}) {
|
||||
logPrint(3, "WARN", format, msg...)
|
||||
}
|
||||
func Error(format string, msg ...interface{}) {
|
||||
logPrint(3, "ERROR", format, msg...)
|
||||
}
|
||||
func Panic(format string, msg ...interface{}) {
|
||||
logPrint(3, "PANIC", format, msg...)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
func Debugln(msg ...interface{}) {
|
||||
logPrint(3, "DEBUG", "%s", format(msg...))
|
||||
}
|
||||
func Infoln(msg ...interface{}) {
|
||||
logPrint(3, "INFO", "%s", format(msg...))
|
||||
}
|
||||
func Warnln(msg ...interface{}) {
|
||||
logPrint(3, "WARN", "%s", format(msg...))
|
||||
}
|
||||
func Errorln(msg ...interface{}) {
|
||||
logPrint(3, "ERROR", "%s", format(msg...))
|
||||
}
|
||||
func Panicln(msg ...interface{}) {
|
||||
logPrint(3, "PANIC", "%s", format(msg...))
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
func logPrint(floor int, level string, format string, msg ...interface{}) {
|
||||
place := findPlace(floor)
|
||||
datetime := fmt.Sprintf("%s", time.Now())[0:19]
|
||||
|
||||
switch level {
|
||||
case "DEBUG":
|
||||
fmt.Printf("\033[1;37;40m")
|
||||
case "INFO":
|
||||
fmt.Printf("\033[1;32;40m")
|
||||
case "WARN":
|
||||
fmt.Printf("\033[1;33;40m")
|
||||
default:
|
||||
fmt.Printf("\033[1;31;40m")
|
||||
|
||||
}
|
||||
|
||||
//fmt.Println(level, datetime, fmt.Sprintf(format, msg...))
|
||||
fmt.Printf("%s\t%v\t%s\n", level, datetime, fmt.Sprintf(format, msg...))
|
||||
|
||||
for _, lv := range stath {
|
||||
if strings.ToUpper(lv) == strings.ToUpper(level) {
|
||||
fmt.Println(place)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findPlace(floor int) string {
|
||||
|
||||
var (
|
||||
place string
|
||||
i = floor
|
||||
)
|
||||
|
||||
for {
|
||||
_, file, line, _ := runtime.Caller(i)
|
||||
if line == 0 {
|
||||
break
|
||||
}
|
||||
i++
|
||||
place = fmt.Sprintf("%s:%d\n%s", file, line, place)
|
||||
}
|
||||
|
||||
return place
|
||||
}
|
||||
|
||||
func format(message ...interface{}) (context string) {
|
||||
for _, msg := range message {
|
||||
context = fmt.Sprintf("%s\t%v", context, msg)
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package logx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
_ "gitee.com/wheat-os/wheatCache/conf"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/event"
|
||||
)
|
||||
|
||||
func TestStd(t *testing.T) {
|
||||
Info("%d%s", 11, "Info")
|
||||
Debug("%d%s", 11, "Debug")
|
||||
Warn("%d%s", 11, "Warn")
|
||||
Error("%d%s", 11, "Error")
|
||||
|
||||
Infoln(1321, "dwad", 0x9812933)
|
||||
Debugln(1321, "dwad", 0x9812933)
|
||||
Warnln(1321, "dwad", 0x9812933)
|
||||
Errorln(1321, "dwad", 0x9812933)
|
||||
|
||||
//Panic("%d%s", 11, "Panic")
|
||||
|
||||
logger := With(context.Background(),
|
||||
event.NewProduce(event.NewDriver(100)))
|
||||
|
||||
logger.Info("%d%s", 11, "Info")
|
||||
logger.Debug("%d%s", 11, "Debug")
|
||||
logger.Warn("%d%s", 11, "Warn")
|
||||
logger.Error("%d%s", 11, "Error")
|
||||
//logger.Panic("%d%s", 11, "Panic")
|
||||
|
||||
logger.Infoln(11, "Info")
|
||||
logger.Debugln(11, "Debug")
|
||||
logger.Warnln(11, "Warn")
|
||||
logger.Errorln(11, "Error")
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package lru
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitee.com/wheat-os/wheatCache/pkg/proto"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/structure"
|
||||
)
|
||||
|
||||
type SingleWorkFunc func() interface{}
|
||||
|
||||
const (
|
||||
OptionEventName = "operateEvent"
|
||||
CleanEventName = "clearEvent"
|
||||
TtlEventName = "ttlEvent"
|
||||
)
|
||||
|
||||
var (
|
||||
lruCacheOnce sync.Once
|
||||
lruCache *SingleCache
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLruMaxSize = 1 * 1024 * 1024 * 1024
|
||||
defaultLruClearSize = 0.5 * 1024 * 1024 * 1024
|
||||
defaultLruEventDriver = 2000
|
||||
)
|
||||
const (
|
||||
defaultWaitTime = 20 * time.Minute
|
||||
)
|
||||
|
||||
type CacheInterface interface {
|
||||
Del() error
|
||||
Get(key *proto.BaseKey) (structure.KeyBaseInterface, bool)
|
||||
Add(key *proto.BaseKey, val structure.KeyBaseInterface) error
|
||||
UpdateLruSize(length structure.UpdateLength)
|
||||
DelByKey(key *proto.BaseKey) error
|
||||
DelToClearSize() error
|
||||
}
|
||||
|
||||
// TTL
|
||||
const (
|
||||
defaultDetachNum = 300
|
||||
defaultTtlMaxLevel = 18
|
||||
)
|
|
@ -1,3 +0,0 @@
|
|||
package define
|
||||
|
||||
// define
|
|
@ -0,0 +1,225 @@
|
|||
package lru
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"sync/atomic"
|
||||
|
||||
_ "gitee.com/wheat-os/wheatCache/conf"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/errorx"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/event"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/event2"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/middle"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/proto"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/structure"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/util"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type keyBaseValue struct {
|
||||
key string
|
||||
val structure.KeyBaseInterface
|
||||
expire int64 // 过期时间戳
|
||||
}
|
||||
|
||||
type SingleCache struct {
|
||||
maxsize int64 //最大的长度
|
||||
clearSize int64 // 清理长度
|
||||
nowSize int64 // 现在的长度
|
||||
li *list.List
|
||||
lruMap map[string]*list.Element
|
||||
lruMaxDiverSize int
|
||||
lruTtlManage *lruTTl // 定时清理器
|
||||
|
||||
lruDriver event2.DriverInterface
|
||||
lruConsumer event2.ConsumerInterface
|
||||
lruCleanProduce event2.ProduceInterface // 发送清理事件
|
||||
|
||||
middleProduce event.ProduceInterface // 中间件驱动
|
||||
}
|
||||
|
||||
// UpdateLruSize 更新现在的长度
|
||||
func (lru *SingleCache) UpdateLruSize(length structure.UpdateLength) {
|
||||
atomic.AddInt64(&lru.nowSize, int64(length))
|
||||
}
|
||||
|
||||
func cacheInit() (int64, int64, int, int) {
|
||||
maxSize := viper.GetString("lruCache.maxSize")
|
||||
retMaxSize, maxErr := util.ParseSizeToBit(maxSize)
|
||||
if maxErr != nil {
|
||||
return 0, 0, 0, 0
|
||||
}
|
||||
if retMaxSize == 0 {
|
||||
retMaxSize = defaultLruMaxSize
|
||||
}
|
||||
|
||||
clearSize := viper.GetString("lruCache.clearSize")
|
||||
retClearSize, clearErr := util.ParseSizeToBit(clearSize)
|
||||
if clearErr != nil {
|
||||
return 0, 0, 0, 0
|
||||
}
|
||||
if retClearSize == 0 {
|
||||
retClearSize = defaultLruClearSize
|
||||
}
|
||||
|
||||
maxDriver := viper.GetInt("lruCache.eventDriverSize")
|
||||
if maxDriver == 0 {
|
||||
maxDriver = defaultLruEventDriver
|
||||
}
|
||||
|
||||
detachNum := viper.GetInt("lruCache.detachNum")
|
||||
if detachNum == 0 {
|
||||
detachNum = defaultDetachNum
|
||||
}
|
||||
|
||||
return retMaxSize, retClearSize, maxDriver, detachNum
|
||||
}
|
||||
|
||||
// NewLRUCache lru初始化
|
||||
func NewLRUCache() *SingleCache {
|
||||
maxSize, clearSize, maxDriverSize, detachNum := cacheInit()
|
||||
lruDriver := event2.NewDriver(maxDriverSize)
|
||||
lruCacheOnce.Do(func() {
|
||||
lru := &SingleCache{
|
||||
maxsize: maxSize,
|
||||
clearSize: clearSize,
|
||||
nowSize: 0,
|
||||
li: list.New(),
|
||||
lruMap: make(map[string]*list.Element),
|
||||
lruMaxDiverSize: maxDriverSize,
|
||||
lruDriver: lruDriver,
|
||||
lruConsumer: event2.NewConsumer(lruDriver),
|
||||
lruCleanProduce: event2.NewProduce(lruDriver),
|
||||
middleProduce: event.NewProduce(middle.NewMiddleWare().GetEventDriver()),
|
||||
lruTtlManage: newLruTTl(detachNum),
|
||||
}
|
||||
lruCache = lru
|
||||
|
||||
// 启动 lru 事件驱动
|
||||
go lru.lruSingleWork()
|
||||
go lru.lruTtlWork()
|
||||
go lru.cleanWork()
|
||||
|
||||
})
|
||||
return lruCache
|
||||
}
|
||||
|
||||
// GetDriver 获取驱动
|
||||
func (lru *SingleCache) GetDriver() event2.DriverInterface {
|
||||
return lru.lruDriver
|
||||
}
|
||||
|
||||
//Add 增加
|
||||
func (lru *SingleCache) Add(key *proto.BaseKey, val structure.KeyBaseInterface) error {
|
||||
|
||||
if key == nil {
|
||||
return errorx.KeyBaseIsNilErr()
|
||||
}
|
||||
|
||||
exp := lru.lruTtlManage.setKeys(key)
|
||||
keyBaseVal := &keyBaseValue{
|
||||
key: key.Key,
|
||||
val: val,
|
||||
expire: exp,
|
||||
}
|
||||
if elVal, ok := lru.lruMap[key.Key]; ok {
|
||||
lru.li.MoveToFront(elVal)
|
||||
oldSize := elVal.Value.(structure.KeyBaseInterface).SizeByte()
|
||||
|
||||
lru.UpdateLruSize(structure.UpdateLength(val.SizeByte() - oldSize))
|
||||
elVal.Value = keyBaseVal
|
||||
return nil
|
||||
}
|
||||
valEl := lru.li.PushFront(keyBaseVal)
|
||||
lru.lruMap[key.Key] = valEl
|
||||
//增加大小
|
||||
lru.UpdateLruSize(structure.UpdateLength(valEl.Value.(*keyBaseValue).val.SizeByte()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 查找key对应的value
|
||||
func (lru *SingleCache) Get(key *proto.BaseKey) (structure.KeyBaseInterface, bool) {
|
||||
|
||||
if key == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if elVal, ok := lru.lruMap[key.Key]; ok {
|
||||
lru.li.MoveToFront(elVal)
|
||||
return elVal.Value.(*keyBaseValue).val, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
//Del 删除机制
|
||||
func (lru *SingleCache) Del() error {
|
||||
if lru.lruMap == nil {
|
||||
return errorx.New("lru is nil")
|
||||
}
|
||||
data := lru.li.Back()
|
||||
delete(lru.lruMap, data.Value.(*keyBaseValue).key)
|
||||
//删除大小
|
||||
lru.UpdateLruSize(structure.UpdateLength(-1 * data.Value.(*keyBaseValue).val.SizeByte()))
|
||||
lru.li.Remove(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
//DelByKey 根据key删除
|
||||
func (lru *SingleCache) DelByKey(key *proto.BaseKey) error {
|
||||
|
||||
if key == nil {
|
||||
return errorx.KeyBaseIsNilErr()
|
||||
}
|
||||
|
||||
if lru.lruMap == nil {
|
||||
return errorx.New("lru is nil")
|
||||
}
|
||||
if el, ok := lru.lruMap[key.Key]; ok {
|
||||
delete(lru.lruMap, key.Key)
|
||||
lru.li.Remove(el)
|
||||
lru.UpdateLruSize(structure.UpdateLength(-1 * el.Value.(*keyBaseValue).val.SizeByte()))
|
||||
return nil
|
||||
}
|
||||
return errorx.New("lru no this key")
|
||||
}
|
||||
|
||||
//DelByKeyAndExTtl 根据key(string)删除已经过期的 key
|
||||
func (lru *SingleCache) delByKeyAndExTtl(key string, beforeTime int64) {
|
||||
if elVal, ok := lru.lruMap[key]; ok {
|
||||
exp := elVal.Value.(*keyBaseValue).expire
|
||||
if exp <= beforeTime {
|
||||
delete(lru.lruMap, key)
|
||||
lru.li.Remove(elVal)
|
||||
lru.UpdateLruSize(structure.UpdateLength(-1 * elVal.Value.(*keyBaseValue).val.SizeByte()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (lru *SingleCache) DelToClearSize() error {
|
||||
if lru.lruMap == nil {
|
||||
return errorx.New("lru is nil")
|
||||
}
|
||||
for lru.nowSize > lru.clearSize {
|
||||
//del自动给nowSize进行大小的改变
|
||||
err := lru.Del()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 更新过期时间
|
||||
func (lru *SingleCache) UpdateTTl(key *proto.BaseKey) error {
|
||||
|
||||
if key == nil {
|
||||
return errorx.KeyBaseIsNilErr()
|
||||
}
|
||||
|
||||
if elVal, ok := lru.lruMap[key.Key]; ok {
|
||||
expire := lru.lruTtlManage.setKeys(key)
|
||||
elVal.Value.(*keyBaseValue).expire = expire
|
||||
}
|
||||
|
||||
return errorx.New("the key is not in lru cache, key:%s", key.Key)
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package lru
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitee.com/wheat-os/wheatCache/pkg/proto"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/structure/stringx"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewLRUCache(t *testing.T) {
|
||||
cache := NewLRUCache()
|
||||
v1 := stringx.NewStringSingle()
|
||||
v2 := stringx.NewStringSingle()
|
||||
v3 := stringx.NewStringSingle()
|
||||
key1 := proto.BaseKey{
|
||||
Key: "1",
|
||||
}
|
||||
key2 := proto.BaseKey{
|
||||
Key: "2",
|
||||
}
|
||||
key3 := proto.BaseKey{
|
||||
Key: "3",
|
||||
}
|
||||
cache.Add(&key1, v1)
|
||||
cache.Add(&key2, v2)
|
||||
cache.Add(&key3, v3)
|
||||
cache.Add(&key1, v1)
|
||||
fmt.Println(cache.nowSize)
|
||||
cache.Del()
|
||||
fmt.Println(cache.nowSize)
|
||||
_, isTrue := cache.Get(&key1)
|
||||
require.Equal(t, isTrue, true)
|
||||
}
|
||||
|
||||
func TestNewLRUCache2(t *testing.T) {
|
||||
//根据key删除
|
||||
cache := NewLRUCache()
|
||||
v1 := stringx.NewStringSingle()
|
||||
v2 := stringx.NewStringSingle()
|
||||
v3 := stringx.NewStringSingle()
|
||||
key1 := proto.BaseKey{
|
||||
Key: "1",
|
||||
}
|
||||
key2 := proto.BaseKey{
|
||||
Key: "2",
|
||||
}
|
||||
key3 := proto.BaseKey{
|
||||
Key: "3",
|
||||
}
|
||||
cache.Add(&key1, v1)
|
||||
cache.Add(&key2, v2)
|
||||
cache.Add(&key3, v3)
|
||||
cache.DelByKey(&key1)
|
||||
_, ok := cache.Get(&key1)
|
||||
require.Equal(t, ok, false)
|
||||
require.Error(t, cache.DelByKey(&key1))
|
||||
}
|
||||
|
||||
func TestLruProcess(t *testing.T) {
|
||||
lru := NewLRUCache()
|
||||
lru.clearSize = 3600
|
||||
|
||||
for i := 100; i < 200; i++ {
|
||||
lru.Add(&proto.BaseKey{
|
||||
Key: fmt.Sprint(i),
|
||||
Ttl: 20 << 2,
|
||||
}, stringx.NewStringSingle())
|
||||
}
|
||||
|
||||
// mock LruKey
|
||||
for i := 0; i < 100; i++ {
|
||||
lru.Add(&proto.BaseKey{
|
||||
Key: fmt.Sprint(i),
|
||||
Ttl: 4,
|
||||
}, stringx.NewStringSingle())
|
||||
}
|
||||
|
||||
require.Equal(t, lru.nowSize, int64(200*24))
|
||||
|
||||
// 自动清理测试
|
||||
fmt.Println(lru.clearSize)
|
||||
require.Equal(t, lru.li.Len(), 200)
|
||||
time.Sleep(3 * time.Second)
|
||||
require.Less(t, lru.nowSize, lru.clearSize+1)
|
||||
|
||||
// TTL 测试, 100-200 key 发生自动清理 留下 50-100 共 100(0-100) + 20个 key,5s 后,前 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))
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package lru
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitee.com/wheat-os/wheatCache/pkg/event2"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/logx"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/proto"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/structure/stringx"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWorker(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
lru := NewLRUCache()
|
||||
produce := event2.NewProduce(lru.GetDriver())
|
||||
workEvent := produce.NewEvent(OptionEventName)
|
||||
workEvent.SetValue(event2.WorkFuncEventKey, event2.EventWorkFunc(func() (interface{}, error) {
|
||||
v1 := stringx.NewStringSingle()
|
||||
key := proto.BaseKey{
|
||||
Key: "v1",
|
||||
}
|
||||
res, _ := v1.Set("123")
|
||||
lru.Add(&key, v1)
|
||||
return res, nil
|
||||
}))
|
||||
workEvent.InitWaitEvent()
|
||||
produce.Call(ctx, workEvent)
|
||||
res, err := workEvent.StartWaitEvent(2 * time.Second)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, res, "123")
|
||||
}
|
||||
|
||||
func TestSingleCache_DelToClearSize(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
lru := NewLRUCache()
|
||||
produce := event2.NewProduce(lru.GetDriver())
|
||||
|
||||
for i := int32(20000); i > 0; i-- {
|
||||
workEvent := produce.NewEvent(OptionEventName)
|
||||
workEvent.SetValue(event2.WorkFuncEventKey, event2.EventWorkFunc(func() (interface{}, error) {
|
||||
v1 := stringx.NewStringSingle()
|
||||
key := proto.BaseKey{
|
||||
Key: string(i),
|
||||
Ttl: 1,
|
||||
}
|
||||
u := v1.Setbit(i, true)
|
||||
lru.Add(&key, v1)
|
||||
return u, nil
|
||||
}))
|
||||
workEvent.InitWaitEvent()
|
||||
produce.Call(ctx, workEvent)
|
||||
workEvent.StartWaitEvent(2 * time.Second)
|
||||
workEvent.Recovery()
|
||||
}
|
||||
|
||||
logx.Info("start size is %d", lru.nowSize)
|
||||
time.Sleep(10 * time.Second)
|
||||
logx.Info("end size is %d", lru.nowSize)
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package lru
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gitee.com/wheat-os/wheatCache/pkg/errorx"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/event"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/event2"
|
||||
"gitee.com/wheat-os/wheatCache/pkg/logx"
|
||||
mMsg "gitee.com/wheat-os/wheatCache/pkg/middle-msg"
|
||||
)
|
||||
|
||||
func (lru *SingleCache) lruSingleWork() {
|
||||
ctx := context.Background()
|
||||
for {
|
||||
workEvent := lru.lruConsumer.Receive(ctx)
|
||||
workFunc, ok := workEvent.GetValue(event2.WorkFuncEventKey)
|
||||
if !ok {
|
||||
workEvent.SetResultErr(errorx.LruNotWorkFuncEventErr())
|
||||
continue
|
||||
}
|
||||
|
||||
switch workEvent.GetEventName() {
|
||||
case OptionEventName:
|
||||
if work, ok := workFunc.(event2.EventWorkFunc); ok {
|
||||
workEvent.ExecWorkAndSendResult(work)
|
||||
}
|
||||
|
||||
case CleanEventName:
|
||||
// 对当前的io数量进行判断
|
||||
ioNum := lru.GetDriver().GetLength()
|
||||
if ioNum > lru.lruMaxDiverSize/2 {
|
||||
lru.lruCleanProduce.Call(ctx, workEvent)
|
||||
continue
|
||||
}
|
||||
if work, ok := workFunc.(event2.EventWorkFunc); ok {
|
||||
workEvent.ExecWorkAndSendResult(work)
|
||||
}
|
||||
|
||||
case TtlEventName:
|
||||
if work, ok := workFunc.(event2.EventWorkFunc); ok {
|
||||
workEvent.ExecWorkAndSendResult(work)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行过期事件
|
||||
func (lru *SingleCache) lruTtlWork() {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// 清理事件
|
||||
go func() {
|
||||
work := event2.EventWorkFunc(func() (interface{}, error) {
|
||||
|
||||
beforeTime := time.Now().Unix()
|
||||
cle := lru.lruTtlManage.detachNum
|
||||
if cle > len(lru.lruTtlManage.memoryKey) {
|
||||
cle = len(lru.lruTtlManage.memoryKey)
|
||||
}
|
||||
|
||||
keys := make([]string, 0)
|
||||
for i := 0; i < cle; i++ {
|
||||
key := <-lru.lruTtlManage.memoryKey
|
||||
keys = append(keys, key)
|
||||
lru.delByKeyAndExTtl(key, beforeTime)
|
||||
}
|
||||
return keys, nil
|
||||
})
|
||||
|
||||
cleanTTlTicker := time.NewTicker(500 * time.Millisecond)
|
||||
defer cleanTTlTicker.Stop()
|
||||
|
||||
for {
|
||||
// 清理事件
|
||||
<-cleanTTlTicker.C
|
||||
if len(lru.lruTtlManage.memoryKey) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
ttlEvent := lru.lruCleanProduce.NewEvent(TtlEventName)
|
||||
ttlEvent.SetValue(event2.WorkFuncEventKey, work)
|
||||
ttlEvent.InitWaitEvent()
|
||||
|
||||
lru.lruCleanProduce.Call(ctx, ttlEvent)
|
||||
keys, err := ttlEvent.StartWaitEvent(time.Second * 2)
|
||||
ttlEvent.Recovery()
|
||||
|
||||
mMsg.SendMiddleMsg(ctx, lru.middleProduce, mMsg.LruTTlContext{
|
||||
Keys: keys.([]string),
|
||||
CleanTime: time.Now(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logx.With(ctx, lru.middleProduce).Errorln(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// 收集事件
|
||||
for {
|
||||
time.Sleep(1 * time.Second)
|
||||
lru.lruTtlManage.ttlKeyToMemoryBySecond()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (lru *SingleCache) cleanWork() {
|
||||
cxt := context.Background()
|
||||
work := event.EventWorkFunc(func() (interface{}, error) {
|
||||
err := lru.DelToClearSize()
|
||||
return nil, err
|
||||
})
|
||||
|
||||
for {
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
if lru.clearSize < lru.nowSize {
|
||||
lruCleanEvent := lru.lruCleanProduce.NewEvent(CleanEventName)
|
||||
lruCleanEvent.SetValue(event2.WorkFuncEventKey, work)
|
||||
|
||||
lruCleanEvent.InitWaitEvent()
|
||||
lru.lruCleanProduce.Call(cxt, lruCleanEvent)
|
||||
_, err := lruCleanEvent.StartWaitEvent(defaultWaitTime)
|
||||
if err != nil {
|
||||
logx.With(cxt, lru.middleProduce).Errorln(err)
|
||||
}
|
||||
|
||||
// 归还
|
||||
lruCleanEvent.Recovery()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package middlemsg
|
||||
|
||||
import "time"
|
||||
|
||||
var (
|
||||
LogContextName = "log-context"
|
||||
)
|
||||
|
||||
type LogContext struct {
|
||||
Level string
|
||||
Data time.Time
|
||||
Msg string
|
||||
Route string
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package middle
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
oneMiddle sync.Once
|
||||
middleWareDriver *MiddleWare
|
||||
)
|
||||
|
||||
const (
|
||||
defaultConsumerCount = 5
|
||||
defaultDriverCount = 1000
|
||||
)
|
|
@ -1 +0,0 @@
|
|||
package define
|
|
@ -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
|
||||
}
|
|
@ -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 err,not key:%s", middleMsg.MiddleMsgKey)
|
||||
continue
|
||||
}
|
||||
|
||||
// 发送事件到 全部的 plugs 里
|
||||
for _, val := range plugs {
|
||||
_, err := val.Exec(msg)
|
||||
if err != nil {
|
||||
logx.With(ctx, m.eventProduce).Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.17.3
|
||||
// source: base.proto
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
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 BaseKey struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Ttl int64 `protobuf:"varint,2,opt,name=ttl,proto3" json:"ttl,omitempty"`
|
||||
Expire *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expire,proto3" json:"expire,omitempty"`
|
||||
}
|
||||
|
||||
func (x *BaseKey) Reset() {
|
||||
*x = BaseKey{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_base_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *BaseKey) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BaseKey) ProtoMessage() {}
|
||||
|
||||
func (x *BaseKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_base_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 BaseKey.ProtoReflect.Descriptor instead.
|
||||
func (*BaseKey) Descriptor() ([]byte, []int) {
|
||||
return file_base_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *BaseKey) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *BaseKey) GetTtl() int64 {
|
||||
if x != nil {
|
||||
return x.Ttl
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *BaseKey) GetExpire() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.Expire
|
||||
}
|
||||
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{
|
||||
0x0a, 0x0a, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x61, 0x0a,
|
||||
0x07, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74,
|
||||
0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x32, 0x0a, 0x06,
|
||||
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,
|
||||
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 (
|
||||
file_base_proto_rawDescOnce sync.Once
|
||||
file_base_proto_rawDescData = file_base_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_base_proto_rawDescGZIP() []byte {
|
||||
file_base_proto_rawDescOnce.Do(func() {
|
||||
file_base_proto_rawDescData = protoimpl.X.CompressGZIP(file_base_proto_rawDescData)
|
||||
})
|
||||
return file_base_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_base_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_base_proto_goTypes = []interface{}{
|
||||
(*BaseKey)(nil), // 0: BaseKey
|
||||
(*External)(nil), // 1: External
|
||||
(*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp
|
||||
}
|
||||
var file_base_proto_depIdxs = []int32{
|
||||
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
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_base_proto_init() }
|
||||
func file_base_proto_init() {
|
||||
if File_base_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_base_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*BaseKey); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
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{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_base_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_base_proto_goTypes,
|
||||
DependencyIndexes: file_base_proto_depIdxs,
|
||||
MessageInfos: file_base_proto_msgTypes,
|
||||
}.Build()
|
||||
File_base_proto = out.File
|
||||
file_base_proto_rawDesc = nil
|
||||
file_base_proto_goTypes = nil
|
||||
file_base_proto_depIdxs = nil
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package proto
|
||||
|
||||
import (
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
type GetKeyBaseInterface interface {
|
||||
GetKey() *BaseKey
|
||||
}
|
||||
|
||||
const (
|
||||
BaseKeyMethodKey = "basekey"
|
||||
)
|
||||
|
||||
// NewBaseKey
|
||||
// key,ttl,expire
|
||||
func NewBaseKey(key string, t ...int64) *BaseKey {
|
||||
var expire *timestamppb.Timestamp = nil
|
||||
var ttl int64
|
||||
|
||||
if len(t) > 1 {
|
||||
expire = ×tamppb.Timestamp{
|
||||
Seconds: t[1],
|
||||
}
|
||||
ttl = t[0]
|
||||
} else if len(t) == 1 {
|
||||
ttl = t[0]
|
||||
}
|
||||
|
||||
return &BaseKey{
|
||||
Key: key,
|
||||
Expire: expire,
|
||||
Ttl: ttl,
|
||||
}
|
||||
}
|
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package structure
|
||||
|
||||
const (
|
||||
defaultLen = 8 // 默认创建的 value 大小
|
||||
)
|
||||
|
||||
type DynamicType int8
|
||||
|
||||
type UpdateLength int64
|
||||
|
||||
const (
|
||||
DynamicNull = DynamicType(iota)
|
||||
DynamicInt
|
||||
DynamicFloat
|
||||
DynamicString
|
||||
)
|
||||
|
||||
type KeyBaseInterface interface {
|
||||
SizeByte() int64
|
||||
|
||||
// RollBack TODO 事务相关, V2 实现
|
||||
RollBack() error
|
||||
// Begin 事务相关, V2 实现
|
||||
Begin() error
|
||||
// Comment 事务相关, V2 实现
|
||||
Comment() error
|
||||
|
||||
Encode() ([]byte, error)
|
||||
}
|
||||
|
||||
type StringXInterface interface {
|
||||
KeyBaseInterface
|
||||
Set(string) (string, UpdateLength)
|
||||
Get() string
|
||||
Add(int32) (string, error)
|
||||
Reduce(int32) (string, error)
|
||||
Setbit(int32, bool) UpdateLength
|
||||
Getbit(int32) (bool, error)
|
||||
Getrange(start, end int32) (string, error)
|
||||
GetLength() int
|
||||
}
|
||||
|
||||
type ListXInterface interface {
|
||||
KeyBaseInterface
|
||||
LPush(...string) UpdateLength
|
||||
RPush(...string) UpdateLength
|
||||
LPop(int) ([]string, UpdateLength)
|
||||
RPop(int) ([]string, UpdateLength)
|
||||
Index(int) (string, error)
|
||||
Insert(int, bool, ...string) (UpdateLength, error)
|
||||
Length() int
|
||||
Slice(start, end int) (UpdateLength, error) // 切片, O(n)复杂度
|
||||
Range(start, end int) ([]string, error)
|
||||
Remove(value string, count int) (int, UpdateLength)
|
||||
}
|
||||
|
||||
type HashXInterface interface {
|
||||
KeyBaseInterface
|
||||
Set(key string, val string) UpdateLength
|
||||
Get(key string) (string, error)
|
||||
Del(key string) (UpdateLength, error)
|
||||
Key() []string
|
||||
Value() []string
|
||||
Item() map[string]string
|
||||
Add(renewal int, key ...string) (int, []string, error) // 访问影响成功的结果
|
||||
SetX(key string, val string) (bool, UpdateLength) // 不存在才插入
|
||||
Length() int
|
||||
Range(consur, count int, regex string) []string
|
||||
}
|
||||
|
||||
type ChannelXInterface interface {
|
||||
KeyBaseInterface
|
||||
Push(value string) UpdateLength
|
||||
Pop() (string, UpdateLength)
|
||||
Length() int
|
||||
Clean() UpdateLength
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
|
||||
}
|
|
@ -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"})
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue