Redis 实战篇 ——《黑马点评》(下)

《引言》

        (下)篇将记录 Redis 实战篇 最后的一些学习内容,希望大家能够点赞、收藏支持一下 Thanks♪ (・ω・)ノ,谢谢大家。

传送门(上):Redis 实战篇 ——《黑马点评》(上)

传送门(中):Redis 实战篇 ——《黑马点评》(中)

传送门(下):当-前-页


四、好友关注

1.关注和取关

        在业务中,用户之间存在着常见的关注功能,通过关注 up主来及时的获取其更新的内容。我们可以关注取关对应的用户。

        想要实现关注与取关功能,首先会在访问页面时查询当前用户与展示的用户之间的关系并展示出来,点击后根据现有关系发送请求进行关注或是取关。

public Result isFollow(Long followUserId) {Long userId = UserHolder.getUser().getId();Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();return Result.ok(count > 0);
}

        查询用户之间的关系,先获取当前用户的 Id,通过其进行查询得到个数,如果查询得到的个数大于 0 则存在返回 true 表示状态为已关注,反之则为 false 表示未关注。

public Result follow(Long followUserId, Boolean isFollow) {Long userId = UserHolder.getUser().getId();if (isFollow){//关注Follow follow = new Follow();follow.setUserId(userId);follow.setFollowUserId(followUserId);boolean isSuccess = save(follow);}else {//取消关注remove(new QueryWrapper<Follow>().eq("user_id",userId).eq("follow_user_id",followUserId));}return Result.ok();
}

        该方法用于进行关注与取关操作,当点击按钮发送请求后,根据传来的 isFollow 值来判断当前进行的操作是关注或是取关。关注操作需要现将其存入数据库中,取关就是根据 Id 从数据库中删除相应的数据。

         点击关注后效果如下所示,上方提示操作成功,同时按钮状态变为取消关注,再次点击后取消关注


2.共同关注

        想要实现查看共同关注功能,需要先完善查看其他用户主页的功能,其主要通过查询用户及其发布的博客来展示在用户的主页上。

@GetMapping("/{id}")
public Result queryUserById(@PathVariable("id") Long userId){// 查询详情User user = userService.getById(userId);if (user == null) {return Result.ok();}UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);// 返回return Result.ok(userDTO);
}

        根据用户 Id 查询用户的信息并返回。

@GetMapping("/of/user")
public Result queryBlogByUserId(@RequestParam(value = "current", defaultValue = "1") Integer current,@RequestParam("id") Long id) {// 根据用户查询Page<Blog> page = blogService.query().eq("user_id", id).page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据List<Blog> records = page.getRecords();return Result.ok(records);
}

        根据用户 Id 查询用户的博客 信息并返回。

点击其他用户头像可以查看其个人信息,效果如下所示

        接下来,就需要实现查看共同关注的功能。而我们可以借助 Redis 中 Set 数据类型的 SINTER 方法来查看两个 Set 集合中的交集部分,我们利用这一点,就可以简单地实现共同关注功能。

        首先,我们需要在关注用户时将信息同步的存入到 Redis 的对应用户的集合中,其中存入 Redis 中的 key 前缀为 follows 后接当前用户 Id 以作区分 。所以在原先的关注功能中关注后存入 Redis 中,同样的,在取消关注的操作成功后,将 Redis 中的数据也删除掉。

public Result follow(Long followUserId, Boolean isFollow) {Long userId = UserHolder.getUser().getId();String followKey = "follows:" + userId;if (isFollow){//关注Follow follow = new Follow();follow.setUserId(userId);follow.setFollowUserId(followUserId);boolean isSuccess = save(follow);if(isSuccess){//关注成功后,存入 Redis 中stringRedisTemplate.opsForSet().add(followKey,followUserId.toString());}}else {//取消关注boolean isSuccess = remove(new QueryWrapper<Follow>().eq("user_id",userId).eq("follow_user_id",followUserId));if (isSuccess) {stringRedisTemplate.opsForSet().remove(followKey, followUserId.toString());}}return Result.ok();
}

        接着,我们就要去实现查询共同关注的功能:

public Result followCommons(Long id) {//获取当前用户Long userId = UserHolder.getUser().getId();String key = "follows:" + userId;String key2 = "follows:" + id;//求交集Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key, key2);if (intersect == null || intersect.isEmpty()){return Result.ok(Collections.emptyList());}//解析 IdList<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());//查询用户List<UserDTO> users = userService.listByIds(ids).stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());return Result.ok(users);
}

        获取当前用户 Id 后,拼接对应的字符串,分别对应自己与正在查看的用户,后用 intersect 方法求取交集,由于其中可能包含多个 Id,通过 stream 流的方式可以高效便捷的处理查询得到的 Id 并通过其进行查询得到对应的用户信息返回,用于展示在页面上。

最终效果如下所示


3.关注推送

        在我们关注某一用户后,当其再次发布新的博客后,系统应将其推送给所有关注他的用户,例如微信的朋友圈。这种方式也称作 Feed 流,可以将内容自动推送给用户。Feed 流种常见的模式:

① TimeLine:对内容进行简单的筛选,按照内容发布时间排序,常用于好友或关注。例如朋友圈

  • 优点:信息全面,不会有缺失。并且实现也相对简单
  • 缺点:信息噪音较多,用户不一定感兴趣,内容获取效率低

② 智能排序:利用智能算法屏蔽掉违规的、用户不感兴趣的内容,推送用户感兴趣信息来吸引用户。例如抖音

  • 优点:投喂用户感兴趣信息,用户粘度很高,容易沉迷
  • 缺点:如果算法不精准,可能起到反作用

        这里选择使用 TimeLine 模式来实现关注后推送最新博客的功能,而这种模式的实现方式又分为种:

  • 拉模式:也称做读扩散,被关注者会将消息先存入发件箱中,关注者获取消息时会从发件箱中拉取消息到收件箱中。但这种模式在用户关注大量用户的情况下拉取消息时的延迟较大。
  • 推模式:也称作写扩散,被关注者会直接将消息推送到关注者的收件箱中,关注者只需查看收件箱即可。但这种模式在用户被大量用户关注的情况下会发送大量消息,导致内存的大量消耗。适合用户量少,没有大量粉丝的用户的场景。
  • 推拉结合模式:根据具体的情况来分配给不同用户不同的模式。对于访问频繁、活跃的用户,对其使用推模式,保证其第一时间获取消息;对于不经常活跃的用户,对其使用拉模式,只有在其登录上线时主动的拉取发件箱的消息到收件箱中。适合用户量庞大,存在拥有大量粉丝的用户的场景。

        在了解了上述的三种实现方式的优劣后,选择通过推模式来实现关注推送的功能。而在选择 Redis 中的数据结构来保存消息时,尤其是消息会随着时间不断的更新的情况下,不会选择使用 List 而是使用 SortedSet,因为 List 不支持滚动分页查询,其只支持角标或是首尾查询;而 SortedSet 可以通过分数(score)来排序,支持范围查询和分页查询,更适合处理消息随时间更新的情况。

        ● 第一步:需要先改造原先的发布博客的部分代码,在发布后将对应博客的 Id 发送给所有粉丝来实现推送。

public Result saveBlog(Blog blog) {// 1.获取登录用户UserDTO user = UserHolder.getUser();blog.setUserId(user.getId());// 2.保存探店博文boolean isSuccess = save(blog);if (!isSuccess){return Result.fail("新增笔记失败");}// 3.查询笔记作者的所有粉丝List<Follow> follows = followService.query().eq("follow_user_id", user.getId()).list();// 4.推送笔记for (Follow follow : follows) {Long userId = follow.getId();String key = "feed:" + userId;stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMill}// 5.返回idreturn Result.ok(blog.getId());
}

        首先,获取当前登录用户的 Id 并保存博客信息。接着查询当前用户的所用粉丝信息,循环推送给每一个粉丝。其中 key 为 feed:+ 粉丝 Id 组成,对应每一个粉丝都有一个收件箱,value 为博客的 Id,分数为当前的时间戳,便于在展示时进行排序。 

        ● 第二步:在粉丝查询关注的用户更新的博客时,要实现对于博客内容的分页查询,而由于收件箱中的博客是按照时间来进行排序的,每当有新的内容时,按照角标查询得到的内容可能就会出现重复内容的错误,所以就需要通过分数来进行查询,记录每次查询的最后位置,从该位置继续查询,这样就避免了出现重复内容的错误。

Redis 中的对应指令

ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

max:分数的最大值

min:分数的最小值

[WITHSCORES]:可选,查询内容是否带上分数

LIMIT:

        offset:偏移量,从小于等于最大值的第 N + 1 个元素开始查。

        count:查询的数量

        其中,我们只需关注 max offset 这两个参数即可。min 默认为 0count 按照规定确定。max 是当前查询时的时间戳,之后的查询中为上一次查询的数据中最小的得分offset 在第一次查询时为 0之后的查询中为 1,表示跳过上次查询的最后一个数据,同时如果出现查询出来得分相同的数据这种状况时,重复出现 n 次,偏移量(offset)就为 n 次。

@Data
public class ScrollResult {private List<?> list;private Long minTime;private Integer offset;
}

        首先,创建一个类来存储用于返回滚动分页查询的数据。

@Override
public Result queryBlogOfFollow(Long max, Integer offset) {//1.获取当前用户Long userId = UserHolder.getUser().getId();//2.查询收件箱String key = FEED_KEY + userId;Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 3);//3.解析数据if (typedTuples == null || typedTuples.isEmpty()){return Result.ok();}List<Long> ids = new ArrayList<>(typedTuples.size());long minTime = 0;int os = 1;for (ZSetOperations.TypedTuple<String> tuple : typedTuples) {ids.add(Long.valueOf(tuple.getValue()));long time = tuple.getScore().longValue();if (time == minTime) {os++;}else {minTime = time;os = 1;}}//4.根据 Id 查询 blogString idStr = StrUtil.join(",", ids);List<Blog> blogs = query().in("id", ids).last("order by field(id," + idStr + ")").list();for (Blog blog : blogs) {//查询笔记状态queryBlogUser(blog);isBlogLiked(blog);}//5.封装并返回ScrollResult r = new ScrollResult();r.setList(blogs);r.setOffset(os);r.setMinTime(minTime);return Result.ok(r);
}

        传入的两个参数分别对应 Redis 命令中的 max offset,获取到当前用户的 id 与前缀进行拼接后查询收件箱得到对应关注用户的更新博客的 id 集合。判断数据非空后对集合进行处理,主要是将其中的 id 存入新的集合中,并用 os 统计其中具有相同最小时间的数据的个数作为下一次查询的 offset

        接着就是根据博客的 id 从数据库中进行查询得到所有博客,但 MP 中普通的 listById 方法是根据 in 来进行查询的,并不能进行排序,所以需要通过 order by 来排序,其中的 idStr 通过 StrUtil 中的 join 方法拼接后得到。

private void queryBlogUser(Blog blog) {Long userId = blog.getUserId();User user = userService.getById(userId);blog.setName(user.getNickName());blog.setIcon(user.getIcon());
}
private void isBlogLiked(Blog blog) {if (UserHolder.getUser() == null) {//用户未登录时无需查询是否点赞return;}Long userId = UserHolder.getUser().getId();String key = BLOG_LIKED_KEY + blog.getId();Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());blog.setIsLike(score != null);
}

        最后,所有的博客的需要进行状态的判断,用于在页面中展示正确的信息,判断后将数据分装进先前定义好的实体类中返回。

最终效果如下所示


五、附近商户

1.Redis 中的 GEO 数据结构

        GEO 就是 Geolocation 的简写形式,代表地理坐标。Redis 在 3.2 版本中加入了对 GEO 的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。常见的命令有:

  • GEOADD:添加一个地理空间信息,包含:经度(longitude) 、纬度(latitude) 、值(member)
  • GEODIST:计算指定的两个点之间的距离并返回
  • GEOHASH:将指定 member 的坐标转为hash字符串形式并返回
  • GEOPOS:返回指定 member 的坐标
  • GEORADIUS:指定圆心、半径,找到该圆内包含的所有 member,并按照与圆心之间的距离排序后返回。
  • GEOSEARCH:在指定范围内搜索 member,并按照与指定点之间的距离排序后返回。范围可以是圆形矩形
  • GEOSEARCHSTORE:GEOSEARCH 功能一致,不过可以把结果存储到一个指定的 key。

        利用 Redis 中 GEO 这种数据结构,可以简单的实现查询附近商铺这种与地理位置信息有关的需求,并且可以根据用户当前位置获取附近商铺的信息。


2.附近商户搜索

        在原先的页面中查看商铺时选择根据距离进行查询时就需要根据地理位置来进行搜索,并在每个商铺右边显示出相距的距离大小,我们可以使用 Redis 中的 GEO 来十分简单的实现这个功能。 

@Test
void loadShopData() {//1.查询店铺信息List<Shop> list = shopService.list();//2.根据类型分组,得到类型id与店铺的映射关系(stream流)Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));for (Map.Entry<Long, List<Shop>> entry : map.entrySet()) {Long typeId = entry.getKey();String key = "shop:geo:" + typeId;List<Shop> value = entry.getValue();List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(value.size());//3.写入redisfor (Shop shop : value) {locations.add(new RedisGeoCommands.GeoLocation<>(shop.getId().toString(),new Point(shop.getX(), shop.getY())));}stringRedisTemplate.opsForGeo().add(key, locations);}
}

        而我们需要先将数据库中商铺的信息存入 Redis 中,并且根据商铺的类型(typeId)分别进行存储,这样在查询时也避免了需要先分类再进行查询,直接按照类型进行查询即可。

注意:因为版本的问题,Redis 中 GEO 类型的一些命令是 6.2 版本后提供的,所以需要保证 SpringBoot 中 SpringDataRedis 的版本支持这些命令

@Override
public Result queryShopByTypeId(Integer typeId, Integer current, Double x, Double y) {// 检查输入坐标是否为null,如果是,则执行类型分页查询if (x == null || y == null){// 根据类型分页查询Page<Shop> page = query().eq("type_id", typeId).page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));// 返回数据return Result.ok(page.getRecords());}// 计算查询的起始和结束范围int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;int end = current * SystemConstants.DEFAULT_PAGE_SIZE;...

        在 Impl 层中,首先对传入的坐标进行判断,不为空则接着计算分页的参数。

...
// 拼接Redis地理信息键名String key = SHOP_GEO_KEY + typeId;// 使用Redis地理信息命令,查询指定坐标附近的商店GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(key,GeoReference.fromCoordinate(new Point(x, y)),new Distance(5000),RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end));// 如果查询结果为空,返回空列表if (results == null){return Result.ok(Collections.emptyList());}List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();// 检查结果列表是否足够覆盖查询的起始范围if (list.size() <= from){return Result.ok(Collections.emptyList());}
...

        接着,将商铺类型 id 与前缀进行拼接后进行查询得到地理位置信息,其中 Distance 表示查询的范围。对得到的结果进行非空判断,然后检查结果是否小于起始值,因为这个分页查询是先查询出总数据后按照范围取出数据展示,如果起始值大于总数,则会造成程序出错

    ...// 初始化商店ID列表和距离映射List<Long> ids = new ArrayList<>(list.size());Map<String, Distance> distanceMap = new HashMap<>(list.size());// 遍历结果列表,提取商店ID和距离信息list.stream().skip(from).forEach(result -> {String shopIdStr = result.getContent().getName();ids.add(Long.valueOf(shopIdStr));Distance distance = result.getDistance();distanceMap.put(shopIdStr, distance);});String idStr = StrUtil.join(",", ids);// 根据商店ID查询商店信息List<Shop> shops = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();// 为每个商店设置距离信息for (Shop shop : shops) {shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());}// 返回return Result.ok(shops);
}

         最后将商店 Id 列表和距离初始化后遍历查询结果并将其存入先前初始化好的列表中。再利用 query 方法根据商铺 Id 查询商铺信息,最后将数据返回即可。

最终效果如下所示


六、用户签到

1.BitMap

        签到功能作为一个耳熟能详的功能常常出现在一些游戏和软件中,作为其中的常客,其主要是通过一些签到奖励来激励用户登录。使用数据库实现时其每个用户每天的签到情况会产生大量的数据,而在 Redis 中可以使用 String 类型来实现 BitMap(位图)这种思路,来简单的存储用户的签到信息。

         通过上面的结构,我们可以简单的用少量的内存存储大量的签到信息。、

BitMap的操作命令有:

  • SETBIT:向指定位置(offset)存入一个 0 或 1
  • GETBIT:获取指定位置(offset)的 bit 值
  • BITCOUNT:统计 BitMap 中值为 1 的 bit 位的数量
  • BITFIELD:操作(查询、修改、自增)  BitMap 中 bit 数组中的指定位置(offset)的值
  • BITFIELD_RO:获取 BitMap 中 bit 数组,并以十进制形式返回
  • BITOP:将多个 BitMap 的结果做位运算(与、或、异或)
  • BITPOS:查找 bit 数组中指定范围内第一个 0 或 1 出现的位置

        其中用到的主要是 SETBIT BITFIELD

SETBIT:

        操作如上图所示,需要注意需要选择 Binary 才能看到对应的格式。

BITFIELD:

         其中 type 表示获取的数量,offset 表示从第几个开始。


2.实现签到功能

        通过 Redis 来实现签到功能,不需要任何参数,只需通过用户 Id 当前时间进行拼接后作为 key 来存储即可。

public Result sign() {//1.获取当前登录用户Long userId = UserHolder.getUser().getId();//2.获取日期LocalDateTime now = LocalDateTime.now();//3.拼接 KeyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = USER_SIGN_KEY + userId + keySuffix;//4.获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();//5.写入 RedisstringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);return Result.ok();
}

        首先获取当前登录用户的 Id 以及当前时间。拼接后再获取今天是本月第几天,用于确定在 BitMap 签到的位置,且注意 getDayOfMonth 方法获取的数是从 1 开始的,而 BitMap 的下标是从 0 开始的,所以方法结果需要 - 1 后再写入。

最终效果如下所示

        发送签到请求后成功的写入到了 Redis 中。注意需要在请求头中添加 authorization 参数否则请求会失败。


3.统计连续签到次数

·        想要统计从本月开始到今天为止的连续签到次数,就需要先先获取本月到今天为止的所有签到信息,然后遍历信息,统计连续签到的次数。其中,可以使用位运算来实现遍历功能,完成连续签到次数的统计。

public Result signCount() {//1.获取当前登录用户Long userId = UserHolder.getUser().getId();//2.获取日期LocalDateTime now = LocalDateTime.now();//3.拼接 KeyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = USER_SIGN_KEY + userId + keySuffix;//4.获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();List<Long> result = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));...

        首先还是获取到当前登录的用户 Id 和当前时间,拼接后查询从本月的第一天到今天的所有签到信息。其中 bitField 中包含两个参数,一个是 key,一个是子命令。子命令中设置范围

    ...if (result == null || result.isEmpty()){return Result.ok(0);}Long num = result.get(0);if (num == null){return Result.ok(0);}int count = 0;while(true){if((num & 1) == 0) {//为 0 未签到,结束break;}else {//不为 0 已签到,计数器++count++;}//将数字无符号右移一位抛弃最后一个 bit 位num >>>= 1;}return Result.ok(count);
}

        之后对查询得到的数据进行非空校验,通过后开始遍历。与 1 进行与运算获取最后一位并对其进行判断,为 0 则直接退出循环,不为 0 则计数后将数字右移移除最后一位数继续循环。结束统计后直接返回计数结果。

最终效果如下所示

        测试后可以看到成功统计出了本月的连续签到次数。


七、UV 统计

        首先,需要了解什么是 UV

  • UV:全称 Unique Visitor,也叫独立访客量,是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站,只记录1次
  • PV:全称Page View,也叫页面访问量或点击量,用户每访问网站的一个页面,记录1次PV,用户多次打开页面,则记录多次PV。往往用来衡量网站的流量。

        想要统计这种大量的数据,就需要使用到 Redis 中的 HyperLogLog 了。其基于 String 类型实现,其占用的内存极小,而代价就是其测量的结果是概率性的,但对于 UV 统计这种大数据量来说误差是可以忽略的。

        根据上图可知,HyperLogLog 不会将重复的内容统计。

后续的测试  o(´^`)o跳过


【下】完结

 传送门(上):Redis 实战篇 ——《黑马点评》(上)

 传送门(中):Redis 实战篇 ——《黑马点评》(中)

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

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

相关文章

WordPress二次开发实现用户注册审核功能

WordPress默认直接注册登录了&#xff0c;不需要任何验证&#xff0c;如果被批量注册就麻烦了&#xff0c;所以添加一个审核功能比较好。 注册用户默认需要手动审核&#xff0c;审核以后才能登陆&#xff0c;开启审核&#xff0c;可以有效防止用户批量注册。 这儿讲解一下如何…

基于SpringBoot的“青少年心理健康教育网站”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“青少年心理健康教育网站”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统总体结构图 实体属性图 系统首页界…

湖仓一体概述

湖仓一体之前&#xff0c;数据分析经历了数据库、数据仓库和数据湖分析三个时代。 首先是数据库&#xff0c;它是一个最基础的概念&#xff0c;主要负责联机事务处理&#xff0c;也提供基本的数据分析能力。 随着数据量的增长&#xff0c;出现了数据仓库&#xff0c;它存储的是…

React:B站评论demo,实现列表渲染、删除按钮显示和功能实现、导航栏渲染切换及高亮显示、评论区的排序

功能要求&#xff1a; 1、渲染评论列表 2、删除评论功能&#xff1a;只显示自己评论的删除按钮&#xff1b;点击删除按钮&#xff0c;删除当前评论&#xff0c;列表中不再显示。 3、渲染导航Tab&#xff08;最新 | 最热&#xff09;和其 高亮实现 4、评论排序功能实现&…

springboot系列教程(三十一):springboot整合Nacos组件,环境搭建和入门案例详解

一、Nacos基础简介 1、概念简介 Nacos 是构建以“服务”为中心的现代应用架构&#xff0c;如微服务范式、云原生范式等服务基础设施。聚焦于发现、配置和管理微服务。Nacos提供一组简单易用的特性集&#xff0c;帮助开发者快速实现动态服务发现、服务配置、服务元数据及流量管…

python爬虫报错信息解决方法

今天遇到了这样一条报错&#xff1a; opt/conda/envs/python35-paddle120-env/bin/python /home/aistudio/work/main.py aistudiojupyter-10415006-8838159:~$ /opt/conda/envs/python35-paddle120-env/bin/python /home/aistudio/work/main.py Traceback (most recent call la…

如何快速的解除oracle dataguard

有些时候&#xff0c;我们为了使oracle dg的standby库另做他用&#xff0c;需要解除oracle dataguard数据同步。我本地因为standby库存储出现故障&#xff0c;导致dg存在问题&#xff0c;故需要解除。今天&#xff0c;我们通过使用部分命令&#xff0c;实现dg的快速解除。 1&a…

【告别双日期面板!一招实现el-date-picker智能联动日期选择】

告别双日期面板&#xff01;一招实现el-date-picker智能联动日期选择 1.需求背景2.DateTimePicker 现状图3.日期选择器实现代码4.日期选择器实现效果图5.日期时间选择器实现代码6.日期时间选择器实现效果图 1.需求背景 在用户使用时间查询时&#xff0c;我们经常需要按月份筛选…

Git GitHub基础

git是什么&#xff1f; Git是一个分布式版本控制系统&#xff0c;用于管理源代码的变更。它允许多个开发者在同一个项目上协作&#xff0c;同时跟踪每个修改的历史记录。 关键词&#xff1a; 分布式版本控制软件 软件 安装到我们电脑上的一个工具 版本控制 例如论文&…

汽车无人驾驶系统中的防撞设计

一、系统方案介绍 无人驾驶汽车的防撞系统是保障行车安全的核心模块&#xff0c;本文设计的系统以STM32F103C8T6单片机为主控制器&#xff0c;结合超声波测距、WiFi通信、人机交互等模块&#xff0c;实现障碍物实时检测、动态阈值设置、多级报警和数据可视化功能。系统通过软…

深度学习笔记——线性回归的从0开始实现

记录学习到的知识&#xff1a; 语义分割是将标签或类别与图片的每个像素关联的一种深度学习算法。 它用来识别构成可区分类别的像素集合。 图像分割是一个端到端图像分析过程&#xff0c;它将数字图像分成多个片段&#xff0c;并对每个区域中包含的信息进行分类。三种图像分割…

神经网络 - 激活函数(ReLU 函数)

一、ReLU函数&#xff1a; ReLU(Rectified Linear Unit&#xff0c;修正线性单元)&#xff0c;也叫 Rectifier 函数 &#xff0c;是目前深度神经网络中经常使用的激活函数&#xff0c;ReLU 实际上是一个斜坡(ramp)函数&#xff0c;其定义为&#xff1a; 也即&#xff1a; Re…

(十 一)趣学设计模式 之 组合模式!

目录 一、 啥是组合模式&#xff1f;二、 为什么要用组合模式&#xff1f;三、 组合模式的实现方式四、 组合模式的优缺点五、 组合模式的应用场景六、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&#xff0c;可以多多支…

【MySQL】事务二

事务二 1.数据库并发的场景2.读-写 2.1 3个记录隐藏字段2.2 undo日志2.3 模拟 MVCC2.4 Read View2.5 RR 与 RC的本质区别 3.读-读4.写-写 点赞???收藏???关注??? 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧??? 关于事务的所有知识上篇博客我们都说过了&…

面向AI 的前端发展及初识大模型

AI带来的开发范式迁移 随着AI的涌现&#xff0c;对前端的发展也有着非常大的影响&#xff0c;总结过去前端的发展路径&#xff0c;目前应该属于又一次的大规模的开发范式迁移阶段。上一个阶段是从jquery到React/Vue/Angular迁移&#xff08;jquery之前的就不讨论了&#xff09…

本地部署大语言模型-DeepSeek

DeepSeek 是国内顶尖 AI 团队「深度求索」开发的多模态大模型&#xff0c;具备数学推理、代码生成等深度能力&#xff0c;堪称"AI界的六边形战士"。 Hostease AMD 9950X/96G/3.84T NVMe/1G/5IP/RTX4090 GPU服务器提供多种计费模式。 DeepSeek-R1-32B配置 配置项 规…

第三百七十二节 JavaFX教程 - JavaFX HTMLEditor

JavaFX教程 - JavaFX HTMLEditor HTMLEditor控件是一个富文本编辑器&#xff0c;具有以下功能。 粗体斜体下划线删除线字体系列字体大小前景色背景颜色缩进项目符号列表编号列表对齐水平线复制文本片段粘贴文本片段 HTMLEditor类返回HTML字符串中的编辑内容。 创建HTML编辑器…

java后端开发day25--阶段项目(二)

&#xff08;以下内容全部来自上述课程&#xff09; 1.美化界面 private void initImage() {//路径分两种&#xff1a;//1.绝对路径&#xff1a;从盘符开始写的路径 D:\\aaa\\bbb\\ccc.jpg//2.相对路径&#xff1a;从当前项目开始写的路径 aaa\\bbb\\ccc.jpg//添加图片的时…

Vscode通过Roo Cline接入Deepseek

文章目录 背景第一步、安装插件第二步、申请API key第三步、Vscode中配置第四步、Deepseek对话 背景 在前期介绍【IDEA通过Contince接入Deepseek】步骤和流程&#xff0c;那如何在vscode编译器中使用deepseek&#xff0c;记录下来&#xff0c;方便备查。 第一步、安装插件 在…

计算机毕业设计SpringBoot+Vue.js智能无人仓库管理系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…