一.Redis配置
1.首先通过Git来管理代码
2.环境搭建
2.1maven配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.2 yml文件配置
2.3 增加配置类
package com.MskTest.config;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* 这个配置文件是用来配置redis序列化器的,通过java存储的redis数据,如果从redis面板直接搜索的话,可能会出现乱码的情况
* 所以我们设置一个序列化器来处理乱码
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
3.代码编写
一. 验证码使用缓存保存
步骤如下:
1.在Controller层自动注入RedisTemplate对象,用于操作Redis
2.在接受验证码的方法中,用redisTemplate接受验证码,使用手机号作为key,同时设置验证时间为5分钟
3.成功登录之后,删除Redis中的缓存数据
/**
* 发送手机验证码
* @param user
* @return
*/
@Autowired
RedisTemplate redisTemplate;
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session) {
//获取手机号
String phone = user.getPhone();
if (StringUtils.isNotEmpty(phone)) {
//生成随机的4位验证码
String code = ValidateCodeUtils.generateValidateCode(4).toString();
log.info("code={}", code);
//调用阿里云提供的短信服务API完成发送短信
//SMSUtils.sendMessage("瑞吉外卖", "", phone, code);
//需要将生成的验证码保存到Session
//session.setAttribute(phone, code);
//将验证码用redis保存起来,有效期为5分钟
redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
return R.success("手机验证码短信发送成功");
}
return R.error("手机验证码短信发送失败");
}
/**
* 移动端用户登录
* @param map
* @param session
* @return
*/
@PostMapping("/login")
public R<User> login(@RequestBody Map map, HttpSession session) {
log.info(map.toString());
//获取手机号
String phone = map.get("phone").toString();
//获取验证码
String code = map.get("code").toString();
//从Session获取保存的验证码
//String codeInSession = session.getAttribute(phone).toString();
//从Redis读取验证码
Object codeInSession = redisTemplate.opsForValue().get(phone);
//进行验证码的比对(页面提交的验证码和Session中保存的验证码比对)
if (codeInSession != null && codeInSession.equals(code)) {
//如果比对成功,说明登录成功
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone, phone);
User user = userService.getOne(queryWrapper);
if (user == null) {
//判断当前手机号对应的用户是否为新用户,如果是新用户就自动完成注册
user = new User();
user.setPhone(phone);
user.setStatus(1);
userService.save(user);
}
session.setAttribute("user",user);
// 因为登录成功了,所以直接删除Redis中的数据
redisTemplate.delete(phone);
return R.success(user);
}
return R.error("登录失败");
}
二.缓存菜品数据
思路如下:
首先是通过菜品ID来查询该数据是否存在,如果不存在就查询MySQL,然后将SQL中的数据存储到Redis中,如果存在则直接从Redis中返回
需要注意的是:当我们对菜品进行修改或者保存的时候,要删除Redis中的所有数据
/**
* 查菜品列表
* @param categoryId
* @return
*/
@GetMapping("/list")
public R<List<DishDto>> ListCategory(String categoryId){
List<DishDto> list_dto = null;
// 首先是确定Redis中是否有我们要查的数据,如果没有就遍历,然后存进去
Object o = redisTemplate.opsForValue().get(categoryId);
if (o!=null){
list_dto = (List<DishDto>) o;
}else {
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
//通过categoryId查出该数据
queryWrapper.eq(Dish::getCategoryId, categoryId);
//同时需要判断该数据是否起售
queryWrapper.eq(Dish::getStatus, 1);
List<Dish> list = dishService.list(queryWrapper);
//最后返回出来
list_dto = list.stream().map((item) -> {
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item, dishDto);
Category category = categoryService.getById(categoryId);
dishDto.setCategoryName(category.getName());
LambdaQueryWrapper<DishFlavor> queryWrapper1 = new LambdaQueryWrapper<>();
queryWrapper1.eq(DishFlavor::getDishId, item.getId());
List<DishFlavor> list_flavor = dishFlavorService.list(queryWrapper1);
dishDto.setFlavors(list_flavor);
return dishDto;
}).collect(Collectors.toList());
// 将list集合存储到Redis中,方便下次查询
redisTemplate.opsForValue().set(categoryId,list_dto,1, TimeUnit.HOURS);
}
return R.success(list_dto);
}
@PutMapping
public R<String> update(@RequestBody DishDto dishDto){
dishService.updateBYDTO(dishDto);
//根据修改的种类删除,精确清理
redisTemplate.delete(dishDto.getCategoryId());
return R.success("成功更新");
}
@PostMapping
public R<String> add(@RequestBody DishDto dto){
dishService.Add(dto);
redisTemplate.delete(dto.getCategoryId());
return R.success("成");
}
4.Spring Cache
@CachePut(value String , key String)
//将数据存储到缓存中,其中value代表的是这个缓存的名称!!!只是名字而已
// 这里的key就是key
@Cacheable(value = "UserName", key = "#id")
@GetMapping("/{id}")
public R<Employee> SelectByID(@PathVariable Long id){
Employee employee = employeeService.getById(id);
if (employee!=null){
return R.success(employee);
}
return R.error("出错了");
}
// 使用这种方法的问题就是,如果sql中如果没有数据,也会缓存
改正之后
//满足条件不为空的时候,才缓存
@Cacheable(value = "UserName", key = "#id",condition = "#result!=null")
@GetMapping("/{id}")
public R<Employee> SelectByID(@PathVariable Long id){
Employee employee = employeeService.getById(id);
if (employee!=null){
return R.success(employee);
}
return R.error("出错了");
}
使用Redis来操作
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
cache:
redis:
host: localhost
port: 6379
database: 0
# 这一步是选择第0号数据库,因为Redis提供了16个数据库,这里选则第一个
time-to-live: 18000
因为Redi中不能使用Condition来接受Result结果,所以需要使用Unless
@Cacheable(value = "UserName", key = "#id",unless = "#result==null")
@GetMapping("/{id}")
public R<Employee> SelectByID(@PathVariable Long id){
Employee employee = employeeService.getById(id);
if (employee!=null){
return R.success(employee);
}
return R.error("出错了");
}
使用Spring Cache配合Redis来查询套餐
步骤如下:
1.第一步配置maven坐标,同时设置好yml文件中的配置
2.在启动类中添加启动的配置@EnableCaching
3.在SetmealController层中进行修改
4.注意在修改和增加时进行删除操作
@Cacheable(value = "SetmealName",key = "#setmeal.id",unless = "#result.data == null")
@GetMapping("/list")
public R<List<SetmealDto>> list(Setmeal setmeal){
LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Setmeal::getCategoryId,setmeal.getCategoryId());
List<Setmeal> list = setmealService.list(queryWrapper);
List<SetmealDto> list_dto = list.stream().map((item) ->{
SetmealDto setmealDto = new SetmealDto();
BeanUtils.copyProperties(item,setmealDto);
LambdaQueryWrapper<SetmealDish> queryWrapper1 = new LambdaQueryWrapper<>();
queryWrapper1.eq(SetmealDish::getSetmealId,item.getId());
List<SetmealDish> list_setmealdish = setmealDishService.list(queryWrapper1);
setmealDto.setSetmealDishes(list_setmealdish);
return setmealDto;
}).collect(Collectors.toList());
return R.success(list_dto);
}
这里需要注意的是,第一次执行该代码时,出现了序列化的问题,因为这里的result是R接口,所以需要把R对象也序列化
/**
* 删除操作
* @param ids
* @return
*/
@CacheEvict(value = "SetmealName",allEntries = true)
@DeleteMapping
public R<String> deleteIds(String ids){
//先解析前端传来的String类型转化为List类型
List<String> list = Arrays.asList(ids.split(","));
setmealService.DeleteWithDish(list);
return R.success("删除成功");
}
三.主从数据库分离设置
将数据库分成主库与从库,主库执行写数据,例如增删改,从库执行读数据,查操作,同时两个数据库建立数据同步(MySql主从复制),这样做的目的是为了,可以减少一个数据库的压力,防止数据库崩溃,同时增加了安全性
MySQL主从复制是一个异步的复制过程,底层是基于Mysql数据库自带的二进制日志,就是一台或多台数据库,从主库,进行日志的复制,然后解析日志,并应用到本身。
主库完成操作,并生成二进制日志,从库通过I/O线程读取该日志文件,并生成自己的日志文件,然后通过SQL线程解析该日志,并最后在自己的数据库中完成日志的所有操作
主从复制:
首先,搭建两台服务器,并分别安装好Mysql数据并启动成功
ip地址分别为:192.168.18.100(master),192.168.18.101(slave)
第二步修改Mysql数据库的配置文件
vim /etc/my.cnf
第三步在主库设置一个用户并赋予从库权限给他
查询主库日志信息
需要注意的是,此时主库不能再进行任何操作,不然这个日志文件的位置和名称会发生变化
此时在从库操作
在从库配置文件中修改数据库ID
然后在从库的mysql中执行以下命令
最后通过
show slave status来展示
最后显示这个就完成了
项目测试:
1.先导入maven坐标
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
2.yml文件的配置
spring:
sharding:
datasource:
names:
master,slave
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql//192.168.18.100:3306/rw?characterEncoding=utf-8
username: root
password: 123123
slave:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql//192.168.18.101:3306/rw?characterEncoding=utf-8
username: root
password: 123123
masterslave:
load-blance-algorithm-type: round_robin
name: datasource
master-data-source-name: master
slave-data-source-name: slave
props:
sql:
show:true
运行就可以了
四.Nginx学习
安装好之后,这是文件夹分类
conf是配置文件,html是存放静态页面的,logs存放日志信息,sbin是存放脚本文件的,用于启动和停止nginx服务
Nginx命令:
1.检查conf文件的正确性,在sbin文件夹中,执行./nginx -t
2.启动nginx,./nginx
3.可以通过 ps -ef | grep nginx 查看进程
4.停止nginx服务,./nginx -s stop
5.修改配置文件之后,要重新加载 ./nginx -s reload
Nginx配置文件的整体结构:
全局块,和Nginx运行相关的全局配置
events块,和网络连接有关的配置
http块 代理,缓存,日志记录,虚拟主机配置
http块又分为
http全局块
Server块
五.Nginx的具体应用
1.部署静态资源:Nginx可以作为静态web服务器来部署静态资源,相对于Tomcat来说,Nginx的效率更高
步骤如下:
1.将需要的静态资源复制到nginx的Html包下
2.启动nginx,即可
配置如下
2. 反向代理
要了解反向代理,必须先知道什么是正向代理
简单的说,就是用户在访问目标服务器时,会先通过一个代理服务器来转发请求,这里的代理服务器有点像自己家的路由器什么的
反向代理与正向代理的不同在于,用户直接访问反向代理的这个服务器就可以返回请求了,不需要知道目标服务器,这个有点感觉像直接访问自己的路由器,然后路由器直接返回我要的数据。
这里的listen是用来监听端口号的
下面是配置反向代理的路径
3.负载均衡
随着目前前端资源的增多,一台服务器可能处理不过来那么多的数据,所以将资源分别配置好几台服务器,这个服务器群就是集群,然后负载均衡和上面的反向代理很像的地方就在于,反向代理只是一个服务器,但是负载均衡是很多服务器
配置文件如下:
上面的upstream是用来存放服务器IP地址以及对应的端口
下面的server是用来代理上面配置好的集群的IP地址的
负载均衡的各种方式
六.前后端分离
1.Swagger
用于生成接口文档文件,使用框架会快一点
步骤如下:
1.导入Maven坐标
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
2.编写Swagger配置类
在WebMvcConfig中添加配置注解
package com.MskTest.config; import com.MskTest.Common.JacksonObjectMapper; import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.List; @Configuration @Slf4j @EnableSwagger2 @EnableKnife4j public class WebMvcConfigurationSupport extends org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport { /** * 所有的静态页面在resources下,需要放行,所以编写一个配置文件 * @param registry */ @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { log.info("开始静态映射"); //第一步使用addResourceHandler来标识什么情况下要映射,第二步addResourceLocations来标识映射到哪里 registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/"); registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/"); } /** * 增加一个消息转换器 * @param converters */ @Override protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { //1.创建一个消息转换器 MappingJackson2HttpMessageConverter MessageConverter = new MappingJackson2HttpMessageConverter(); //2,设置对象转换器,底层使用Jackson将Java对象转成json MessageConverter.setObjectMapper(new JacksonObjectMapper()); //3.将上面设置的转换器增加到转换器中,并放置第一位,优先使用该转换器 converters.add(0,MessageConverter); } }
同时在该WebMvcConfig类下编写这两个方法
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.MskTest.reggie.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("瑞吉外卖")
.version("1.0")
.description("API文档")
.build();
}
3.设置静态资源映射
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
log.info("开始静态映射");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:META-INF/resources/webjars/");
4.设置过滤器
七.Swagger的常用注解