github项目地址javascript
微服务架构图html
项目描述前端
conf/my.cnf
vue
[mysqld] init_connect='SET collation_connection = utf8_unicode_ci' init_connect='SET NAMES utf8' character-set-server=utf8 collation-server=utf8_unicode_ci skip-character-set-client-handshake skip-name-resolve [client] default-character-set=utf8 [mysql] default-character-set=utf8
启动容器java
docker run --name mysql3306 -p 3306:3306 -v /var/touchAirMallVolume/mysql/data:/var/lib/mysql -v /var/touchAirMallVolume/mysql/conf/my.cnf:/etc/mysql/my.cnf -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7.31
docker run --name mysql -p 3306:3306 --restart=always -v /var/local/mysql/data:/var/lib/mysql -v /var/local/mysql/conf/my.cnf:/etc/mysql/my.cnf -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7.31 docker run -p 6379:6379 --name redis --restart=always \ -v /var/local/redis/data:/data \ -v /var/local/redis/conf/redis.conf:/etc/redis/redis.conf \ -d redis redis-server /etc/redis/redis.conf
conf/redis.conf
mysql
启动容器nginx
docker run -p 6379:6379 --name redis6379 \ -v /var/touchAirMallVolume/redis/data:/data \ -v /var/touchAirMallVolume/redis/conf/redis.conf:/etc/redis/redis.conf \ -d redis redis-server /etc/redis/redis.conf
验证redisgit
docker exec -it redis6379 redis-cli
默认redis是不持久化的,存在内存中github
修改redis.conf
开启持久化web
vim /var/touchAirMallVolume/redis/conf/redis.conf #添加如下内容 appendonly yes
重启redis容器
使用人人开源项目,快速搭建先后端分离项目
renren-fast
、renren-fast-vue
、renren-generator
使用逆向工程,快速生成基本CRUD代码
Spring Cloud alibaba 为分布式应用开发提供了一站式解决方案。它包含开发分布式应用程序所需的全部组件,使您能够轻松地使用 Spring Cloud 开发应用程序
使用阿里巴巴的 Spring Cloud,你只须要添加一些注释和少许配置,就能够将 Spring Cloud 应用程序链接到阿里巴巴的分布式解决方案上,并使用阿里巴巴的中间件构建一个分布式应用系统
Flow control and service degradation
(流量控制和服务降级)Service registration and discovery
(服务注册和发现)Distributed Configuration
(分布式配置)Event-driven
(事件驱动)Message Bus
(消息总线):使用 Spring Cloud Bus RocketMQ 的分布式系统的连接节点Distributed Transaction
(分布式事务 Seata)Dubbo RPC
Nacos 致力于帮助您发现、配置和管理微服务,Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施
docker安装
docker pull nacos/nacos-server docker run --env MODE=standalone --name nacos -d -p 8848:8848 nacos/nacos-server
测试是否安装成功
浏览器输入:ip:8848/nacos 登陆帐号密码:nacos nacos
第一步:修改pom.xml文件,引入 Nacos Discovery Starter
<!--nacos 服务注册与发现--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
第二步:在微服务的yml配置文件中,配置上nacos server的地址,并指定应用名称
spring: datasource: username: root password: 123456 url: jdbc:mysql://192.168.83.133:3306/touch_air_mall_pms driver-class-name: com.mysql.cj.jdbc.Driver cloud: nacos: discovery: server-addr: 192.168.83.133:8848 application: name: touch-air-mall-product
第三步:在启动类添加注解 @EnableDiscoveryClient 开启服务注册与发现功能
第四步:启动应用,观察nacos服务列表是否已注册进服务中心
注意:每个应用都应该有名字,这样才能成功注册进去
注意:项目中部分微服务的 配置文件在nacos中,可自行修改
Fegin是一个声明式的HTTP客户端,它的目的就是让远程调用更加简单。Fegin提供了HTTP请求的模板,经过编写简单的接口和插入注解,就能够定义好HTTP请求的参数、格式、地址等信息
Fegin整合了Ribbon(负载均衡)和Hystrix(服务熔断),可让咱们再也不须要显示地使用这两个组件
SpringCloud Fegin在NetFlix Fegin的基础上扩展了对SpringMVC注解的支持,在其实现下,咱们只须要建立一个接口并用注解的方式来配置它,便可完成对服务提供方的接口绑定。简化了SpringCloud Ribbon自选封装服务调用客户端的开发量
第一步:引入依赖(对应版本)
<!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.2.3.RELEASE</version> </dependency>
第二步:开启feign功能
@EnableDiscoveryClient
第三步:声明远程接口
测试
正常启动 用户服务8000和优惠券服务7000
优惠券服务7000宕机
一、 CouponFeignService.saveSpuBounds(spuBoundTO) 1.一、@RequestBody 将这个对象转为json 1.二、找到coupon服务,给 /coupon/spubounds/save 发送请求 将上一步转的json 放在请求体位置,发送请求 1.三、对方服务收到请求,请求体里有json数据 @RequestBody SpuBoundsEntity spuBounds:将请求体中的json 转为 SpuBoundsEntity 这个类型 二、只有json 数据模型是兼容的,双方服务无需使用同一个to
两种不一样的请求处理
feign 请求的两种写法 1. 让全部请求过网关 1.1 @FeignClient("touch-air-mall-gateway"):给网关服务发请求 1.2 /api/product/skuinfo/info/{skuId} 2. 直接指定具体某个微服务处理 2.1 @FeignClient("touch-air-mall-product"):给商品服务发请求 2.2 /product/skuinfo/info/{skuId}
第一步:引入依赖
<!--nacos 配置中心作配置管理--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
第二步:在应用的 /src/main/resources/bootstrap.properties
配置文件中配置 Nacos Config 元数据
bootstrap.properties
文件内容会优先于 yml
文件被加载
spring.application.name=touch-air-mall-coupon spring.cloud.nacos.config.server-addr=192.168.83.133
观察启动类 新增配置的默认ID(默认当前应用的名称 application name + properties
)
测试
@Value("${coupon.user.name}") private String name; @Value("${coupon.user.age}") private int age; @RequestMapping("/nacos/config") public R testConfig(){ return R.ok().put("name", name).put( "age", age); }
在线修改 nacos 配置中心的配置
必须重启,再次请求接口
动态获取 加上注解 @RefreshScope
无需重启,动态刷新配置
用于进行租户粒度的配置隔离。不一样的命名空间下,能够存在相同的Group或Data ID的配置。NameSpace的经常使用场景之一是不一样环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等
public(保留空间):默认新增的全部配置都在public空间
利用命名空间来作环境隔离:开发、测试、生产。注意:在bootstrap.properties中,配置使用哪一个命名空间
每个微服务之间互相隔离配置,每个微服务都建立本身的命名空间,只加载本身命名空间下的全部配置
相似配置文件名
默认全部的配置集都属于:DEFAULT_GROUP
随着服务增加,配置文件会愈来愈多,不易于维护。全部一般按类拆分红多个配置文件
只须要在bootstrap.properties
中说明加载配置中心的哪些配置文件便可
spring.cloud.nacos.config.extension-configs[0].data-id=datasource.yaml spring.cloud.nacos.config.extension-configs[0].group=dev spring.cloud.nacos.config.extension-configs[0].refresh=true spring.cloud.nacos.config.extension-configs[1].data-id=mybatis.yaml spring.cloud.nacos.config.extension-configs[1].group=dev spring.cloud.nacos.config.extension-configs[1].refresh=true spring.cloud.nacos.config.extension-configs[2].data-id=other.yaml spring.cloud.nacos.config.extension-configs[2].group=dev spring.cloud.nacos.config.extension-configs[2].refresh=true
网关做为流量的入口,经常使用功能包括路由转发、权限校验、限流控制等。而SpringCloud gateway做为SpringCloud官方推出的第二代网关框架,取代了Zuul网关
组件 | RPS(每秒处理请求) |
---|---|
SpringCloud Gateway | 32213.38 |
Zuul | 20800.13 |
Linkerd | 28050.76 |
网关提供API全托管服务,丰富的API管理功能,辅助企业管理大规模的API,以下降管理成本和安全风险,包括协议适配、协议转发、安全策略、防刷、流量、监控日志等功能
SpringCloud Gateway 旨在提供一种简单有效的方式来对API进行路由,并为他们提供切面,例如:安全性、监控指标和弹性等
网关使用先后对比
web请求,经过一些匹配条件,定位到真正的服务节点。并在这个转发过程的先后,进行一些精细化控制。perdicate就是咱们的匹配条件。而Filter就能够理解为一个无所不能的拦截器,有了这两个元素,再加上目标uri,就能够实现一个具体的路由了
Gateway流程:客户端向Spring Cloud Gateway发出请求。而后在Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到Gateway Web Handler
Handler再经过指定的过滤器链来将请求发送到咱们实际的服务执行业务逻辑,而后返回
过滤器之间用虚线分开是由于过滤器可能会在发送代理请求以前(“pre”)或以后(“post”)执行业务逻辑
Filter在“pre”类型的过滤器能够作参数校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中能够作响应内容、响应头的修改,日志的输出,流量监控等有很是重要的做用
第一步:引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
第二步:编写配置文件
application.properties
服务名称和nacos注册中心地址
spring.cloud.nacos.discovery.server-addr=192.168.83.133:8848 spring.application.name=touch-air-mall-gateway
启动类添加注解,开启服务注册与发现
@EnableDiscoveryClient
bootstrap.properties
#默认加载的配置文件 应用名.properties spring.application.name=touch-air-mall-gateway #配置中心地址 spring.cloud.nacos.config.server-addr=192.168.83.133 #命名空间 spring.cloud.nacos.config.namespace=fa94e939-311c-427a-915a-a6c37bc403ae spring.cloud.nacos.config.extension-configs[0].data-id=touch-air-mall-gateway.yaml spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP spring.cloud.nacos.config.extension-configs[0].refresh=true
nacos配置中心,新增命名空间 touch-air-mall-gateway
,生成的id 对应上面配置文件的namespace
启动服务,查看状况
application.yml
,编写路由规则,详情参考官方文档
请求路径带参数:url
,而且当值等于 baidu 时,转发到百度
当值等于 qq 时,转发到 qq
spring: cloud: gateway: routes: - id: test_route uri: https://www.baidu.com predicates: - Query=url,baidu - id: qq_route uri: https://www.qq.com predicates: - Query=url,qq
重启
阿里云官网开通OSS服务
建立bucket
设置子帐户,得到accessKeyId
和accessKeySecret
并给子帐户添加权限
1:普通上传方式
用户将文件上传至应用服务器,应用服务器拿到文件流,经过java代码将文件流数据上传到文件存储服务器,流数据通过后台一层处理,在高并发场景下,安全但性能差
2:服务端签名后直传
操做对象存储的帐号密码信息仍是存储在后端服务中
用户/前端上传以前先向服务器请求上传策略,服务器利用阿里云存储服务的帐号密码生成一个防伪的签名,签名中包含访问OSS的受权令牌、以及具体的存储位置等信息,返回给前端
前端带着这个防伪的令牌签名和要上传的文件,直接上传到OSS
服务器端加密,前端直传,须要配置跨域
第一步:商品三级分类实体类CategoryEntity
中添加字段
//建表忽略字段 @TableField(exist = false) private List<CategoryEntity> children;
CategoryServiceImpl
具体实现
Java8
新特性,Stream
流(前提准备)
@Override public List<CategoryEntity> listWithTree() { // 一、查询出全部分类 List<CategoryEntity> categoryEntities = baseMapper.selectList(null); // 二、组装成父子的树形结构 // 2.一、找出全部的一级分类 // 2.二、递归查出子类 List<CategoryEntity> level1Menu = categoryEntities.stream().filter(categoryEntity -> categoryEntity.getParentCid() == 0 ).map(menu-> { menu.setChildren(getChildrens(menu, categoryEntities)); return menu; }).sorted((menu1,menu2)->{ return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort()); }).collect(Collectors.toList()); return level1Menu; } /** * 递归查找全部菜单的子菜单 */ public List<CategoryEntity> getChildrens(CategoryEntity root,List<CategoryEntity> all){ List<CategoryEntity> children = all.stream().filter(categoryEntity -> { return categoryEntity.getParentCid().equals(root.getCatId()); }).map(categoryEntity -> { categoryEntity.setChildren(getChildrens(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; }
application.yml
文件
spring: cloud: gateway: routes: - id: test_route uri: https://www.baidu.com predicates: - Query=url,baidu - id: qq_route uri: https://www.qq.com predicates: - Query=url,qq - id: admin_route uri: lb://renren-fast predicates: - Path= /api/** filters: - RewritePath= /api/(?<segment>/?.*),/renren-fast/$\{segment}
配置说明
注意是:admin_route
前端项目统一请求都带有 /api
前缀
lb:renren-fast
:LoadBalance 负载均衡
知足predicates
:http://localhost:9527/api/captcha.jpg ---> http://renren-fast:8080/api/captcha.jpg
可是默认的请求是:http://localhost:8080/renren-fast/captcha.jpg
使用Gateway的Filter 重写路径(重写规则参考官方文档)
跨域:指的是浏览器不能执行其余网站的脚本。它是由浏览器的同源策源形成的,是浏览器对javascript
施加的安全限制
同源策略:是指协议、域名、端口都要相同,其中有一个不一样都会产生跨域
URL | 说明 | 是否容许通讯 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js |
同一域名下 | 容许 |
http://www.a.com/ab/a.js http://www.a.com/script/b.js |
同一域名不一样文件夹下 | 容许 |
http://www.a.com:8000/a.js http://www.a.com/b.js |
同一域名,不一样端口 | 不容许 |
http://www.a.com/a.js https://www.a.com/b.js |
同一域名不一样协议 | 不容许 |
http://www.a.com/a.js http://70.32.92.74/b.js |
域名和域名对应ip | 不容许 |
http://www.a.com/a.js http://script.a.com/b.js |
主域相同,子域不一样 | 不容许 |
http://www.a.com/a.js http://a.com/b.js |
同一域名,不一样二级域名 | 不容许(cookie这种也不容许访问) |
http://www.a.com/a.js http://www.a.com/b.js |
不一样域名 | 不容许 |
非简单请求(PUT、DELETE)等,须要先发送预检请求
第一种:使用nginx
部署为同一域
第二种:配置当次请求容许跨域
MallCorsConfiguration
网关服务添加跨域配置类
@Configuration public class MallCorsConfiguration { @Bean public CorsWebFilter corsWebFilter(){ UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration(); //跨域配置 corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.setAllowCredentials(true); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration); return new CorsWebFilter(urlBasedCorsConfigurationSource); } }
注意:这里须要注释掉
renren-fast
中的跨域配置,统一由网关进行配置
数据库关联关系图
数据库关联关系图
File-Settings-Editor-Live Templates
给Bean添加校验注解:javax.validation.constraints
,并定义本身的message提示
开启校验功能@Valid
效果:校验错误之后会有默认的响应
给校验的bean后紧跟一个BindingResult
,就能够得到到校验的结果
每一个方法都这样作,太麻烦且代码冗余
controller
添加注解 @Validated{(AddGroup.class, UpdateGroup.class)}
编写一个自定义的校验注解
能够指定多个不一样的校验器,适配不一样类型的校验
编写一个自定义的校验器
关联自定义的校验器和校验注解
集中处理全部异常
@RestControllerAdvice 等价于 @ControllerAdvice和@ResponseBody 处理全局异常并以json格式返回
x系统错误码和错误信息定义类
错误码定义规则为5位数字
前两位表示业务场景,最后三位表示错误码
例如:10001 10:通用 001:系统未知异常
维护错误码后须要维护错误描述,将他们定义为枚举形式
错误码列表:
10:通用
11:商品
12:订单
13:购物车
14:物流
new
关键字建立,由GC
回收UML
元件领域模型中的领域对象。封装业务逻辑的java
对象,经过调用DAO方法,结合PO、VO进行业务操做。business object:业务对象 主要做用是把业务逻辑封装为一个对象。这个对象能够包括一个或多个对象。好比一个简历,有教育经历、工做经历、社会关系等等。咱们能够把教育经历对应一个PO,工做经历对应一个PO,社会关系对应一个PO,创建一个简历的BO对象去处理简历,每一个BO都包含这些PO。这样处理业务逻辑时,咱们就能够针对BO去处理sun
的标准j2ee
设计模式,这个模式中有个接口就是DAO,它负责持久层的操做,为业务层接口。此对象用于访问数据库。一般和PO结合使用,DAO中包含了各类数据库的操做方法。经过它的方法,结合PO对数据库进行相关的操做,夹在业务逻辑与数据库资源中间,配合VO,提供数据库的CRUD操做