Redis实现分布式会话
1、什么是分布式会话
1、如果非常多的人访问,因为单台服务器的访问承受能力是有限的,那么我们就想用多态服务器来承担压力
2、一般通过负载均衡的方式来实现,来分担服务器的压力。
3、负载均衡解释。
官方解释: 网络专用术语,负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。
4、解决方案。
-
session 复制,也就是当一个服务器有新的session保存的时候,通过服务器通信机制,然后将session复制到其他的服务器,如果服务器较多的话,会存在大量的网路和io占用,效率低下。
-
redis实现session共享。
2 准备条件
1、导入资料中的代码
注意修改mysql和redis的地址
访问端口:http://localhost:8081/shop-type/list 如果有数据显示,说明项目部署成功。
2、导入前端代码
3 启动代码
在nginx所在目录下打开一个CMD窗口,输入命令:start nginx
3 验证码
1 redis序列化配置
package com.xinzhi.config;
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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 创建Template
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// key和 hashKey采用 string序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
// value和 hashValue采用 JSON序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}
2、controller
@PostMapping("code")
public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
return userService.sendCode(phone, session);
}
3、service
public interface IUserService extends IService<User> {
Result sendCode(String phone, HttpSession session);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
private RedisTemplate redisTemplate;
@Override
public Result sendCode(String phone, HttpSession session) {
//1 校验手机号
if(RegexUtils.isPhoneInvalid(phone)){
//2 如果不符合返回错误消息
return Result.fail("手机号格式错误");
}
// 3 生成验证码
String code = RandomUtil.randomNumbers(6);
// 4 保存验证码到session
session.setAttribute("code",code);
// 5 发送验证码,发送短信验证大家添加
log.debug("验证码发送成功:"+code);
return Result.ok();
}
}
4 登录
1 controller
/**
* 登录功能
* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码
*/
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm,HttpSession session){
return userService.login(loginForm,session);
}
2 service
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
//1 校验手机号
String phone = loginForm.getPhone();
if(RegexUtils.isPhoneInvalid(phone)){
return Result.fail("手机号码格式错误");
}
// 2 从session中获取code并校验
Object cacheCode = session.getAttribute("code");
if(cacheCode==null || !cacheCode.toString().equals(loginForm.getCode())){
return Result.fail("验证码错误");
}
// 3 根据手机号查找用户
User user = query().eq("phone", phone).one();
// 4 用户不存在则创建用户
if(user==null){
user = createUserByPhone(phone);
}
// 5 用户保存到session
session.setAttribute("user", user);
return Result.ok();
}
private User createUserByPhone(String phone) {
User user = new User();
user.setPhone(phone);
user.setNickName("xinzhi_" + RandomUtil.randomString(8));
save(user);
return user;
}
3、创建intercepter包,创建拦截器
package com.xinzhi.intercepter;
import com.xinzhi.entity.User;
import com.xinzhi.utils.UserHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginIntercepter implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1 获取session
HttpSession session = request.getSession();
// 2 从session中获取user对象
Object user = session.getAttribute("user");
// 3 判断session中时候有对象
if(user==null){
// 4 不存在的话,设置401状态
response.setStatus(401);
return false;
}
//5 存在的话保存到threadlocal中
UserHolder.saveUser((User)user);
//6 放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}
4 将拦截器添加到 WebMvcConfigurer 中
package com.xinzhi.config;
import com.xinzhi.intercepter.LoginIntercepter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginIntercepter())
.excludePathPatterns(
"/user/code",
"/user/login",
"/blog/hot",
"/shop-type/**",
"/shop/**",
"/upload/**",
"/voucher/**"
).order(1);
}
}
5 user/me处理
@GetMapping("/me")
public Result me(){
User user = UserHolder.getUser();
return Result.ok(user);
}