• 一次简单的redis记录聊天群7天消息实现


    最近做了个小功能,群聊天,包含发,收,改消息等基本功能

         (预估下成熟的群组聊天系统, 群组量大,群组内成员多,消息发送多,消息发送完后通知每个成员量特别大.成员读取消息,同一个消息被多次读取,读取量也会很大.我的想法,热消息也是内存存储方式会比较好,历史消息归档)

    没有考虑消息量比较大的场景,这个后续如果有机会设计,再补,这里就是记录下这种非常简单的群消息的实现方式

    方式: 简单地从两个维度,群组 和时间 (一天N片), 一个分片用redis的list来存储,效率很高,查询从最新的片开始查,逐步往后翻,时间,群组id固定可以计算出固定的redis key.

    图:

    代码:

    /**
    * 使用redis的list结构按照时间每天4个key来保存数据
    * @param groupOrderIMDtoList
    * @return
    */
    @Override
    public Response<String> createGroupOrderIM(List<GroupOrderIMDto> groupOrderIMDtoList) {
    for (GroupOrderIMDto groupOrderIMDto: groupOrderIMDtoList){
    try {
    Calendar cal = Calendar.getInstance();
    String imKey = this.generateRedisKey(getPreviousDateHourOfMonth(0,cal),groupOrderIMDto.getGroupUUid());
    groupOrderIMDto.setCreateAt(System.currentTimeMillis());
    groupOrderIMDto.setCreateOrderType(groupOrderIMDto.getCreateOrderUserType());
    factory.getExpireListService().setExpireList(imKey, Lists.newArrayList(groupOrderIMDto), redisKeyExpireTime);

    boolean needPush = factory.getLockService().setEx(generateRedisKeyTodayLockSymbol(cal,groupOrderIMDto.getGroupUUid()),"pushed",redisKeyExpireTime);
    if (needPush){
    threadPoolExecutor.submit(new PushOper(groupOrderIMDto.getGroupUUid(),"createGroupOrderIM"));
    }
    }catch (Exception e){
    log.error("createGroupOrderIM exception: ",e.getStackTrace());
    }
    }
    return Response.createSuccess("success");
    }

    /**
    * 更新消息状态,下个迭代实现
    * @param groupOrderIMDto
    * @return
    */
    @Override
    public Response<String> updateGroupOrderIM(GroupOrderIMDto groupOrderIMDto) {
    try{
    //todo
    }catch (Exception e){
    log.error(e.getMessage(),e.getStackTrace());
    }
    return Response.createSuccess("success");
    }

    /**
    * 分页查询群消息,如果有给出上一条消息,查出上一条消息往前的pageSize条。
    * @param groupOrderListDto
    * @return
    */
    @Override
    public Response<PageResult<GroupOrderIMDto>> listGroupOrderIM(GroupOrderListDto groupOrderListDto) {
    log.info("in param is : " + JSONObject.toJSONString(groupOrderListDto));
    PageResult<GroupOrderIMDto> pageResult = new PageResult();
    List<GroupOrderIMDto> ret = Lists.newArrayList();
    try{
    //标识是否已经知道入参里的消息,如果入参你消息orderUUid为空,则为true
    boolean isSame = true;
    if (groupOrderListDto.getOrderUUid() != null && groupOrderListDto.getOrderUUid().length()>0){
    isSame = false;
    }
    Calendar cal = Calendar.getInstance();
    int hour = 0;
    int jumpNum = (groupOrderListDto.getCurrPage()-1) * groupOrderListDto.getPageSize();//根据分页判断可以在list跳过的消息条数
    while(true){
    String imKey = this.generateRedisKey(getPreviousDateHourOfMonth(hour,cal), groupOrderListDto.getGroupUUid());
    Long size = factory.getExpireListService().getListSize(imKey);
    hour = hour-spliteHour;
    if (size == null || size <= 0) {
    if (hour < (redisKeyExpireDay*(-1)* 24-spliteHour)){
    //获取不到数据,且已经遍历了前7天的key,则推出循环
    break;
    }
    //获取不到数据,还未遍历了前7天的key,则继续下一次循环
    continue;
    }

    if (jumpNum >= size){
    jumpNum -= size;
    continue;
    }
    int loopSize = 10;
    /*
    * 下面的逻辑是从reids获取数据,如果有不是第一页,会跳过 jumpNum条数来取数据,一直取到pageSize 条数据,或者取到最后,返回
    */
    for (long i = size-jumpNum-1;i>=0; i=i-loopSize ){
    List<GroupOrderIMDto> list =
    factory.getExpireListService().getListValue(
    imKey,(i-loopSize+1)>0?(i-loopSize+1):0,!isSame?i+1:i,GroupOrderIMDto.class);
    log.info("get from redis is :{} , key is {}, isSame value is {} ", JSONObject.toJSONString(list),imKey,isSame);
    if (list != null && list.size()>0){
    for (int j=list.size()-1;j>=0;j--){
    //GroupOrderIMDto groupOrderIMDto1 = list.get(j); 这里特别奇怪
    if (!isSame){
    if (list.get(j).getOrderUUid().equals(groupOrderListDto.getOrderUUid())){
    isSame = true;
    }
    }else{
    ret.add(list.get(j));
    }
    if (ret.size()>=groupOrderListDto.getPageSize()){
    pageResult.setHasMore(true);
    List<GroupOrderIMDto> ret2 = Lists.newArrayList();
    for (int k = ret.size()-1;k>=0;k--){
    ret2.add(ret.get(k));
    }
    pageResult.setList(ret2);
    return Response.createSuccess(pageResult);
    }
    }
    }
    }
    jumpNum = 0;
    }
    List<GroupOrderIMDto> ret2 = Lists.newArrayList();
    for (int k = ret.size()-1;k>=0;k--){
    ret2.add(ret.get(k));
    }
    pageResult.setList(ret2);
    pageResult.setHasMore(true);
    return Response.createSuccess(pageResult);
    }catch (Exception e){
    log.error("listGroupOrderIM exception: ",e.getMessage(),e.getStackTrace());
    }
    return Response.createError("get error");
    }

    //private static final String PREFIX = "API:PICKRIDECOMM:";
    //private static final String dayRedisKey = RedisGroupKeyEnum.GROUP_ROUTE_IM_LIST.getKey() ;
    private static final Integer redisKeyExpireDay = 7;
    private static final Integer redisKeyExpireTime = redisKeyExpireDay*24*60*60 ;//second
    private static final int spliteHour = 6;

    /**
    * 根据入参拼接rediskey
    * @param i
    * @param groupUuid
    * @return
    */
    private String generateRedisKey(String i,String groupUuid) {
    return String.format(RedisGroupKeyEnum.GROUP_ROUTE_IM_LIST.getKey(),i,groupUuid);
    }

    /**
    * 计算返回当前在一月中的天数 和 小时/6 拼接的字符串
    * @param i
    * @param cal
    * @return
    */
    private String getPreviousDateHourOfMonth(int i,Calendar cal) {
    //Calendar cal = Calendar.getInstance();
    if (i < 0) {
    cal.add(Calendar.HOUR, i);
    }
    return cal.get(Calendar.DAY_OF_MONTH)+":"+cal.get(Calendar.HOUR_OF_DAY)/spliteHour;
    }
    private String generateRedisKeyTodayLockSymbol(Calendar cal,String groupUuid) {
    return String.format(RedisGroupKeyEnum.GROUP_ROUTE_IM_LIST.getKey(),cal.get(Calendar.DAY_OF_MONTH),groupUuid)+":PUSHSYMBOL";
    }

  • 相关阅读:
    SMTP发邮件(直接可用)实例
    ADO.NET(二)
    ADO.NET(一)
    C# 反射(一)
    APサーバ
    DB2 相关操作
    ArrayList与LinkedList时间复杂度之对比
    java泛型问题 关于警告:XX is a raw type
    Java编程中提高性能的几点建议
    STRUTS2核心控制器:FilterDispatcher
  • 原文地址:https://www.cnblogs.com/thinkqin/p/12795930.html
Copyright © 2020-2023  润新知