购物网站的redis相关实现(Java)

news/2024/4/27 8:52:41/文章来源:https://blog.csdn.net/weixin_34233856/article/details/88890092

购物网站的redis相关实现


1、使用Redis构建文章投票网站(Java)

本文主要内容:

  • 1、登录cookie
  • 2、购物车cookie
  • 3、缓存数据库行
  • 4、测试

必备知识点

WEB应用就是通过HTTP协议对网页浏览器发出的请求进行相应的服务器或者服务(Service).

一个WEB服务器对请求进行响应的典型步骤如下:

  • 1、服务器对客户端发来的请求(request)进行解析.
  • 2、请求被转发到一个预定义的处理器(handler)
  • 3、处理器可能会从数据库中取出数据。
  • 4、处理器根据取出的数据对模板(template)进行渲染(rander)
  • 5、处理器向客户端返回渲染后的内容作为请求的相应。

以上展示了典型的web服务器运作方式,这种情况下的web请求是无状态的(stateless),
服务器本身不会记住与过往请求有关的任何信息,这使得失效的服务器可以很容易的替换掉。


每当我们登录互联网服务的时候,这些服务都会使用cookie来记录我们的身份。

cookies由少量数据组成,网站要求我们浏览器存储这些数据,并且在每次服务发出请求时再将这些数据传回服务。

对于用来登录的cookie ,有两种常见的方法可以将登录信息存储在cookie里:

  • 签名cookie通常会存储用户名,还有用户ID,用户最后一次登录的时间,以及网站觉得有用的其他信息。

    • 令牌cookie会在cookie里存储一串随机字节作为令牌,服务器可以根据令牌在数据库中查找令牌的拥有者。

签名cookie和令牌cookie的优点和缺点:

* ------------------------------------------------------------------------------------------------
* |  cookie类型       |                  优点                    |           缺点                 |
* -------------------------------------------------------------------------------------------------
* |    签名           |  验证cookkie所需的一切信息都存储在cookie  |  正确的处理签名很难,很容易忘记  |                      |                                      |
* |   cookie          |  还可以包含额外的信息                    |  对数据签名或者忘记验证数据签名, |
* |                   |  对这些前面也很容易                      |  从而造成安全漏洞               |
* -------------------------------------------------------------------------------------------------
* |   令牌            |     添加信息非常容易,cookie体积小。      |   需要在服务器中存储更多信息,   |                    |                                          |
* |   cookie          |  移动端和较慢的客户端可以更快的发送请求    |  使用关系型数据库,载入存储代价高 |                           |                                      |
* -------------------------------------------------------------------------------------------------

因为该网站没有实现签名cookie的需求,所以使用令牌cookie来引用关系型数据库表中负责存储用户登录信息的条目。
除了登录信息,还可以将用户的访问时长和已浏览商品的数量等信息存储到数据库中,有利于更好的像用户推销商品


(1)登录和cookie缓存

/*** 使用Redis重新实现登录cookie,取代目前由关系型数据库实现的登录cookie功能* 1、将使用一个散列来存储登录cookie令牌与与登录用户之间的映射。* 2、需要根据给定的令牌来查找与之对应的用户,并在已经登录的情况下,返回该用户id。*/
public String checkToken(Jedis conn, String token) {//1、String token = UUID.randomUUID().toString();//2、尝试获取并返回令牌对应的用户return conn.hget("login:", token);
}
/*** 1、每次用户浏览页面的时候,程序需都会对用户存储在登录散列里面的信息进行更新,* 2、并将用户的令牌和当前时间戳添加到记录最近登录用户的集合里。* 3、如果用户正在浏览的是一个商品,程序还会将商品添加到记录这个用户最近浏览过的商品有序集合里面,* 4、如果记录商品的数量超过25个时,对这个有序集合进行修剪。*/
public void updateToken(Jedis conn, String token, String user, String item) {//1、获取当前时间戳long timestamp = System.currentTimeMillis() / 1000;//2、维持令牌与已登录用户之间的映射。conn.hset("login:", token, user);//3、记录令牌最后一次出现的时间conn.zadd("recent:", timestamp, token);if (item != null) {//4、记录用户浏览过的商品conn.zadd("viewed:" + token, timestamp, item);//5、移除旧记录,只保留用户最近浏览过的25个商品conn.zremrangeByRank("viewed:" + token, 0, -26);//6、为有序集key的成员member的score值加上增量increment。通过传递一个负数值increment 让 score 减去相应的值,conn.zincrby("viewed:", -1, item);}
}
/***存储会话数据所需的内存会随着时间的推移而不断增加,所有我们需要定期清理旧的会话数据。* 1、清理会话的程序由一个循环构成,这个循环每次执行的时候,都会检查存储在最近登录令牌的有序集合的大小。* 2、如果有序集合的大小超过了限制,那么程序会从有序集合中移除最多100个最旧的令牌,* 3、并从记录用户登录信息的散列里移除被删除令牌对应的用户信息,* 4、并对存储了这些用户最近浏览商品记录的有序集合中进行清理。* 5、于此相反,如果令牌的数量没有超过限制,那么程序会先休眠一秒,之后在重新进行检查。*/
public class CleanSessionsThread extends Thread {private Jedis conn;private int limit = 10000;private boolean quit ;public CleanSessionsThread(int limit) {this.conn = new Jedis("localhost");this.conn.select(14);this.limit = limit;}public void quit() {quit = true;}public void run() {while (!quit) {//1、找出目前已有令牌的数量。long size = conn.zcard("recent:");//2、令牌数量未超过限制,休眠1秒,并在之后重新检查if (size <= limit) {try {sleep(1000);} catch (InterruptedException ie) {Thread.currentThread().interrupt();}continue;}long endIndex = Math.min(size - limit, 100);//3、获取需要移除的令牌IDSet<String> tokenSet = conn.zrange("recent:", 0, endIndex - 1);String[] tokens = tokenSet.toArray(new String[tokenSet.size()]);ArrayList<String> sessionKeys = new ArrayList<String>();for (String token : tokens) {//4、为那些将要被删除的令牌构建键名sessionKeys.add("viewed:" + token);}//5、移除最旧的令牌conn.del(sessionKeys.toArray(new String[sessionKeys.size()]));//6、移除被删除令牌对应的用户信息conn.hdel("login:", tokens);//7、移除用户最近浏览商品记录。conn.zrem("recent:", tokens);}}
}

(2)使用redis实现购物车

/*** 使用cookie实现购物车——就是将整个购物车都存储到cookie里面,* 优点:无需对数据库进行写入就可以实现购物车功能,* 缺点:怎是程序需要重新解析和验证cookie,确保cookie的格式正确。并且包含商品可以正常购买* 还有一缺点:因为浏览器每次发送请求都会连cookie一起发送,所以如果购物车的体积较大,* 那么请求发送和处理的速度可能降低。* -----------------------------------------------------------------* 1、每个用户的购物车都是一个散列,存储了商品ID与商品订单数量之间的映射。* 2、如果用户订购某件商品的数量大于0,那么程序会将这件商品的ID以及用户订购该商品的数量添加到散列里。* 3、如果用户购买的商品已经存在于散列里面,那么新的订单数量会覆盖已有的。* 4、相反,如果某用户订购某件商品数量不大于0,那么程序将从散列里移除该条目* 5、需要对之前的会话清理函数进行更新,让它在清理会话的同时,将旧会话对应的用户购物车也一并删除。*/
public void addToCart(Jedis conn, String session, String item, int count) {if (count <= 0) {//1、从购物车里面移除指定的商品conn.hdel("cart:" + session, item);} else {//2、将指定的商品添加到购物车conn.hset("cart:" + session, item, String.valueOf(count));}
}

5、需要对之前的会话清理函数进行更新,让它在清理会话的同时,将旧会话对应的用户购物车也一并删除。

只是比CleanSessionsThread多了一行代码,伪代码如下:

long endIndex = Math.min(size - limit, 100);
//3、获取需要移除的令牌ID
Set<String> tokenSet = conn.zrange("recent:", 0, endIndex - 1);
String[] tokens = tokenSet.toArray(new String[tokenSet.size()]);ArrayList<String> sessionKeys = new ArrayList<String>();
for (String token : tokens) {//4、为那些将要被删除的令牌构建键名sessionKeys.add("viewed:" + token);//新增加的这两行代码用于删除旧会话对应的购物车。sessionKeys.add("cart:" + sess);
}
//5、移除最旧的令牌
conn.del(sessionKeys.toArray(new String[sessionKeys.size()]));
//6、移除被删除令牌对应的用户信息
conn.hdel("login:", tokens);
//7、移除用户最近浏览商品记录。
conn.zrem("recent:", tokens);

(3)数据行缓存

/*** 为了应对促销活动带来的大量负载,需要对数据行进行缓存,具体做法是:* 1、编写一个持续运行的守护进程,让这个函数指定的数据行缓存到redis里面,并不定期的更新。* 2、缓存函数会将数据行编码为JSON字典并存储在Redis字典里。其中数据列的名字会被映射为JSON的字典,* 而数据行的值则被映射为JSON字典的值。* -----------------------------------------------------------------------------------------* 程序使用两个有序集合来记录应该在何时对缓存进行更新:* 1、第一个为调用有序集合,他的成员为数据行的ID,而分支则是一个时间戳,* 这个时间戳记录了应该在何时将指定的数据行缓存到Redis里面* 2、第二个有序集合为延时有序集合,他的成员也是数据行的ID,* 而分值则记录了指定数据行的缓存需要每隔多少秒更新一次。* ----------------------------------------------------------------------------------------------* 为了让缓存函数定期的缓存数据行,程序首先需要将hangID和给定的延迟值添加到延迟有序集合里面,* 然后再将行ID和当前指定的时间戳添加到调度有序集合里面。*/
public void scheduleRowCache(Jedis conn, String rowId, int delay) {//1、先设置数据行的延迟值conn.zadd("delay:", delay, rowId);//2、立即对需要行村的数据进行调度conn.zadd("schedule:", System.currentTimeMillis() / 1000, rowId);
}

/*** 1、通过组合使用调度函数和持续运行缓存函数,实现类一种重读进行调度的自动缓存机制,* 并且可以随心所欲的控制数据行缓存的更新频率:* 2、如果数据行记录的是特价促销商品的剩余数量,并且参与促销活动的用户特别多的话,那么最好每隔几秒更新一次数据行缓存:* 另一方面,如果数据并不经常改变,或者商品缺货是可以接受的,那么可以每隔几分钟更新一次缓存。*/
public class CacheRowsThreadextends Thread {private Jedis conn;private boolean quit;public CacheRowsThread() {this.conn = new Jedis("localhost");this.conn.select(14);}public void quit() {quit = true;}public void run() {Gson gson = new Gson();while (!quit) {//1、尝试获取下一个需要被缓存的数据行以及该行的调度时间戳,返回一个包含0个或一个元组列表Set<Tuple> range = conn.zrangeWithScores("schedule:", 0, 0);Tuple next = range.size() > 0 ? range.iterator().next() : null;long now = System.currentTimeMillis() / 1000;//2、暂时没有行需要被缓存,休眠50毫秒。if (next == null || next.getScore() > now) {try {sleep(50);} catch (InterruptedException ie) {Thread.currentThread().interrupt();}continue;}//3、提前获取下一次调度的延迟时间,String rowId = next.getElement();double delay = conn.zscore("delay:", rowId);if (delay <= 0) {//4、不必在缓存这个行,将它从缓存中移除conn.zrem("delay:", rowId);conn.zrem("schedule:", rowId);conn.del("inv:" + rowId);continue;}//5、继续读取数据行Inventory row = Inventory.get(rowId);//6、更新调度时间,并设置缓存值。conn.zadd("schedule:", now + delay, rowId);conn.set("inv:" + rowId, gson.toJson(row));}}
}

(4)测试

PS:需要好好补偿英语了!!需要全部的可以到这里下载官方翻译Java版

public class Chapter02 {public static final void main(String[] args)throws InterruptedException {new Chapter02().run();}public void run()throws InterruptedException {Jedis conn = new Jedis("localhost");conn.select(14);testLoginCookies(conn);testShopppingCartCookies(conn);testCacheRows(conn);testCacheRequest(conn);}public void testLoginCookies(Jedis conn)throws InterruptedException {System.out.println("\n----- testLoginCookies -----");String token = UUID.randomUUID().toString();updateToken(conn, token, "username", "itemX");System.out.println("We just logged-in/updated token: " + token);System.out.println("For user: 'username'");System.out.println();System.out.println("What username do we get when we look-up that token?");String r = checkToken(conn, token);System.out.println(r);System.out.println();assert r != null;System.out.println("Let's drop the maximum number of cookies to 0 to clean them out");System.out.println("We will start a thread to do the cleaning, while we stop it later");CleanSessionsThread thread = new CleanSessionsThread(0);thread.start();Thread.sleep(1000);thread.quit();Thread.sleep(2000);if (thread.isAlive()) {throw new RuntimeException("The clean sessions thread is still alive?!?");}long s = conn.hlen("login:");System.out.println("The current number of sessions still available is: " + s);assert s == 0;}public void testShopppingCartCookies(Jedis conn)throws InterruptedException {System.out.println("\n----- testShopppingCartCookies -----");String token = UUID.randomUUID().toString();System.out.println("We'll refresh our session...");updateToken(conn, token, "username", "itemX");System.out.println("And add an item to the shopping cart");addToCart(conn, token, "itemY", 3);Map<String, String> r = conn.hgetAll("cart:" + token);System.out.println("Our shopping cart currently has:");for (Map.Entry<String, String> entry : r.entrySet()) {System.out.println("  " + entry.getKey() + ": " + entry.getValue());}System.out.println();assert r.size() >= 1;System.out.println("Let's clean out our sessions and carts");CleanFullSessionsThread thread = new CleanFullSessionsThread(0);thread.start();Thread.sleep(1000);thread.quit();Thread.sleep(2000);if (thread.isAlive()) {throw new RuntimeException("The clean sessions thread is still alive?!?");}r = conn.hgetAll("cart:" + token);System.out.println("Our shopping cart now contains:");for (Map.Entry<String, String> entry : r.entrySet()) {System.out.println("  " + entry.getKey() + ": " + entry.getValue());}assert r.size() == 0;}public void testCacheRows(Jedis conn)throws InterruptedException {System.out.println("\n----- testCacheRows -----");System.out.println("First, let's schedule caching of itemX every 5 seconds");scheduleRowCache(conn, "itemX", 5);System.out.println("Our schedule looks like:");Set<Tuple> s = conn.zrangeWithScores("schedule:", 0, -1);for (Tuple tuple : s) {System.out.println("  " + tuple.getElement() + ", " + tuple.getScore());}assert s.size() != 0;System.out.println("We'll start a caching thread that will cache the data...");CacheRowsThread thread = new CacheRowsThread();thread.start();Thread.sleep(1000);System.out.println("Our cached data looks like:");String r = conn.get("inv:itemX");System.out.println(r);assert r != null;System.out.println();System.out.println("We'll check again in 5 seconds...");Thread.sleep(5000);System.out.println("Notice that the data has changed...");String r2 = conn.get("inv:itemX");System.out.println(r2);System.out.println();assert r2 != null;assert !r.equals(r2);System.out.println("Let's force un-caching");scheduleRowCache(conn, "itemX", -1);Thread.sleep(1000);r = conn.get("inv:itemX");System.out.println("The cache was cleared? " + (r == null));assert r == null;thread.quit();Thread.sleep(2000);if (thread.isAlive()) {throw new RuntimeException("The database caching thread is still alive?!?");}}}

参考

Redis实战

Redis实战相关代码,目前有Java,JS,node,Python

2.Redis 命令参考

代码地址

https://github.com/guoxiaoxu/...

后记

如果你有耐心读到这里,请允许我说明下:

  • 1、因为技术能力有限,没有梳理清另外两小节,待我在琢磨琢磨。后续补上。
  • 2、看老外写的书像看故事一样,越看越精彩。不知道你们有这种感觉么?
  • 3、越学越发现自己需要补充的知识太多了,给我力量吧,欢迎点赞。
  • 4、感谢所有人,感谢SegmentFault,让你见证我脱变的过程吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_743618.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

给网站配置免费的HTTS证书

原文&#xff1a;http://www.ymq.io/2018/06/08/certs-https/ ACME 使用 LetEncrypt 证书作为博客的 https 实现方式。 acme.sh 实现了 acme 协议, 可以从 letsencrypt 生成免费的证书. github https://github.com/Neilpang/acme.sh 主要步骤: 安装 acme.sh生成证书copy 证书到…

iis ckfinder目录权限_使用WebDeploy部署远程IIS网站

目录 使用WebDeploy部署远程IIS网站后台服务部署服务器配置本地WebDeploy发布文件配置前端页面部署WebDeploy服务端配置WebDeploy发布文件配置使用WebDeploy部署远程网站后台服务部署服务器配置打开IIS管理器(开始->控制面板->管理工具->IIS管理器)添加网站(右键网站…

php登录判断系统是否维护,phpcms v9判断用户是否登录_CMS系统建站教程

phpcms v9搜索功能怎么做_CMS系统建站教程phpcms v9实现搜索功能的方法&#xff1a;首先进入phpcms后台&#xff1b;然后依次选择“模块->全站搜索->添加搜索分类”&#xff1b;接着添加“form”表单并设置“typeid”&#xff1b;最后修改“list.html”搜索页面文件即可。…

网站集群需什么服务器,集群服务器的特点有哪些

经常说到的租用服务器&#xff0c;我们会考虑很多关于技术方面的问题&#xff0c;而最新出现的服务器集群可能一些站长并不怎么了解&#xff0c;本文主要介绍一下集群服务器有哪些主要的特点。第一&#xff0c;高可用用。一般来说&#xff0c;实用性很强的产品它都需要保持全天…

手游传奇架设教程_传奇开服架设教程:传奇网站添加个性音乐教程

大家晚上好啊&#xff0c;这两天我可能和音乐杠上了&#xff0c;打开的传奇sf网站比较多&#xff0c;发现有部分网站的背景音乐真的该换了&#xff0c;音乐响起吓人一跳&#xff0c;一首好音乐可以让玩家在浏览网站时配合光柱效果被吸引、震撼到&#xff0c;从而下载登录器体验…

百度云服务器网站打不开,让人失望的百度云虚拟主机bch

主机吧从今年1月开始&#xff0c;就抱着新鲜的态度购买的百度云虚拟主机&#xff0c;本来以为一个小小博客用最小空间即可&#xff0c;结果提示内存不够&#xff0c;又换大&#xff0c;三个月内一直不断升级&#xff0c;直到升级到现在的1G内存型号的&#xff0c;价格续费也高达…

宝塔搭建的网站怎么去掉首页的html,宝塔面板怎么搭建WordPress网站

宝塔Linux面板是提升运维效率的服务器管理软件&#xff0c;支持一键LAMP/LNMP/集群/监控/网站/FTP/数据库/JAVA等100多项服务器管理功能。分享如何通过宝塔面板搭建WordPress网站前期准备&#xff1a;1.一台搭建好宝塔面板的云服务器(国内的主机需要备案)2.解析好的域名3.下载好…

建网站必须要用mysql_手把手教你从零开始用WordPress建站

最近&#xff0c;很多人来问我&#xff0c;怎么做一个实际项目&#xff1f;其实这个说简单也简单&#xff0c;说复杂也复杂&#xff0c;但是不管怎么样&#xff0c;从今天开始&#xff0c;一步一步的走&#xff0c;你肯定可以做一个属于自己的站点。之前我也在群里说了&#xf…

gravatar配置和使用【让你的网站使用全球通用头像】

前言 我们在很多博客或者网站留言&#xff0c;评论的时候会看到有的人头像很酷很个性化&#xff0c;但是这个博客和网站本身并没有提供设置头像的功能&#xff0c;感觉有点神奇&#xff0c;那么是怎么做到的呢&#xff1f;其实这是使用了Gravatar。 Gravatar是Globally Recogni…

技术书源码下载网站分享

华章科技&#xff1a;www.hzbook.com 6.18买了一些技术书回来&#xff0c;其中包括Spring实战第五版&#xff0c;书中提示去异步社区https://www.epubit.com/books下载配套源码&#xff0c;于是访问该网站 确实是可以下载的&#xff0c;不用图书编号什么的 于是我试了试其他…

电子书下载网站

1淘链客 http://www.toplinks.cc/s/ 2云海电子图书馆&#xff1a;http://www.pdfbook.cn/ 3 国学网&#xff1a;http://www.guoxue.com/

[转载 js] YUI解决mouseout事件冒泡的办法

原文出处&#xff1a;http://design.alibaba-inc.com/?qnode/727&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&am…

如何构建高扩展性网站?

本篇通过阅读《高扩展性网站的50条原则》&#xff0c;总结出以下内容。 一方面博主没有实际的架构经验&#xff0c;另一方面知识面也不够宽阔&#xff0c;所以只能系统的总结书中的要点&#xff0c;并根据自己的理解做些归纳。 主要内容 本书从多个方面围绕高扩展性提出了50条建…

电商网站jQuery放大镜代码

分享一款电商网站jQuery放大镜代码。这是一款基于jquery.elevatezoom插件实现的类似淘宝放大镜代码&#xff0c;提供40多种参数&#xff0c;可自由配置多种效果&#xff0c;适合电商或图片类网站使用。效果图如下&#xff1a; 在线预览 源码下载 实现的代码。 html代码&…

8月上旬国内网站流量统计TOP5: 百度腾讯坚守前二

IDC评述网&#xff08;idcps.com&#xff09;08月18日报道&#xff1a;根据中国互联网协会-中国网站排名公布的最新数据显示&#xff0c;截至2015年8月16日&#xff0c;国内网站独立访问量前五位依次是百度、腾讯网、搜狗、网易、微博。详细情况如下图所示&#xff1a;观察上图…

从上百幅架构图中学得半点大型网站建设经验(上)

从上百幅架构图中学大型网站建设经验&#xff08;上&#xff09;引言近段时间以来&#xff0c;通过接触有关海量数据处理和搜索引擎的诸多技术&#xff0c;常常见识到不少精妙绝伦的架构图。除了每每感叹于每幅图表面上的绘制的精细之外&#xff0c;更为架构图背后所隐藏的设计…

cefsharp 加载网页慢_网站访问慢的排查方案(最详细)

说实话&#xff0c;比起网站打不开&#xff0c;网站访问慢更让人抓狂。因为造成网站访问慢的因素太多了&#xff0c;一般用户根本无从下手&#xff01;任他千头万绪的问题&#xff0c;从以下三个方面入手&#xff0c;也能轻松破解访问慢的大难题 &#xff1a;响应时间、执行时间…

无法从该网站添加应用_教大家如何在iOS13的iPhone苹果手机和iPad的主屏幕上添加网站...

是否想将网站直​​接放在iPhone或iPad主屏幕上&#xff0c;以实现超级便捷的访问?如果您有一个经常访问的网站(当然像osxdaily.com)&#xff0c;则可能需要将该网站添加到iPhone或iPad的主屏幕中。这会将所选网站的图标放置在设备主屏幕上&#xff0c;然后可以像其他任何应用…

[asp.net]网站数据安全之验证码

数据安全是网站实现必不可少的其中一环&#xff0c;其中最基本的就有防止暴力破解这一类的机器人攻击。 机器人攻击&#xff0c;顾名思义&#xff0c;单纯地由程序算法计算出用户名对应的密码&#xff0c;达到破解账户的功能。 机器人攻击的原理如下&#xff1a; 网页与服务器是…

定制 LAMP 网站服务平台

安装环境&#xff1a;系统&#xff1a;CentOS release 6.2 (Final)Mysql: mysql-5.1.62.tar.gz http:httpd-2.2.25.tar.gzphp: php-5.4.19.tar.gz前期准备:先将需要的软件包拖到虚拟服务器root上&#xff0c;如下图[rootlocalhost ~]# unzip LAMP.zip[rootlocalhost ~]# yum …