电商项目 前台系统的架构

零、目录

  • 前台架构设计
  • 前台分类树
  • 跨域请求技术
    • jsonp
    • httpClient

一 、前台架构设计

  1. 不能直接访问数据库 , 需要通过后台访问数据
  2. 架构: 单通道连接资源 , 数据资源不能交叉访问
  3. 安全: 前台 对外网挂接 , 不适合访问数据库 , 有安全隐患 , 前台需要跨系统访问数据
  4. 跨系统访问数据用到的的技术:
    1. jsonp
    2. httpClient
    3. RabbitMQ

二、 前台分类树

  1. 商品分类:

    1. 在后台系统中的商品分类请求的设计是分级请求的 , 先展示一级分类 ,然后点击时获取被点击的id , 获取他的子分类
    2. 但是在前台系统商品分类的设计中 , 只发起一次ajax异步请求 , 请求到所有的封装好的三层结构的商品分类数据(使用map+ list+ 三层循环嵌套实现)
  2. 商品分类数据的结构
  3. 数据的要求

    1. 前台需要一个json串 , ,需要构建一个对象ItemCatResult,内部只有一个data属性的集合
    2. 整体的返回结构都在data中完成
    3. 每个data中的list对象进行封装嵌套完成3层结构

      u, n, i;
      其中u和n都是字符串
      i是list集合,集合的元素类型又是itemCatData
      
    4. 按照以上要求完成pojo设计

      public class ItemCatResult {@JsonProperty("data")private List<ItemCatData> itemCats ;public List<ItemCatData> getItemCats() {return itemCats;}public void setItemCats(List<ItemCatData> itemCats) {this.itemCats = itemCats;}} public class ItemCatData {@JsonProperty("u")//传递时以u传递 , 减少跨域传递数据的字节数 , 加快传递速度private String url;@JsonProperty("n")private String name;@JsonProperty("i")private List<?> items;public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List<?> getItems() {return items;}public void setItems(List<?> items) {this.items = items;}}
      完成三成结构数据的封装
      处理前台分类树 请求,返回具有三层结构偶的数据@RequestMapping("web/itemcat/all")@ResponseBodypublic ItemCatResult queryItemCat() {//整理三层结构的数据//先获取所有的数据List<ItemCat> itemCats = itemCatService.queryAll();//创建一个返回对象ItemCatResult result = new ItemCatResult();//引入一个map + list + 三层for循环嵌套//map维护当前分类中的父子关系,  key保存一个id , value保存该id的所有子类(list形式)Map<Long , List<ItemCat>> map  = new HashMap<Long , List<ItemCat>>();//对map进行处理for(ItemCat itemCat : itemCats) {//从当前的所有list中获取一个parentIdif(!map.containsKey(itemCat.getParentId())) {//创建一个map的元素 ,key为整个parentId , value为该id的所有子类(list形式)map.put(itemCat.getParentId(), new ArrayList<ItemCat>());}//把当前对象放入map维护的父子关系中 。 map.get(itemCat.getParentId()).add(itemCat);}//开始构建result对象//一级菜单内容 , 从map获取一级分类的listList<ItemCat> itemCatList01 = map.get(new Long(0));//从一级菜单入手完成result的dataList<ItemCatData> itemCatDataList1 = new ArrayList<ItemCatData>();//完成data里的数据for(ItemCat itemCat : itemCatList01) {ItemCatData itemCatData1 = new ItemCatData();itemCatData1.setUrl("/products/"+itemCat.getId()+".html");itemCatData1.setName("<a href='"+itemCatData1.getUrl()+"'>"+itemCat.getName()+"</a>");//进行当前对象的二层菜单的数据封装个 , 使用第二层forList<ItemCatData> itemCatDataList2 = new ArrayList<ItemCatData>();List<ItemCat> itemCatList02 = map.get(itemCat.getId());for(ItemCat itemCats2 : itemCatList02) {ItemCatData itemCatData2 = new ItemCatData();itemCatData2.setUrl("/products/"+itemCats2.getId()+".html");itemCatData2.setName("<a href='"+itemCatData2.getUrl()+"'>"+itemCats2.getName()+"</a>");List<String> itemCatDataList3 = new ArrayList<String>();List<ItemCat> itemCatList03 = map.get(itemCats2.getId());for(ItemCat itemCat3 : itemCatList03) {itemCatDataList3.add("/products/"+itemCat3.getId()+".html"+itemCat3.getName());}itemCatData2.setItems(itemCatDataList3);itemCatDataList2.add(itemCatData2);}itemCatData1.setItems(itemCatDataList2);itemCatDataList1.add(itemCatData1);}result.setItemCats(itemCatDataList1);return result;}
      

三 、 跨域请求问题

  1. 在电商项目中 , 为了提高数据库的可用性 , 引入了缓存技术 , 而像电商项目这样的大型项目一般都需需要进行横向拆分成多个特定功能的系统来同时开发 , 但是由于多个系统去访问数据库执行魂村逻辑时, 可能引起数据混乱 , 这就限制了数据只能单通道访问 , 前台系统在缓存中没有数据时不能直接访问数据库 , 而是通过后台系统去请求数据 。 , 这就引出了跨域访问的种种问题 。

四、 Jsonp跨域请求数据

  1. 前台系统的页面发出ajax请求 , 到后台系统请求商品分类数据
  2. 但是前台和后台是两个系统 , 从后台访问数据属于跨域访问 , 无法使用json格式数据 , 需要jsonp
  3. 前台调用的jquery代码

    $.getJSONP(url  , 参数){}
    
  4. json广泛流行的原因是 , json是js原声支持的格式, 在js中可以自动将json格式的数据转换为对象后调用其中的属性 。
  5. 但是由于js的同源策略导致前台系统获取到后台系统的数据后不能够解析 , 想要解决js同源策略引起的跨域请求问题 , 需要引入jsonp技术
  6. jsonp实际上就是在json字符串外包装一个方法 , 利用script标签可以将跨域的数据请求到 , 然后通过一个js方法即可解析 , 但是要注意js的方法名必须要与json外层包装的方法名一致
  7. 但是后台通过@ResponseBody返回的数据默认就是json格式 , 需要自己构建一个responseBody类似的对象将返回的数据转换成jsonp需要的格式 。
  8. 开发步骤:

    1. 前台发出请求 url?callback=xxxx
    2. 后台controller处理这个请求时不接受callback参数 , 正常返回数据即可
    3. 自定义responseBody转换器

          public class CallbackMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {// 做jsonp的支持的标识,在请求参数中加该参数private String callbackName;@Overrideprotected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException,HttpMessageNotWritableException {// 从threadLocal中获取当前的Request对象HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();String callbackParam = request.getParameter(callbackName);if (StringUtils.isEmpty(callbackParam)) {// 没有找到callback参数,直接返回json数据super.writeInternal(object, outputMessage);} else {JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());try {//将对象转换为json串,然后用回调方法包括起来String result = callbackParam + "(" + super.getObjectMapper().writeValueAsString(object)+ ");";IOUtils.write(result, outputMessage.getBody(), encoding.getJavaName());} catch (JsonProcessingException ex) {throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);}}}public String getCallbackName() {return callbackName;}public void setCallbackName(String callbackName) {this.callbackName = callbackName;}}
      
    4. 将自定义的转换器配置在spring-mvc配置文件中

      <!-- MVC注解驱动 --><mvc:annotation-driven><!-- 采用自定义方案 --><mvc:message-converters><!-- 定义原有的文本转化器  , 如果不定义 则会被后定义的文本转换器覆盖--><bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg index="0" value="UTF-8" /></bean><!-- 添加自定义json转化器,完成jsonp格式数据的峰装 , 支持json跨域 --><beanclass="com.jt.common.spring.exetend.jackson.CallbackMappingJackson2HttpMessageConverter"><!-- 跨域请求中的请求参数名 --><!-- 当参数中有callback属性时 , 调用这个转换器 --><!-- 从参数中寻找callback , 在代码中处理 --><property name="callbackName" value="callback"></property></bean></mvc:message-converters></mvc:annotation-driven>
      
    5. 这样在返回就过时 , 就会在结果外层包装一个callback参数对应的方法 , 形成jsonp格式的数据 。

四、 httpClient跨域请求

  1. 自定义httpClientService

    @Service
    public class HttpClientService {private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientService.class);@Autowired(required=false)private CloseableHttpClient httpClient;@Autowired(required=false)private RequestConfig requestConfig;/*** 执行get请求* * @param url* @return* @throws Exception*/public String doGet(String url,Map<String, String> params,String encode) throws Exception {LOGGER.info("执行GET请求,URL = {}", url);if(null != params){URIBuilder builder = new URIBuilder(url);for (Map.Entry<String, String> entry : params.entrySet()) {builder.setParameter(entry.getKey(), entry.getValue());}url = builder.build().toString();}// 创建http GET请求HttpGet httpGet = new HttpGet(url);httpGet.setConfig(requestConfig);CloseableHttpResponse response = null;try {// 执行请求response = httpClient.execute(httpGet);// 判断返回状态是否为200if (response.getStatusLine().getStatusCode() == 200) {if(encode == null){encode = "UTF-8";}return EntityUtils.toString(response.getEntity(), encode);}} finally {if (response != null) {response.close();}// 此处不能关闭httpClient,如果关闭httpClient,连接池也会销毁}return null;}public String doGet(String url, String encode) throws Exception{return this.doGet(url, null, encode);}public String doGet(String url) throws Exception{return this.doGet(url, null, null);}/*** 带参数的get请求* * @param url* @param params* @return* @throws Exception*/public String doGet(String url, Map<String, String> params) throws Exception {return this.doGet(url, params, null);}/*** 执行POST请求* * @param url* @param params* @return* @throws Exception*/public String doPost(String url, Map<String, String> params,String encode) throws Exception {// 创建http POST请求HttpPost httpPost = new HttpPost(url);httpPost.setConfig(requestConfig);if (null != params) {// 设置2个post参数,一个是scope、一个是qList<NameValuePair> parameters = new ArrayList<NameValuePair>(0);for (Map.Entry<String, String> entry : params.entrySet()) {parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));}// 构造一个form表单式的实体UrlEncodedFormEntity formEntity = null;if(encode!=null){formEntity = new UrlEncodedFormEntity(parameters,encode);}else{formEntity = new UrlEncodedFormEntity(parameters);}// 将请求实体设置到httpPost对象中httpPost.setEntity(formEntity);}CloseableHttpResponse response = null;try {// 执行请求response = httpClient.execute(httpPost);// 判断返回状态是否为200if (response.getStatusLine().getStatusCode() == 200) {return EntityUtils.toString(response.getEntity(), "UTF-8");}} finally {if (response != null) {response.close();}}return null;}/*** 执行POST请求* * @param url* @param params* @return* @throws Exception*/public String doPost(String url, Map<String, String> params) throws Exception {// 创建http POST请求HttpPost httpPost = new HttpPost(url);httpPost.setConfig(requestConfig);if (null != params) {// 设置2个post参数,一个是scope、一个是qList<NameValuePair> parameters = new ArrayList<NameValuePair>(0);for (Map.Entry<String, String> entry : params.entrySet()) {parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));}// 构造一个form表单式的实体UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters);// 将请求实体设置到httpPost对象中httpPost.setEntity(formEntity);}CloseableHttpResponse response = null;try {// 执行请求response = httpClient.execute(httpPost);// 判断返回状态是否为200if (response.getStatusLine().getStatusCode() == 200) {return EntityUtils.toString(response.getEntity(), "UTF-8");}} finally {if (response != null) {response.close();}}return null;}public String doPostJson(String url, String json) throws Exception {// 创建http POST请求HttpPost httpPost = new HttpPost(url);httpPost.setConfig(requestConfig);if(null != json){//设置请求体为 字符串StringEntity stringEntity = new StringEntity(json,"UTF-8");httpPost.setEntity(stringEntity);}CloseableHttpResponse response = null;try {// 执行请求response = httpClient.execute(httpPost);// 判断返回状态是否为200if (response.getStatusLine().getStatusCode() == 200) {return EntityUtils.toString(response.getEntity(), "UTF-8");}} finally {if (response != null) {response.close();}}return null;}}
    
  2. 在spring配置文件中配置

    <!-- 定义httpclient连接池 -->
    <bean id="httpClientConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager" destroy-method="close"><!-- 设置连接总数 --><property name="maxTotal" value="${http.pool.maxTotal}"></property><!-- 设置每个地址的并发数 --><property name="defaultMaxPerRoute" value="${http.pool.defaultMaxPerRoute}"></property>
    </bean><!-- 定义 HttpClient工厂,这里使用HttpClientBuilder构建-->
    <bean id="httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder" factory-method="create"><property name="connectionManager" ref="httpClientConnectionManager"></property>
    </bean><!-- 得到httpClient的实例 -->
    <bean id="httpClient" factory-bean="httpClientBuilder" factory-method="build"/><!-- 定期清理无效的连接 -->
    <bean class="com.jt.common.util.IdleConnectionEvictor" destroy-method="shutdown"><constructor-arg index="0" ref="httpClientConnectionManager" /><!-- 间隔一分钟清理一次 --><constructor-arg index="1" value="60000" />
    </bean><!-- 定义requestConfig的工厂 -->
    <bean id="requestConfigBuilder" class="org.apache.http.client.config.RequestConfig.Builder"><!-- 从连接池中获取到连接的最长时间 --><property name="connectionRequestTimeout" value="${http.request.connectionRequestTimeout}"/><!-- 创建连接的最长时间 --><property name="connectTimeout" value="${http.request.connectTimeout}"/><!-- 数据传输的最长时间 --><property name="socketTimeout" value="${http.request.socketTimeout}"/><!-- 提交请求前测试连接是否可用 --><property name="staleConnectionCheckEnabled" value="${http.request.staleConnectionCheckEnabled}"/>
    </bean> <!-- 得到requestConfig实例 -->
    <bean id="requestConfig" factory-bean="requestConfigBuilder" factory-method="build" />
    
  3. 配置文件所需要的参数信息 httpclient.properties

        #从连接池中获取到连接的最长时间http.request.connectionRequestTimeout=500#5000http.request.connectTimeout=5000#数据传输的最长时间http.request.socketTimeout=30000#提交请求前测试连接是否可用http.request.staleConnectionCheckEnabled=true#设置连接总数http.pool.maxTotal=200#设置每个地址的并发数http.pool.defaultMaxPerRoute=100
    

大数据互联网架构阶段 前台系统架构 跨域请求相关推荐

  1. 基于大数据的情报分析与服务系统架构设计

    一.大数据在军事领域中的应用  技术作为一项从大量数据中获取有用知识的实用技术,已被广泛应用于各行各业并取得了较大的经济和社会效益,而其在军事领域的应用也具有很大的潜力. 1 提升情报获取能力 现代战 ...

  2. 创建Maven分布式前台系统架构,写出京动态导航,跨域返Json数据

    前台系统架构 分层的架构有什么好处: 有利于系统的维护,扩展. 分层的结构是按照功能细化,细化之后就能够分布式的部署. 灵活性 前台系统与服务层可以分离 开发团队可以分开,提高开发效率 缺点: 服务器 ...

  3. [转]携程大数据实践:高并发应用架构及推荐系统案例

    本文来自携程技术中心基础业务研发部的<应用架构涅槃>系列分享.据基础业务研发部负责人李小林介绍,互联网二次革命的移动互联网时代,如何吸引用户.留住用户并深入挖掘用户价值,在激烈的竞争中脱颖 ...

  4. 携程大数据实践:高并发应用架构及推荐系统案例

    本文来自携程技术中心基础业务研发部的<应用架构涅槃>系列分享.据基础业务研发部负责人李小林介绍,互联网二次革命的移动互联网时代,如何吸引用户.留住用户并深入挖掘用户价值,在激烈的竞争中脱颖 ...

  5. 大数据实时处理:百分点实时计算架构和算法

    当今时代,数据不再昂贵,但从海量数据中获取价值变得昂贵,而要及时获取价值则更加昂贵,这正是大数据实时计算越来越流行的原因.以百 分点公司为例,在高峰期每秒钟会有近万HTTP请求发送到百分点服务器上,这 ...

  6. 【BDTC 2017讲师专访】彭冬:微博商业基础大数据平台(D+)的架构演进

    BDTC 2017中国大数据技术大会将于12月7日-9日在北京新云南皇冠假日酒店举行,大会为期三天.届时,近百位技术专家将为现场数千名的大数据行业精英.技术专家及意见领袖带来多场技术演讲,分享最新技术 ...

  7. 【模块间的通讯】数据接口及通讯代理系统架构

    系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录 前言 为什么需要数据接口及通讯代理系统架构 解决这种数据接口问题和一对一通 ...

  8. 【2015年第4期】城市交通大数据技术及智能应用系统

    城市交通大数据技术及智能应用系统 熊刚1,2,董西松1,2,3,朱凤华1,2,季统凯2 1. 中国科学院自动化研究所复杂系统管理与控制国家重点实验室 北京 100190: 2. 中国科学院云计算中心 ...

  9. 谭安林:大数据在智能外呼系统的应用

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由云加社区技术沙龙发表于云+社区专栏 谭安林,腾讯高级工程师,2015年加入腾讯,8年互联网从业经历,从事大数据平台与产品开发相关工作: ...

最新文章

  1. linux 查看磁盘空间 文件 文件夹 大小
  2. 天翼云从业认证(4.1)上云迁移实战
  3. Matrix Completion with Noise
  4. XP MSTSC连接WIN7或WIN8问题
  5. 配置Nginx来支持php
  6. 信号 09 | 函数pause
  7. [html] 你写一个页面需要多长时间?
  8. 有关启动图片Launch的设置
  9. 在 React 工程中利用 Mota 编写面向对象的业务模型
  10. 一条数据的HBase之旅,简明HBase入门教程1:开篇
  11. CSS权威指南(第三版)笔记
  12. {Unity} iOS 9 字体的坑
  13. 解决win7资源监视器不能开启
  14. jquery boxy
  15. AD18安装及其中英文切换
  16. 入手Invicta 8926 OB潜水自动机械腕表
  17. 添加按钮声音nbsp;nbsp;播放声音
  18. 苹果id界面无法打开解决方法「iphone技巧」
  19. 新手司机上路 请多关照
  20. 安卓桌面壁纸_效仿安卓?iOS14或将支持“快应用” 功能 可玩性更强了

热门文章

  1. Android内存优化2—使用软引用和弱引用
  2. mac 无法ssh localhost,错误提示:bash: /usr/local/bin/ssh_session: Permission denied
  3. 今天写的上传类,纯练手之作,供新人学习
  4. ubuntu18.04安装windows版本微信
  5. python3 面向对象详解_Python3面向对象
  6. 电脑没有ps怎么改照片dpi_PS入门的小技巧来啦!小白们还在等什么呢?快进来啊!...
  7. 七种方式求斐波那契(Fibonacci)数列通项
  8. ServiceComb开放性设计
  9. 极限编程阅读笔记--第二篇
  10. python自学笔记之开源小工具:SanicDB介绍