网上商城系统简介
微服务+分布式+全栈+集群+部署+自动化运维+可视化CICD 1.分布式基础 全栈开发 后端springboot + mybatis springcloud docker 前端vue.js + ElementUI ES6 Node.js:npm Babel Webpack 实现后台管理系统,提升全栈开发能力。 2.分布式高级 微服务架构 实现一整套商城业务逻辑(商品服务,秒杀,购物车,订单,库存),学习微服务开发的技术栈,提升项目架构能力 基础springboot + springcloud;搭配alibaba Nacos注册中心,配置中心;Sentinel实现流量保护, 网关,远程调用,链路追踪,缓存,session同步方案,全文检索,线程池,压力测试,调优,分布式锁 3.高可用集群 架构师提升 搭建k8s集群 一主两从(kubernetes),使用kubesphere一站式平台,将整个应用部署到k8s集群, 学习DevOPS技术栈,全套可视化CI/CD(持续集成,持续部署流程:开发者在github拉取代码打包成Docker镜像,) 集群技术:mysql集群,redis集群,RabbitMQ的镜像队列
Nacos:注册中心原理
Nacos注册中心分为server与client,server采用Java编写,为client提供注册发现服务与配置服务。而client可以用多语言实现,client与微服务嵌套在一起。服务注册的策略的是每5秒向nacos server发送一次心跳,心跳带上了服务名,服务ip,服务端口等信息。同时 nacos server也会向client 主动发起健康检查,支持tcp/http检查。如果15秒内无心跳且健康检查失败则认为实例不健康,如果30秒内健康检查失败则剔除实例。Server上采用了ConcurrentHashMap保存实例信息,如果服务配置了持久化,会被保存到数据库。配置中心原理:客户端发起长轮训请求,设置超时时间为30 S,服务端将该请求加入到一个叫allSubs的队列中,等待配置发生变更时由任务DataChangeTask 主动去触发;服务端采用事件订阅的方式来监听本地数据变化的事件,一旦收到事件,则触发DataChangeTask的通知,并且遍历allStubs队列中的ClientLongPolling,将变更后的数据写入响应对象,通过HTTP写回给客户端。 Feign远程调用:核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,最终转换成HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVA Bean,放回给调用者。Feign远程调用的基本流程,大致如下图所示。 从上图可以看到,Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的 Request 请求。通过Feign以及JAVA的动态代理机制,使得Java 开发人员,可以不用通过HTTP框架去封装HTTP请求报文的方式,完成远程服务的HTTP调用。
GateWay:
Gateway的客户端回向Spring Cloud Gateway发起请求,请求首先会被HttpWebHandlerAdapter进行提取组装成网关的上下文,然后网关的上下文会传递到DispatcherHandler。DispatcherHandler是所有请求的分发处理器,DispatcherHandler主要负责分发请求对应的处理器,比如将请求分发到对应RoutePredicateHandlerMapping(路由断言处理器映射器)。路由断言处理映射器主要用于路由的查找,以及找到路由后返回对应的FilteringWebHandler。FilteringWebHandler主要负责组装Filter链表并调用Filter执行一系列Filter处理,然后把请求转到后端对应的代理服务处理,处理完毕后,将Response返回到Gateway客户端。
在Filter链中,通过虚线分割Filter的原因是,过滤器可以在转发请求之前处理或者接收到被代理服务的返回结果之后处理。所有的Pre类型的Filter执行完毕之后,才会转发请求到被代理的服务处理。被代理的服务把所有请求完毕之后,才会执行Post类型的过滤器。
- 分布式基础
围绕后台管理系统,实现一整套增删改查逻辑,前后分离的方式实现商品系统,远程调用微服务功能
1 安装visualBox虚拟机,安装vagrant快速创建centos虚拟机环境 。虚拟机安装docker,docker安装mysql和redis。 2 配置vscode作为前端管理系统(Node.js的npm包管理工具),IDEA,配置maven,作为后端开发环境。 安装git-ssh,在gitee新建仓库gulimall 3 微服务:商品服务product,存储服务ware,订单服务order,优惠券服务coupon,用户服务member 4 数据库:课件提供了SQL语句,执行后生成表,没有数据,(数据库设计.pdm(表的设计),PowerDesigner打开) 5 根据数据库设计,使用逆向工程,为商品服务创建基本的增删改查代码,简化开发 码云gitee上下载renren-fast(后端),renren-fast-vue(前端)项目 (renren-fast脚手架工程把基本的要用的代码,一些表,权限设计都加上了,只需做少量修改) 1) renren-fast db中的mysql.sql,执行创建后台管理数据库gulimall-admin。修改application-dev.yml中的配置信息,数据库链接信息(地址,名称,用户名及密码) 2) renren-fast-vue,npm install下载依赖后打开 3) 因为renren-fast-vue前端项目默认配置了调用地址为localhost:8080,所以可以成功从我们刚才启动的renren-fast管理系统后端项目中拿到验证码信息。 4) renren-generator(代码生成器) 为每个微服务逆向生成代码, 修改application.yml, generator.properties配置,运行RenrenApplication 生成mybatis的mapper文件,controller, dao, entity, service全部生成(接口,实现类),后续需要进行修改或者添加新的方法 5) 配置&测试微服务基本CRUD功能 整合MyBatis-Plus,导入依赖,配置数据源(导入数据库驱动,application.yml配置数据源信息);配置MyBatis-Plus(@MapperScan,告诉mybatis-plus mapper接口在哪里;告诉mybatis-plus sql映射文件位置)
6 SpringCloud Alibaba
阿里18年开发的微服务一站式解决方案
7 Nacos,作为注册中心和配置中心
1)安装启动nacos 2)注册中心:Common中配置依赖nacos-discovery;使用 @EnableDiscoveryClient 注解开启服务注册与发现功能;配置文件application.yml中,配置了Nacos Sever地址,当前微服务名字 3)Feign(远程调用):声明式的HTTP客户端 4)配置中心: 不在application.properties等文件中配置了,而是放到nacos配置中心公用,这样无需每台机器都改。 4.1)Common中添加依赖nacos-config; 4.2)bootstrap.properties(springboot中规定,优先级高于application.properties)中配置当前应用名字,配置中心地址; 4.3)@Value("${coupon.user.name}") 获取某个配置的值 private String name; //从application.properties中获取//不要写user.name,他是环境里的变量 4.4)浏览器Nacos里的配置列表,新建配置(当前应用名.properties); 4.5)动态获取刷新配置:生产中加入@RefreshScope,配置中心中配置修改,实时修改 4.6)配置中心优先级高于当前应用的配置文件
4.7)命名空间:nacos.config.namespace配置唯一idb176a68a-6800-4648-83。。。
开发、测试、开发各一个命名空间;每个微服务一个命名空间 配置分组:默认DEFAULT_GROUP,双十一,618的优惠策略改分组 nacos.config.group=
4.8)加载多配置集 把application.yml抽离出多个配置。在nacos里创建,再把配置导入bootstrap.properties spring.cloud.nacos.config.ext-config[0].data-id=datasource.yml spring.cloud.nacos.config.ext-config[0].group=dev spring.cloud.nacos.config.ext-config[0].refresh=true 以前springboot任何方法从配置文件中获取值,都能使用
8 GateWay网关:路由转发,权限校验,限流控制
1) 使用initilizer创建gateway,pom.xml加上common依赖 2) 按7方法,将网关注册到nacos中,注册中心和配置中心,网关也能通过nacos找到其他服务 3) 在项目里创建application.yml,根据条件转发到uri等 ES6新特性: Let&const,解构&字符串,箭头函数,对象优化,map&reduce,promise异步编排,模块化 Vue前端框架: 指令单向绑定&双向绑定,v-on,v-for,v-if,计算属性和侦听器,组件化基础,生命周期和钩子函数,使用Vue脚手架进行模块化开发,整合ElementUI快速开发 Node.js npm 包管理工具 Babel:JavaScript编译器, 使用es新语法,不用担心浏览器兼容问题 Webpack:自动化项目构建工具
9 三级分类
1) 查询-递归树形结构数据获取 @TableField(exist = false) //此属性在数据表中不存在 private List children; //包含所有子分类
public List<CategoryEntity> listWithTree() {
List<CategoryEntity> entities = baseMapper.selectList(null);
List<CategoryEntity> level1Menus = entities.stream().filter((categoryEntity) -> {
return categoryEntity.getParentCid() == 0;
}).map((menu) -> {
menu.setChildren(getClildrens(menu, entities));
return menu;
}).sorted((menu1, menu2) -> {
return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort());
}).collect(Collectors.toList());
return level1Menus;
}
private List<CategoryEntity> getClildrens(CategoryEntity root, List<CategoryEntity> all) {
List<CategoryEntity> children = all.stream().filter(categoryEntity -> {
return categoryEntity.getParentCid().equals(root.getCatId());
}).map(categoryEntity -> {
categoryEntity.setChildren(getClildrens(categoryEntity, all));
return categoryEntity;
}).sorted((menu1, menu2) -> {
return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort());
}).collect(Collectors.toList());
return children;
}
2) 配置网关路由与路径重写 人人快速开发平台中新建目录:商品系统,在商品系统中新增菜单:分类维护。后台管理系统中gulimall_admin表中有了商品系统,分类维护。视图页面url为product/category,即在views/modules/product/category.vue中显示。编辑category.vue,使用element ui中的Tree树形控件,并且给商品系统发送获取数据的请求。在index.js中修改api接口请求地址为网关地址。此时登陆时验证码不显示(404):因为验证码由renren-fast服务生成,所以需要将发送给网关的请求先代理给后台管理系统renren-fast:将renren-fast注册到注册中心;修改网关路由规则,将任意请求默认负载均衡(lb:)到renren-fast。前端发送请求url:localhost:88/api/captcha.jpg?uuid=,此时网关转发给renren-fast:8080/api/captcha.jpg,而默认获取验证码为localhost:8080/renren-fast/captcha.jpg,使用网关中filters中的路径重写功能,转换为默认路径。登陆请求报错(403),即跨域问题(从8001访问88,引发CORS跨域请求,浏览器会拒绝跨域请求。具体来说当前页面是8001端口,但是要跳转88端口,这是不可以的(post请求json可以)) 3) 网关统一配置跨域 跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对js施加的安全限制。(ajax可以) 同源策略:是指协议,域名,端囗都要相同,其中有一个不同都会产生跨域 跨域流程:这个跨域请求的实现是通过预检请求实现的,先发送一个OPSTIONS探路,收到响应允许跨域后再发送真实请求 跨域请求流程: 非简单请求(PUT、DELETE)等,需要先发送预检请求 -----1、预检请求、OPTIONS ------> <----2、服务器响应允许跨域 ------ 浏览器 | | 服务器 -----3、正式发送真实请求 --------> <----4、响应数据 -------------- 跨域的解决方案 方法1:设置nginx包含admin和gateway。都先请求nginx,这样端口就统一了 方法2:让服务器告诉预检请求能跨域 解决方法:在网关中定义“GulimallCorsConfiguration”类,该类用来做过滤,允许所有的请求跨域。 @Configuration // gateway 标注这是一个配置类 public class GulimallCorsConfiguration { @Bean // 在容器中添加过滤器 public CorsWebFilter corsWebFilter(){ // 基于url跨域,选择reactive包下的 UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource(); // 跨域配置信息 CorsConfiguration corsConfiguration = new CorsConfiguration(); // 允许跨域的头 corsConfiguration.addAllowedHeader(""); // 允许跨域的请求方式 corsConfiguration.addAllowedMethod(""); // 允许跨域的请求来源 corsConfiguration.addAllowedOrigin("*"); // 是否允许携带cookie跨域 corsConfiguration.setAllowCredentials(true);
// 任意url都要进行跨域配置
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
} 此时依然拦截跨源请求,出现了多个请求,并且也存在多个跨源请求。需要修改renren-fast项目,注释掉“io.renren.config.CorsConfig”类。然后再次进行访问。 4) 查询-树形展示三级分类数据 之前解决了登录验证码的问题,/api/请求重写成了/renren-fast,此时网关上所做的路径映射不正确,映射后的路径为http://localhost:8001/renren-fast/product/category/list/tree。网关中定义product路由规则,进行路径重写。访问 localhost:88/api/product/category/list/tree invalid token,非法令牌,后台管理系统中没有登录,所以没有带令牌。原因:先匹配的先路由,fast和product路由重叠,fast要求登录。修正:在路由规则的顺序上,将精确的路由规则放置到模糊的路由规则的前面,否则的话,精确的路由规则将不会被匹配到,类似于异常体系中try catch子句中异常的处理顺序。 修改前端category.vue,这里改的是点击分类维护后的右侧显示,data解构,加上{},把data的地方改成menus。 //方法集合 methods: { getMenus() { this.KaTeX parse error: Expected '}', got 'EOF' at end of input: … url: this.http.adornUrl("/product/category/list/tree"), // 体会一下我们要重写product项目里这个controller method: “get” }) .then(({ data }) => { // success 响应到数据后填充到绑定的标签中 this.menus = data.data; // 数组内容,把数据给menus,就是给了vue实例,最后绑定到视图上 }) //fail .catch(() => {}); }, 此时有了3级结构,但是没有数据,在category.vue的模板中,数据是menus,而还有一个props。这是element-ui的规则,
而在data中 defaultProps: { children: "children", label: "name" } 5) 删除-页面效果 1.使用render-content指定渲染函数,该函数返回需要的节点区内容即可。渲染函数的用法请参考 Vue 文档。 2.使用 scoped slot 会传入两个参数node和data,分别表示当前节点的 Node 对象和当前节点的数据。(前面ajax发送请求,拿到data,赋值给menus属性,而menus属性绑定到标签的data属性。而node是ui的默认规则) 在中加入
标签,其中就有两个按钮,插入和删除。methods中相应加入append和delete方法。属性expend-on-click-node:true点击展开分类,设为false,这样点击append或者delete就不会展开分类。button属性v-if:设置有子节点才能删除,层级为1或2才能插入。el-tree属性show-checkbox:给节点加上选择框。属性node-key:唯一标识树节点(catId)。 6) 删除-逻辑删除
7)
|