Redis实战篇笔记(最终篇)

news2024/11/16 12:23:39

Redis实战篇笔记(七)


文章目录

  • Redis实战篇笔记(七)
  • 前言
    • 达人探店
      • 发布和查看探店笔记
      • 点赞
      • 点赞排行榜
    • 好友关注
      • 关注和取关
      • 共同关注
      • 关注推送
      • 关注推荐的实现
  • 总结


前言

本系列文章是Redis实战篇笔记的最后一篇,那么到这里Redis实战篇的内容就要结束了,本系列文件涵盖了Redis作为缓存在实战项目中的大多数用法


达人探店

发布和查看探店笔记

这个两个功能就是普通的业务,没有用到 redis,所以我把他们合到一起了

发布探店笔记
我们点击下面的加号就可以发布一篇探店笔记
image.png
这里的上传图片,如果是一般的业务会上传到一个文件服务器,但是他这里选择上传到了我们的前端服务器
image.png
这个 IMAGE_UPLOAD_DIR 就是前端服务器放图片的地方,要改成自己所对应的位置
image.png
我们像这样就写好了一篇探店笔记,然后我们点发布,就会跳转到个人中心,并且在主页的最后也能看见我们发布的笔记
image.png
image.png
image.png

我们点击看我们刚发布的笔记,会报错,是因为我们还没有实现这个功能,我们点击查看,就可以看到前端访问的接口,接下来我们就去实现它
image.png

**这就是 查看笔记的方法 ** queryBlogById 是我们要实现的方法, querHotBlog 是代码已经写好的。由于没有涉及到 Redis 的操作,这里就不再过多解释了。

@Resource
    private IUserService userService;


    @Override
    public Result queryHotBlog(Integer current) {
        // 根据用户查询
        Page<Blog> page = this.query()
                .orderByDesc("liked")
                .page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));
        // 获取当前页数据
        List<Blog> records = page.getRecords();
        // 查询用户
        records.forEach(this::queryBlogUser);
        return Result.ok(records);
    }

    @Override
    public Result queryBlogById(Long id) {
        // 1. 查询blog
        Blog blog = getById(id);
        if(blog==null){
            return Result.fail("笔记不存在");
        }
        // 2. 查询blog有关的用户
        queryBlogUser(blog);
        return Result.ok(blog);
    }

    private void queryBlogUser(Blog blog) {
        Long userId = blog.getUserId();
        User user = userService.getById(userId);
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
    }

实现后,效果就是这样了。
image.png

点赞

代码中实现的点赞是 连续点赞的功能,但是这样的功能是不好的,如果有人调用这个接口,一直刷赞,数据库直接就爆了,所以我们要对这个功能进行改造

改造后的需求:

  1. 同一个用户只能点赞一次,再次点击则取消点赞
  2. 如果当前用户已经点赞,则点赞按钮高亮显示(前端已实现,判断Blog类的 isLike 属性)

那我们怎样来标记用户是否点赞过? 用 Redis 的 set集合可以实现,set 集合我们已经用了好几次了,set中的元素是不能重复的,可以用来标记

    @Override
    public Result likeBlog(Long id) {
        // 1.获取登录用户
        Long userId = UserHolder.getUser().getId();
        // 2.判断当前登录用户是否已经点赞

        Boolean isMember = stringRedisTemplate.opsForSet().isMember(BLOG_LIKED_KEY, userId.toString());
        // 3.如果未点赞,可以点赞
        if(BooleanUtil.isFalse(isMember)){
             // 起始这里我觉得也可以做一个异步任务,利用 Redis的高效性,去实现与用户的交互
            // 用异步任务来去修改数据库,感觉 Redis 和 数据库都可以这么来用
            // 3.1 数据库点赞数+1
            boolean isUpdate = update().setSql("liked=liked+1").eq("id", id).update();
            // 3.2 保存用户到 Redis的 set 集合
            if(isUpdate){
                stringRedisTemplate.opsForSet().add(BLOG_LIKED_KEY,userId.toString());
            }
        }else {
            // 4. 如果已经点赞,取消点赞
            // 4.1 数据库点赞数 -1
            boolean isUpdate = update().setSql("liked=liked-1").eq("id", id).update();
            // 4.1 把用户从Redis的 set 集合移除
            if(isUpdate){
                stringRedisTemplate.opsForSet().remove(BLOG_LIKED_KEY,userId.toString());
            }
        }
        return Result.ok();
    }

点赞排行榜

我们在探店笔记的详情页面,应该按照点赞的时间显示出来,比如最早点赞的 TOP5,形成点赞排行榜
但是我们刚才在实现点赞功能的时候,用的是 set 集合,但 set 集合中的元素是无序的,这就不符合我们的功能,所以我们要换一个数据结构,即能保留 set 的无序特点,又会使其中的元素有序,那就是 Redis 的 SortedSet
那接下来,我们就要去改造一下我们之前写的点赞功能

其实就是把之前用 set 集合的操作 换成 SortSet

    @Override
    public Result likeBlog(Long id) {
        // 1.获取登录用户
        Long userId = UserHolder.getUser().getId();
        // 2.判断当前登录用户是否已经点赞

        Double score = stringRedisTemplate.opsForZSet().score(BLOG_LIKED_KEY, userId.toString());
        // 3.如果未点赞,可以点赞
        if(score==null){
            // 3.1 数据库点赞数+1
            boolean isUpdate = update().setSql("liked=liked+1").eq("id", id).update();
            // 3.2 保存用户到 Redis的 set 集合
            if(isUpdate){
                stringRedisTemplate.opsForZSet().add(BLOG_LIKED_KEY,userId.toString(),System.currentTimeMillis());
            }
        }else {
            // 4. 如果已经点赞,取消点赞
            // 4.1 数据库点赞数 -1
            boolean isUpdate = update().setSql("liked=liked-1").eq("id", id).update();
            // 4.1 把用户从Redis的 set 集合移除
            if(isUpdate){
                stringRedisTemplate.opsForZSet().remove(BLOG_LIKED_KEY,userId.toString());
            }
        }
        return Result.ok();
    }

    private void isLiked(Blog blog){
        // 1.获取登录用户
        Long userId = UserHolder.getUser().getId();
        // 2.判断当前登录用户是否已经点赞
        Double score = stringRedisTemplate.opsForZSet().score(BLOG_LIKED_KEY, userId.toString());
        blog.setIsLike(score!=null);
    }

**那接下来我们就去实现这个点赞排行榜

@Override
    public Result queryBlogLikes(Long id) {
        String key= BLOG_LIKED_KEY+id;
        // 1. 查询 top5 的点赞用户 zrange key 0 4
        Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
        if(top5==null||top5.isEmpty()){
            return Result.ok(Collections.emptyList());
        }
       // 这里用了大量的 stream流来处理集合,不太懂 stream流的朋友可以先去学习一下stream流
        List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
        String idStr = StrUtil.join(",", ids);
      // 这里的 sql,没有默认的是因为,默认的排序它会按 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());
        return Result.ok(userDTOS);
    }
// 因为未登录用户会获取 user失败,所以这里加一下,其实这样做是不好的,业务容易乱,以后未登录
// 用户要使用的地方肯定还会有,所以建议还是写在 拦截器里。
 private void isLiked(Blog blog){
        // 1.获取登录用户
        UserDTO user = UserHolder.getUser();
        if (user==null){
            return;
        }
        Long userId = user.getId();
        // 2.判断当前登录用户是否已经点赞
        Double score = stringRedisTemplate.opsForZSet().score(BLOG_LIKED_KEY, userId.toString());
        blog.setIsLike(score!=null);
    }

至此,达人探店的模块我们也学完了,在这个模块,我们主要去使用了 Redis 中关于 set 和 SortedSet 的使用。🤗

好友关注

关注和取关

这个功能也没有用到 Redis, 也只是简单的业务,这里也就简单的记一下
其实这也是个挺常见的功能,不跟视频自己也可以手敲出来,这里就只记录 service层的代码

 @Override
    public Result follow(Long followUserId, Boolean isFollow) {
        Long userId = UserHolder.getUser().getId();
        // 1.判断到底是关注还是取关
        if(isFollow==true){
            Follow follow = new Follow();
            follow.setUserId(userId);
            follow.setFollowUserId(followUserId);
            save(follow);
        }else {
            remove(new QueryWrapper<Follow>().eq("user_id",userId).eq("follow_user_id",followUserId));
        }
        return Result.ok();
    }

    @Override
    public Result isFollow(Long followUserId) {
        Long userId = UserHolder.getUser().getId();
        // 1.查询是否关注
        Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();

        return Result.ok(count>0);
    }

共同关注

其实之后的课程有点为了练这个 Redis 而去开发的这个功能😢,但还是学完吧,也没有几节了
既然要实现 共同关注,那肯定要 先获取 两个用户的关注列表,然后去求交集,那在 Redis 中 set 集合是可以求交集的,所以我们这次用 set 集合来实现求共同关注的功能。在实现这个功能之前,我们先来实现下面两段代码
这两段 代码与共同关注没有什么关系,是用来完善用户的一些信息

// UserController
// 这个是用来点击头像,进入主页
@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);
    }
// BlogController
// 这个是进入主页后,显示这个博主的博客
@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);
    }

下面我们就来写共同关注的代码,首先我们要把之前写的关注稍微改一下,就是操作完数据库后,把关注列表形成一个 set 添加到 Redis 中。

@Override
    public Result follow(Long followUserId, Boolean isFollow) {
        Long userId = UserHolder.getUser().getId();
        // 1.判断到底是关注还是取关
        if(isFollow==true){
            Follow follow = new Follow();
            follow.setUserId(userId);
            follow.setFollowUserId(followUserId);
            boolean save = save(follow);
            if(save){
                stringRedisTemplate.opsForSet().add("follows:"+userId,followUserId.toString());
            }
        }else {
            remove(new QueryWrapper<Follow>().eq("user_id",userId).eq("follow_user_id",followUserId));
            stringRedisTemplate.opsForSet().remove("follows:"+userId,followUserId.toString());
        }
        return Result.ok();
    }

然后我们来写 共同关注

@Override
    public Result followCommons(Long id) {
        // 这里还是用来一些流操作的,不熟悉的朋友还是建议去看看流。
        // 其实这里真正要说的也就是求交集了 set 的 intersect命令
        Long userId = UserHolder.getUser().getId();
        String key="follows:"+userId;
        String followedKey="follows:"+id;
        Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key, followedKey);
        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);
    }

其实上面两个真的没有什么太多新的东西,用到的 Redis 的部分也是比较少的,想要学 Redis 的朋友也可以跳过这两节.

关注推送

关注推送也叫 Feed 流,直译为 投喂,为用户持续的提供 “沉浸式” 的体验,通过无限下拉获取新的信息

Feed 流产品有两种常见模式:

  • TimeLine: 不做内容筛选,简单的按照内容发布时间排序,常用于好友或关注.例如朋友圈
    • 优点: 信息全面,不会缺失.并且实现也相对简单
    • 缺点: 信息噪音较多,用户不一定感兴趣,内容获取效率低
  • 智能排序: 利用智能算法屏蔽掉违规的,用户不感兴趣的内容,推送用户感兴趣的信息来吸引用户
    • 优点: 投喂用户感兴趣信息,用户粘度高,容易沉迷
    • 缺点: 如果算法不精确,可能起反作用

我们这个 个人页面,是基于关注的好友来做 Feed 流,因此采用 TIMELine 的模式,该模式的实现有三种

  1. 拉模式
  2. 推模式
  3. 推拉结合

拉模式

每个博主都会有一个发件箱,当他们发布消息的时候,都会先发到他们自己的发件箱当中,并且都带上时间戳,然后当有一个用户下拉刷新它的收件箱的时候,这时候,系统会从这个用户所关注的博主的发件箱中拉取信息,然后按时间戳排序.下面这个国就演示了这个过程,但是我们想想,它每下拉一次我们就都要给它拉取一次,并且还要排序,那这样性能是不是就不是很好,那我们接下来继续看 推模式
image.png

推模式

而推模式就与拉模式不太一样了,每个博主没有收件箱 了,而是把信息直接发给粉丝的收件箱,并且在收件箱内部排好序,这样粉丝下来刷新的时间,就直接从收件箱中取就可以了.这样就弥补了拉模式的效率问题.但是推模式同样有一个问题,就是如果有一个博主的粉丝很多,那它要给粉丝发消息就要发多份,这个数据量上来了,系统也没法承受,那么能不能把 这两种模式的优点结合起来呢,那就是接下的推拉模式了
image.png

推拉模式

在推拉模式中,我们将博主分为 大V 和普通博主,大V的粉丝数很多,通常几千万,而普通博主的粉丝数就比较少了.
我们也把粉丝分为普通粉丝和活跃粉丝.
对于 大V来说,他的粉丝数很多,所以肯定不能用推模式,所以就用拉模式.但是对于一些活跃粉丝还是用推模式,因为这些活跃粉丝经常取看他们博主的信息,所以效率要高一些,而对于哪些普通粉丝就用拉模式,因为他们对博主的关注也不是很多,所以效率吗,慢一点也就慢一点了,而对于普通博主来说,他的粉丝数目没有那么多,所以用推模式也耗费不少资源.
image.png
下面我们再来对比一下这三种模式的优缺点.
image.png
那对于我们这个系统,不会有大v,所以我们这采用推模式来实现.

关注推荐的实现

需求:

  1. 修改新增探店笔记的业务,在保存 blog 到数据库的时候,推送的收件箱
  2. 收件箱满足可以根据时间戳排序,必须用 Redis 的数据结构实现
  3. 查询收件箱时,可以实现分页查询。

我们先来修改新增博客的业务,这个业务用到了 Redis的 set结构来作为用户的收件箱,并把这个博客推送到这个博客的主人的粉丝的收件箱。

 @Override
    public Result saveBlog(Blog blog) {
        // 获取登录用户
        UserDTO user = UserHolder.getUser();
        blog.setUserId(user.getId());
        // 保存探店博文
        boolean isSuccess = save(blog);
        if(!isSuccess){
            return Result.fail("新增笔记失败");
        }
        List<Follow> follows = followService.query().eq("follow_user_id", user.getId().toString()).list();
        for (Follow follow : follows) {
            //获取粉丝 id
            Long userId = follow.getUserId();
            //推送
            String key="feeds:"+userId;
            stringRedisTemplate.opsForZSet().add(key,blog.getId().toString(),System.currentTimeMillis());
        }
        // 返回id
        return Result.ok(blog.getId());
    }

然后我们再来实现粉丝查看自己的收件箱,展示出这个用户所关注的博主的文章
但是我们这里想一下,这里的分页还能是传统的分页吗?
我们看下面这条图,t1时,查询5条,但是 t2这是又传过来了一个数据,t3这时候又查询5条数据,从头开始查的话,会查重一个。这就不是我们想要的,那怎么解决查重,就是滚动查询
image.png
在滚动查询的时候
t1 和 t2 都是与上述一样,但是 t3时刻读取第二页,是从上回 的lastId 的下一个开始查的,这样就避免了查重,但是在 Redis 中如何实现呢,我们可以利用 SortedSet 来实现。
image.png
**SortedSet 中有一条命令是 **
ZREVRANGEBYSCORE 是用 score 来搜索
image.png
其中 max ,min是排序的范围,max是最大值,min是最小值。WITHSCORES是返回时带着分数 offset是偏移量,是从最大值的哪一个开始排序,0就是从最大值开始,1就是从最大值的下一个开始。count就是查几个。

那么我们就可以用时间戳来当分数,最新的时间戳就是最大的分数,排在第一位。第一次的时候可以拿当前时间戳,因为对于当前来说,当前时间戳是最大的,最小值我们不关心,就用 0. 第一次 offset 用0,因为第一次分数最大的我们也要。
然后往后 max 就应该是上回查询的最小分数,最小还是0,但是这时的 offset就应该是 1了,因为这次的最小分数是上一会的最小元素,我们上回已经查过了,这次不需要了。所以 offset 要用 1. 具体如下
image.png
但是如果两个元素的时间戳一样怎么办?如果这个用户关注了很多博主,这些博主可能会在同一时间发布文章,都会推送到这个用户的收件箱。我们看下面的图看一下有什么问题

我们看一下,m7和m6的分数都是 6,第二次查看的时候还是出现了 6,这是为什么,因为,我们第二次查询的时候 max 是上回的 min,上回的 min是6,而我们的第二次的 offser 是 1,也就是 从分数为 6 的下一个开始,那分数为6 的从上往下 第一个是 m7,第二个是 m6,那可不是要从 m6开始查嘛,所以我们的 offset也要改,就是上一次最小分数的个数是多少,我们下一次的 offset就是多少,还是这个我们来开,6有两个,那第二次我们的 offser 就是 2,分数为6的第一个是m7,往下移动 2位,不就刚好把 上一次我们查到的 分数为6的隔过去了嘛、
有的朋友可能这里会有的疑惑,如果我 m5 也是 6,你offset不就是 3了,不就把 m5 也隔过去了?
其实不是这样的,查重复的,我们只在上一次我们查到的里面查重复,不是对于整个 set 查。下面看效果
image.png

image.png
那思路有了,代码怎么实现呢,我们先来看接口的规范

image.png
我们在一次查询中,就要把 本次的最小时间戳和下一次要用的偏移量算出来,传给前端,前端下一次再调用这个接口的时候,就用这两个。下面是具体代码实现

 @Override
    public Result queryBlogOfFollow(Long max, Integer offset) {

        //1. 获取当前用户
        Long userId = UserHolder.getUser().getId();
        //2. 查询收件箱 ZREVRANGEBYSCORE key max min LIMIT offset count
        String key="feeds:"+userId;
        //3. 解析数据:blogId,timestamp,offset
        // 这里的 TypedTuple 是一个元组,里面有你要查的数据,以及分数
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(key, 0, max, offset, 3);
        // 非空判断
        if(typedTuples==null||typedTuples.isEmpty()){
            return Result.ok();
        }
        //4. 根据id查询blog
        List<Long> ids=new ArrayList<>(typedTuples.size());
        long minTime=0;
        int os=1;
        // 接下来就是算 mintime 和 offset,其实这里我们还是用了一点点小算法,用一个 mintime
        // 变量来接受最小时间戳,然后每次从元组获取到时间戳,我们就赋给 mintime,这样遍历完
        // 后,mintime 就是最小的

        // 然后是 算 offset,这里我们根据 mintime,我们刚才不是说了嘛,遍历的过程中每获取一次
        // time ,就赋给 mintime,那么我们在赋之前,加一步,判断当前获取的这个 time 与 mintime
       //是否相等,相等,就让 os++,不相等就让 minTime=time,最后重置 os,到最后 os 一定是
       // 最小的时间戳的重复次数。

        // 其实else 里面 的赋值可以去掉,因为最后还会赋值。
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            //4.1 获取博客id
            ids.add(Long.valueOf(typedTuple.getValue()));
            long time = typedTuple.getScore().longValue();
            if(time==minTime){
                os++;
            }else {
                minTime=time;
                os=1;
            }
            minTime = time;
        }
        //5. 封装并返回
        String idStr = StrUtil.join(",", ids);
        List<Blog> blogs = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();
        for (Blog blog : blogs) {
            // 查询 blog 有关的用户
            queryBlogUser(blog);
            // 查询blog 是否被点赞
            isLiked(blog);
        }
        ScrollResult scrollResult = new ScrollResult();
        scrollResult.setList(blogs);
        scrollResult.setOffset(os);
        scrollResult.setMinTime(minTime);
        return Result.ok(scrollResult);
    }

那么今天关于好友关注这个模块就学完了,虽然前面的比较简单,但是最后一个理解起来还是有一定难度的


总结

最后的最后,还是希望Redis实战篇系列比较可以对大家的学习以及工作有一定的帮助,那我们的实战篇笔记就到这里撒花完结了,朋友们,我们高级篇再见。

我是Mayphyr,从一点点到亿点点,我们下次再见

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

界面组件DevExpress Reporting v23.1新版亮点 - UX功能增强

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表 界面组件DevExpress Reporting v23.1已于前段时间…

探讨Unity中的动画融合技术(BlendTree)

动画在游戏和虚拟现实应用中扮演着关键的角色&#xff0c;而动画融合技术则是使角色动作更加流畅和逼真的核心。在Unity引擎中&#xff0c;我们可以使用动画混合树&#xff08;Blend Trees&#xff09;来实现这一目标。本篇技术博客将深入讨论动画融合技术的实现原理、在Unity中…

C++ 指针详解

目录 一、指针概述 指针的定义 指针的大小 指针的解引用 野指针 指针未初始化 指针越界访问 指针运算 二级指针 指针与数组 二、字符指针 三、指针数组 四、数组指针 函数指针 函数指针数组 指向函数指针数组的指针 回调函数 指针与数组 一维数组 字符数组…

FreeRTOS调度器启动过程分析

目录 引出思考 vTaskStartScheduler()启动任务调度器 xPortStartScheduler()函数 FreeRTOS启动第一个任务 vPortSVCHandler()函数 总结 引出思考 首先想象一下如何启动第一个任务&#xff1f; 假设我们要启动的第一个任务是任务A&#xff0c;那么就需要将任务A的寄存器值…

腾讯云手动下发指令到设备-用于设备调试

打开腾讯云API Explorer&#xff0c;Publish Msg https://console.cloud.tencent.com/api/explorer?Productiotcloud&Version2021-04-08&ActionPublishMessagehttps://console.cloud.tencent.com/api/explorer?Productiotcloud&Version2021-04-08&ActionPub…

【模电】设置静态工作点的必要性

设置静态工作点的必要性 静态工作点为什么要设置静态工作点 静态工作点 在放大电路中&#xff0c;当有信号输入时&#xff0c;交流量与直流量共存。将输入信号为零、即直流电源单独作用时晶体管的基极电流 I B I\tiny B IB、集电极电流 I C I\tiny C IC、b - e间电压 U B E U\t…

语义分割网络FCN

语义分割是一种像素级的分类&#xff0c;输出是与输入图像大小相同的分割图&#xff0c;输出图像的每个像素对应输入图像每个像素的类别&#xff0c;每一个像素点的灰度值都是代表当前像素点属于该类的概率。 语义分割任务需要解决的是如何把定位和分类这两个问题一起解决&…

佛罗里达大学利用神经网络,解密 GPCR-G 蛋白偶联选择性

内容一览&#xff1a;G 蛋白偶联受体 (GPCRs) 是一种将细胞膜外的刺激&#xff0c;传递到细胞膜内的跨膜蛋白&#xff0c;广泛参与到人体生理活动当中。近日&#xff0c;佛罗里达大学的研究者测定了 GPCRs 和 G 蛋白的结合选择性&#xff0c;并开发了预测二者选择性的算法&…

kubernetes监控GPA安装部署

本文在于指导如何对k8s的监控GPA(Grafana&#xff0c;prometheus以及alertmanager)进行安装部署。 1. 介绍 Prometheus 在真正部署Prometheus之前&#xff0c;应了解一下Prometheus的各个组件之间的关系及作用&#xff1a; 1&#xff09;MertricServer&#xff1a;是k8s集群…

朋友圈7大黄金发圈时间

众所周知&#xff0c;朋友圈运营是私域运营必不可少的重要环节。 因为做好朋友圈运营&#xff0c;能够打造形成高质量、高价值的私域流量&#xff0c;加快实现用户成交。 那么如何形成一个吸粉又吸金的人设&#xff0c;做出高质量的朋友圈发圈内容呢&#xff1f; 那么如何确保能…

SSM整合(注解版)

SSM 整合是指将学习的 Spring&#xff0c;SpringMVC&#xff0c;MyBatis 进行整合&#xff0c;来进行项目的开发。 1 项目基本的配置类 1.1 Spring 配置类 这个配置类主要是管理 Service 中的 bean&#xff0c;controller 层的 bean 对象是 SpringMVC 管理的 package cn.ed…

二极管:二极管的基本原理

一、认识导体、绝缘体、半导体 什么是导体&#xff1f; 导体 conductor &#xff0c;是指电阻率很小&#xff0c;且容易传导电流的物质。导体中存在大量可自由移动的带电粒子&#xff0c;也称为载流子。在外电场的作用下&#xff0c;载流子作定向运动&#xff0c;形成电流。 …

安装配置JDK1.8

JDK1.8的下载及配置 1.进入甲骨文官网甲骨文官网往下翻找到java8并且点击windows. 2.下载Java8必须登录账号 3下载完后点击进入安装&#xff0c;直接下一步就可以&#xff0c;记住这个路径。 4.右击我的电脑进入环境配置&#xff0c;新增变量。 CLASSPATH .;%JAVAHOME%\lib;…

3.C程序编译步骤

目录 1 预处理 2 编译 3 汇编 4 链接 5 文件大小情况 依次执行下面4个步骤 预处理 将所有头文件展开&#xff0c;比如stdio.h等&#xff0c;展开就相当于把stdio.h中的所有代码粘贴到你的代码里。将所有的宏文件展开&#xff0c;像stdio.h是官方定义的头文件&#x…

C# - Opencv应用(3) 之矩阵Mat使用[图像截取粘贴、ROI操作、位运算、数学计算]

C# - Opencv应用&#xff08;3&#xff09; 之矩阵Mat使用[图像截取粘贴、ROI操作、位运算、数学计算] 图像读取&#xff0c;大小、截取、位运算图像ROI操作&#xff1a;粘贴赋值、滤波图像数学计算部分结果如下&#xff1a; 1.图像读取&#xff0c;大小、截取、位运算 //图…

计算机辅助药物设计AIDD-小分子-蛋白质|分子生成|蛋白质配体相互作用预测

文章目录 计算机辅助药物设计AIDD【小分子专题】AIDD概述及药物综合数据库学习机器学习辅助药物设计图神经网络辅助药物设计自然语言处理辅助药物设计药物设计与分子生成 计算机辅助药物设计【蛋白质专题】蛋白质数据结构激酶-Kinase相似性学习基于序列的蛋白质属性预测基于结构…

解决xshell连接诶树莓派中文乱码的问题

系统版本 解决办法 在根目录下找到 /etc/profile 修改profile文件,添加以下两行.以便重启之后也能生效: export LANGzh_CN.utf8 export LC_ALLzh_CN.utf8注意: /etc/profile的修改需要root权限才能修改! 在xshell的编码格式改为UTF-8

一次性客户的笔记总结

创建一次性客户&#xff0c;系统会给出一个客户编码&#xff1b; 每次记账的时候&#xff0c;在录入过账码及客户编码后&#xff0c;点击回车&#xff0c;都需要录入这个客户的详细信息&#xff08;比如 客户名称等&#xff09; 一次性客户的信息存储在BSEC表中&#xff0c;这种…

飞致云1panel + 雷池WAF

可能有许多人都有这个需求&#xff1a;为自己的个人站点套上WAF&#xff0c;增加安全性&#xff0c;本文将介绍如何将1panel面板深度结合长亭雷池防火墙&#xff0c;实现为个人站点套上WAF并且自动续签ssl证书。 前提条件&#xff1a; 服务器IP已绑定域名 完整的1panel环境 …

springboot简单集成上传和下载(带页面)

来学习一下文件上传和下载 一、页面开发 整体思路 登录页 主页 二、库表设计 SET FOREIGN_KEY_CHECKS0;-- ---------------------------- -- Table structure for t_files -- ---------------------------- DROP TABLE IF EXISTS t_files; CREATE TABLE t_files (id int(11) N…