本博客代码都是参考《Redis IN ACTION》这本书,由于书中代码都是python所写,所以本文代码为java语言编写,方便读者查阅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196 1public class Chapter01
2{
3 private static final int ONE_WEEK_IN_SECONDS = 7 * 86400; //用来计算超过7天之后的文章不可以投票
4 private static final int VOTE_SCORE = 432; //432来源 这个常量是通过将一天的秒数(86400)除以文章展示一天所需的支持票数量(200 -支持票超过200就算有趣文章)得出的,文章每获得一张支持票,程序要需要将文章的评分增加432分
5 private static final int ARTICLES_PER_PAGE = 25;
6
7 public static final void main(String[] args) {
8 new Chapter01().run();
9 }
10
11 public void run() {
12 Jedis conn = new Jedis("192.168.32.128");
13 conn.select(15);
14
15 // 初始化10篇文章
16// for(int i=1;i<11;i++)
17// {
18// String articleId = postArticle(conn, "username_"+i, "A title"+i);
19// System.out.println("create a new article: " + articleId);
20// Map<String, String> articleData = conn.hgetAll("article:" + articleId);
21// for(Map.Entry<String, String> entry : articleData.entrySet())
22// {
23// System.out.println(" " + entry.getKey() + ": " + entry.getValue());
24// }
25// }
26
27 // 投票代码开始
28// articleVote(conn, "jack", "article:1");
29// articleVote(conn, "jack", "article:2");
30// articleVote(conn, "jack", "article:3");
31// articleVote(conn, "jack", "article:4");
32// articleVote(conn, "jack", "article:5");
33// articleVote(conn, "jack", "article:6");
34// articleVote(conn, "jack", "article:8");
35// articleVote(conn, "jack", "article:9");
36// articleVote(conn, "jack1", "article:1");
37// articleVote(conn, "jack2", "article:1");
38// articleVote(conn, "jack3", "article:1");
39// articleVote(conn, "jack4", "article:1");
40// articleVote(conn, "jack2", "article:3");
41// articleVote(conn, "jack3", "article:3");
42// String votes = conn.hget("article:1", "votes");
43// System.out.println("We voted for the article, it now has votes: " + votes);
44
45 //得到从高到低的排名
46// System.out.println("The currently highest-scoring articles are:");
47// List<Map<String,String>> articles = getArticles(conn, 1);
48// printArticles(articles);
49
50 // 给文章分组
51// addGroups(conn, "1", new String[]{"jack01-group","jack02-group"});
52// addGroups(conn, "2", new String[]{"jack01-group","jack02-group"});
53// addGroups(conn, "5", new String[]{"jack01-group","jack02-group"});
54// addGroups(conn, "6", new String[]{"jack01-group","jack03-group"});
55// addGroups(conn, "7", new String[]{"jack03-group","jack02-group"});
56// addGroups(conn, "10", new String[]{"jack01-group","jack03-group"});
57// addGroups(conn, "8", new String[]{"jack03-group","jack02-group"});
58// addGroups(conn, "9", new String[]{"jack03-group","jack02-group"});
59// addGroups(conn, "4", new String[]{"jack03-group","jack02-group"});
60
61 val articles = getGroupArticles(conn, "jack03-group", 1);
62 printArticles(articles);
63 }
64
65 /**
66 * 1,生成文章ID
67 * 2,将发布者ID增加到已投票用户名单集合中
68 * 3,使用HMSET命令来存储文章相关信息
69 * 4,将文章初始评分和发布时间分别添加到2个相应的有序集合中
70 * @return
71 */
72 public String postArticle(Jedis conn, String userName, String title) {
73 String articleId = String.valueOf(conn.incr("article:"));
74
75 String voted = "voted:" + articleId;
76
77 //set
78 conn.sadd(voted, userName);
79 conn.expire(voted, ONE_WEEK_IN_SECONDS);
80
81 ////hash
82 long now = System.currentTimeMillis() / 1000;
83 String article = "article:" + articleId;
84 HashMap<String,String> articleData = new HashMap<String,String>();
85 articleData.put("title", title);
86 articleData.put("user", userName);
87 articleData.put("now", String.valueOf(now));
88 conn.hmset(article, articleData);
89
90 //zset
91 conn.zadd("score:", now + VOTE_SCORE, article);
92 //发布的时候 默认作者本人为文章投票
93 conn.zadd("time:", now, article);
94
95 return articleId;
96 }
97
98 /**
99 * 文章投票
100 * 1,每一票对应一个常量
101 * @return
102 */
103 public void articleVote(Jedis conn, String user, String article) {
104 long cutoff = (System.currentTimeMillis() / 1000) - ONE_WEEK_IN_SECONDS;
105 if (conn.zscore("time:", article) < cutoff){ //计算是否超过文章投票截止时间
106 System.out.println("文章发布时间太长了");
107 return;
108 }
109
110 String articleId = article.substring(article.indexOf(':') + 1);
111 if (conn.sadd("voted:" + articleId, user) == 1) { //如果值不==1 则用户已经投过此文章了 1 标识原set中没有,本次添加成功
112 //增加score的分值,使其增加VOTE_SCORE
113 conn.zincrby("score:", VOTE_SCORE, article);
114 //修改文章中votes的票数,使其增加1
115 conn.hincrBy(article, "votes", 1);
116 }
117 }
118
119 /**
120 * 分页数据
121 * @param conn
122 * @param page
123 * @return
124 */
125 public List<Map<String,String>> getArticles(Jedis conn, int page) {
126 return getArticles(conn, page, "score:");
127 }
128
129 /**
130 * 根据order获取排名靠前的数据
131 * @param conn
132 * @param page
133 * @param order
134 * @return
135 */
136 public List<Map<String,String>> getArticles(Jedis conn, int page, String order) {
137 int start = (page - 1) * ARTICLES_PER_PAGE;
138 int end = start + ARTICLES_PER_PAGE - 1;
139
140 Set<String> ids = conn.zrevrange(order, start, end);
141 List<Map<String,String>> articles = new ArrayList<Map<String,String>>();
142 for (String id : ids){
143 Map<String,String> articleData = conn.hgetAll(id);
144 articleData.put("id", id);
145 articles.add(articleData);
146 }
147
148 return articles;
149 }
150
151 /**
152 * 文章分组
153 * @return
154 */
155 public void addGroups(Jedis conn, String articleId, String[] toAdd) {
156 String article = "article:" + articleId;
157 for (String group : toAdd) {
158 conn.sadd("group:" + group, article);
159 }
160 }
161
162 public List<Map<String,String>> getGroupArticles(Jedis conn, String group, int page) {
163 return getGroupArticles(conn, group, page, "score:");
164 }
165
166 /**
167 * 按顺序得到某一组内的文章
168 * 这里会使用到关系型数据库里面的\
169 * Redis Zinterstore 命令计算给定的一个或多个有序集的交集 默认情况下,结果集中某个成员的分数值是所有给定集下该成员分数值之和。
170 * 使用 WEIGHTS 选项,你可以为 每个 给定有序集 分别 指定一个乘法因子(multiplication factor),每个给定有序集的所有成员的 score 值在传递给聚合函数(aggregation function)之前都要先乘以该有序集的因子。
171 * 使用 AGGREGATE 选项,你可以指定并集的结果集的聚合方式。默认使用的参数 SUM ,可以将所有集合中某个成员的 score 值之 和 作为结果集中该成员的 score 值;使用参数 MIN ,可以将所有集合中某个成员的 最小 score 值作为结果集中该成员的 score 值;而参数 MAX 则是将所有集合中某个成员的 最大 score 值作为结果集中该成员的 score 值。
172 * @return
173 */
174 public List<Map<String,String>> getGroupArticles(Jedis conn, String group, int page, String order) {
175 String key = order + group;
176 if (!conn.exists(key)) {
177 ZParams params = new ZParams().aggregate(ZParams.Aggregate.MAX);
178 conn.zinterstore(key, params, "group:" + group, order);
179 conn.expire(key, 60);
180 }
181 return getArticles(conn, page, key);
182 }
183
184 private void printArticles(List<Map<String,String>> articles){
185 for (Map<String,String> article : articles){
186 System.out.println(" id: " + article.get("id"));
187 for (Map.Entry<String,String> entry : article.entrySet()){
188 if (entry.getKey().equals("id")){
189 continue;
190 }
191 System.out.println(" " + entry.getKey() + ": " + entry.getValue());
192 }
193 }
194 }
195}
196
文章中redis采用单机模式,这里小编在写上面代码的时候遇到一个问题,至今没找到原因,有路过的大神希望不吝赐教:
一开始上面代码是使用springboot + rediscluster 模式实现,但是使用redistemplate 执行 zinterstore 的时候报出
1
2 1ZINTERSTORE can only be executed when all keys map to the same slot
2
我们知道redis cluster 值是有16384个卡槽分布在集群的master上存储数据的,每个master分别存储部分数据,这里难道需要我把数据集中到一个slot中才能调用此方法吗?找了很久也没找到合适的解决方案,有了解过的朋友希望留言告知。
开开心心编码,快快乐乐生活。