包含商品上架、整合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;
}