C#客户端Redis服务器的分布式缓存

2019-12-30 11:51:33于丽

当需要存储相关的数据集和收集统计信息,例如answer -> queustion给答案或问题投票时,Redis集合就非常好使。假设我们有很多的问题(queustion)和答案(answer ),需要将它们存储在缓存中。使用Redis,我们可以这么做:

 



    /// <summary>  /// Gets or sets the Redis Manager. The built-in IoC used with ServiceStack autowires this property.  /// </summary>  IRedisClientsManager RedisManager { get; set; }  /// <summary>  /// Delete question by performing compensating actions to  /// StoreQuestion() to keep the datastore in a consistent state  /// </summary>  /// <param name="questionId">  public void DeleteQuestion(long questionId)  {      using (var redis = RedisManager.GetClient())      {          var redisQuestions = redis.As<question>();            var question = redisQuestions.GetById(questionId);          if (question == null) return;            //decrement score in tags list          question.Tags.ForEach(tag => redis.IncrementItemInSortedSet("urn:tags", tag, -1));            //remove all related answers          redisQuestions.DeleteRelatedEntities<answer>(questionId);            //remove this question from user index          redis.RemoveItemFromSet("urn:user>q:" + question.UserId, questionId.ToString());            //remove tag => questions index for each tag          question.Tags.ForEach("urn:tags>q:" + tag.ToLower(), questionId.ToString()));            redisQuestions.DeleteById(questionId);      }  }    public void StoreQuestion(Question question)  {      using (var redis = RedisManager.GetClient())      {          var redisQuestions = redis.As<question>();            if (question.Tags == null) question.Tags = new List<string>();          if (question.Id == default(long))          {              question.Id = redisQuestions.GetNextSequence();              question.CreatedDate = DateTime.UtcNow;                //Increment the popularity for each new question tag              question.Tags.ForEach(tag => redis.IncrementItemInSortedSet("urn:tags", tag, 1));          }            redisQuestions.Store(question);          redisQuestions.AddToRecentsList(question);          redis.AddItemToSet("urn:user>q:" + question.UserId, question.Id.ToString());            //Usage of tags - Populate tag => questions index for each tag          question.Tags.ForEach(tag => redis.AddItemToSet          ("urn:tags>q:" + tag.ToLower(), question.Id.ToString()));      }  }    /// <summary>  /// Delete Answer by performing compensating actions to  /// StoreAnswer() to keep the datastore in a consistent state  /// </summary>  /// <param name="questionId">  /// <param name="answerId">  public void DeleteAnswer(long questionId, long answerId)  {      using (var redis = RedisManager.GetClient())      {          var answer = redis.As<question>().GetRelatedEntities<answer>          (questionId).FirstOrDefault(x => x.Id == answerId);          if (answer == null) return;            redis.As<question>().DeleteRelatedEntity<answer>(questionId, answerId);            //remove user => answer index          redis.RemoveItemFromSet("urn:user>a:" + answer.UserId, answerId.ToString());      }  }    public void StoreAnswer(Answer answer)  {      using (var redis = RedisManager.GetClient())      {          if (answer.Id == default(long))          {              answer.Id = redis.As<answer>().GetNextSequence();              answer.CreatedDate = DateTime.UtcNow;          }            //Store as a 'Related Answer' to the parent Question          redis.As<question>().StoreRelatedEntities(answer.QuestionId, answer);          //Populate user => answer index          redis.AddItemToSet("urn:user>a:" + answer.UserId, answer.Id.ToString());      }  }    public List<answer> GetAnswersForQuestion(long questionId)  {      using (var redis = RedisManager.GetClient())      {          return redis.As<question>().GetRelatedEntities<answer>(questionId);      }  }    public void VoteQuestionUp(long userId, long questionId)  {      //Populate Question => User and User => Question set indexes in a single transaction      RedisManager.ExecTrans(trans =>      {          //Register upvote against question and remove any downvotes if any          trans.QueueCommand(redis =>          redis.AddItemToSet("urn:q>user+:" + questionId, userId.ToString()));          trans.QueueCommand(redis =>          redis.RemoveItemFromSet("urn:q>user-:" + questionId, userId.ToString()));            //Register upvote against user and remove any downvotes if any          trans.QueueCommand(redis =>          redis.AddItemToSet("urn:user>q+:" + userId, questionId.ToString()));          trans.QueueCommand(redis =>          redis.RemoveItemFromSet("urn:user>q-:" + userId, questionId.ToString()));      });  }    public void VoteQuestionDown(long userId, long questionId)  {      //Populate Question => User and User => Question set indexes in a single transaction      RedisManager.ExecTrans(trans =>      {          //Register downvote against question and remove any upvotes if any          trans.QueueCommand(redis =>          redis.AddItemToSet("urn:q>user-:" + questionId, userId.ToString()));          trans.QueueCommand(redis =>          redis.RemoveItemFromSet("urn:q>user+:" + questionId, userId.ToString()));            //Register downvote against user and remove any upvotes if any          trans.QueueCommand(redis =>          redis.AddItemToSet"urn:user>q-:" + userId, questionId.ToString()));          trans.QueueCommand(redis =>          redis.RemoveItemFromSet("urn:user>q+:" + userId, questionId.ToString()));      });  }    public void VoteAnswerUp(long userId, long answerId)  {      //Populate Question => User and User => Question set indexes in a single transaction      RedisManager.ExecTrans(trans =>      {          //Register upvote against answer and remove any downvotes if any          trans.QueueCommand(redis =>          redis.AddItemToSet("urn:a>user+:" + answerId, userId.ToString()));          trans.QueueCommand(redis =>          redis.RemoveItemFromSet("urn:a>user-:" + answerId, userId.ToString()));            //Register upvote against user and remove any downvotes if any          trans.QueueCommand(redis =>          redis.AddItemToSet("urn:user>a+:" + userId, answerId.ToString()));          trans.QueueCommand(redis =>          redis.RemoveItemFromSet("urn:user>a-:" + userId, answerId.ToString()));      });  }    public void VoteAnswerDown(long userId, long answerId)  {      //Populate Question => User and User => Question set indexes in a single transaction      RedisManager.ExecTrans(trans =>      {          //Register downvote against answer and remove any upvotes if any          trans.QueueCommand(redis =>          redis.AddItemToSet("urn:a>user-:" + answerId, userId.ToString()));          trans.QueueCommand(redis =>          redis.RemoveItemFromSet("urn:a>user+:" + answerId, userId.ToString()));            //Register downvote against user and remove any upvotes if any          trans.QueueCommand(redis =>          redis.AddItemToSet("urn:user>a-:" + userId, answerId.ToString()));          trans.QueueCommand(redis =>          redis.RemoveItemFromSet("urn:user>a+:" + userId, answerId.ToString()));      });  }    public QuestionResult GetQuestion(long questionId)  {      var question = RedisManager.ExecAs<question>      (redisQuestions => redisQuestions.GetById(questionId));      if (question == null) return null;        var result = ToQuestionResults(new[] { question })[0];      var answers = GetAnswersForQuestion(questionId);      var uniqueUserIds = answers.ConvertAll(x => x.UserId).ToHashSet();      var usersMap = GetUsersByIds(uniqueUserIds).ToDictionary(x => x.Id);        result.Answers = answers.ConvertAll(answer =>          new AnswerResult { Answer = answer, User = usersMap[answer.UserId] });        return result;  }    public List<user> GetUsersByIds(IEnumerable<long> userIds)  {      return RedisManager.ExecAs<user>(redisUsers => redisUsers.GetByIds(userIds)).ToList();  }    public QuestionStat GetQuestionStats(long questionId)  {      using (var redis = RedisManager.GetReadOnlyClient())      {          var result = new QuestionStat          {              VotesUpCount = redis.GetSetCount("urn:q>user+:" +questionId),              VotesDownCount = redis.GetSetCount("urn:q>user-:" + questionId)          };          result.VotesTotal = result.VotesUpCount - result.VotesDownCount;          return result;      }  }    public List<tag> GetTagsByPopularity(int skip, int take)  {      using (var redis = RedisManager.GetReadOnlyClient())      {          var tagEntries = redis.GetRangeWithScoresFromSortedSetDesc("urn:tags", skip, take);          var tags = tagEntries.ConvertAll(kvp => new Tag { Name = kvp.Key, Score = (int)kvp.Value });          return tags;      }  }    public SiteStats GetSiteStats()  {      using (var redis = RedisManager.GetClient())      {          return new SiteStats          {              QuestionsCount = redis.As<question>().TypeIdsSet.Count,              AnswersCount = redis.As<answer>().TypeIdsSet.Count,              TopTags = GetTagsByPopularity(0, 10)          };      }  }