分布式高级篇--1

包含商品上架、整合Tymeleaf、配置Nginx方向代理

P128 商品上架

上架的商品的有关数据保存在ES当中,尽量减少内存的消耗,所以只保存展示在页面上得数据,商品的其他详细信息再去数据库中查。

        public R setData(Object obj){
        put("data",obj);
        return this;
    }
    //利用fastjson进行逆转
    public <T> T getData(TypeReference<T> typeReference){
        Object data = get("data");
        String s = JSON.toJSONString(data);
        T t = JSON.parseObject(s,typeReference);
        return t;
    }
这东西后面要用到,先记着留个印象
商品上架的流程分析

1、发送远程调用,库存系统查看是否有库存
2、设置商品的热度,操作太复杂,现在先设为零
3、查询品牌(名字,Logo)和分类的名字信息
4、查询当前所有Sku所有可以被检索的规格参数
5、将数据发送给ES进行保存:gulimall-search
6、修改当前spu的状态
7、重复调用?接口幂等性;

1、up(上架)功能传入的参数是spuId,所以编写的Controller实际上就是在product/spuinfo中,controller--service--serviceImpl,根据业务的分析流程,我们需要拿到sku的具体信息才能够执行其他的流程,所以先完成第4步骤。
通过spuId(传过来的参数)拿到对应的sku所有信息,使用strem流将skuId提取出来,通道skuId去AttrValue属性表拿到对应的Attr属性,但是有的Attr是不能被检索到了,也就是不能参与检索,这关乎到一个数据库字段search_type,所以我们需要自定义一个方法,自己去重写SQL语句,返回真正可被检索的属性,将可被检索的AttrId转换为一个HashSet,通过stream流的fiflter过滤,set.contains(item.getAttrId()),接着使用map将拿到的属性copy给SkuEsModel.Attrs,这样就完成了SkuEsModel的List<Attr>的赋值,拿到可被检索的规格参数。
2、涉及到远程调用,所以需要使用到Feign,由于是去查看是否有库存,所以需要在Ware服务上先完成这个功能。
同样的也是controller--service---impl----dao

<select id="getSkuStock" resultType="java.lang.Long">
        select SUM(stock-stock_locked) from `wms_ware_sku` where sku_id=#{skuId}
</select>

完成服务的远程调用后,将sku的属性值拷贝给真正要用的SkuEsModel,同时设置好不一样的参数值。
3、设置热度skuEsModel.setHotScore(0L);为了省事先这样
4、设置品牌和分类的信息,同时将在上面求出来的List<Attr>赋值给SkuEsModel。
5、发送给ElasticSearch

    @PostMapping("/product")
    public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels){
        boolean b = false;
        try {
            b = productSaveService.productStatusUp(skuEsModels);
            return R.ok();
        }catch (Exception e){
            log.error("ElasticSaveController 商品上架错误",e);

        }
        if(b){ return R.ok();
        }else {return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg());}
    }
@Slf4j
@Service
public class ProductSaveServiceImpl implements ProductSaveService {
    @Autowired
    RestHighLevelClient restHighLevelClient;
    @Override
    public boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {
        //保存到ES
        //1、给ES建立索引,建立好映射关系
        //2、给ES保存这些数据
        BulkRequest bulkRequest = new BulkRequest();
        for(SkuEsModel model:skuEsModels){
            //1、构造保存请求
            //建立索引,参数是索引的名称,表示按照这个名称来存
            IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
            indexRequest.id(model.getSkuId().toString());
            String s = JSON.toJSONString(model);
            indexRequest.source(s, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, GulimallESConfig.COMMON_OPTIONS);
        //统计返回结果
        //TODO 1、如果批量错误
        boolean b = bulk.hasFailures();
        List<String> collect = Arrays.stream(bulk.getItems()).map(item -> {
            return item.getId();
        }).collect(Collectors.toList());
        log.info("商品完成:{}",collect);
        return b;
    }
        R r = searchFeignService.productStatusUp(skuEsModels);
        if(r.getCode() == 0){
            //远程调用成功
            //TODO 6、修改当前spu的状态
            baseMapper.updateSpuStatus(spuId, ProductConstant.StatusEnum.SPU_UP.getCode());
        }else {
            //远程调用失败
            //TODO 7、重复调用?接口幂等性;
        }

所有的代码参考SpuInfoServiceImpl
Feign调用流程:
1.构造请求数据,将对象转为JSON;
RequestTemplate template = buildTemplateFromArgs.create(argv);
2.发送请求进行执行(执行成功会解码响应数据)
executeAndDecode(template);
3.执行请求会有重试机制

         while(true) {
            try {
               executeAndDecode(template);
            } catch (RetryableException var8) {
                RetryableException e = var8;
                try {
                    retryer.continueOrPropagate(e);
                } catch (RetryableException var7) {
                    Throwable cause = var7.getCause();
                  continue;
                }
             }
          }

P136 整合Thymeleaf渲染页面

访问所有请求全部访问的是Nginx,Nginx作为反向代理将所有数据转换给网关GetWay,网关再路由给各个服务,有网关的好处就是我们可以做统一的鉴权认证以及限流工作等等,而加上Nginx我们在后面部署的时候,可以将每个微服务自己里面的页面的静态资源部署到Nginx里面,做到了部署期间的动静分离,静就是静态资源,让Nginx返回,动就是动态请求,所有要经过服务器处理的动态业务请求,就称为动的资源,这样做的好处就是分担微服务的压力。
每一个微服务都可以独立部署,运行,升级。独立自治的

加依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

开发期间关闭Thymeleaf缓存
  thymeleaf:
    cache: false

建立一个web包,跟web有关的页面放到包中,改controller为app
静态资源放在static路径下,就可按照路径直接访问
页面放在templates下,直接访问
页面修改不重启服务器实时更新
1、引入dev--tools
2、修改完页面后按 controller  shift  f9重新自动编译下页面    记住一定要关缓存。如果是代码配置,还是推荐重启

P137渲染一级分类

编写控制器方法返回一级分类数据,然后在index页面上使用Tymeleaf进行一级分类渲染

    @GetMapping({"/","index.html"})
    public String indexPage(Model model){
        //获取所有的一级分类
        List<CategoryEntity> entities = categoryService.getOneCategory();
        model.addAttribute("categorys",entities);
        return "index";
    }

<li th:each="category:${categorys}">      <!--th:attr="tg-data=${category.catId}" 唯一标识,可以是id  <b th:text="${category}">家用电器</b> 页面展示-->
              <a href="#" class="header_main_left_a" th:attr="ctg-data=${category.catId}">
                <b th:text="${category.name}">家用电器</b></a>
            </li>

P138渲染二级三级分类

修改static/index/catelogLoader 中的function地址为"index/catalog.json",同时将原本的catalog.json
删除,接着自己编写一个控制器方法。

    @ResponseBody
    @GetMapping("/index/catalog.json")
    public Map<String, List<Catelog2Vo>> getCatalogJson(){
        Map<String, List<Catelog2Vo>> map = categoryService.getCatalogJson();
        return map;
    }
返回的是JSON数据,加上@ResponseBody,封装好的Vo详情再参考代码

P139Nginx搭建域名访问环境

按照开发的正常流程,如果项目上线的话,需要买一台服务器,有公网ip地址,这样别人都能访问到这个服务器,我们需要为这个公网ip地址绑定上域名,然后作一些备案等操作,这样别人就能通过域名来访问服务器。
我们这里先使用的是Nginx+Windows搭建域名访问环境。

正向代理与反向代理

正向代理:例如访问谷歌,国内的IP访问不了的,所以使用代理服务器,通过代理服务器访问,拿到数据后,代理服务器再帮我们返回。(代理服务器帮我们上网),能够隐藏客户端信息。
反向代理:搭建集群环境的时候非常需要,它的作用是屏蔽内网服务器信息,负载均衡访问。(搭建项目环境的时候一定要用到)
请求gulimall的时候先访问Nginx,由Nginx转交到后台的服务集群

在本机上配置Linux地址,将其地址映射给gulimall
还有一种访问项目的方式,就是输入公网ip,会有一个bns将其转换为ip地址然后访问,我们在项目中指示进行模拟而已,真实的公司肯定是有公网ip的。

搭建域名访问环境使用SwitchHosts

在Linux 中的/mydata/nginx/conf/conf.d目录中,将default.conf赋值出一份gulimall.conf,修改里面的
    server_name  gulimall.com;     proxy_pass http://192.168.56.1:10100
表示gulimall域名的所有请求反向代理到商品服务中
server {
    listen       80;
    server_name  gulimall.com;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
        proxy_pass http://192.168.56.1:10100;
    }
修改完需要重启Nginx    docker restart nginx

成功访问到商品服务,原理是什么?
浏览器访问gulimall.com,在Windows中指定了gulimall.com映射的虚拟机ip,所以浏览器访问gulimall.com实际上访问的就是虚拟机,来到虚拟机,虚拟机的Nginx监听的是80端口(默认端口),所以访问的是Nginx的index页面,接着我们在Nginx的配置文件中指定---凡是以gulimall.com域名来访问都转到商品服务去。

P140 Nginx负载均衡到网关

Nginx将请求交给网关,由网关从注册中心动态发现商品服务都在哪,然后由网关负载均衡到商品服务。网关也可以部署多个,Nginx将请求负载均衡到网关,由网关再给我们转发出去。
Nginx负载均衡配置请参考官网:http://nginx.org/en/docs/http/load_balancing.html

配置上游服务器
这是在http块中
upstream gulimall{
        server 192.168.56.1:88;
}
负载均衡的配置,这是在Server块中的。表示路由到网关
location / {
        proxy_pass http://gulimall;
}
上面表示的是将192.168.56.1:88代理到http://gulimall

配置好Nginx的配置后,网关也要进行配置,否则报404

这一段配置一定要在最后面!!!

只要是gulimall的所有请求都转给product服务

        - id: gulimall_host_route
          uri: lb://gulimall-product
          predicates:
            - Host=**.gulimall.com

Nginx路由到网关了,但是却不能访问得到gulimall.com

在这里说一个很严重的问题,为什么网关没把gulimall路由过来?

Hosts主机地址没匹配上,为什么没匹配上,跟Nginx有关。

Nginx代理给网关的时候会丢失Hosts信息,如何解决?

    location / {
        proxy_set_header Host $host;
        proxy_pass http://gulimall;
    }

加上 proxy_set_header Host $host;然后重启nginx
Nginx的基本配置就是这样
浏览器代理给Nginx----GetWay---Product

在Nginx的配置文件http块中
    upstream gulimall{
    server 192.168.56.1:88;
    }

在Nginx的配置文件Server块中
server {
    listen       80;
    server_name  gulimall.com;

    location / {
    proxy_set_header Host $host;
    proxy_pass http://gulimall;
    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 考查分数 上午 6-8分 考查点: 数据库模式 2 ER模型 ==5== 下午题考查 15分 关系代数 ==...
    集祈0808阅读 281评论 0 0
  • 一、测试计划:有且只有一个,可以理解成一个jmeter项目 二、线程组 常用线程组 1、setup :在执行普通线...
    小波114207阅读 472评论 0 0
  • 如何落实语文教学核心素养? 语文素养的培养已占据着语文教育教学中核心的位置,这就要求教师在教育教学的过程中,实施科...
    水忆雪禅阅读 429评论 0 0
  • 函数的作用域: 函数的作用域的作用有:保护数据的安全( 全局的不安全 ),减少命名冲突 作用域分为三个作用域:全局...
    Dream_1324阅读 169评论 0 0
  • 小宝不小心把眼罩落到了学校,妈妈为了惩罚她,要她用自己的零花钱买一个新的。小宝轻飘飘的说可以啊。妈妈说,给我50块...
    香酥灰枣阅读 130评论 0 0