当前位置: 首页 > news >正文

保定网站建设技术支持营销策划公司怎么收费

保定网站建设技术支持,营销策划公司怎么收费,商务酒店网站建设,服务器证书与网站不符Redis 学习笔记 3#xff1a;黑马点评 准备工作 需要先导入项目相关资源#xff1a; 数据库文件 hmdp.sql后端代码 hm-dianping.zip包括前端代码的 Nginx 启动后端代码和 Nginx。 短信登录 发送验证码 PostMapping(code) public Result sendCode(RequestP…Redis 学习笔记 3黑马点评 准备工作 需要先导入项目相关资源 数据库文件 hmdp.sql后端代码 hm-dianping.zip包括前端代码的 Nginx 启动后端代码和 Nginx。 短信登录 发送验证码 PostMapping(code) public Result sendCode(RequestParam(phone) String phone, HttpSession session) {// 发送短信验证码并保存验证码return userService.sendCode(phone, session); }Log4j2 Service public class UserServiceImpl extends ServiceImplUserMapper, User implements IUserService {Overridepublic Result sendCode(String phone, HttpSession session) {if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail(不是合法的手机号);}String code RandomUtil.randomNumbers(6);session.setAttribute(code, code);// 发送短信log.debug(发送短信验证码{}, code);return Result.ok();} }登录 PostMapping(/login) public Result login(RequestBody LoginFormDTO loginForm, HttpSession session) {// 实现登录功能return userService.login(loginForm, session); }Override public Result login(LoginFormDTO loginForm, HttpSession session) {// 验证手机号和验证码if (RegexUtils.isPhoneInvalid(loginForm.getPhone())) {return Result.fail(手机号不合法);}String code (String) session.getAttribute(code);if (code null || !code.equals(loginForm.getCode())) {return Result.fail(验证码不正确);}// 检查用户是否存在QueryWrapperUser qw new QueryWrapper();qw.eq(phone, loginForm.getPhone());User user this.baseMapper.selectOne(qw);if (user null) {user this.createUserByPhone(loginForm.getPhone());}// 将用户信息保存到 sessionsession.setAttribute(user, user);return Result.ok(); }private User createUserByPhone(String phone) {User user new User();user.setPhone(phone);user.setNickName(user_ RandomUtil.randomString(5));this.baseMapper.insert(user);return user; }统一身份校验 定义拦截器 public class LoginInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 从 session 获取用户信息HttpSession session request.getSession();User user (User) session.getAttribute(user);if (user null) {response.setStatus(401);return false;}// 将用户信息保存到 ThreadLocalUserDTO userDTO new UserDTO();userDTO.setIcon(user.getIcon());userDTO.setId(user.getId());userDTO.setNickName(user.getNickName());UserHolder.saveUser(userDTO);return true;}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();} }添加拦截器 Configuration public class WebMVCConfig implements WebMvcConfigurer {Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(/shop/**,/voucher/**,/shop-type/**,/upload/**,/blog/hot,/user/code,/user/login);} }使用 Redis 存储验证码和用户信息 用 Session 存储验证码和用户信息的系统无法进行横向扩展因为多台 Tomcat 无法共享 Session。如果改用 Redis 存储就可以解决这个问题。 修改后的 UserService Log4j2 Service public class UserServiceImpl extends ServiceImplUserMapper, User implements IUserService {Autowiredprivate StringRedisTemplate stringRedisTemplate;private static final ObjectMapper OBJECT_MAPPER new ObjectMapper();Overridepublic Result sendCode(String phone, HttpSession session) {if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail(不是合法的手机号);}String code RandomUtil.randomNumbers(6);stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY phone, code, LOGIN_CODE_TTL);// 发送短信log.debug(发送短信验证码{}, code);return Result.ok();}Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {// 验证手机号和验证码if (RegexUtils.isPhoneInvalid(loginForm.getPhone())) {return Result.fail(手机号不合法);}String code stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY loginForm.getPhone());if (code null || !code.equals(loginForm.getCode())) {return Result.fail(验证码不正确);}// 检查用户是否存在QueryWrapperUser qw new QueryWrapper();qw.eq(phone, loginForm.getPhone());User user this.baseMapper.selectOne(qw);if (user null) {user this.createUserByPhone(loginForm.getPhone());}// 将用户信息保存到 sessionString token UUID.randomUUID().toString(true);UserDTO userDTO new UserDTO();BeanUtils.copyProperties(user, userDTO);try {stringRedisTemplate.opsForValue().set(LOGIN_USER_KEY token,OBJECT_MAPPER.writeValueAsString(userDTO), LOGIN_USER_TTL);} catch (JsonProcessingException e) {e.printStackTrace();throw new RuntimeException(e);}return Result.ok(token);}private User createUserByPhone(String phone) {User user new User();user.setPhone(phone);user.setNickName(user_ RandomUtil.randomString(5));this.baseMapper.insert(user);return user;} }修改后的登录校验拦截器 public class LoginInterceptor implements HandlerInterceptor {private final StringRedisTemplate stringRedisTemplate;private static final ObjectMapper OBJECT_MAPPER new ObjectMapper();public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 从头信息获取 tokenString token request.getHeader(Authorization);if (ObjectUtils.isEmpty(token)) {// 缺少 tokenresponse.setStatus(401);return false;}// 从 Redis 获取用户信息String jsonUser this.stringRedisTemplate.opsForValue().get(LOGIN_USER_KEY token);UserDTO userDTO OBJECT_MAPPER.readValue(jsonUser, UserDTO.class);if (userDTO null) {response.setStatus(401);return false;}// 将用户信息保存到 ThreadLocalUserHolder.saveUser(userDTO);// 刷新 token 有效期stringRedisTemplate.expire(LOGIN_USER_KEY token, LOGIN_USER_TTL);return true;}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();} }还需要添加一个更新用户信息有效期的拦截器 public class RefreshTokenInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 如果请求头中有 token且 redis 中有 token 相关的用户信息刷新其有效期String token request.getHeader(Authorization);if (ObjectUtils.isEmpty(token)) {return true;}if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(LOGIN_USER_KEY token))) {stringRedisTemplate.expire(LOGIN_USER_KEY token, LOGIN_USER_TTL);}return true;} }添加这个新的拦截器并且确保其位于登录验证拦截器之前 Configuration public class WebMVCConfig implements WebMvcConfigurer {Autowiredprivate StringRedisTemplate stringRedisTemplate;Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns(/**);registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns(/shop/**,/voucher/**,/shop-type/**,/upload/**,/blog/hot,/user/code,/user/login);} }商户查询 缓存 对商户类型查询使用 Redis 缓存以提高查询效率 Service public class ShopTypeServiceImpl extends ServiceImplShopTypeMapper, ShopType implements IShopTypeService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Overridepublic Result queryTypeList() {String jsonTypeList stringRedisTemplate.opsForValue().get(CACHE_TYPE_LIST_KEY);if (!StringUtils.isEmpty(jsonTypeList)) {ListShopType typeList JSONUtil.toList(jsonTypeList, ShopType.class);return Result.ok(typeList);}ListShopType typeList this.query().orderByAsc(sort).list();if (!typeList.isEmpty()){stringRedisTemplate.opsForValue().set(CACHE_TYPE_LIST_KEY, JSONUtil.toJsonStr(typeList), CACHE_TYPE_LIST_TTL);}return Result.ok(typeList);} }对商户详情使用缓存 Service public class ShopServiceImpl extends ServiceImplShopMapper, Shop implements IShopService {private static final ObjectMapper OBJECT_MAPPER new ObjectMapper();Autowiredprivate StringRedisTemplate stringRedisTemplate;Overridepublic Result queryById(Long id) {// 先从 Redis 中查询String jsonShop stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY id);if (!StringUtils.isEmpty(jsonShop)) {Shop shop JSONUtil.toBean(jsonShop, Shop.class);return Result.ok(shop);}// Redis 中没有从数据库查Shop shop this.getById(id);if (shop ! null) {jsonShop JSONUtil.toJsonStr(shop);stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY id, jsonShop, CACHE_SHOP_TTL);}return Result.ok(shop);} }缓存更新策略 在编辑商户信息时将对应的缓存删除 Override public Result update(Shop shop) {if (shop.getId() null) {return Result.fail(商户id不能为空);}// 更新商户信息this.updateById(shop);// 删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY shop.getId());return Result.ok(); }缓存穿透 缓存穿透指如果请求的数据在缓存和数据库中都不存在就不会生成缓存数据每次请求都不会使用缓存会对数据库造成压力。 可以通过缓存空对象的方式解决缓存穿透问题。 在查询商铺信息时缓存空对象 Override public Result queryById(Long id) {// 先从 Redis 中查询String jsonShop stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY id);if (!StringUtils.isEmpty(jsonShop)) {Shop shop JSONUtil.toBean(jsonShop, Shop.class);return Result.ok(shop);}// Redis 中没有从数据库查Shop shop this.getById(id);if (shop ! null) {jsonShop JSONUtil.toJsonStr(shop);stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY id, jsonShop, CACHE_SHOP_TTL);return Result.ok(shop);} else {// 缓存空对象到缓存中stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY id, , CACHE_NULL_TTL);return Result.fail(店铺不存在);} }在这里缓存中的空对象用空字符串代替并且将缓存存活时间设置为一个较短的值比如说2分钟。 在从缓存中查询到空对象时返回商铺不存在 Override public Result queryById(Long id) {// 先从 Redis 中查询String jsonShop stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY id);if (!StringUtils.isEmpty(jsonShop)) {Shop shop JSONUtil.toBean(jsonShop, Shop.class);return Result.ok(shop);}// 如果从缓存中查询到空对象表示商铺不存在if (.equals(jsonShop)) {return Result.fail(商铺不存在);}// ... }缓存击穿 缓存击穿问题也叫热点Key问题就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了无数的请求访问会在瞬间给数据库带来巨大的冲击。 常见的解决方案有两种 互斥锁逻辑过期 可以利用 Redis 做互斥锁来解决缓存击穿问题 Override public Result queryById(Long id) {// return queryWithCachePenetration(id);return queryWithCacheBreakdown(id); }/*** 用 Redis 创建互斥锁** param name 锁名称* return 成功/失败*/ private boolean lock(String name) {Boolean result stringRedisTemplate.opsForValue().setIfAbsent(name, 1, Duration.ofSeconds(10));return BooleanUtil.isTrue(result); }/*** 删除 Redis 互斥锁** param name 锁名称*/ private void unlock(String name) {stringRedisTemplate.delete(name); }/*** 查询店铺信息-缓存击穿** param id* return*/ private Result queryWithCacheBreakdown(Long id) {// 先查询是否存在缓存String jsonShop stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY id);if (!StringUtils.isEmpty(jsonShop)) {Shop shop JSONUtil.toBean(jsonShop, Shop.class);return Result.ok(shop);}// 如果从缓存中查询到空对象表示商铺不存在if (.equals(jsonShop)) {return Result.fail(商铺不存在);}// 缓存不存在尝试获取锁并创建缓存final String lockName lock:shop: id;try {if (!lock(lockName)){// 获取互斥锁失败休眠一段时间后重试Thread.sleep(50);return queryWithCacheBreakdown(id);}// 获取互斥锁成功创建缓存// 模拟长时间才能创建缓存Thread.sleep(100);Shop shop this.getById(id);if (shop ! null) {jsonShop JSONUtil.toJsonStr(shop);stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY id, jsonShop, CACHE_SHOP_TTL);return Result.ok(shop);} else {// 缓存空对象到缓存中stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY id, , CACHE_NULL_TTL);return Result.fail(店铺不存在);}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {// 释放锁unlock(lockName);} }下面是用逻辑过期解决缓存击穿问题的方式。 首先需要将热点数据的缓存提前写入 Redis缓存预热 public class ShopServiceImpl extends ServiceImplShopMapper, Shop implements IShopService {/*** 创建店铺缓存** param id 店铺id* param duration 缓存有效时长*/public void saveShopCache(Long id, Duration duration) {Shop shop getById(id);RedisCacheShop redisCache new RedisCache();redisCache.setExpire(LocalDateTime.now().plus(duration));redisCache.setData(shop);stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY id, JSONUtil.toJsonStr(redisCache));}// ... }SpringBootTest class HmDianPingApplicationTests {Autowiredprivate ShopServiceImpl shopService;Testpublic void testSaveShopCache(){shopService.saveShopCache(1L, Duration.ofSeconds(1));}}Data public class RedisCacheT {private LocalDateTime expire; //逻辑过期时间private T data; // 数据 }Redis 中的缓存信息包含两部分过期时间和具体信息。大致如下 {data: {area: 大关,openHours: 10:00-22:00,sold: 4215,// ...},expire: 1708258021725 }且其 TTL 是-1也就是永不过期。 具体的缓存读取和重建逻辑 /*** 用逻辑过期解决缓存击穿问题** return*/ private Result queryWithLogicalExpiration(Long id) {//检查缓存是否存在String jsonShop stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY id);if (StringUtils.isEmpty(jsonShop)) {// 缓存不存在return Result.fail(店铺不存在);}// 缓存存在检查是否过期RedisCacheShop redisCache JSONUtil.toBean(jsonShop, new TypeReferenceRedisCacheShop() {}, true);if (redisCache.getExpire().isBefore(LocalDateTime.now())) {// 如果过期尝试获取互斥锁final String LOCK_NAME LOCK_SHOP_KEY id;if (lock(LOCK_NAME)) {// 获取互斥锁后单独启动线程更新缓存CACHE_UPDATE_ES.execute(() - {try {// 模拟缓存重建的延迟Thread.sleep(200);saveShopCache(id, Duration.ofSeconds(1));} catch (Exception e) {throw new RuntimeException(e);} finally {unlock(LOCK_NAME);}});}}// 无论是否过期返回缓存对象中的信息return Result.ok(redisCache.getData()); }封装 Redis 缓存工具类 可以对对 Redis 缓存相关逻辑进行封装可以避免在业务代码中重复编写相关逻辑。封装后分别对应以下方法 设置缓存数据TTL设置缓存数据逻辑过期时间从缓存获取数据用空对象解决缓存穿透问题从缓存获取数据用互斥锁解决缓存击穿问题从缓存获取数据用逻辑过期解决缓存击穿问题 工具类的完整代码可以参考这里。 本文的完整示例代码可以从这里获取。 参考资料 黑马程序员Redis入门到实战教程
文章转载自:
http://www.morning.xhjjs.cn.gov.cn.xhjjs.cn
http://www.morning.zlfxp.cn.gov.cn.zlfxp.cn
http://www.morning.wjlnz.cn.gov.cn.wjlnz.cn
http://www.morning.srkzd.cn.gov.cn.srkzd.cn
http://www.morning.dkqbc.cn.gov.cn.dkqbc.cn
http://www.morning.gskzy.cn.gov.cn.gskzy.cn
http://www.morning.mtmnk.cn.gov.cn.mtmnk.cn
http://www.morning.ydtdn.cn.gov.cn.ydtdn.cn
http://www.morning.tkrdg.cn.gov.cn.tkrdg.cn
http://www.morning.dtzxf.cn.gov.cn.dtzxf.cn
http://www.morning.pqcbx.cn.gov.cn.pqcbx.cn
http://www.morning.wjlhp.cn.gov.cn.wjlhp.cn
http://www.morning.lbxcc.cn.gov.cn.lbxcc.cn
http://www.morning.jmmz.cn.gov.cn.jmmz.cn
http://www.morning.cnvlog.cn.gov.cn.cnvlog.cn
http://www.morning.gwgjl.cn.gov.cn.gwgjl.cn
http://www.morning.rqbr.cn.gov.cn.rqbr.cn
http://www.morning.fldrg.cn.gov.cn.fldrg.cn
http://www.morning.ywzqk.cn.gov.cn.ywzqk.cn
http://www.morning.mcfjq.cn.gov.cn.mcfjq.cn
http://www.morning.cbnxq.cn.gov.cn.cbnxq.cn
http://www.morning.jycr.cn.gov.cn.jycr.cn
http://www.morning.bqdgr.cn.gov.cn.bqdgr.cn
http://www.morning.ychrn.cn.gov.cn.ychrn.cn
http://www.morning.ktrzt.cn.gov.cn.ktrzt.cn
http://www.morning.pkggl.cn.gov.cn.pkggl.cn
http://www.morning.fwjfh.cn.gov.cn.fwjfh.cn
http://www.morning.wpqwk.cn.gov.cn.wpqwk.cn
http://www.morning.zknxh.cn.gov.cn.zknxh.cn
http://www.morning.fkrzx.cn.gov.cn.fkrzx.cn
http://www.morning.kqxwm.cn.gov.cn.kqxwm.cn
http://www.morning.lcbgf.cn.gov.cn.lcbgf.cn
http://www.morning.tjqcfw.cn.gov.cn.tjqcfw.cn
http://www.morning.lfdrq.cn.gov.cn.lfdrq.cn
http://www.morning.jbtwq.cn.gov.cn.jbtwq.cn
http://www.morning.xnnpy.cn.gov.cn.xnnpy.cn
http://www.morning.rgpbk.cn.gov.cn.rgpbk.cn
http://www.morning.skdrp.cn.gov.cn.skdrp.cn
http://www.morning.fhrgk.cn.gov.cn.fhrgk.cn
http://www.morning.tbjtm.cn.gov.cn.tbjtm.cn
http://www.morning.qxnlc.cn.gov.cn.qxnlc.cn
http://www.morning.ssqrd.cn.gov.cn.ssqrd.cn
http://www.morning.pnjsl.cn.gov.cn.pnjsl.cn
http://www.morning.gmyhq.cn.gov.cn.gmyhq.cn
http://www.morning.lxwjx.cn.gov.cn.lxwjx.cn
http://www.morning.bpyps.cn.gov.cn.bpyps.cn
http://www.morning.fxpyt.cn.gov.cn.fxpyt.cn
http://www.morning.rsdm.cn.gov.cn.rsdm.cn
http://www.morning.tlpgp.cn.gov.cn.tlpgp.cn
http://www.morning.hsflq.cn.gov.cn.hsflq.cn
http://www.morning.smnxr.cn.gov.cn.smnxr.cn
http://www.morning.jmtrq.cn.gov.cn.jmtrq.cn
http://www.morning.rfpq.cn.gov.cn.rfpq.cn
http://www.morning.lgsfb.cn.gov.cn.lgsfb.cn
http://www.morning.jpzcq.cn.gov.cn.jpzcq.cn
http://www.morning.rmmz.cn.gov.cn.rmmz.cn
http://www.morning.bwttj.cn.gov.cn.bwttj.cn
http://www.morning.rscrj.cn.gov.cn.rscrj.cn
http://www.morning.jzxqj.cn.gov.cn.jzxqj.cn
http://www.morning.tnwgc.cn.gov.cn.tnwgc.cn
http://www.morning.mtsgx.cn.gov.cn.mtsgx.cn
http://www.morning.zcqtr.cn.gov.cn.zcqtr.cn
http://www.morning.jkfyt.cn.gov.cn.jkfyt.cn
http://www.morning.lxlfr.cn.gov.cn.lxlfr.cn
http://www.morning.rcmwl.cn.gov.cn.rcmwl.cn
http://www.morning.nrbqf.cn.gov.cn.nrbqf.cn
http://www.morning.ykrg.cn.gov.cn.ykrg.cn
http://www.morning.wwthz.cn.gov.cn.wwthz.cn
http://www.morning.mqmxg.cn.gov.cn.mqmxg.cn
http://www.morning.hkgcx.cn.gov.cn.hkgcx.cn
http://www.morning.qrqcr.cn.gov.cn.qrqcr.cn
http://www.morning.ccyns.cn.gov.cn.ccyns.cn
http://www.morning.zfcfk.cn.gov.cn.zfcfk.cn
http://www.morning.dfwkn.cn.gov.cn.dfwkn.cn
http://www.morning.sgbjh.cn.gov.cn.sgbjh.cn
http://www.morning.mnsmb.cn.gov.cn.mnsmb.cn
http://www.morning.gqtxz.cn.gov.cn.gqtxz.cn
http://www.morning.tqrbl.cn.gov.cn.tqrbl.cn
http://www.morning.csnmd.cn.gov.cn.csnmd.cn
http://www.morning.tmxtr.cn.gov.cn.tmxtr.cn
http://www.tj-hxxt.cn/news/268003.html

相关文章:

  • 上海市建设安全协会成绩查询的网站wordpress 页面显示最新文章
  • 做智能网站系统下载长沙网站搭建优化
  • 简单电子商务网站建设wordpress教程文档
  • 数学老师做直播的网站上海装修公司排名87
  • 零基础学习网站建设小程序推广赚佣金平台
  • 建设企业网站的需求分析北京网站建设公司 fim
  • 延吉网站建设彩票江苏建设局网站
  • 用python做购物网站酷站网
  • 网站设计风格类型韶关市建设局网站
  • 彩票开发网站建设应该要注意哪些问题兰州学校网站建设
  • 企业网站推广17网站建设与管理学什么
  • 芜湖网站设计怎么给网站做反链
  • 建一个网站大概多少钱电商网站有哪些官网
  • 网站开发 后端青州网站建设优化排名
  • 营销型类型网站多少钱些网站商城注意事项
  • 无锡网站建设价格深圳设计师招聘
  • 网站后台编辑教程黄骅百度贴吧招聘
  • 长沙建设品牌网站眼科医院网站开发策划
  • 湖南营销型网站建设报价网站建设综合实训设计报告
  • iis6.1的网站建设及权限设置wordpress获取文章页id
  • 手机网站建设与制作旅游建设投资公司中网站
  • 虚拟邮箱注册网站网络规划设计师对应中级
  • 马关县住房和城乡建设局网站做网站数据需要的软件
  • 免费下载网站软件建筑工程网络教育网
  • 简单网站建设方案策划wordpress免费版
  • 网站开发者常见问题聊城网站建设费用
  • 高明网站建设哪家好鞋网站建设方案
  • 怎么做微信里的网站链接南山网站建设哪家便宜
  • 南方医科大学精品课程建设网站微网站建设网络
  • 怎么攻击php做的网站网站建设的具体奖罚措施