瑞吉外卖2.0 Redis 项目优化 Spring Cache MySQL主从复制 sharding-JDBC Nginx YApi Swagger

Git版本控制

Linux从安装到实战&瑞吉外卖项目部署

Redis基础

Redis入门 redis.io

nosql没有表的概念

注意关闭防火墙

systemctl stop firewalld

启动redis

 src/redis-server ./redis.conf

数据类型

常用命令

字符串 string 操作命令
哈希 hash 操作命令
列表list(类似 栈 )操作命令
集合set 操作命令

sdiff key1 [key2] :key1-key2;

有序集合 sorted set (zset) 操作命令
通用命令

TTL return -1 表示永久;

在Java中操作Redis

介绍
Jedis
Spring Data Redis

Redis服务默认会给16个数据库在redis.windows.conf里面修改

默认是在0号数据库操作,更换数据库:select 1

String
hash
List
Set
ZSet
通用操作,针对不同数据类型都可以操作

项目优化-缓存优化

环境搭建

如何查看本地Ip

配置RedisConfig:为了自定义序列化器
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        //默认的Key序列化器为:JdkSerializationRedisSerializer
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
}

缓存短信验证码

保存方式:Session-->Redis

  @Autowired
    private 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);
            //将生成的验证码保存到Session
            redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);

            return R.success("手机验证码短信发送成功");
        }
        return R.error("短信发送失败");
    }
    @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中获取保存的验证码
        //Object codeInSession = session.getAttribute(phone);
        //从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.getId());
            //如果用户登录成功,删除redis中缓存的验证码
            redisTemplate.delete(phone);
            return R.success(user);
        }
        return R.error("登录失败");
    }

链接redis报错 ERROR org.springframework.boot. ; ERR invalid password;java.io.IOException:

缓存菜品数据

 @GetMapping("/list")//改造list
    public R<List<DishDto>> list(Dish dish){
        List<DishDto> dishDtoList =null;
        //动态构造key
        String key="dish_"+dish.getCategoryId()+"_"+dish.getStatus();//

        //1.先从redis中获取缓存数据,按照菜单分类缓存
        dishDtoList= (List<DishDto>) redisTemplate.opsForValue().get(key);

        if(dishDtoList != null) {
            //2.如果存在!=null,直接返回,不用查询数据库
            return R.success(dishDtoList);
        }
        //构造查询条件
        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(dish.getCategoryId() != null ,Dish::getCategoryId,dish.getCategoryId());
        //添加条件,查询状态为1(起售状态)的菜品
        queryWrapper.eq(Dish::getStatus,1);

        //添加排序条件 排序顺序,创建时间倒序
        queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
        List<Dish> list = dishService.list(queryWrapper);

        dishDtoList = list.stream().map((item) -> {
            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(item,dishDto);

            Long categoryId = item.getCategoryId();//分类id
            //根据id查询分类对象
            Category category = categoryService.getById(categoryId);

            if(category != null){
                String categoryName = category.getName();
                dishDto.setCategoryName(categoryName);
            }

            //当前菜品的id
            Long dishId = item.getId();
            LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);
            //SQL:select * from dish_flavor where dish_id = ?
            List<DishFlavor> dishFlavorList = dishFlavorService.list(lambdaQueryWrapper);
            dishDto.setFlavors(dishFlavorList);
            return dishDto;
        }).collect(Collectors.toList());
        //3.不存在,需要查询数据库,将查询到的数据缓存到redis
        redisTemplate.opsForValue().set(key,dishDtoList,60, TimeUnit.HOURS);//60分钟
        return R.success(dishDtoList);
    }
@PostMapping
    public R<String> save(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());

        dishService.saveWithFlavor(dishDto);
        //1.更新完 就清理所有缓存数据
        //Set keys = redisTemplate.keys("dish_*");//获得所有以‘dish_’开头的key
        //redisTemplate.delete(keys);

        //2.精确 清理缓存数据(只清理 被更新类别 的缓存数据)
        String key="dish_" + dishDto.getCategoryId()+"_1";
        redisTemplate.delete(key);
        return R.success("新增菜品成功");
    }

@PutMapping//改造update&save
    public R<String> update(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());

        dishService.updateWithFlavor(dishDto);

        //1.更新完 就清理所有缓存数据
        //Set keys = redisTemplate.keys("dish_*");//获得所有以‘dish_’开头的key
        //redisTemplate.delete(keys);

        //2.精确 清理缓存数据(只清理 被更新类别 的缓存数据)
        String key="dish_" + dishDto.getCategoryId()+"_1";
        redisTemplate.delete(key);
        
        return R.success("修改菜品成功");
    }

Spring Cache框架

介绍 基于注解的缓存 (Cache译为缓存)
常用注解
#result: 代表方法返回值(condition关键字里没有result用unless代替); #root.method: 方法对象 ;

#root.args[0] / #p0 / #a0(a0我感觉也行) : 方法第一个参数

使用方式

缓存套餐数据

项目优化2—读写分离

Mysql主从复制

配置主库Master

先登录 mysql -uroot -p1234(你的密码)

GRANT REPLICATION SLAVE ON *.* to 'xiaoming'@'%' identified by 'Root@1234';(我的密码比他少两位)

从库Slave #192.168.138.132; root@1234

关闭防火墙链接mysql

 change master to master_host='192.168.138.100',master_user='xiaoming',master_password='Root@1234',master_log_file='mysql-bin.000001',master_log_pos=439;

后两个要跟这里的对应;

测试主从复制连接

主库怎么操作,从库也会复制操作;就说明链接成功

读写分离案例

背景
sharding-JDBC介绍
入门案例
项目实现读写分离

直接往主库里面导入,从库就会自动复制;

报错:

create connection SQLException, url: jdbc:mysql://192.168.138.100:3306/reggie?characterEncoding=utf-8, errorCode 0, state 08S01

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

create connection SQLException, url: jdbc:mysql://192.168.138.100:3306/reggie?characterEncoding=utf-8, errorCode 0, state 08S01

修改两个URL:

url: jdbc:mysql://192.168.138.101:3306/rw?characterEncoding=utf-8&useSSL=false

Nginx

Nginx概述

1.5安装wget

4.5 创建文件夹 :mkdir -p /usr/local/nginx ; 6:先编译然后 install; 安装tree命令查看目录结构

Nginx命令

查看版本

要先cd /usr/local/nginx/sbin

检查配置文件正确性
启动和停止

关闭防火墙: systemctl stop firewalld ; 如下页面,访问成功

重新加载配置文件

要写路径启动nginx -->不想写路径;

解决方案:把nginx的二进制文件路径配置到系统的环境变量 实现 nginx 即可启动的效果

vim /etc/profile
PATH=$JAVA_HOME/bin:$PATH   # 追加成下面的
PATH=/usr/local/nginx/sbin:$JAVA_HOME/bin:$PATH
source /etc/profile
nginx -s reload  # 不报错说明添加环境变量成功

Nginx配置文件结构

全局块、Events块、Http块

Nginx具体应用

部署静态资源

部署html成功

反向代理(用的最多)
区别:正向代理一般是在客户端设置代理服务器,

反向代理客户端不知道反向代理服务器的存在,客户端只需要访问服务器就会返回相应资源,用户不知道代理服务器存在。

负载均衡

项目优化3—前后端分离开发

前后端分离开发

介绍
开发流程
前端技术栈

Yapi (一种定义接口的Web服务)

YApi-教程
YApi-内网搭建
windows环境下部署YApi

我没有跟 原因是需要mangoDB

Swagger

介绍
使用方式
反正就是maven依赖冲突 卡了三个小时还是解决不了 先往后走吧 主要是knife4j的Swagger框架pom.xml导不进去;
com.google.guava:guava:jar:27.0.1-android failed to transfer from https://repo.maven.apache.org/maven2 during a previous attempt. This failure was cached in the local repository and resolution is not reattempted until the update interval of central has elapsed or updates are forced. Original error: Could not transfer artifact com.google.guava:guava:jar:27.0.1-android from/to central (https://repo.maven.apache.org/maven2): transfer failed for https://repo.maven.apache.org/maven2/com/google/guava/guava/27.0.1-android/guava-27.0.1-android.jar

Try to run Maven import with -U flag (force update snapshots)

https://blog.csdn.net/yiguoxiaohai/article/details/125708088

几个月之后 ,当我又需要这个项目的时候又可以了 ,我想了一下 之前也总遇到导包不成功问题,有一次 我发现之前没设置Maven自动导包,后来设置上了;就导包成功了,可能是因为这个,又成了

因为两个数据库对的 所以我觉得就是我第二个虚拟机的mysql的版本不对 瑞吉的mysql是5.几

常用注解

项目部署

部署架构
部署环境说明

麻烦没搞

部署前端项目
部署后端项目

完结撒花!

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软工菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值