高并发高可用高可靠性的千人千面项目技术架构分析

  • 项目需求目标及实现方案
    • 海量数据存储
    • 基于LRU的热点数据缓存
    • 数据一致性问题
    • 实现高性能的千人千面
    • 进一步优化
    • 代码展示
    • 学习交流

项目需求目标及实现方案

1.项目需求,在营销领域不断迭代发展的过程中,如何去挖掘顾客的需求并且去满足顾客的需求,一直是营销领域研究的课题。如何针对不同的客户需求进行个性化营销,众多电商公司、营销平台一直在不断探索和深入研究;这也是算法和大数据商业应用很重要的一个方面。例如淘宝2013提出了千人千面的概念,其实就是推荐算法,其实众多电商公司在这一方面均有应用。我们做的这个项目是营销活动的千人千面,其实就是帮助卖家去更精准的对顾客进行营销,顾客看到的也是自己感兴趣的活动,会有更好的体验。
2.项目目标,我们这个项目的三个目标:
–高并发,tps在30w以上
–高可用性,tp99在10ms内,百分之99的请求需要在10毫秒内完成响应
–高可靠性,这基本是项目的必然要求
3.实现方案,在实现方案上会分如下几个部分去分析。

海量数据存储

为了保证程序的可用性,我们将数据存放在Redis集群中,如果以k/v方式去存储会浪费大量的资源,并且用一般的序列化和反序列化工具也很难满足性能需求。因此我们选择了Google 的一款工具protobuf来作为我们存储和使用数据的一种格式;经过对比测试protobuf比使用fastjson进行序列化之后存储,数据量减少了三分之二,并且反序列化的性能也非常好。protobuf的使用

基于LRU的热点数据缓存

我们这个项目是基于活动的千人千面项目,每个商家都可以设置自己的活动体,每个活动体中有一系列的子活动;因此每个活动体的数据量就比较大,并且活动体的数据也很多,如果每次推荐计算都从Redis取数据的话,在tps30w的情况下网络带宽肯定会被占满。我们的解决办法是缓存一部分的热点活动体,让大部分的推荐计算直接在缓存中取数据。那么问题来了什么样的活动体是热点的活动体呢?当然就是被访问次数多并且最近经常被使用的。我们缓存的数据量肯定是有限的,没有办法把所有的数据都缓存下来,我们需要不断的更新替换热点数据,把相对热的数据缓存,把之前热目前不热的数据暂时移除。我们自己设计过这样的缓存策略,发现有现程的基于LRU(最近最少使用)的缓存工具,我们对比了几款,发现Google 的ConcurrentLinkedHashMap和我当初设计的思路完全吻合,当然Google 的这个工具已经经过验证的肯定比我们自己实现出来的稳定,我们就选取了这个缓存工具。关于我们缓存实现的设计思路和对几款缓存工具的比较后面会再用一个篇幅去阐述。

数据一致性问题

举例说明,商家的活动提数据是可以随时更新的,如果同一个商家的同一个活动体数据同时请求了多个更新,分布式环境下,由于网络等原因可能最后一个更新先执行而之前的更新后执行,那么最后更新的结果将不是最新的数据。怎么解决这个问题呢,加上了一个时间戳,处理请求时通过时间戳来判断这个请求是不是最新的。下面代码部分附上了代码。

实现高性能的千人千面

解决了数据存储问题、基于LRU热点数据缓存的问题,接下来就是实现推荐匹配算法了;这里参考了es的语法,直接在代码部分用代码展示,活动体protobuf模型在代码部分展示。

进一步优化

在从Redis获取多种数据时使用多线程并行执行
日志优化,在最大程度上精简日志
避免服务间跨机房访问

代码展示

解决数据一致性代码展示.

// 通过时间戳判断是否是最新请求
String key_nx = key+SPLIT+SUBFIX_NX;
String key_stamp = key+SPLIT+SUBFIX_STAMP;
boolean excute = false;
long recordTime = System.currentTimeMillis();
while(!excute) {if(System.currentTimeMillis()-recordTime>MAX_TIME) {throw new CommonException("操作超时");}if(jimDBUtil.setnx(key_nx, timestamp.toString(), 1l)) {excute = true;String stamp = jimDBUtil.get(key_stamp);if(StringUtils.isNotBlank(stamp) &&timestamp<Long.valueOf(stamp)){throw new CommonException("当前操作已过时");}jimDBUtil.set(key_stamp, timestamp.toString());jimDBUtil.del(key);//得先移除jimDB中的数据   //广播同步缓存中的数据jmqProducerUtil.send(sync_cache_topic,key,SELLE_RRULE_BUSINESSID);jimDBUtil.del(key_nx);}
}

活动体的protobuf模型.

// 活动体模型message PSubRuleInfo {PSellerRule sellerRule = 1;string content = 2;string contentType = 3;uint32 level = 4;string subId = 5;uint64 startTime = 6;uint64 endTime = 7;}message PRuleInfo {uint64 startTime = 1;uint64 endTime = 2;repeated PSubRuleInfo sruleList = 3;}message PBoo {   map<string, string> term = 1; // 定义Map对象map<string, string> terms=2;//map<string, PRangeEntity> range=3;//repeated PSellerRule ruleList = 4; // repeated 列表
}
message PRangeEntity{string gte = 1;string lte = 2;string gt = 3;string lt = 4;
}message PSellerRule {PBoo must = 1;PBoo must_not = 2;PBoo should =3 ;
}

匹配算法.

// 匹配算法
public boolean excuteRule(PSellerRule rule, Map<String, Object> attriMap) throws Exception {if(null == rule || null == attriMap || attriMap.size()==0) {throw new CommonException("rule or attribute is null");}PBoo must = rule.getMust();Map<FieldDescriptor, Object> mustFields = must.getAllFields();if(null != must && mustFields != null && mustFields.size()>0) {if(!mustRule(must,attriMap)) {return false;}}PBoo must_not = rule.getMustNot();Map<FieldDescriptor, Object> mustNotFields = must_not.getAllFields();if(null != must_not && mustNotFields != null && mustNotFields.size()>0) {if(!mustNotRule(must_not,attriMap)) {return false;}}PBoo should = rule.getShould();if(null != should) {if(!shouldRule(should,attriMap)) {return false;}}List<PSellerRule> ruleList = must.getRuleListList();if(ruleList != null && ruleList.size()>0) {for (PSellerRule sellerRule : ruleList) {if(!excuteRule(sellerRule,attriMap)) {return false;}}}List<PSellerRule> ruleList2 = must_not.getRuleListList();if(ruleList2 != null && ruleList2.size()>0) {for (PSellerRule sellerRule : ruleList2) {if(excuteRule(sellerRule,attriMap)) {return false;}}}boolean shouldBoo = true;List<PSellerRule> ruleList3 = should.getRuleListList();if(ruleList3 != null && ruleList3.size()>0) {shouldBoo = false;for (PSellerRule sellerRule : ruleList3) {if(excuteRule(sellerRule,attriMap)) {shouldBoo = true;}}}if(!shouldBoo) {return false;}return true;}private boolean mustRule(PBoo must,Map<String, Object> attriMap) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {if(null != must) {Map<String,String> term = must.getTerm();if(term != null && term.size()>0) {if(!mustTerm(term,attriMap)) {return false;}}Map<String,PRangeEntity> range = must.getRange();if(null != range && range.size()>0) {if(!mustRange(range,attriMap)) {return false;}}Map<String, String> terms = must.getTerms();if(null != terms && terms.size()>0) {if(!mustTerms(terms,attriMap)) {return false;}}}return true;}private boolean mustNotRule(PBoo must_not,Map<String, Object> attriMap) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {if(null != must_not) {Map<String,String> term = must_not.getTerm();if(term != null && term.size()>0) {if(!mustNotTerm(term,attriMap)) {return false;}}Map<String,PRangeEntity> range = must_not.getRange();if(null != range && range.size()>0) {if(!mustNotRange(range,attriMap)) {return false;}}Map<String, String> terms = must_not.getTerms();if(null != terms && terms.size()>0) {if(!mustNotTerms(terms,attriMap)) {return false;}}}return true;}private boolean shouldRule(PBoo should,Map<String, Object> attriMap) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {if(null != should) {Map<String,String> term = should.getTerm();if(term != null && term.size()>0) {if(shouldTerm(term,attriMap)) {return true;}}Map<String,PRangeEntity> range = should.getRange();if(null != range && range.size()>0) {if(shouldRange(range,attriMap)) {return true;}}Map<String, String> terms = should.getTerms();if(null != terms && terms.size()>0) {if(shouldTerms(terms,attriMap)) {return true;}}if((term==null||term.size()==0)&&(terms==null||terms.size()==0)&&(range==null||range.size()==0)) {return true;}}return false;}private boolean mustTerm(Map<String,String> term,Map<String, Object> attriMap) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {for (Map.Entry<String, String> entry : term.entrySet()) {String attri = String.valueOf(attriMap.get(entry.getKey()));if(!compare(attri,entry.getValue())) {return false;}}return true;}private boolean mustNotTerm(Map<String,String> term,Map<String, Object> attriMap) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {for (Map.Entry<String, String> entry : term.entrySet()) {String attri = String.valueOf(attriMap.get(entry.getKey()));if(compare(attri,entry.getValue())) {return false;}}return true;}private boolean shouldTerm(Map<String,String> term,Map<String, Object> attriMap) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {for (Map.Entry<String, String> entry : term.entrySet()) {String attri = String.valueOf(attriMap.get(entry.getKey()));if(compare(attri,entry.getValue())) {return true;}}return false;}private boolean mustRange(Map<String,PRangeEntity> range,Map<String, Object> attriMap) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {for (Map.Entry<String, PRangeEntity> map : range.entrySet()) {Object attri = attriMap.get(map.getKey());String type = attriType.get(map.getKey());if(!range(attri,type,map.getValue())) {return false;}}return true;}private boolean mustNotRange(Map<String,PRangeEntity> range,Map<String, Object> attriMap) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {for (Map.Entry<String, PRangeEntity> map : range.entrySet()) {Object attri = attriMap.get(map.getKey());String type = attriType.get(map.getKey());if(range(attri,type,map.getValue())) {return false;}}return true;}private boolean shouldRange(Map<String,PRangeEntity> range,Map<String, Object> attriMap) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {for (Map.Entry<String, PRangeEntity> map : range.entrySet()) {Object attri = attriMap.get(map.getKey());String type = attriType.get(map.getKey());if(range(attri,type,map.getValue())) {return true;}}return false;}private boolean mustTerms(Map<String, String> terms,Map<String, Object> attriMap) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {for (Map.Entry<String, String> map : terms.entrySet()) {String attri = String.valueOf(attriMap.get(map.getKey()));if(!isContains(attri,map.getValue())) {return false;}}return true;}private boolean mustNotTerms(Map<String, String> terms,Map<String, Object> attriMap) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {for (Map.Entry<String, String> map : terms.entrySet()) {String attri = String.valueOf(attriMap.get(map.getKey()));if(isContains(attri,map.getValue())) {return false;}}return true;}private boolean shouldTerms(Map<String, String> terms,Map<String, Object> attriMap) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {for (Map.Entry<String, String> map : terms.entrySet()) {String attri = String.valueOf(attriMap.get(map.getKey()));if(isContains(attri,map.getValue())) {return true;}}return false;}//不支持空查询private boolean compare(Object attri,Object rule) {if(null == attri || null == rule ) {return false;}return String.valueOf(attri).equals(String.valueOf(rule));}private boolean range(Object value, String type, PRangeEntity rangeEntity) {String gte = rangeEntity.getGte();String lte = rangeEntity.getLte();String gt = rangeEntity.getGt();String lt = rangeEntity.getLt();if(null == value || (StringUtils.isBlank(gte) && StringUtils.isBlank(lte)&& StringUtils.isBlank(gt)&& StringUtils.isBlank(lt))) {return false;}boolean result = true;if ("int".equals(type)) {int valueInt = ((Integer) value).intValue();if(StringUtils.isNotBlank(gte)) {result = (valueInt >= Integer.valueOf(gte));if(!result) {return false;}}if(StringUtils.isNotBlank(lte)) {result = (valueInt <= Integer.valueOf(lte));if(!result) {return false;}}if(StringUtils.isNotBlank(gt)) {result = (valueInt > Integer.valueOf(gt));if(!result) {return false;}}if(StringUtils.isNotBlank(lt)) {result = (valueInt < Integer.valueOf(lt));if(!result) {return false;}}return result;} else if ("double".equals(type)) {double valueDouble = ((Double) value).doubleValue();if(StringUtils.isNotBlank(gte)) {result = (valueDouble >= Double.valueOf(gte));if(!result) {return false;}}if(StringUtils.isNotBlank(lte)) {result = (valueDouble <= Double.valueOf(lte));if(!result) {return false;}}if(StringUtils.isNotBlank(gt)) {result = (valueDouble > Double.valueOf(gt));if(!result) {return false;}}if(StringUtils.isNotBlank(lt)) {result = (valueDouble < Double.valueOf(lt));if(!result) {return false;}}return result;} else if ("long".equals(type)) {long valueLong = ((Long) value).longValue();if(StringUtils.isNotBlank(gte)) {result = (valueLong >= Long.valueOf(gte));if(!result) {return false;}}if(StringUtils.isNotBlank(lte)) {result = (valueLong <= Long.valueOf(lte));if(!result) {return false;}}if(StringUtils.isNotBlank(gt)) {result = (valueLong > Long.valueOf(gt));if(!result) {return false;}}if(StringUtils.isNotBlank(lt)) {result = (valueLong < Long.valueOf(lt));if(!result) {return false;}}return result;} else{String valueStr = String.valueOf(value);if(StringUtils.isNotBlank(gte)) {result = (valueStr.compareTo(String.valueOf(gte))>=0);if(!result) {return false;}}if(StringUtils.isNotBlank(lte)) {result = (valueStr.compareTo(String.valueOf(lte))<=0);if(!result) {return false;}}if(StringUtils.isNotBlank(gt)) {result = (valueStr.compareTo(String.valueOf(gt))>0);if(!result) {return false;}}if(StringUtils.isNotBlank(lt)) {result = (valueStr.compareTo(String.valueOf(lt))<0);if(!result) {return false;}}return result;}}private boolean isContains(String value,String arrStr) {if(StringUtils.isBlank(arrStr) || "[]".equals(arrStr) || StringUtils.isBlank(value)) {return false;}if(!arrStr.startsWith("[") || !arrStr.endsWith("]")) {throw new ExpressionException("terms option with "+arrStr+" is not valid");}String str = arrStr.substring(1, arrStr.length()-1);String[] split = str.split(",");for (String e : split) {if(StringUtils.isNotBlank(e)) {if(e.trim().equals(value)) {return true;}}}return false;}

学习交流

写此文章的目的也是为了和大家更好的学习交流,有什么好的建议欢迎大家给我留言,也欢迎大家关注我的博客,和微信公众号。

高并发高可用高可靠性的千人千面项目技术架构分析相关推荐

  1. 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题

    2019独角兽企业重金招聘Python工程师标准>>> 导语 发布app后,开发者最头疼的问题就是如何解决交付后的用户侧问题的还原和定位,是业界缺乏一整套系统的解决方案的空白领域,闲 ...

  2. 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题 1

    导语 发布app后,开发者最头疼的问题就是如何解决交付后的用户侧问题的还原和定位,是业界缺乏一整套系统的解决方案的空白领域,闲鱼技术团队结合自己业务痛点在flutter上提出一套全新的技术思路解决这个 ...

  3. 解读什么是千人千面算法、猜你喜欢、个性化推荐算法,

    引用 本文为作者个人的一些拙见进行整理后,对千人千面,智能推荐算法这套产品架构思维进行分析所写的文章,可能有所不足,还望各位进行指点 ,后面会写一篇关于怎么使用 ElasticSearch + Spa ...

  4. Java 高并发_JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!...

    JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过! 1.JPG (37.82 KB, 下载次数: 0) 2018-12-3 09:40 上传 2.JPG (28 ...

  5. 什么是并发、高并发以及实现高并发需要考虑的因素

    文章目录 1.什么是并发 2.什么是高并发 3.实现高并发需要考虑的因素 3.1 高性能 3.1.1 网络 3.1.2 CPU 3.1.3 内存 3.1.3.4 IO 3.2 高可用性 3.3 高扩展 ...

  6. 搞懂分布式技术30:高并发解决方案——提升高并发量服务器性能解决思路

    高并发解决方案--提升高并发量服务器性能解决思路 一个小型的网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构.性能的要求都很 ...

  7. java项目: ElasticSearch+Spark构建高相关性搜索服务千人千面推荐系统

    文章目录 1 概述 2 需求分析 3 项目基础搭建[业务系统之基础能力] 4 用户服务.运营后台.商户服务的搭建 用户模型前后端 运营后台 商户入驻: 商户创建.商户查询.商户禁用 5 基础服务: 品 ...

  8. 【java】 java 高并发解决方案和高负载优化方法

    [java] java 高并发解决方案和高负载优化方法 参考文章: (1)[java] java 高并发解决方案和高负载优化方法 (2)https://www.cnblogs.com/lonelywo ...

  9. 【项目介绍】ElasticSearch7+Spark 构建高相关性搜索服务千人千面推荐系统

    我做的项目是在慕课网买的 项目介绍 项目需求背景:模仿大众点评应用提供用户线下搜索推荐服务门店的需求 技术选型:后端业务:SpringBoot:后端存储:MySQL.mybatis接入:搜索系统:El ...

  10. Spring Cloud Alibaba 分布式微服务高并发数据平台化(中台)思想+多租户saas企业开发架构技术选型和设计方案

    基于Spring Cloud Alibaba 分布式微服务高并发数据平台化(中台)思想+多租户saas设计的企业开发架构,支持源码二次开发.支持其他业务系统集成.集中式应用权限管理.支持拓展其他任意子 ...

最新文章

  1. 机器视觉系统的几个问题解析
  2. Dispatch 执行ABC任务,执行完成之后刷新UI,指定任务D
  3. python opencv按照一定间隔保存视频帧
  4. Python2.7+selenium2自动化测试环境搭建
  5. 数据结构-树和二叉树01(定义、度、深度、有序树、森林)
  6. JSON进阶第二篇 AJAX方式传递JSON数据
  7. 学Web前端要用哪些工具?学成后薪资如何?
  8. LOJ.6435.[PKUSC2018]星际穿越(倍增)
  9. PHP header的一些用法
  10. JavaScript运动应用一
  11. c语言小程序 祝你生日快乐!
  12. 黑客文化与介绍:黑客精英轶事
  13. compass watch出错
  14. (附源码)spring boot智能服药提醒app 毕业设计 102151
  15. java sapi.spvoice_SAPI使用总结——SpVoice的使用方法
  16. VT系列一:VT简述
  17. 【云驻共创】华为云HCIA-IoT V2.5培训系列内容之物联网概览
  18. 安卓开发的深度技术实战详解
  19. 从零开始搭建创业公司全新技术栈
  20. 【优化分配】基遗传算法求解医疗资源分配优化问题【含Matlab源码 1419期】

热门文章

  1. 摸着石头过河__投石问路
  2. 如何免费注册一个域名?
  3. 微信小程序—带qq表情的评论输入框
  4. php 依赖安装顺序6,构建PHP框架:第6部分-依赖倒置,控制倒置,哦,天哪!
  5. python自动输入饥荒控制台代码
  6. HDLC协议的基本概念和帧
  7. 群辉监控Surveillance Station不支持H265解决方案(解决群辉不支持的摄像头)
  8. swagger 怎么去掉get delete_橡皮擦英文单词怎么读
  9. 【HD Tune变红/警告】硬盘 SMART 检测参数详解
  10. 企业遇到什么问题一定要用360评估?