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

#Redis黑马点评#(七)实战篇完结

目录

一 达人探店

1 发布探店笔记

2 查看探店笔记

3 点赞功能

​编辑

4 点赞排行榜(top5)

​编辑

二 好友关注

1 关注与取关

2 共同关注

3 Feed流实现关注推送

4 实现滚动分页查询

三 附近商店

1 GEO数据结构

2 附近商户搜索功能

四 用户签到

1 BitMap

2 签到功能

3 签到统计

五 UV统计

1 HyperLogLog


一 达人探店

1 发布探店笔记

上传成功并实现回显功能(这里保存到本地当中)

这里定义了相关方法(同时在配置类当中定义了文件保存的地址)

package com.hmdp.controller;import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.Result;
import com.hmdp.utils.SystemConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import java.io.File;
import java.io.IOException;
import java.util.UUID;@Slf4j
@RestController
@RequestMapping("upload")
public class UploadController {@PostMapping("blog")public Result uploadImage(@RequestParam("file") MultipartFile image) {try {// 获取原始文件名称String originalFilename = image.getOriginalFilename();// 生成新文件名String fileName = createNewFileName(originalFilename);// 保存文件image.transferTo(new File(SystemConstants.IMAGE_UPLOAD_DIR, fileName));// 返回结果log.debug("文件上传成功,{}", fileName);return Result.ok(fileName);} catch (IOException e) {throw new RuntimeException("文件上传失败", e);}}@GetMapping("/blog/delete")public Result deleteBlogImg(@RequestParam("name") String filename) {File file = new File(SystemConstants.IMAGE_UPLOAD_DIR, filename);if (file.isDirectory()) {return Result.fail("错误的文件名称");}FileUtil.del(file);return Result.ok();}private String createNewFileName(String originalFilename) {// 获取后缀String suffix = StrUtil.subAfter(originalFilename, ".", true);// 生成目录String name = UUID.randomUUID().toString();int hash = name.hashCode();int d1 = hash & 0xF;int d2 = (hash >> 4) & 0xF;// 判断目录是否存在File dir = new File(SystemConstants.IMAGE_UPLOAD_DIR, StrUtil.format("/blogs/{}/{}", d1, d2));if (!dir.exists()) {dir.mkdirs();}// 生成文件名return StrUtil.format("/blogs/{}/{}/{}.{}", d1, d2, name, suffix);}
}

配置类

package com.hmdp.utils;public class SystemConstants {public static final String IMAGE_UPLOAD_DIR = "D:\\kaifa\\dianp\\nginx-1.18.0\\nginx-1.18.0\\html\\hmdp\\imgs\\";public static final String USER_NICK_NAME_PREFIX = "user_";public static final int DEFAULT_PAGE_SIZE = 5;public static final int MAX_PAGE_SIZE = 10;
}

2 查看探店笔记

Controller控制层

    /*** 查询博文详情** @param id* @return*/@GetMapping("/{id}")public Result queryBlogById(@PathVariable("id") Long id) {return blogService.queryBlogById(id);}

Service业务层

    /*** 根据id查询* @param id* @return*/Result queryBlogById(Long id);

业务层实现类

    /*** 查询笔记详情** @param id* @return*/@Overridepublic Result queryBlogById(Long id) {// 查询blogBlog blog = getById(id);if (blog == null) {return Result.fail("笔记不存在");}//  查询用户queryBlogUser(blog);//查询blog是否被点赞了被点赞之后需要缓存到redis中queryBlogLikes(blog);return Result.ok(blog);}

3 点赞功能

点赞功能使用Redis当中的set(判断用户是否存在点赞过返回数值,没点赞返回空值)

controller控制层

    /*** 点赞博文** @param id* @return*/@PutMapping("/like/{id}")public Result likeBlog(@PathVariable("id") Long id) {return blogService.likeBlog(id);}

Service业务层

    /*** 点赞博客* @param id* @return*/Result likeBlog(Long id);

Service业务层实现类

    /*** 点赞** @param id* @return*/@Overridepublic Result likeBlog(Long id) {// 1判断当前登录用户Long userId = UserHolder.getUser().getId();// 2盘带当前用户是否已经点赞String key = RedisConstants.BLOG_LIKED_KEY + id;Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());// 3如果未点赞,可以点赞if (score == null) {// 3.1数据库点赞数量加1boolean update = update().setSql("liked = liked + 1").eq("id", id).update();// 3.2保存用户点赞记录到Redis集合set当中if (update) {stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());}} else {//  4如果已经点赞// 4.1数据库点赞数量减1boolean update = update().setSql("liked = liked - 1").eq("id", id).update();// 4.2删除用户点赞记录if (update) {stringRedisTemplate.opsForZSet().remove(key, userId.toString());}}// 5返回点赞结果return Result.ok();}

4 点赞排行榜(top5)

这里我们使用的是SortedSet(这里可以使用时间戳来设置score值)

这里没有set当中的isMember但是有zscore获取元素的分数(有的话就返回分数没有就返回空)

Controller控制层

    /*** 点赞列表top5*/@GetMapping("/likes/{id}")public Result queryBlogLikes(@PathVariable("id") Long id) {return blogService.queryBlogLikes(id);}

Service业务层

    /*** 查询博客点赞top5* @param id* @return*/Result queryBlogLikes(Long id);

Service业务层实现类

    /*** 查询笔记点赞用户前五** @param id* @return*/@Overridepublic Result queryBlogLikes(Long id) {// 1查询top5的点赞用户 zrange key 0 4String key = RedisConstants.BLOG_LIKED_KEY + id;Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);if (top5 == null || top5.isEmpty()) {return Result.ok(Collections.emptyList());}// 2解析出用户idList<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());// 将 List<Long> 转换为 List<String>List<String> idStars = ids.stream().map(String::valueOf).collect(Collectors.toList());// 使用 String.join 拼接字符串String idStr = String.join(",", idStars);// 3根据用户id查询用户List<UserDTO> userDTOS = userService.query().in("id", ids).last("order by field(id," + idStr + ")").list().stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());// 4返回用户return Result.ok(userDTOS);}

二 好友关注

1 关注与取关

判断是否在set集合当中

Controller控制层

    /*** 关注或取关* @param followUserId* @param isFollow* @return*/@PutMapping("/{id}/{isFollow}")public Result follow(@PathVariable("id") Long followUserId, @PathVariable("isFollow") Boolean isFollow) {return followService.follow(followUserId, isFollow);}

Servcie业务层

    /*** 关注或取消关注* @param followUserId* @param isFollow* @return*/Result follow(Long followUserId, Boolean isFollow);

Service业务层实现类

    /*** 关注或取关** @param followUserId* @param isFollow* @return*/@Overridepublic Result follow(Long followUserId, Boolean isFollow) {// 1.获取当前登录用户Long userId = UserHolder.getUser().getId();// 2.判断当前登录用户是关注还是取关if (isFollow) {//关注Follow follow = new Follow();follow.setUserId(userId);follow.setFollowUserId(followUserId);boolean save = save(follow);if (save) {//把关注的用户id保存到redis的set集合中String key = "follow:" + userId;stringRedisTemplate.opsForSet().add(key, followUserId.toString());}} else {//取关boolean isSuccess = remove(new QueryWrapper<Follow>().eq("user_id", userId).eq("follow_user_id", followUserId));if (isSuccess) {//把关注的用户id从redis的set集合中移除String key = "follow:" + userId;stringRedisTemplate.opsForSet().remove(key, followUserId.toString());}}return Result.ok();}

2 共同关注

SINTER user:1001:friends user:1002:friends 利用共同元素的原理

根据id查询用户

    /*** 查询用户** @param —— userid 用户id* @return*/@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查询用户的博文

    /*** 查询用户博文** @param current* @param id* @return*/@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);}

获取共同关注(当前登录用户与被访问的用户的共同关注)

    /*** 获取共同关注* @param id* @return*/@GetMapping("/common/{id}")public Result followCommons(@PathVariable("id") Long id) {return followService.followCommons(id);}

Service业务层

    /*** 共同关注** @param id* @return*/Result followCommons(Long id);

Service业务层实现类(使用Redis的Set存储用户的关注列表,键格式为follow:{userId},值为被关注用户的ID集合。)SINTER命令求两个集合的交集,返回共同关注的用户ID集合。

    /*** 查询共同关注** @param id* @return*/@Overridepublic Result followCommons(Long id) {Long userId = UserHolder.getUser().getId();String key = "follow:" + userId;String key2 = "follow:" + id;Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key, key2);if (intersect == null || intersect.isEmpty()) {return Result.ok(Collections.emptyList());}List<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);}

3 Feed流实现关注推送

feed直译为投喂的意思。为用户持续的提供沉浸式的体验,通过无线下拉实现获取新的信息。

Feed流的模式

我们使用的是对关注列表用户发送博文时间的推送(推模式)

实现方式(推拉)

1 推模式:

实现原理当用户发送内容是系统自动会将内容推送到所有关注者的收件箱当中。

会导致写入的压力过大,导致内容在多个收件箱都重复存储,有些不登录的用户也会被推送。

2 拉模式:

用户查看Feed,系统会对用户的关注对象的信息进行拉取。

写入的成本会下降,但是关注数较多的用户会出现查询性能下降的问题。

3 推拉结合:

分用户策略

大V用户:存在一个发件箱,活跃粉丝直接推送给其收件箱,而普通粉丝上线再从发件箱当中拉去信息。

普通用户:采用推模式,推送的压力较小,可直接推送给粉丝用户。

首先修改:在发布博文时需要将信息推送给粉丝用户

    /*** 发布博文** @param blog* @return*/@Overridepublic Result saveBlog(Blog blog) {// 获取登录用户UserDTO user = UserHolder.getUser();blog.setUserId(user.getId());// 保存探店博文boolean save = save(blog);if (!save) {return Result.fail("新增笔记失败");}// 查询笔记作者的所有粉丝List<Follow> follows = followService.query().eq("follow_user_id", user.getId()).list();// 将笔记的id给所有粉丝for (Follow follow : follows) {// 获取粉丝idLong userId = follow.getUserId();// 给粉丝发送消息String key = RedisConstants.FEED_KEY + userId;stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());}// 返回idreturn Result.ok(blog.getId());}

4 实现滚动分页查询

Controller控制层

    /*** 分页滚动查询* @param max* @param offset* @return*/@GetMapping("/of/follow")public Result queryBlogOfFollow(@RequestParam("lastId") Long max, @RequestParam(value = "offset", defaultValue = "0") Integer offset) {return blogService.queryBlogOfFollow(max, offset);}

Service业务层接口

    /*** 分页查询博客* @param max* @param offset* @return*/Result queryBlogOfFollow(Long max, Integer offset);

Service业务层实现类

    /*** 滚动分页查询笔记** @param max* @param offset* @return*/@Overridepublic Result queryBlogOfFollow(Long max, Integer offset) {// 1获取当前用户Long userId = UserHolder.getUser().getId();String key = RedisConstants.FEED_KEY + userId;// 2查询收件箱 ZREVRANGEBYSCORE key  max min LIMIT offset countSet<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2);if (typedTuples == null || typedTuples.isEmpty()) {return Result.ok();}// 3解析数据blogId,minTime,offsetList<Long> ids = new ArrayList<>(typedTuples.size());long minTime = 0;int os = 1;for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {//获取idids.add(Long.valueOf(typedTuple.getValue()));// 获取分数(时间戳)long time = typedTuple.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);//查询blog是否被点赞了被点赞之后需要缓存到redis中queryBlogLikes(blog);}// 5封装blogScrollResult scrollResult = new ScrollResult();scrollResult.setList(blogs);scrollResult.setOffset(os);scrollResult.setMinTime(minTime);return Result.ok(scrollResult);}

在传统的角标分页查询中(如LIMIT offset, count),若数据发生增删会导致分页错乱(如重复加载或遗漏数据)。为解决这一问题,我们基于Redis的Sorted Set特性,通过分数(时间戳)范围和偏移量(offset)实现稳定分页。

1. 数据存储与查询基础

  • Sorted Set结构:以时间戳作为分数(score),动态ID作为值(value),按分数倒序排列(新数据在前)。

  • 分页目标:每次查询固定数量的数据,并精准定位下一页起始位置,避免数据变动导致分页混乱。

Set<TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2);
  • key:Sorted Set的键名(如用户收件箱feed:1001)。

  • min=0:分数下限(闭区间),固定为0以查询所有历史数据。

  • max:分数上限(闭区间),首次查询设为当前时间戳,后续查询使用上一页的最小分数(即更早的时间)。

  • offset:偏移量,表示从当前分数段的第几条开始取数据。初始值为0(从第一条开始),若上一页存在相同分数的数据,则传递其数量用于跳过已读记录。

  • count=2:每页数量,此处硬编码为2条。

三 附近商店

1 GEO数据结构

Redis 的 GEO 是专门用于处理地理位置信息的数据类型,基于 Sorted Set(有序集合) 实现,并通过 GeoHash 编码 将二维的经纬度信息转换为一维的数值,支持高效的地理位置存储、查询和计算。

2 附近商户搜索功能

这里的登录用户的地址信息便于接口的实现是写死的,同时这里的分页使用的是传统分页

根据商户类型做分组,类型相同的为同一组,一typeId为key存入同一个GEO集合当中即可。

首先为了准备数据先将店铺当中分类好的数据写入Redis当中

    @Testpublic void loadShopData() {// 1 查询店铺信息List<Shop> list = shopService.list();// 2 将店铺分组(每个Long类型的数值对应一个List集合)Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));// 3 分批写入Redisfor (Map.Entry<Long, List<Shop>> entry : map.entrySet()) {// 3.1 获取类型idLong typeId = entry.getKey();String key = "shop:geo:" + typeId;// 3.2 获取同类型的店铺集合List<Shop> value = entry.getValue();List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(value.size());// 3.3 写入redis geoadd key lng lat memberfor (Shop shop : value) {// 3.3.1 获取店铺的经纬度Double x = shop.getX();Double y = shop.getY();String shopId = shop.getId().toString();locations.add(new RedisGeoCommands.GeoLocation<>(shopId, new Point(x, y)));}stringRedisTemplate.opsForGeo().add(key, locations);}}

实现结果展示

按照距离进行排序分页展示

代码实现:

Controller控制层

    /*** 根据类型分页查询商铺信息** @param typeId 商铺类型* @param current 页码* @return 商铺列表*/@GetMapping("/of/type")public Result queryShopByType(@RequestParam("typeId") Integer typeId,@RequestParam(value = "current", defaultValue = "1") Integer current,@RequestParam(value = "x", required = false) Double x,@RequestParam(value = "y", required = false) Double y) {return shopService.queryShopByType(typeId, current, x, y);}

Service业务层接口

    /*** 根据商铺类型分页查询商铺信息** @param typeId 商铺类型* @param current 页码* @param x 纬度* @param y 经度* @return 商铺列表*/Result queryShopByType(Integer typeId, Integer current, Double x, Double y);

Service业务层实现类

    /*** 根据类型查询店铺信息** @param typeId  类型id* @param current 页码* @param x       x坐标* @param y       y坐标* @return Result*/@Overridepublic Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {// 1判断是否需要根据坐标查询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());}// 2计算分页参数int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;int end = current * SystemConstants.DEFAULT_PAGE_SIZE;// 3 查询Redis按照距离排序分页 结果:shopId、distanceString key = RedisConstants.SHOP_GEO_KEY + typeId;GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(key,GeoReference.fromCoordinate(x, y),new Distance(5000),RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end));// 4 解析出idif (results == null) {return Result.ok(Collections.emptyList());}List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();if (list.size() <= from) {// 4.1没有下一页数据return Result.ok(Collections.emptyList());}// 4.2截取使用skipList<Long> ids = new ArrayList<>(list.size());Map<String, Distance> distanceMap = new HashMap<>(list.size());list.stream().skip(from).forEach(result -> {// 4.3获取店铺idString shopId = result.getContent().getName();ids.add(Long.valueOf(shopId));// 4.4获取距离Distance distance = result.getDistance();distanceMap.put(shopId, distance);});// 5 根据id查询String idStr = StrUtil.join(",", ids);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());}// 6 返回return Result.ok(shops);}

结果展示:

四 用户签到

1 BitMap

BitMap(位图)是一种基于 二进制位(bit) 的高效数据结构,通过每个二进制位的值(0或1)表示某种状态或标记。Redis 中虽然没有独立的 BitMap 类型,但通过 String 类型 的位操作命令实现了 BitMap 的功能。

常用命令

使用:

命令作用示例
SETBIT key offset 0/1设置指定偏移量的位值(0或1)SETBIT sign:202310 100 1(用户100已签到)
GETBIT key offset获取指定偏移量的位值GETBIT sign:202310 100 → 1
BITCOUNT key [start end]统计值为1的位数(支持字节范围)BITCOUNT sign:202310 → 30(30人签到)
BITOP operation destkey key1 key2对多个BitMap进行位运算(AND/OR/XOR/NOT)BITOP AND active_users user:day1 user:day2

2 签到功能

代码实现:

Controller控制层

    /*** 签到功能* @return*/@PostMapping("/sign")public Result sign() {return userService.sign();}

Service业务层接口

    /*** 签到功能* @return*/Result sign();

Service业务层实现类

    /*** 签到功能** @return*/@Overridepublic Result sign() {//  1.获取当前用户Long userId = UserHolder.getUser().getId();//  2.获取日期String keySuffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM"));//  3.拼接keyString key = RedisConstants.USER_SIGN_KEY + userId + ":" + keySuffix;//  4.获取今天是本月的第几天int dayOfMonth = LocalDateTime.now().getDayOfMonth();//  5.写入redis setBit key offset valuestringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);return Result.ok();}

3 签到统计

需求:

代码实现:

Controller控制层

    /*** 统计当前连续签到天数* @return*/@GetMapping("/sign/count")public Result signCount() {return userService.signCount();}

Service业务层接口

    /*** 统计当前连续签到天数* @return*/Result signCount();

Service业务层实现类

    /*** 签到** @return {@link Result}*/@Overridepublic Result signCount() {// 获取本月所有的签到天数记录//  1.获取当前用户Long userId = UserHolder.getUser().getId();//  2.获取日期String keySuffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM"));//  3.拼接keyString key = RedisConstants.USER_SIGN_KEY + userId + ":" + keySuffix;//  4.获取今天是本月的第几天int dayOfMonth = LocalDateTime.now().getDayOfMonth();// 5.获取本月截至当前为止所有的签到数据,返回数据为一个十进制List<Long> result = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));if (result == null || result.isEmpty()) {return Result.ok(0);}Long num = result.get(0);if (num == null || num == 0) {return Result.ok(0);}// 6.循环遍历int dayCount = 0;while (true) {// 7.判断当前日期是否被签到if ((num & 1) == 0) {break;} else {// 8.计数器+1dayCount++;// 9.右移num >>>= 1;}}return Result.ok(dayCount);}

五 UV统计

1 HyperLogLog

实现:

实现结果:

相关文章:

#Redis黑马点评#(七)实战篇完结

目录 一 达人探店 1 发布探店笔记 2 查看探店笔记 3 点赞功能 ​编辑 4 点赞排行榜&#xff08;top5&#xff09; ​编辑 二 好友关注 1 关注与取关 2 共同关注 3 Feed流实现关注推送 4 实现滚动分页查询 三 附近商店 1 GEO数据结构 2 附近商户搜索功能 四 用户…...

初始C++:类和对象(中)

概述&#xff1a;本篇博客主要介绍类和对象的相关知识。 1. 类的默认成员函数 默认成员函数就是用户没有显示实现&#xff0c;编译器会自动生成的成员函数称为默认成员函数。一个类&#xff0c;在不写任何代码的情况下编译器会默认生成以下六个默认函数&#xff0c;在六个默认…...

Java开发经验——阿里巴巴编码规范实践解析3

摘要 本文深入解析了阿里巴巴编码规范中关于错误码的制定与管理原则&#xff0c;强调错误码应便于快速溯源和沟通标准化&#xff0c;避免过于复杂。介绍了错误码的命名与设计示例&#xff0c;推荐采用模块前缀、错误类型码和业务编号的结构。同时&#xff0c;探讨了项目错误信…...

ChatGPT:OpenAI Codex—一款基于云的软件工程 AI 代理,赋能 ChatGPT,革新软件开发模式

ChatGPT&#xff1a;OpenAI Codex—一款基于云的软件工程 AI 代理&#xff0c;赋能 ChatGPT&#xff0c;革新软件开发模式 导读&#xff1a;2025年5月16日&#xff0c;OpenAI 发布了 Codex&#xff0c;一个基于云的软件工程 AI 代理&#xff0c;它集成在 ChatGPT 中&#xff0c…...

iOS 内存分区

iOS内存分区 文章目录 iOS内存分区前言五大分区static、extern、const关键字比较conststaticextern与.h文件的关系extern引用变量extern声明 static和const联合使用extern和const联合使用 前言 笔者之前学习OC源码的时候,发现对于这里的几个static,extern,const的内容有遗忘,所…...

LWIP的Socket接口

Socket接口简介 类似于文件操作的一种网络连接接口&#xff0c;通常将其称之为“套接字”。lwIP的Socket接口兼容BSD Socket接口&#xff0c;但只实现完整Socket的部分功能 netconn是对RAW的封装 Socket是对netconn的封装 SOCKET结构体 struct sockaddr { u8_t sa_len; /* 长…...

【Linux笔记】——线程同步条件变量与生产者消费者模型的实现

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;Linux &#x1f339;往期回顾&#x1f339;&#xff1a;【Linux笔记】——线程互斥与互斥锁的封装 &#x1f516;流水不争&#xff0c;争的是滔滔不息 一、线程同步的…...

Popeye

概览与定位 Popeye 是由 derailed 团队开源的 Kubernetes 集群资源 “Sanitizer”&#xff0c;它以只读方式扫描集群内的各种资源&#xff08;如 Pod、Service、Ingress、PVC、RBAC 等&#xff09;&#xff0c;并基于社区最佳实践给出问题等级及修复建议&#xff0c;覆盖配置误…...

ES(ES2023/ES14)最新更新内容,及如何减少内耗

截至2023年10月,JavaScript(ECMAScript)的最新版本是 ES2023(ES14)。 ES2023 引入了许多新特性,如findLast、toSorted等,同时优化了性能。通过减少全局变量、避免内存泄漏、优化循环、减少DOM操作、使用Web Workers、懒加载、缓存、高效数据结构和代码压缩,可以显著降低…...

电子数据取证(数字取证)技术全面指南:从基础到实践

为了后续查阅方便&#xff0c;推荐工具先放到前面 推荐工具 数字取证基础工具 综合取证平台 工具名称类型主要功能适用场景EnCase Forensic商业全面的证据获取和分析、强大的搜索能力法律诉讼、企业调查FTK (Forensic Toolkit)商业高性能处理和索引、集成内存分析大规模数据处…...

【通用智能体】Serper API 详解:搜索引擎数据获取的核心工具

Serper API 详解&#xff1a;搜索引擎数据获取的核心工具 一、Serper API 的定义与核心功能二、技术架构与核心优势2.1 技术实现原理2.2 对比传统方案的突破性优势 三、典型应用场景与代码示例3.1 SEO 监控系统3.2 竞品广告分析 四、使用成本与配额策略五、开发者注意事项六、替…...

基于 STM32 的手持式安检金属探测器设计与实现

一、硬件设计:芯片与功能模块选型及接线 1. 主控芯片选型 芯片型号:STM32F103C8T6 核心优势: 32 位 Cortex-M3 内核,主频 72MHz,满足实时数据处理需求64KB Flash+20KB SRAM,支持程序存储与数据缓存丰富外设:2 路 USART、2 路 SPI、1 路 I2C、12 位 ADC,适配多模块通信…...

虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系

虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系 code review! 文章目录 虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系1.Default Pawn与Camera的关系1.1. Default Pawn 是什么&#xff1f;1.2. Default Pawn 的主要组件1.3. Default…...

Spring源码主线全链路拆解:从启动到关闭的完整生命周期

Spring源码主线全链路拆解&#xff1a;从启动到关闭的完整生命周期 一文看懂 Spring 框架从启动到销毁的主线流程&#xff0c;结合原理、源码路径与伪代码三位一体&#xff0c;系统学习 Spring 底层机制。 1. 启动入口与环境准备 原理说明 Spring Boot 应用入口是标准 Java 应…...

飞帆控件:可配置post/get接口

先上链接&#xff1a; post_get_ithttps://fvi.cn/796看一下这个控件的配置&#xff1a; 当 url 有某个 get 参数时&#xff0c;例如某些接口回传的参数。使用这个接口会发生这些&#xff1a; 如果检测到 url 中有该 url 参数则继续执行选择是否从 url 中删除该参数将这个参数…...

Android 自定义悬浮拖动吸附按钮

一个悬浮的拨打电话按钮&#xff0c;使用CardViewImageView可能会出现适配问题&#xff0c;也就是图片显示不全&#xff0c;出现这种问题&#xff0c;就直接替换控件了&#xff0c;因为上述的组合控件没有FloatingActionButton使用方便&#xff0c;还可以有拖动和吸附效果不是更…...

Spring AI Alibaba集成阿里云百炼大模型应用

文章目录 1.准备工作2.引入maven依赖3.application.yml4.调用4.1.非流式调用4.2.流式调用 阿里云百炼推出的智能体应用、工作流应用和智能体编排应用&#xff0c;有效解决了大模型在处理私有领域问题、获取最新信息、遵循固定流程以及自动规划复杂项目等方面的局限&#xff0c;…...

UI-TARS本地部署

UI-TARS本地部署 UI-TARS本地部署 UI-TARS 论文&#xff08;arXiv&#xff09; UI-TARS 官方仓库&#xff1a;包含部署指南、模型下载链接及示例代码。 UI-TARS-Desktop 客户端&#xff1a;支持本地桌面应用的交互控制。 模型部署框架&#xff1a;vLLM本地部署 1.下载项目…...

如何利用内网穿透实现Cursor对私有化部署大模型的跨网络访问实践

文章目录 前言1.安装Ollama2.QwQ-32B模型安装与运行3.Cursor安装与配置4. 简单使用测试5. 调用本地大模型6. 安装内网穿透7. 配置固定公网地址总结 前言 本文主要介绍如何在Windows环境下&#xff0c;使用Cursor接入Ollama启动本地部署的千问qwq-32b大模型实现辅助编程&#x…...

Linux的进程概念

目录 1、冯诺依曼体系结构 2、操作系统(Operating System) 2.1 基本概念 ​编辑 2.2 目的 3、Linux的进程 3.1 基本概念 3.1.1 PCB 3.1.2 struct task_struct 3.1.3 进程的定义 3.2 基本操作 3.2.1 查看进程 3.2.2 初识fork 3.3 进程状态 3.3.1 操作系统的进程状…...

(10)python开发经验

文章目录 1 cp35 cp36什么意思2 找不到pip3 subprocess编码错误4 导出依赖文件包含路径5 使用自己编译的python并且pyinstall打包程序 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;Qt开发 &#x1f448;&#x1f449;python开发 &#x1f448; 1 cp35 cp36什…...

什么是时间戳?怎么获取?有什么用

时间戳的定义 时间戳&#xff08;Timestamp&#xff09;是指记录某个事件发生的具体时间点&#xff0c;通常以特定的格式表示。它可以精确到秒、毫秒甚至更小的单位&#xff0c;用于标识某个时刻在时间轴上的位置。 获取时间戳的方法 在不同的编程语言中&#xff0c;获取时间…...

Zookeeper 入门(二)

4. Zookeeper 的 ACL 权限控制( Access Control List ) Zookeeper 的ACL 权限控制,可以控制节点的读写操作,保证数据的安全性&#xff0c;Zookeeper ACL 权 限设置分为 3 部分组成&#xff0c;分别是&#xff1a;权限模式&#xff08;Scheme&#xff09;、授权对象&#xff08…...

[创业之路-361]:企业战略管理案例分析-2-战略制定-使命、愿景、价值观的失败案例

一、失败案例 1、使命方面的失败案例 真功夫创业者内乱&#xff1a;真功夫在创业过程中&#xff0c;由于股权结构不合理&#xff0c;共同创始人及公司大股东潘宇海与实际控制人、董事长蔡达标产生管理权矛盾。双方在公司发展方向、管理改革等方面无法达成一致&#xff0c;导致…...

dijkstra算法加训上 之 分层图最短路

来几个分层图的题练习下哈 P4568 [JLOI2011] 飞行路线 P4568 [JLOI2011] 飞行路线 - 洛谷https://www.luogu.com.cn/problem/P4568 题目描述 Alice 和 Bob 现在要乘飞机旅行&#xff0c;他们选择了一家相对便宜的航空公司。该航空公司一共在 n 个城市设有业务&#xff0c;设这…...

赋予AI更强的“思考”能力

刚刚&#xff01;北大校友、OpenAI前安全副总裁Lilian Weng最新博客来了&#xff1a;Why We Think 原文链接&#xff1a;Why We Think by Lilian Weng 这篇文章关注&#xff1a;如何让AI不仅仅是“知道”答案&#xff0c;更能“理解”问题并推导出答案。通过赋予AI更强的“思…...

微服务项目->在线oj系统(Java版 - 1)

相信自己,终会成功 目录 C/S架构与B/S架构 C/S架构&#xff08;Client/Server&#xff0c;客户端/服务器架构&#xff09; 特点&#xff1a; 优点&#xff1a; 缺点&#xff1a; 典型应用&#xff1a; B/S架构&#xff08;Browser/Server&#xff0c;浏览器/服务器架构&a…...

【深度学习】使用块的网络(VGG)

虽然 AlexNet 证明深层神经网络卓有成效&#xff0c;但它没有提供一个通用的模板来指导后续的研究人员设计新的网络。 也就是说尽管我知道了更深更大的网络更有效&#xff0c;但是不清楚怎么让它更深更大&#xff0c;从而起到一个更好的效果。 于是&#xff0c;研究人员开始从单…...

Python数据可视化 - Pyecharts绘图示例

文章目录 一、Pyecharts简介及安装1. Pyecharts简介2. 安装Pyecharts 二、准备数据三、饼图示例1. 初始化选项配置2. 饼图相关设置3. 全局配置项3.1 标题配置项3.2 图例配置项3.3 提示框配置项3.4 工具箱配置项3.5 视觉映射配置项 4. 系列配置项4.1 标签选项配置4.2 图元样式配…...

Day29

复习日 知识点回顾 类的装饰器装饰器思想的进一步理解&#xff1a;外部修改、动态类方法的定义&#xff1a;内部定义和外部定义 作业&#xff1a;复习类和函数的知识点&#xff0c;写下自己过去29天的学习心得&#xff0c;如对函数和类的理解&#xff0c;对python这门工具的理…...

Python列表全面解析:从入门到精通

文章目录 Python列表全面解析&#xff1a;从入门到精通一、列表基础1. 什么是列表&#xff1f;2. 列表特性总结表 二、列表的基本操作(基础)1. 访问元素2. 修改列表 三、列表的常用方法(基础)1. 添加元素的方法2. 删除元素的方法3. 查找和统计方法4. 排序和反转 四、列表的高级…...

Nacos数据写入流程

在 3 节点的 Nacos 集群中&#xff0c;数据写入流程和主节点&#xff08;Leader&#xff09;的角色基于 Nacos 的分布式一致性协议&#xff08;通常使用 Raft 协议&#xff09;来实现。以下以 Markdown 格式详细说明 3 节点 Nacos 集群的数据写入流程以及主节点的角色和确定方式…...

《P4551 最长异或路径》

题目描述 给定一棵 n 个点的带权树&#xff0c;结点下标从 1 开始到 n。寻找树中找两个结点&#xff0c;求最长的异或路径。 异或路径指的是指两个结点之间唯一路径上的所有边权的异或。 输入格式 第一行一个整数 n&#xff0c;表示点数。 接下来 n−1 行&#xff0c;给出…...

Ansible模块——文件属性查看,文件或目录创建和属性修改

ansible.builtin.stat 可以查看文件信息。 选项 类型 默认值 描述 pathstrnull 要检查的文件或目录的完整路径&#xff08;必需&#xff09;。 followboolfalse 如果是符号链接&#xff0c;是否跟随到目标路径上获取其状态。 get_attributesbooltrue 是否返回扩展属性&#…...

【图像生成大模型】Wan2.1:下一代开源大规模视频生成模型

Wan2.1&#xff1a;下一代开源大规模视频生成模型 引言Wan2.1 项目概述核心技术1. 3D 变分自编码器&#xff08;Wan-VAE&#xff09;2. 视频扩散 Transformer&#xff08;Video Diffusion DiT&#xff09;3. 数据处理与清洗 项目运行方式与执行步骤1. 环境准备2. 安装依赖3. 模…...

AGI大模型(25):LangChain提示词模版

我们也可以创建prompt template, 并引入一些变量到prompt template中,这样在应用的时候更加灵活。 1 代码实现 # 我们也可以创建prompt template, 并引入一些变量到prompt template中,这样在应用的时候更加灵活 from langchain_core.prompts import ChatPromptTemplate from…...

mybatis中的resultMap的association及collectio的使用

目录 1.reusltmap的说明 2.association的使用 3.collection的使用 4.总结 1.reusltmap的说明 resultmap定义了数据库的结果映射到java对象的规则&#xff0c;resultmap包含4个属性&#xff1a; id: ResultMap 的唯一标识 type: 映射到的 Java 类型&#xff08;全限定类名或…...

静态网站部署:如何通过GitHub免费部署一个静态网站

GitHub提供的免费静态网站托管服务可以无需担心昂贵的服务器费用和复杂的设置步骤&#xff0c;本篇文章中将一步步解如何通过GitHub免费部署一个静态网站&#xff0c;帮助大家将创意和作品快速展现给世界。 目录 了解基础情况 创建基础站点 在线调试站点 前端项目部署 部署…...

Android 手写签名功能详解:从原理到实践

Android 手写签名功能详解 1. 引言2. 手写签名核心实现&#xff1a;SignatureView 类3. 交互层实现&#xff1a;MainActivity 类4. 布局与配置5. 性能优化与扩展方向 1. 引言 在电子政务、金融服务等移动应用场景中&#xff0c;手写签名功能已成为提升用户体验与业务合规性的关…...

【iOS(swift)笔记-9】WKWebView无法访问网络

对于iOS 在info中添加App Transport Security Settings&#xff0c;然后在App Transport Security Settings里添加Allow Arbitrary Loadstrue 对于macOS 除了上面的操作&#xff0c;还需在项目信息的App Sandbox里有个Network打钩选项...

Adapter适配器模式

Adapter适配器模式是一种结构设计模式&#xff0c;用于解决接口不兼容的问题&#xff0c;通过适配器类&#xff0c;可以将一个类的接口转换为客户渴望的另一个接口&#xff0c;从而使原来无法协作的对象能够一起工作。 角色和职责&#xff1a; 目标接口&#xff08;Target&…...

七、xlib窗口渲染

文章目录 1.渲染图片2.双缓冲3.混合图片4.渐变窗口 1.渲染图片 在上篇文章中的最后&#xff0c;我们使用libpng加载了一个png图片&#xff0c;并显示到窗口上&#xff0c;但是我们可以看到显示到窗口的图片周边有黑色的背景。原因是在我测试的操作系统下使用xlib创建的窗口默认…...

python中http.cookiejar和http.cookie的区别

在Python中&#xff0c;http.cookiejar和http.cookie&#xff08;通常指http.cookies模块&#xff09;是两个不同的模块&#xff0c;它们的主要区别如下&#xff1a; 1. 功能定位 http.cookiejar 用于管理HTTP客户端的Cookie&#xff0c;提供自动化的Cookie存储、发送和接收功…...

架构设计模式:构建健壮、可扩展的 Serverless 应用

架构设计模式:构建健壮、可扩展的 Serverless 应用 到目前为止,我们已经掌握了 Serverless 的基本概念,了解了 FaaS 和 BaaS 如何协同工作,学会了使用框架进行开发部署,并知道了如何监控和排查问题。现在,是时候从“能用”向“好用”迈进了。 仅仅将代码部署到 Lambda 函…...

2- PyTorch

文章目录 1. Overview2. 线性模型 1. Overview 在人的智能中&#xff0c;最经常做的事情是推理和预测&#xff0c;在机器学习中也是如此。我们在以往的算法课中&#xff0c;所接触的穷举、贪心、分治和动规等算法都是由人设计的&#xff0c;而在机器学习中&#xff0c;算法是由…...

MinIO:从入门到精通,解锁云原生存储的奥秘

一、引言&#xff1a;为什么 MinIO 正在重塑存储世界&#xff1f; 在云计算和大数据时代&#xff0c;传统存储系统面临扩展性差、成本高、兼容性不足等挑战。MinIO 凭借其 S3 兼容性、分布式架构、高性能存储 等特性&#xff0c;成为企业构建现代化存储基础设施的首选。 本文…...

【LeetCode 热题100】739:每日温度(详细解析)(Go语言版)

&#x1f321;️ LeetCode 739&#xff1a;每日温度&#xff08;详解 单调栈 多种思路对比&#xff09; &#x1f4cc; 题目描述 给定一个整数数组 temperatures&#xff0c;表示每天的温度&#xff0c;返回一个数组 answer&#xff0c;其中 answer[i] 是指在第 i 天之后&am…...

Linux学习笔记|GCC编译指令基础|静动态库|makefile

一、GCC 编译指令基础 基本编译命令 gcc -o code code.c和gcc code.c -o code&#xff1a;这两条命令功能相同&#xff0c;都是使用 GCC 编译器将code.c源文件编译成名为code的可执行文件。-o选项用于指定输出文件名&#xff0c;选项位置在源文件前后不影响最终结果。 编译过程…...

【LeetCode 热题100】17:电话号码的字母组合(详细解析)(Go语言版)

☎️ LeetCode 17. 电话号码的字母组合&#xff08;回溯 DFS 详解&#xff09; &#x1f4cc; 题目描述 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按任意顺序返回。 数字到字母的映射如下&#xff08;与电话按键相同&#xff09;…...

C++学习:六个月从基础到就业——C++17:std::optional/variant/any

C学习&#xff1a;六个月从基础到就业——C17&#xff1a;std::optional/variant/any 本文是我C学习之旅系列的第四十七篇技术文章&#xff0c;也是第三阶段"现代C特性"的第九篇&#xff0c;主要介绍C17引入的三个重要工具类型&#xff1a;std::optional、std::varia…...