谷粒商城分布式高级篇

本文详细介绍了谷粒商城的分布式系统架构,包括ElasticSearch的商品发布代码、es索引设计、Nginx的配置与应用、前端商城系统架构、压力测试、缓存与分布式锁、商品检索、社交登录及单点登录的实现、购物车设计以及订单流程。深入探讨了各个组件的原理和配置,如Feign源码分析、Spring Session、RabbitMQ在分布式事务中的应用,以及JDK8的时间日期API等。
摘要由CSDN通过智能技术生成

ElasticSearch

商品发布代码

es索引的设计

(1)方便检索{

skuId:1

spuId:1

skuTitle:华为xx

price:9988

saleCount:99

attrs:[

{尺寸:5寸}

{CPU:高通945}

]}

冗余:100万*20 = 1000000 * 2kb = 2G

(2)

sku索引{

skuId:1

spuId:1

skuTitle:华为xx

price:9988

saleCount:99

}

attr索引{

spuId:1

attrs:[

{尺寸:5寸}

{CPU:高通945}

]}}

es 的数据都是存储在内存中的

搜索:小米

10000个,4000spu

分步,4000个spu对应的所有属性;

esClient:spuId:[4000个spuId] 4000 * 8 = 32000byte = 32kb

来100万个并发

32kb * 1000000 = 32 GB

问题:网络传输的数据量太大,相对于空间的冗余我们的网络IO更珍贵,所以选用第一种。

“index”: false, 不能作为检索字段,就是 query 的时候不能使用这个
“doc_values”: false 插入的时候该字段不进行倒排索引维护,就是不进行分词处理

nested

image-20200627171111600

PUT product
{
   
  "mappings": {
   
    "properties": {
   
      "skuId": {
   
        "type": "long"
      },
      "spuId": {
   
        "type": "keyword"
      },
      "skuTitle": {
   
        "type": "text",
        "analyzer": "ik_smart"
      },
      "skuPrice": {
   
        "type": "keyword"
      },
      "skuImg": {
   
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "saleCount": {
   
        "type": "long"
      },
      "hasStock": {
   
        "type": "boolean"
      },
      "hotScore": {
   
        "type": "long"
      },
      "brandId": {
   
        "type": "long"
      },
      "catalogId": {
   
        "type": "long"
      },
      "brandName": {
   
         "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "brandImg": {
   
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "catalogName": {
   
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "attrs": {
   
        "type": "nested",
        "properties": {
   
          "attrId": {
   
            "type": "long"
          },
          "attrName": {
   
            "type": "keyword",
            "index": false,
            "doc_values": false
          },
          "attrValue": {
   
            "type": "keyword"
          }
        }
      }
    }
  }
}

feign 源码分析

SynchronousMethodHandler

/*
1. 构造请求数据,将对象转为json
	RequestTemplate template = buildTemplateFromArgs.create(argv); 
2. 发送请求进行执行(执行成功会解码响应数据)
	executeAndDecode(template, options);
3. 执行请求会有重试机制
	while(true){
		try{
			executeAndDecode(template, options);
		}catch(){
			 retryer.continueOrPropagate(e);
		}
	}
*/
public Object invoke(Object[] argv) throws Throwable {
   
    // 构建一个RestTemplate
    RequestTemplate template = buildTemplateFromArgs.create(argv); 
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    // 循环调用
    while (true) {
   
      try {
   
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
   
        try {
   
          // 出现异常就会执行重试机制
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
   
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
   
            throw cause;
          } else {
   
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
   
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

前端商城系统的架构

服务端渲染式开发

用户访问nginx ,nginx反向代理到网关,网关在转发请求给我们的微服务。在网关进行统一的鉴权认证、限流、日志收集等工作。nginx还存储静态资源(比如css、js),动态资源放在微服务里面(就是html模板),这就是实现了动静分离。静态资源放在nginx,所有要经过服务器处理的页面放在对应的微服务中。动静分离的好处减轻微服务的压力。

image-20200627171045748

Nginx搭建域名访问环境

image-20200630220633057

域名映射效果

  • nginx直接代理给网关,网关判断
    • 如果/api/,转交给对应的服务器
    • 如果是 满足域名,转交给对应的服务

访问流程:用户访问页面(gulimall.com)-> 请求来到nginx,nginx根据server 的配置转发请求 -> nginx将请求转发给gateway -> gateway 转发请求给具体的微服务 -> 微服务被调用

正向代理和反向代理

image-20200630220550277

所谓正向代理就是顺着请求的方向进行的代理,即代理服务器他是由你配置为你服务,去请求目标服务器地址。

所谓反向代理正好与正向代理相反,**代理服务器是为目标服务器服务的。

Nginx

nginx 配置文件结构

image-20200630221014551

nginx 动静分离

image-20200630221105778

image-20200702182718512

image-20200630221204867

访问 http://gulimall.com/static/index/js/hello.js 回去找 /usr/share/nginx/html/static/index/hello.js,就是将端口号后面的路径拼接上我们配置的root 路径来寻找静态资源。

注意点

  1. Nginx 代理时会丢失请求的host信息等

压力测试 & 性能监控

缓存与分布式锁

商品检索

kibana测试检索语句

需求:模糊匹配(skuTitle),过滤(按照分类,品牌(多值匹配),价格区间,库存,属性(按照属性id和属性值匹配)),排序(按照价格排序),分页,高亮(skuTitle),聚合分析(有几个分类、几个品牌、几个属性、对应的值和名字

相关api:查询结果字段高亮nested类型字段检索和聚合api

注意

  • 使用filter 的目的是不计算相关性得分

    • term 是精确匹配,
    • terms也是精确匹配,但是匹配的对象可以有多个值(比如匹配数组、nested类型的字段)

细节点

- 我们可以通过子聚合拿到按照品牌聚合之后,品牌的名字
- 聚合的数据是在我们query查询的结果进行的。子聚合是在父聚合的结果进行的聚合。
GET /gulimall_product/_search
{
   
  "query": {
   
    "bool": {
   
      "must": [
        {
   
          "match": {
   
            "skuTitle": "华为"
          }
        }
      ],
      "filter": [
        {
   
          "term": {
   
            "catalogId": "225"
          }
        },
        {
   
          "terms": {
   
            "brandId": [
              "9",
              "1",
              "2"
            ]
          }
        },
        {
   
          "range": {
   
            "skuPrice": {
   
              "gte": 6000,
              "lte": 9000
            }
          }
        },
        {
   
          "term": {
   
            "hasStock": "false"
          }
        },
        {
   
          "nested": {
   
            "path": "attrs",
            "query": {
   
              "bool": {
   
                "must": [
                  {
   
                    "term": {
   
                      "attrs.attrId": {
   
                        "value": "15"
                      }
                    }
                  },
                  {
   
                    "terms": {
   
                      "attrs.attrValue": [
                        "海思(Hisilicon)",
                        "以官网信息为准"
                      ]
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  },
  "sort": [
    {
   
      "skuPrice": {
   
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 10,
  "highlight": {
   
    "fields": {
   
      "skuTitle": {
   }
    },
    "pre_tags": "<b style='color:red'>",
    "post_tags": "</b>"
  },
  "aggs": {
   
    "cata_log_agg": {
   
      "terms": {
   
        "field": "catalogId",
        "size": 10
      },
      "aggs": {
   
        "cata_name_agg": {
   
          "terms": {
   
            "field": "catalogName",
            "size": 10
          }
        }
      }
    },
    "brand_id_agg":{
   
      "terms": {
   
        "field": "brandId",
        "size": 10
      },"aggs": {
   
        "brand_name_agg": {
   
          "terms": {
   
            "field": "brandName",
            "size": 10
          }
        },
         "brand_img_agg": {
   
          "terms": {
   
            "field": "brandImg",
            "size": 10
          }
        }
      }
    },
      "attrs_agg":{
   
        "nested": {
   
          "path": "attrs"
        },"aggs": {
   
          "attr_id_agg": {
   
            "terms": {
   
              "field": "attrs.attrId",
              "size": 10
            },"aggs": {
   
              "attr_name_agg": {
   
                "terms": {
   
                  "field": "attrs.attrName",
                  "size": 10
                }
              },
              "attr_value_agg": {
   
                "terms": {
   
                  "field": "attrs.attrValue",
                  "size": 10
                }
              }
            }
          }
        }
      }
  }
}

ESCliennt 的使用

导入依赖

<properties>
  <!-- 注意:SpringBoot 默认就写好了es的版本,我们需要改成我们自己的 -->
  <elasticsearch.version>7.6.2</elasticsearch.version>
</properties>
<!-- 导入 es的rest-high-level-client -->
<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>elasticsearch-rest-high-level-client</artifactId>
  <version>7.6.2</version>
</dependency>

配置类

请求设置项

配置类的编写

@Configuration
public class GulimallElasticSearchConfig {
   
    // 因为发送请求的时候需要这个类型的参数
    public static final RequestOptions COMMON_OPTIONS;

    static {
   
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
//    builder.addHeader("Authorization", "Bearer " + TOKEN);
//    builder.setHttpAsyncResponseConsumerFactory(
//        new HttpAsyncResponseConsumerFactory
//            .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));
        COMMON_OPTIONS = builder.build();
    }

    @Bean
    public RestHighLevelClient restHighLevelClient() {
   
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("192.168.1.10", 9200, "http")));
        return client;
    }
}

使用步骤

  1. 创建SearchRequest,里面可以存放我们编写的DSL

  2. 发送请求给es,restHighLevelClient.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS)

  3. 数据的序列化和反序列化使用json

// 创建检索请求
SearchRequest searchRequest = new SearchRequest().indices("bank");

// 构造检索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
....
searchRequest.source(searchSourceBuilder);

// 发送检索请求
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
....
  
// 发序列化结果为对象
Account account = JSON.parseObject(sourceAsString, Account.class);

浏览器和URLEncoder的区别


                
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值