路飞项目----day09(登录与注册前端页面与后端接口接通,Redis介绍与安装,python普通连接和连接池连接 redis,python操作Redis之字符串类型相关方法)

昨日回顾

1 测试腾讯短信  v3 sdk 提供发送短信
	-模板  {1}  {2} {3}


2 发送短信做成包,以后无论在什么框架中,直接copy进去,直接导入使用
-包名 send_sms_v3
	-__init__.py  # 导入了给外部使用的函数
	-settings.py  # 配置信息---》APPID。。。
	-sms.py       # 核心:获取 n 位数字验证码,send_sms


3 发送短信接口
	-csrf:解决方案
	-前端使用post请求,携带手机号    {mobile:'13454646',sign:asfasdfas}
    -后端路由---》使用action装饰 send_sms---》
    	-取出手机号,生成验证码,存到【缓存】中----》
        	cache.set(key,value,过期事件) 
            	# 重点:value值可以是什么类型?任意类型都可以
                # 如何存的?序列化 pickle 
            cache.get(key)

        -调用封装发送短信,【同步发送异步发送】


4 短信登录接口
	-前端:post请求  {mobile:1334535,code:8888}
    -后端:action装饰器----》
    	-视图类的代码,跟之前多方式登录的代码一模一样,使用的序列化类不一样
        	-重写get_serializer_class
    	-把逻辑写在序列化类中
            -封装


5 短信注册接口
	-前端:post请求  {mobile:12344,code:123,password:123}
    -后端:写了个新的视图类
    	-重写create---》自动生成路由
    	-核心逻辑再序列化类

.
.
.
.
.

今日内容

1 登录页面分析

# 点击登录,弹出登录组件,盖住整个屏幕(定位)
# 点击登录组件中的X,关闭登录组件(子传父)

.
.
.
.
.
.

Header.vue

<template>
  <div class="header">

    <div class="slogan">
      <p>老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活</p>
    </div>

    <div class="nav">
      <ul class="left-part">
        <li class="logo">
          <router-link to="/">
            <img src="../assets/img/head-logo.svg" alt="">
          </router-link>
        </li>

        <!--active的作用,就是对应的值如果等于true该标签下面就会多一条下划线出来-->
        <li class="ele">
          <span @click="goPage('/free-course')" :class="{active: url_path === '/free-course'}">免费课</span>
        </li>

        <li class="ele">
          <span @click="goPage('/actual-course')" :class="{active: url_path === '/actual-course'}">实战课</span>
        </li>

        <li class="ele">
          <span @click="goPage('/light-course')" :class="{active: url_path === '/light-course'}">轻课</span>
        </li>

      </ul>

       <div class="right-part">
         <div v-if="!username">
            <div>
              <span @click="put_login">登录</span>
              <span class="line">|</span>
              <span @click="put_register">注册</span>
            </div>
         </div>

         <div v-else>
            <div>
              <span >后面有空放个头像</span>
              <span class="line">|</span>
              <span >{{username}}</span>
              <span class="line">|</span>
              <span >注销</span>
            </div>
         </div>

       </div>
    </div>



    <Login v-if="is_login" @close1="close_login" @go1="put_register" @success="success_login"/>

    <Register v-if="is_register" @close2="close_register" @go2="put_login" @success2="success_register"/>

  </div>

</template>

<script>
import Login from "@/components/Login"
import Register from "@/components/Register";

export default {
  name: "Header",
  data() {
    return {

      // 当前所在路径,去sessionStorage取的,如果取不到,就是 / 根路径
      url_path: sessionStorage.url_path || '/',
      is_login: false,
      is_register: false,
      username: this.$cookies.get('username'),
      token: this.$cookies.get('token'),
    }
  },
  methods: {
    goPage(url_path) {
      // 已经是当前路由就没有必要重新跳转
      if (this.url_path !== url_path) {
        this.$router.push(url_path);
      }
      sessionStorage.url_path = url_path;
    },


    put_login() {
      this.is_login = true;  // 点登录按钮,控制登录组件显示
      this.is_register = false;  // 控制注册组件不显示
    },
    put_register() {
      this.is_login = false;  // 控制登录组件不显示
      this.is_register = true;  // 控制注册组件显示
    },

    close_login() {
      this.is_login = false;  // 控制登录组件不显示
    },


    close_register() {
      this.is_register = false;  // 控制注册组件不显示
    },


    success_login(data) {
      this.is_login = false;
      this.username = this.$cookies.get('username');
      this.token = this.$cookies.get('token');

    },


    logout() {
      this.token = '';
      this.username = '';
      this.user_id = '';
      this.$cookies.remove('username');
      this.$cookies.remove('token');
      this.$cookies.remove('user_id');
    },

    success_register() {
      this.is_register = false;
      this.is_login = true;
    }


  },

  created() {

    // 组件加载完成,就取出当前的路径this.$route.path ,存到sessionStorage里面去
    sessionStorage.url_path = this.$route.path;
    // 把url_path = 当前路径
    this.url_path = this.$route.path;
  },

  components: {
    Login,
    Register,
  }

}
</script>

<!--这个判断active的值的整体的思路是,一开始页面刚加载的时候,由于sessionStorage没有值,所以url_path的值是根/-->
<!--但是Vue对象创建完成,该头组件管理的标签模板未挂载前,执行created钩子,把当前的路径加到sessionStorage里面去,并将url_path重新赋值-->
<!--当点击跳转按钮时,先判断跳转的路由是否是当前路由,不是跳转到想要跳的路由上去,并改掉sessionStorage里面的路由,然后又会触发created-->
<!--往sessionStorage再写一次,并将url_path重新赋值,目的可能是如果用户不是触发点击事件来到的该页面,也能正常的在对应标签下多条下划线出来-->


<style scoped>
.header {
  background-color: white;
  box-shadow: 0 0 5px 0 #aaa;
}

.header:after {
  content: "";
  display: block;
  clear: both;
}

.slogan {
  background-color: #eee;
  height: 40px;
}

.slogan p {
  width: 1200px;
  margin: 0 auto;
  color: #aaa;
  font-size: 13px;
  line-height: 40px;
}

.nav {
  background-color: white;
  user-select: none;
  width: 1200px;
  margin: 0 auto;

}

.nav ul {
  padding: 15px 0;
  float: left;
}

.nav ul:after {
  clear: both;
  content: '';
  display: block;
}

.nav ul li {
  float: left;
}

.logo {
  margin-right: 20px;
}

.ele {
  margin: 0 20px;
}

.ele span {
  display: block;
  font: 15px/36px '微软雅黑';
  border-bottom: 2px solid transparent;
  cursor: pointer;
}

.ele span:hover {
  border-bottom-color: orange;
}

.ele span.active {
  color: orange;
  border-bottom-color: orange;
}

.right-part {
  float: right;
}

.right-part .line {
  margin: 0 10px;
}

.right-part span {
  line-height: 68px;
  cursor: pointer;
}
</style>

.
.
.
.
.
.
.

2 登录小组件

2.1 Login.vue

<template>
  <div class="login">
    <div class="box">
      <!-- 叉号,点击登录的弹窗页面就关闭了-->
      <i class="el-icon-close" @click="close_login"></i>

      <div class="content">

        <div class="nav">
                    <span :class="{active: login_method === 'is_pwd'}"
                          @click="change_login_method('is_pwd')">密码登录</span>

          <span :class="{active: login_method === 'is_sms'}"
                @click="change_login_method('is_sms')">短信登录</span>
          <!--通过点击事件修改login_method对应的值,然后让span标签下面多道杠-->

        </div>

        <!--多方式登录的表单标签-->
        <el-form v-if="login_method === 'is_pwd'">
          <el-input
              placeholder="用户名/手机号/邮箱"
              prefix-icon="el-icon-user"
              v-model="username"
              clearable>
          </el-input>

          <el-input
              placeholder="密码"
              prefix-icon="el-icon-key"
              v-model="password"
              clearable
              show-password>
          </el-input>
          <!--clearable就是框里面的小叉号,点一下就清空框里面内容-->
          <!--show-password 点一下就能看到被挡住的密码-->

          <el-button type="primary" @click="login">登录</el-button>
          <!--登录按钮点击后,发送ajax请求到后端的 2 多方式登录的接口-->

        </el-form>


        <!--短信验证码登录的表单标签-->
        <el-form v-if="login_method === 'is_sms'">
          <el-input
              placeholder="手机号"
              prefix-icon="el-icon-phone-outline"
              v-model="mobile"
              clearable
              @blur="check_mobile">
            <!--手机号输入完触发失去焦点事件,发送ajax请求到后端的 1 校验手机号是否存在的接口-->

          </el-input>
          <el-input
              placeholder="验证码"
              prefix-icon="el-icon-chat-line-round"
              v-model="sms"
              clearable>
            <template slot="append">
              <span class="sms" @click="send_sms">{{ sms_interval }}</span>
              <!--获取验证码按钮点击后,发送ajax请求到后端的 3 发送短信验证码的接口-->

            </template>
          </el-input>
          <el-button @click="mobile_login" type="primary">登录</el-button>
          <!--登录按钮点击后,发送ajax请求到后端的 4 短信验证码登录接口-->

        </el-form>


        <div class="foot">
          <span @click="go_register">立即注册</span>
        </div>

      </div>  <!--content-->

    </div>  <!--box-->
  </div>
</template>

<script>
export default {
  name: "Login",
  data() {
    return {
      username: '',
      password: '',
      mobile: '',
      sms: '',  // 验证码
      login_method: 'is_pwd',
      sms_interval: '获取验证码',
      is_send: false,
    }
  },
  methods: {
    close_login() {
      // 通过this.$emit 触发父组件的自定义事件close对应的函数
      this.$emit('close1',)
    },
    go_register() {
      // 通过this.$emit 触发父组件的自定义事件go对应的函数
      this.$emit('go1',)
    },

    change_login_method(method) {
      this.login_method = method;
    },

    // 手机号输入完触发失去焦点事件
    check_mobile() {
      // 先判断手机号的输入框有没有值,没有值return,函数直接结束
      if (!this.mobile) return;

      // js正则语法 '字符串'.match(/正则语法/)
      if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {
        // 如果不符合手机号码的正则匹配,走下面代码,就会有个带着信息弹出框弹出
        this.$message({
          message: '手机号有误',
          type: 'warning',
          duration: 1000,  // 弹出框维持1秒
          onClose: () => {
            this.mobile = '';
          }
          // 弹出框一关闭,将输入框置为空
        });
        return false;
      }    // 走到这前端对手机号校验已经结束了

      // 开始后台校验手机号是否已存在
      this.$axios({
        url: this.$settings.BASE_URL + '/user/userinfo/check_mobile/?mobile=' + this.mobile,
        method: 'get',

      }).then(res => {
        if (res.data.code == 101) {
          this.$message({
            message: '账号正常',
            type: 'success',
            duration: 1000,
          });
          // 当在短信登录界面输入手机号后,失去焦点后,先进行正则匹配确认是手机号
          // 在发送ajax请求,调用后端的查询手机号码是否存在接口,若得到的返回值code等于101 说明手机号存在
          // 当this.is_send等于true后,才能让获取验证码按钮的点击事件函数正常往下走,不然点击事件就直接结束了
          this.is_send = true;

        } else {
          this.$message({
            message: '账号不存在',
            type: 'warning',
            duration: 1000,
            onClose: () => {
              this.mobile = '';
              // 如果code不对,也弹出提示信息,并清空输入框
            }
          })
        }
      }).catch(() => {
      });
    },


    // 发送短信验证码按钮的点击事件
    send_sms() {
      // this.is_send 如果是false 取反后,直接return 函数直接结束  不会往下走了
      if (!this.is_send) return;

      // 按钮点一次立即禁用,不让你一直点击获取验证码按钮,从而一直触发发送短信验证码的点击事件
      this.is_send = false;

      let sms_interval_time = 60;   // 定义一个剩余时间的变量
      this.sms_interval = "发送中...";  // 把按钮上原来获取验证码几个字变成 发送中...

      // 定时器: setInterval(fn, time, args)

      // 往后台发送验证码
      this.$axios({
        url: this.$settings.BASE_URL + '/user/userinfo/send_msg/',
        method: 'post',
        data: {
          mobile: this.mobile
        }

      }).then(res => {
        if (res.data.code == 100) { // 已发送短信

          // 启动循环定时任务
          let timer = setInterval(() => {
            if (sms_interval_time <= 1) {
              clearInterval(timer);
              this.sms_interval = "获取验证码";
              this.is_send = true; // 重新恢复点击按钮发送短信功能
              // 如果剩余时间是否小于1s,清除循环定时任务

            } else {
              // 如果剩余时间大于1,就把剩余时间自减1s
              sms_interval_time -= 1;
              this.sms_interval = `${sms_interval_time}秒后再发`;
            }
          }, 1000);
          // 每隔一秒执行一次 ,循环定时任务里面的函数

        } else {  // 发送失败
          this.sms_interval = "重新获取";
          this.is_send = true;
          this.$message({
            message: '短信发送失败',
            type: 'warning',
            duration: 3000
          });
        }
      }).catch(() => {
        // ajax 请求报错了,显示频率过快
        this.sms_interval = "频率过快";
        this.is_send = true;
      })
    },

    // 多方式登录接口
    login() {
      //
      if (!(this.username && this.password)) {
        this.$message({
          message: '请填好账号密码',
          type: 'warning',
          duration: 1500
        });
        return false  // 直接结束逻辑
      }

      this.$axios({
        url: this.$settings.BASE_URL + '/user/userinfo/login_multiple/',
        method: 'post',
        data: {
          username: this.username,
          password: this.password,
        }

      }).then(res => {
        if (res.data.code == 100) {
          let username = res.data.username;
          let token = res.data.token;

          this.$cookies.set('username', username, '7d');
          this.$cookies.set('token', token, '7d');

          this.$emit('success', res.data); // 调用父组件里的自定义success事件对应的方法运行,并传值
        } else {
          this.$message({
            message: res.data.msg,
            type: 'warning',
            duration: 1500
          });
        }
      }).catch(error => {
        console.log(error.response.data)
      })
    },


    // 手机号验证码登录接口
    mobile_login() {
      // 先判断两个输入框有没有为空的,有空就弹个框
      if (!(this.mobile && this.sms)) {
        this.$message({
          message: '请填好手机与验证码',
          type: 'warning',
          duration: 1500
        });
        return false  // 直接结束逻辑
      }

      // 两个框不是空继续往下走,带着手机号与验证码,发送post请求,调用短信验证码登录的接口,
      this.$axios({
        url: this.$settings.BASE_URL + '/user/userinfo/login_msg/',
        method: 'post',
        data: {
          mobile: this.mobile,
          code: this.sms,
        }

      }).then(res => {
        if (res.data.code == 100) {
          let username = res.data.username;
          let token = res.data.token;
          // 放到cookie中
          this.$cookies.set('username', username, '7d');
          this.$cookies.set('token', token, '7d');

          this.$emit('success', res.data);  // 关闭登录框
        } else {
          this.$message({
            message: res.data.msg,
            type: 'warning',
            duration: 1500
          });
        }
      }).catch(error => {
        console.log(error.response.data)
      })
    }
  }
}
</script>

<style scoped>
.login {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10;
  background-color: rgba(0, 0, 0, 0.4);
  /* z-index 先沿着z轴浮动起来,再fixed,起巨幕遮盖的效果 */
}

.box {
  width: 400px;
  height: 420px;
  background-color: white;
  border-radius: 10px;
  position: relative;
  top: calc(50vh - 210px);
  left: calc(50vw - 200px);
}

.el-icon-close {
  position: absolute;
  font-weight: bold;
  font-size: 20px;
  top: 10px;
  right: 10px;
  cursor: pointer;
}

.el-icon-close:hover {
  color: darkred;
}

.content {
  position: absolute;
  top: 40px;
  width: 280px;
  left: 60px;
}

.nav {
  font-size: 20px;
  height: 38px;
  border-bottom: 2px solid darkgrey; /* 让标签下面有一道杠 */
}

.nav > span {
  margin: 0 20px 0 35px;
  color: darkgrey;
  user-select: none;
  cursor: pointer;
  padding-bottom: 10px;
  border-bottom: 2px solid darkgrey;
}

.nav > span.active {
  color: black;
  border-bottom: 3px solid black;
  padding-bottom: 9px;
}

.el-input, .el-button {
  margin-top: 40px;
}

.el-button {
  width: 100%;
  font-size: 18px;
}

.foot > span {
  float: right;
  margin-top: 20px;
  color: orange;
  cursor: pointer;
}

.sms {
  color: orange;
  cursor: pointer;
  display: inline-block;
  width: 70px;
  text-align: center;
  user-select: none;
}
</style>

.
.
.
.
.

3 注册小组件

Register.vue

<template>
  <div class="register">
    <div class="box">
      <i class="el-icon-close" @click="close_register"></i>

      <div class="content">
        <div class="nav">
          <span class="active">新用户注册</span>
        </div>
        <el-form>
          <el-input
              placeholder="手机号"
              prefix-icon="el-icon-phone-outline"
              v-model="mobile"
              clearable
              @blur="check_mobile">
            <!--手机号输入完触发失去焦点事件,发送ajax请求到后端的 1 校验手机号是否存在的接口-->
          </el-input>

          <el-input
              placeholder="密码"
              prefix-icon="el-icon-key"
              v-model="password"
              clearable
              show-password>
          </el-input>

          <el-input
              placeholder="验证码"
              prefix-icon="el-icon-chat-line-round"
              v-model="sms"
              clearable>
            <template slot="append">
              <span class="sms" @click="send_sms">{{ sms_interval }}</span>
              <!--获取验证码按钮点击后,发送ajax请求到后端的 3 发送短信验证码的接口-->
            </template>

          </el-input>

          <el-button @click="register" type="primary">注册</el-button>
          <!--注册按钮点击后,发送ajax请求到后端 5 手机号加短信验证码加密码注册账号接口-->


        </el-form>

        <div class="foot">
          <span @click="go_login">立即登录</span>
        </div>


      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "Register",
  data() {
    return {
      mobile: '',
      password: '',
      sms: '',
      sms_interval: '获取验证码',
      is_send: false,
    }
  },
  methods: {
    close_register() {
      this.$emit('close2', false)
    },

    go_login() {
      this.$emit('go2')
    },

    // 手机号输入完触发失去焦点事件,核对手机号码是否存在
    check_mobile() {
      if (!this.mobile) return;
      // js正则语法 '字符串'.match(/正则语法/)
      if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {
        this.$message({
          message: '手机号有误',
          type: 'warning',
          duration: 1000,
          onClose: () => {
            this.mobile = '';
          }
        });
        return false;
      }
      // 后台校验手机号是否已存在
      this.$axios({
        url: this.$settings.BASE_URL + '/user/userinfo/check_mobile/',
        method: 'get',
        params: {
          mobile: this.mobile
        }
      }).then(res => {
        if (res.data.code !== 101) {
          // 手机号在数据库中不存在才能注册,和登录时判断手机号是否存在逻辑是反的
          this.$message({
            message: '欢迎注册我们的平台',
            type: 'success',
            duration: 1500,
          });

          // 发送验证码按钮才可以被点击
          this.is_send = true;
        } else {
          this.$message({
            message: '账号已存在,请直接登录',
            type: 'warning',
            duration: 1500,
          })
        }
      }).catch(() => {
      });
    },

    // 发送短信验证码按钮的点击事件
    send_sms() {
      // this.is_send必须允许发生验证码,才可以往下执行逻辑
      if (!this.is_send) return;
      // 按钮点一次立即禁用
      this.is_send = false;

      let sms_interval_time = 60;
      this.sms_interval = "发送中...";

      // 定时器: setInterval(fn, time, args)

      // 往后台发送验证码
      this.$axios({
        url: this.$settings.BASE_URL + '/user/userinfo/send_msg/',
        method: 'post',
        data: {
          mobile: this.mobile
        }
      }).then(res => {
        if (res.data.code == 100) { // 发送成功
          let timer = setInterval(() => {
            if (sms_interval_time <= 1) {
              clearInterval(timer);
              this.sms_interval = "获取验证码";
              this.is_send = true; // 重新回复点击发送功能的条件
            } else {
              sms_interval_time -= 1;
              this.sms_interval = `${sms_interval_time}秒后再发`;
            }
          }, 1000)
        } else {  // 发送失败
          this.sms_interval = "重新获取";
          this.is_send = true;
          this.$message({
            message: '短信发送失败',
            type: 'warning',
            duration: 3000
          });
        }
      }).catch(() => {
        this.sms_interval = "频率过快";
        this.is_send = true;
      })
    },

    // 注册按钮的点击事件
    register() {
      if (!(this.mobile && this.sms && this.password)) {
        this.$message({
          message: '请填好手机、密码与验证码',
          type: 'warning',
          duration: 1500
        });
        return false  // 直接结束逻辑
      }

      this.$axios({
        url: this.$settings.BASE_URL + '/user/register/',
        method: 'post',
        data: {
          mobile: this.mobile,
          code: this.sms,
          password: this.password
        }

      }).then(res => {
        if (res.data.code == 108) {
          this.$message({
            message: '注册成功,3秒跳转登录页面',
            type: 'success',
            duration: 3000,
            showClose: true,
            onClose: () => {
              // 注册成功,去向登录页面
              this.$emit('success2')
            }
          });
        } else {
          this.$message({
            message: res.data.msg,
            type: 'warning',
            duration: 1500
          });
        }

      }).catch(error => {
        this.$message({
          message: '注册失败,请重新注册',
          type: 'warning',
          duration: 1500,
          showClose: true,
          onClose: () => {
            // 清空所有输入框
            this.mobile = '';
            this.password = '';
            this.sms = '';
          }
        });
      })
    }
  }
}
</script>

<style scoped>
.register {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10;
  background-color: rgba(0, 0, 0, 0.3);
}

.box {
  width: 400px;
  height: 480px;
  background-color: white;
  border-radius: 10px;
  position: relative;
  top: calc(50vh - 240px);
  left: calc(50vw - 200px);
}

.el-icon-close {
  position: absolute;
  font-weight: bold;
  font-size: 20px;
  top: 10px;
  right: 10px;
  cursor: pointer;
}

.el-icon-close:hover {
  color: darkred;
}

.content {
  position: absolute;
  top: 40px;
  width: 280px;
  left: 60px;
}

.nav {
  font-size: 20px;
  height: 38px;
  border-bottom: 2px solid darkgrey;
}

.nav > span {
  margin-left: 90px;
  color: darkgrey;
  user-select: none;
  cursor: pointer;
  padding-bottom: 10px;
  border-bottom: 2px solid darkgrey;
}

.nav > span.active {
  color: black;
  border-bottom: 3px solid black;
  padding-bottom: 9px;
}

.el-input, .el-button {
  margin-top: 40px;
}

.el-button {
  width: 100%;
  font-size: 18px;
}

.foot > span {
  float: right;
  margin-top: 20px;
  color: orange;
  cursor: pointer;
}

.sms {
  color: orange;
  cursor: pointer;
  display: inline-block;
  width: 70px;
  text-align: center;
  user-select: none;
}
</style>

.
.
.
.
.
.

4 Redis介绍与安装


# redis:缓存数据库【大部分时间做缓存,不仅仅可以做缓存】,非关系型数据库【区别于mysql关系型数据库】
	-nosql:非关系型的数据库
	-c语言写的 服务(监听端口),
	用来存储数据的,数据是存储在内存中,取值,放值速度非常快, 10w qps

-----------------------------------

# 面试题:redis为什么这么快
	-1  纯内存操作
	-2  网络模型使用的IO多路复用(epoll)(可以处理的请求数更多)
	-3  6.x之前,单进程,单线程架构,没有线程进程间切换,更少的消耗资源
	-4  6.x之后,虽然使用多进程,多线程架构,但是操作数据的时候还是单进程单线程

# key-value形式存储,没有表的概念
-----------------------------------
-----------------------------------

# 版本
	最新: 7.x
	公司里 5.x比较多


# 安装
	-mac   源码编译安装
	-linux 源码编译安装
	-win   微软自己,基于源码,改动,编译成安装包
		# 最新5.x版本 https://github.com/tporadowski/redis/releases/
		# 最新3.x版本 https://github.com/microsoftarchive/redis/releases

	一路下一步,安装完释放出两个命令,会把redis自动加入到服务中
	redis-server   #  相当于mysqld  服务端的启动命令
	redis-cli      # 相当于mysql    客户端的启动命令


------------------------------------------

# 安装目录下主要文件
	redis-server
	redis-cli
	redis.windows-service.conf    # 配置文件
		bind 127.0.0.1   # 服务端,跑在的地址
		port 6379        # 监听的端口

# mysql与redis 网络传输的协议都不是用的http协议
# 配置文件打开后,可以改端口号,
# 绑定的ip地址,如果改成bind 0.0.0.0,所有局域网都能访问你的服务端了

------------------------------------------

# 启动redis服务端
1 方式一:
	-在服务中,点击启动,后台启动

2 方式二:使用命令
	redis-server 指定配置文件  如果不指定,会默认
	redis-server  --port 6380   # 启动了一个端口号是6380的redis服务端

------------------------------------------

# 客户端连接redis
1 方式一
	redis-cli   # 默认连接本地的6379端口


2 方式二:  # 主要作用是连接远程的别人电脑的redis服务端用的
	redis-cli -h 地址 -p 端口
	redis-cli -h 127.0.0.1 -p 6379

3 方式三:使用图形化客户端操作
	-Redis Desktop Manager :开源的,原来免费,后来收费了  推荐用
	-resp-2022.1.0.0.exe    一路下一步,安装完启动起来
	-Redis Client  小众


# 图形化界面,连接redis 输入地址和端口,点击连接即可

# redis默认有16个库,默认连进去就是第0个
# 配置文件里面可以改库的数量
------------------------------------------------
------------------------------------------------
补充
# Qt5  qt是个平台,专门用来做图形化界面的
	-可以使用c++写
	-可以使用python写  pyqt5  使用python写图形化界面 (少量公司在用)

------------------------------------------------

如何将redis做成开机自启动 ?
image
.
.
将程序做成服务的本质是
当启动服务的时候,执行了一条命令
"D:\Program Files\Redis\redis-server.exe" --service-run "D:\Program Files\Redis\redis.windows-service.conf"
最后面的路径是配置文件的路径
image
.
image
.
.
.
.
.
.
.

5 python普通连接和连接池连接 redis

# 使用python来操作redis
# python相当于客户端,操作redis

# 需要安装模块:pip install redis


#补充: django 中操作mysql,没有连接池的,一个请求就是一个mysql连接
	-可能会有问题,并发数过高,导致mysql连接数过高,影响mysql性能
	-使用django连接池:https://blog.51cto.com/liangdongchang/5140039

.
.
.

5.1 python普通连接

# 安装redis 模块:pip install redis

# 1 导入模块的Redis类
from redis import Redis

# 2 实例化得到对象
conn = Redis(host='127.0.0.1', port=6379)

# 3 使用conn,操作redis
# 获取name的值
# res = conn.get('name')  # 返回数据是bytes格式

# 4 设置值
conn.set('age',19)


conn.close()

.
.
.
.
.

5.2 python连接池连接

# 连接池的目的: 就是不要创建出太多的连接数,影响服务的性能

###pool.py
import redis
POOL = redis.ConnectionPool(max_connections=10, host='127.0.0.1', port=6379)  # 创建一个大小为10的redis连接池

-------------------------------------------
### 测试代码
from threading import Thread
import redis
from pool import POOL
# 做成模块后,导入,无论导入多少次,导入的都是那一个POOL对象


def task():
    # 从池里面拿一个连接
    conn = redis.Redis(connection_pool=POOL)  # 如果报错了,可能是连接池的数量设置的太小,线程太多导致
    print(conn.get('name'))


for i in range(100):
    t = Thread(target=task, )  # 每次都是一个新的连接,会导致redis的连接数过多
    t.start()

# 一般要把连接池对象做成单例,否则有可能在每个线程中创建一个连接池,性能更低
# 使用模块导入的方式

---------------------------------------------

# 单例模式:设计模式  23 中设计模式
-全局只有一个 这个对象
    p1=Person()  # p1 对象
    p2=Person()  # p2 新对象

-单例模式的6种方式
    -1 模块导入方式
    -2 。。。


# 小补充: django连接mysql默认是不带连接池的,一个请求就是一个mysql连接
# django使用连接池的用法,要下载第三方模块
# 参考 https://blog.51cto.com/liangdongchang/5140039

.
.
.
.
.
.
.

6 python操作Redis之字符串类型相关方法

# redis 是key-value形式存储
# redis 数据放在内存中,如果断电,数据丢失---》所以需要有持久化的方案

# redis 的数据  如果服务端正常关闭,数据是不会丢失的,
# 会自动把内存上的数据持久化到硬盘上,自动创 dump.rdb 文件保存数据


# redis有  5种数据类型,value类型
-字符串:用的最多,做缓存;做计数器
-列表: 简单的消息队列
-字典(hash):缓存
-集合:去重
-有序集合:排行榜

------------------------------------------

.
.

#字符串类型使用

'''
1 set(name, value, ex=None, px=None, nx=False, xx=False)
2 setnx(name, value)
3 setex(name, value, time)
4 psetex(name, time_ms, value)
5 mset(*args, **kwargs)
6 get(name)
7 mget(keys, *args)
8 getset(name, value)
9 getrange(key, start, end)
10 setrange(name, offset, value)
11 setbit(name, offset, value)
12 getbit(name, offset)
13 bitcount(key, start=None, end=None)
14 bitop(operation, dest, *keys)
15 strlen(name)
16 incr(self, name, amount=1)
# incrby
17 incrbyfloat(self, name, amount=1.0)
18 decr(self, name, amount=1)
19 append(key, value)
'''

import redis

conn = redis.Redis()

# 1 set(name, value, ex=None, px=None, nx=False, xx=False)
# ex,过期时间(秒)
# px,过期时间(毫秒)
# nx,如果设置为True,内存中有键对应的值,就不修改该键对应的值,没有就修改
# xx,如果设置为True,内存中有键对应的值,就修改该键对应的值,值不存在,不会设置新值


# conn.set('hobby','篮球',ex=3)
# conn.set('hobby','篮球',px=3)

# conn.set('name','lqz888',)
# conn.set('name','lqz',nx=False)
# conn.set('hobby','篮球',xx=True)
# conn.set('hobby','篮球',xx=False)
# redis---》实现分布式锁,底层基于nx实现的


# 2 setnx(name, value)  # 内存中有键对应的值,就不改
# 等同于:conn.set('name','lqz',nx=True)
# conn.setnx('name', '刘亦菲')


# 3 setex(name, value, time)
# 等同于:conn.set('name','lqz',ex=3)
# conn.setex('wife', 3, '刘亦菲')


# 4 psetex(name, time_ms, value)
# conn.psetex('wife',3000,'刘亦菲')


# 5 mset(*args, **kwargs)  # 批量设置值
# conn.mset({'wife': '刘亦菲', 'hobby': '篮球'})


# 6 get(name)   # 获取值
# print(str(conn.get('wife'),encoding='utf-8'))
# print(conn.get('wife'))


# 7 mget(keys, *args)    # 批量获取值
# res=conn.mget(['wife','hobby'])
# res=conn.mget('wife','hobby')   # 两种方法都可以
# print(res)


# 8 getset(name, value)    # 既获取值,又设置值,一次网络请求,完成两个操作
# res=str(conn.getset('wife','迪丽热巴'),encoding='utf-8')
# print(res)


# 9 getrange(key, start, end)  # 拿对应字节长度区间的值
# res = str(conn.getrange('wife', 0, 2), encoding='utf-8')  # 字节长度,不是字符长度  前闭后闭区间
# print(res)


# 10 setrange(name, offset, value)  # 从对应的偏移量后,替换成设置的值
# conn.setrange('wife',2,'bbb')   # 两个字节长度后的3个字符替换成bbb 其他字符不变

-------------------------------------
# ---- 比特位---操作
# 11 setbit(name, offset, value)
# 12 getbit(name, offset)
# 13 bitcount(key, start=None, end=None)
# 14 bitop(operation, dest, *keys)   获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值
# ---- 比特位---操作
-------------------------------------

# 15 strlen(name)
# res=conn.strlen('hobby')   # 统计值对应的字节长度
# print(res)


-------------------------------------------

# 16 incr(self, name, amount=1)  # 自增一个整数
# 不会存在并发安全问题,单线程架构,并发量高
# conn.incr('age')   自增一
# incrby 和 incr 作用一样


# 17 incrbyfloat(self, name, amount=1.0)   # 自增一个小数
# conn.incrbyfloat('age',1.2)


# 18 decr(self, name, amount=1)  # 自减
# conn.decrby('age')  自减1



# 19 append(key, value)   # 往对应的值上拼接字符串
# conn.append('hobby','sb')

# print(conn.strlen('hobby'))

conn.close()


'''
你需要记住的
set
get
strlen  字节长度
incr
'''

.
.
.
.
.

作业

1 前端登录注册完成
3 装好redis
4 使用普通和连接池连接

5 照着比较测试所有string类型的方法
posted @   tengyifan  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示