前言

很多时候,我们会根据用户最近一段时间的行为,做出一些相应的策略,从而改变系统的运动轨迹。

举个简单的例子来说明一下:

假设A公司现在有两个合作伙伴(B和C),B和C都是提供天气数据的,现在A公司做了一个聚合接口,把B和C的接口融合了,那么这个时候,要怎么去B和C公司获取数据呢?

其实这个要考虑的东西有很多很多,下面根据本文的主题,拿出其中一个点来讨论说明。

最简单的做法就是,随机调用。当然不是那么简单的随机调用。

根据调用的最近一百条数据的得到成功率,耗时等指标,再根据这些指标去判断一次查询要去那个公司获取数据。

思路已经有了,这个时候就是怎么实践的问题了。

本文介绍的做法是借助redis来完成的。

如何用redis来处理

redis的list类型可以说非常适合用来处理这个情况。

首先,可以把查询按顺序写进去,一个个的入队。

其次,写进去之后可以对它进行裁剪,保留最近的100条数据。(换句话说,我们可以保证在这个list里面,最多就是100条数据)

最后,获取这个list里面的100条数据,进行计算即可。

正常情况下,我们不会把计算放在查询的过程里面,在查询的时候,只需要一个决策的结果值就可以了,当然这个结果值也是计算后写进redis的。

所以要将这个计算的过程从查询中独立出来,定时去执行即可。

总结上面所说的,大概可以画出下面这样一样图。

其中的第三步操作,将查询记录写进list,然后进行裁剪这两个操作,可以直接操作redis,也可以考虑通过MQ去写,虽说没什么太大的必要。

简单的示例代码

查询的控制器

[Route("api/[controller]")]
[ApiController]
public class AreaController : ControllerBase
{
private readonly ILogger _logger;
private readonly IRedisCachingProvider _provider; public AreaController(ILoggerFactory loggerFactory, IRedisCachingProvider provider)
{
_logger = loggerFactory.CreateLogger<AreaController>();
_provider = provider;
} // GET api/area/11
[HttpGet("provinceId")]
public async Task<string> GetAsync(string provinceId)
{
// get datasource
var datasource = await GetQueryDataSourceIdAsync(provinceId); if (string.IsNullOrWhiteSpace(datasource)) return "not support"; var beginTime = DateTime.Now; // query
var (val, isSucceed) = await QueryDataSourceAsync(datasource); var endTime = DateTime.Now; // datasource
var dsInfo = new DataSourceInfo
{
Cost = (long)endTime.Subtract(endTime).TotalMilliseconds,
IsSucceed = isSucceed
}; // record
_ = Task.Run(async () =>
{
try
{
await _provider.LPushAsync($"info:{datasource}", new List<DataSourceInfo> { dsInfo });
await _provider.LTrimAsync($"info:{datasource}", 0, 99);
}
catch (Exception ex)
{
_logger.LogError(ex, $"record #{datasource}# error");
}
}); return val;
} private async Task<string> GetQueryDataSourceIdAsync(string provinceId)
{
var datasourceIds = GetDataSourceIdProvinceId(provinceId); if (datasourceIds.Count <= 0) return string.Empty; var cacheKey = "dskpi"; var kpis = await _provider.HMGetAsync(cacheKey, datasourceIds); var datasource = datasourceIds.First(); if (kpis != null && kpis.Any())
{
// policy
datasource = kpis.OrderByDescending(x => x.Value).First().Key;
} return datasource;
} private async Task<(string val, bool isSucceed)> QueryDataSourceAsync(string datasource)
{
await Task.Delay(100); var rd = new Random().NextDouble(); return (datasource, rd > 0.5d);
} private List<string> GetDataSourceIdProvinceId(string provinceId)
{
return new List<string> { "100", "900" };
}
}

由调度系统触发的计算控制器

[Route("api/cal")]
[ApiController]
public class CalculatiionController : ControllerBase
{
private readonly ILogger _logger;
private readonly IRedisCachingProvider _provider; public CalculatiionController(ILoggerFactory loggerFactory, IRedisCachingProvider provider)
{
_logger = loggerFactory.CreateLogger<CalculatiionController>();
_provider = provider;
} // GET api/cal/
[HttpGet]
public string Get()
{
var id = Guid.NewGuid().ToString("N"); _ = Task.Run(async () => await CalAsync(id)); return "ok";
} private async Task CalAsync(string id)
{
_logger.LogInformation($"{id} begin at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}"); var datasourceIds = GetAllDataSourceIds(); foreach (var item in datasourceIds)
{
try
{
var topN = await _provider.LRangeAsync<DataSourceInfo>($"info:{item}", 0, 99); var cost = topN.Average(x => x.Cost);
var rate = topN.Count(x => x.IsSucceed) / 100; var score = GetScore(cost, rate); await _provider.HSetAsync($"dskpi", item, score.ToString()); }
catch (Exception ex)
{
_logger.LogError(ex, $"{id} {item} calculate fail ...");
}
} _logger.LogInformation($"{id} end at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
} private int GetScore(double cost, int rate)
{
return new Random().Next(1, 100);
} private List<string> GetAllDataSourceIds()
{
return new List<string> { "100", "900" };
}
}

也可以在Github上面找到上面的示例代码 RecentRecordsDemo

使用Redis实现最近N条数据的决策的更多相关文章

  1. net.sz.framework 框架 ORM 消消乐超过亿条数据排行榜分析 天王盖地虎

    序言 天王盖地虎, 老婆马上生孩子了,在家待产,老婆喜欢玩消消乐类似的休闲游戏,闲置状态,无聊的分析一下消消乐游戏的一些技术问题: 由于我主要是服务器研发,客户端属于半吊子,所以就分析一下消消乐排行榜 ...

  2. nginx+redis缓存微信的token数据

    上一篇文章我们讲了如何在负载均衡的项目中使用redis来缓存session数据,戳这里. 我们在项目的进展过程中,不仅需要缓存session数据,有时候还需要缓存一些别的数据,比如说,微信的acces ...

  3. (转)php读取文件使用redis的pipeline导入大批量数据

    第一次写博客,哈哈,纯属用来记录一下自己工作中遇到的问题及解决办法. 昨天因为工作的需求,需要做一个后台上传TXT文件,读取其中的内容,然后导入redis库中.要求速度快,并且支持至少10W以上的数据 ...

  4. redis学习(九)——数据持久化

    一.概述 Redis的强大性能很大程度上都是因为所有数据都是存储在内存中的,然而当Redis重启后,所有存储在内存中的数据将会丢失,在很多情况下是无法容忍这样的事情的.所以,我们需要将内存中的数据持久 ...

  5. Redis 安装,配置以及数据操作

    Nosql介绍 Nosql:一类新出现的数据库(not only sql)的特点 不支持SQL语法 存储结构跟传统关系型数据库中那种关系表完全不同,nosql中存储的数据都是k-v形式 Nosql的世 ...

  6. Redis实践系列丨Codis数据迁移原理与优化

    Codis介绍 Codis 是一种Redis集群的实现方案,与Redis社区的Redis cluster类似,基于slot的分片机制构建一个更大的Redis节点集群,对于连接到codis的Redis客 ...

  7. redis持久化,主从及数据备份

    http://blog.csdn.net/lang_man_xing/article/details/38386113 现在在项目里已经大量使用redis了,为了提高redis的性能和可靠性我们需要知 ...

  8. redis主从配置 从而实现数据备份和读写分离

    首先打开cmd,用cd找到你的redis文件夹,我的操作是 在你的第一个redis客户端文件夹配置文件中,搜索port,找到如下位置 端口号设置为6379(默认的.后面一个,两个或者多个客户端分别修改 ...

  9. filebeat收集日志传输到Redis集群,logstash从Redis集群中拉取数据

    前提:已配置好Redis集群,并设置的有统一的访问密码 架构是filebeat-->redis集群-->logstash->elasticsearch,需要修改filebeat的输出 ...

随机推荐

  1. GithubPages+Hexo博客搭建记录

    目录 前言 安装Node.js 安装Git 安装Hexo 查看效果 建立Github Pages 注册Github帐户 建立托管博客的仓库 制作SSH密钥 添加公钥到Github 测试连接 把本地的博 ...

  2. java对 zip文件的压缩和解压(ant解决中文乱码)

    说明: 1.对于压缩的文件,当文件名称是中文时,若使用JDK API中自带的类(java.util.zip.ZipEntry; java.util.zip.ZipOutputStream;)进行压缩, ...

  3. Java之属性集(Properties类)

    Properties概述 java.util.Properties类 继承于 Hashtable ,来表示一个持久的属性集.它使用键值结构存储数据,每个键及其对应值都是一个字符串.该类也被许多Java ...

  4. 字典树(Trie)详解

    详解字典树(Trie) 本篇随笔简单讲解一下信息学奥林匹克竞赛中的较为常用的数据结构--字典树.字典树也叫Trie树.前缀树.顾名思义,它是一种针对字符串进行维护的数据结构.并且,它的用途超级广泛.建 ...

  5. 深挖计算机基础:趣谈Linux操作系统学习笔记

    参考极客时间专栏<趣谈Linux操作系统>学习笔记 核心原理篇:内存管理 趣谈Linux操作系统学习笔记:第二十讲 趣谈Linux操作系统学习笔记:第二十一讲 趣谈Linux操作系统学习笔 ...

  6. 【algo&ds】4.B树、字典树、红黑树、跳表

    上一节内容[algo&ds]4.树和二叉树.完全二叉树.满二叉树.二叉查找树.平衡二叉树.堆.哈夫曼树.散列表 7.B树 B树的应用可以参考另外一篇文章 8.字典树Trie Trie 树,也叫 ...

  7. JS基础研语法---函数基础总结---定义、作用、参数、返回值、arguments伪数组、作用域、预解析

    函数: 把一些重复的代码封装在一个地方,在需要的时候直接调用这个地方的代码就可以了 函数作用: 代码重用 函数的参数: 形参:函数定义的时候,函数名字后面的小括号里的变量 实参:函数调用的时候,函数名 ...

  8. CSS学习笔记-动画模块

    动画模块:    1.过渡和动画之间的异同        1.1不同点            (1)过渡必须人为触发才能执行            (2)动画不需要人为触发就可以执行        1 ...

  9. 利用Azure虚拟机安装Dynamics 365 Customer Engagement之七:安装前端服务器及部署管理器

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  10. [转]Blue Prism Opening a password protected Excel workbook?

    本文转自:https://www.rpaforum.net/threads/opening-a-password-protected-excel-workbook.470/ 问: As the tit ...