二刷苍穹外卖 day01

发布于:2025-06-11 ⋅ 阅读:(27) ⋅ 点赞:(0)

nginx

nginx反向代理

将前端发送的请求由nginx转发到后端服务器
好处:
提速:nginx本身可缓存数据
负载均衡:配置多台服务器,大量请求来临可均衡分配
保证后端安全:不暴露后端服务真实地址

server{
    listen 80;
    server_name localhost;
    
    location /api/{
        proxy_pass http://localhost:8080/admin/; #反向代理
    }
}

**proxy_pass:**该指令是用来设置代理服务器的地址,可以是主机名称,IP地址加端口号等形式。

如上代码的含义是:监听80端口号, 然后当我们访问 http://localhost:80/api/…/…这样的接口的时候,它会通过 location /api/ {} 这样的反向代理到 http://localhost:8080/admin/上来。

负载均衡

upstream webservers{
    server 192.168.100.128:8080;
    server 192.168.100.129:8080;
}
server{
    listen 80;
    server_name localhost;
    
    location /api/{
        proxy_pass http://webservers/admin;#负载均衡
    }
}

upstream:如果代理服务器是一组服务器,可以使用upstream指令配置后端服务器
如上代码的含义是:监听80端口号, 然后当我们访问 http://localhost:80/api/…/…这样的接口的时候,它会通过 location /api/ {} 这样的反向代理到 http://webservers/admin,根据webservers名称找到一组服务器,根据设置的负载均衡策略(默认是轮询)转发到具体的服务器。

注:upstream后面的名称可自定义,但要上下保持一致。

负载策略:

轮询:默认

upstream webservers{
    server 192.168.100.128:8080;
    server 192.168.100.129:8080;
}

weight:添加权重

upstream webservers{
    server 192.168.100.128:8080 weight=90;
    server 192.168.100.129:8080 weight=10;
}

配置前端环境

nginx.conf

  

#user nobody;

worker_processes 1;

  

#error_log logs/error.log;

#error_log logs/error.log notice;

#error_log logs/error.log info;

  

#pid logs/nginx.pid;

  
  

events {

worker_connections 1024;

}

  

http {

include mime.types;

default_type application/json;

  

sendfile on;

keepalive_timeout 65;

upstream webservers{

server 127.0.0.1:8080 weight=90 ;

#server 127.0.0.1:8088 weight=10 ;

}

server {

listen 80;

server_name localhost;

# 指定前端项目所在的位置

location / {

root html/sky;

index index.html index.htm;

}

  

error_page 500 502 503 504 /50x.html;

location = /50x.html {

root html;

}

  

# location /api {

# default_type application/json;

# #internal;

# keepalive_timeout 30s;

# keepalive_requests 1000;

# #支持keep-alive

# proxy_http_version 1.1;

# rewrite /api(/.*) $1 break;

# proxy_pass_request_headers on;

# #more_clear_input_headers Accept-Encoding;

# proxy_next_upstream error timeout;

# #proxy_pass http://127.0.0.1:8081;

# proxy_pass http://backend;

# }

# 反向代理,处理管理端发送的请求

location /api/ {

proxy_pass http://localhost:8080/admin/;

#proxy_pass http://webservers/admin/;

}

# 反向代理,处理用户端发送的请求

location /user/ {

proxy_pass http://webservers/user/;

}

# WebSocket

# location /ws/ {

# proxy_pass http://webservers/ws/;

# proxy_http_version 1.1;

# proxy_read_timeout 3600s;

# proxy_set_header Upgrade $http_upgrade;

# proxy_set_header Connection "$connection_upgrade";

# }

}

  

# upstream backend {

# server 127.0.0.1:8080 weight=90 ;

# # server 127.0.0.1:8081 max_fails=5 fail_timeout=10s weight=1;

# # server 127.0.0.1:8082 max_fails=5 fail_timeout=10s weight=1;

# }

}

输入nginx -s reload重启服务,访问80端口有效
![[Pasted image 20250610191304.png]]

项目整体架构:

sky-take-out 父工程,通过pom统一管理依赖版本并聚合其他子模块
sky-common 存放常量,工具类,异常类等子模块
sky-pojo 存放实体,VO、DTO等
sky-server 子模块,存放配置文件、Controller、Service、mapper等配置文件

common:

constant 相关常量
context 上下文
enumeration 枚举操作
exception 自定义异常
json 处理json转换 基于jackson将Java对象转为json,或者将json转为Java对象
properties 存放springboot相关配置属性类,如阿里OSS服务,jwt令牌和微信支付接口等配置
result 返回结果封装,分页返回类和标准返回格式

Serializable接口是Java提供的一个标记接口,用于表示一个类的对象是否可被序列化,序列化意味着将对象转换为字节流,还能从字节重构对象,
- 一个类只有实现了 Serializable 接口,它的对象才是可序列化的。因此如果要序列化某些类的对象,这些类就必须实现 Serializable 接口。而实际上,Serializable 的源码是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。

util 常见工具类

pojo

entity :存放与数据库表对应的实体
DTO :数据传输对象,在后端各层中进行传输
VO :视图,用于为前端展示数据所提供的对象

server

config:配置类
controller
interceptor:拦截器类
mapper:
service:
SkyApplication :启动类

前后端整体实现思路:

![[Pasted image 20250610195506.png]]

controller层:

@PostMapping("/login")  
public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {  
    log.info("员工登录:{}", employeeLoginDTO);  
  
    Employee employee = employeeService.login(employeeLoginDTO);  
  
    //登录成功后,生成jwt令牌  
    Map<String, Object> claims = new HashMap<>();  
    claims.put(JwtClaimsConstant.EMP_ID, employee.getId());  
    String token = JwtUtil.createJWT(  
            jwtProperties.getAdminSecretKey(),  
            jwtProperties.getAdminTtl(),  
            claims);  
  
    EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder()  
            .id(employee.getId())  
            .userName(employee.getUsername())  
            .name(employee.getName())  
            .token(token)  
            .build();  
  
    return Result.success(employeeLoginVO);  
}
1.接受登陆请求:

方法接收了一个EmplyeeLoginDTO类型的参数,这个DTO包括前端传来的用户名和密码,用于员工登陆,@RequestBody注解表示请求体以JSON格式传递

2.调用Service层进行验证

调用emplyeeService的login()方法,将登陆信息传递给服务层,服务层若验证成功会返回对应的Employee实体对象

3.生成JWT Token

使用JwtUtil.createJWT()方法根据员工id生成JWT令牌
令牌包括用户标识emplyee.getId()
过期时间以及签名算法

4.构建返回值对象给EmployeeLoginVO

创建EmployeeLoginVO对象,封装登录成功后需要返回给前端的信息
包括员工ID,用户名、姓名,token(用于后续请求的身份凭证)

service层:

public Employee login(EmployeeLoginDTO employeeLoginDTO) {  
    String username = employeeLoginDTO.getUsername();  
    String password = employeeLoginDTO.getPassword();  
  
    //1、根据用户名查询数据库中的数据  
    Employee employee = employeeMapper.getByUsername(username);  
  
    //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)  
    if (employee == null) {  
        //账号不存在  
        throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);  
    }  
  
    //密码比对  
    // TODO 后期需要进行md5加密,然后再进行比对  
    if (!password.equals(employee.getPassword())) {  
        //密码错误  
        throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);  
    }  
  
    if (employee.getStatus() == StatusConstant.DISABLE) {  
        //账号被锁定  
        throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);  
    }  
  
    //3、返回实体对象  
    return employee;  
}

1.获取用户名与密码

从传入的DTO中提取用户名和密码

2.查询数据库中的员工信息

emplyeeMapper.getByUsername(Username),通过MyBatis框架调用数据库接口

3.异常处理逻辑

异常类型:AccountNofFoundException(用户名不存在)

4.密码比对并检查账号状态

5.返回员工实体

所有校验通过后,返回查询到的员工对象,该对象将用于生成JWT Token,并构建成功响应

Mapper层

@Select("select * from employee where username = #{username}")  
Employee getByUsername(String username);

这部分代码定义了一个名为getByUsername的方法,该方法接收一个String类型的参数username@Select注解表示这是一个用于执行 SQL 查询的语句,其注解内的 SQL 语句select * from employee where username = #{username} 意思是从名为employee的表中查询所有列,条件是username字段的值等于传入的username参数值。方法返回类型为Employee,即查询结果会封装成Employee对象返回,整体功能是根据给定的用户名从employee表中获取对应的员工信息。

完善登录功能:

Employee employee = employeeMapper.getByUsername(username);  
log.info("password={}",password);

使用MD5加密的方式对明文密码进行加密得到:
e10adc3949ba59abbe56e057f20f883e
将sql里的admin密码从123456改为以上

     password = DigestUtils.md5DigestAsHex(password.getBytes());
        if (!password.equals(employee.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

修改代码后登陆成功

Swagger

用于生成、描述、调用和可视化RESful风格的Web服务,作用:
使得前后端分离开发更加方便
接口文档自动生成,减轻后端开发人员编写借口的负担
可进行功能测试
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍!

目前,一般都使用knife4j框架。

步骤

1.导入pom坐标

<dependency>
   <groupId>com.github.xiaoymin</groupId>
   <artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>

2.在配置类中加入knife4j相关配置

WebMvcConfiguration.java

/**
     * 通过knife4j生成接口文档
     * @return
*/
    @Bean
    public Docket docket() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

3.设置静态资源映射

WebMvcConfiguration.java

/**
     * 设置静态资源映射
     * @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}

4.访问

在http://localhost:8080/doc.html页面进行访问

常见注解

@Api 类上,例如Controller,说明类
@ApiModel 用于类,例如entity,DTO,VO
@ApiModelProperty 用于属性,描述属性信息
@ApiOperation 用于方法上,说明方法用途、作用

使用上述注解,生成可读性更好的接口文档

@ApiModel(description = "员工登录时传递的数据模型")
public class EmployeeLoginDTO implements Serializable 

@ApiModel(description = "员工登录返回的数据格式")
public class EmployeeLoginVO implements Serializable

 @ApiOperation(value = "员工登录")
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) 

总结

1. Nginx 反向代理的作用是什么?如何配置?

作用

  • 转发前端请求到后端服务器,隐藏真实地址,保障安全;
  • 缓存数据提速,支持负载均衡分配多台服务器请求。
    配置示例
server {
    listen 80;
    server_name localhost;
    location /api/ {
        proxy_pass http://localhost:8080/admin/;  # 反向代理到后端地址
    }
}
2. Nginx 负载均衡的策略有哪些?如何配置权重?

常见策略

  • 轮询(默认):请求依次分配到各服务器;
  • weight 权重:按权重比例分配,适用于性能不同的服务器。
    权重配置示例
upstream webservers {
    server 192.168.100.128:8080 weight=90;  # 权重90%
    server 192.168.100.129:8080 weight=10;  # 权重10%
}
3. 苍穹外卖项目的整体架构如何设计?各模块的作用是什么?

架构设计

  • sky-take-out:父工程,管理依赖版本并聚合子模块;
  • sky-common:存放常量、工具类、异常类等公共资源;
  • sky-pojo:定义实体类(Entity)、数据传输对象(DTO)、视图对象(VO);
  • sky-server:核心模块,包含配置、Controller、Service、Mapper 等业务逻辑。
4. 登录功能的后端实现流程是什么?如何保证密码安全?

流程

  1. Controller 层:接收前端登录请求,解析用户名和密码;
  2. Service 层:根据用户名查询数据库,验证密码和账号状态(是否锁定);
  3. 生成 JWT 令牌:登录成功后,基于员工 ID 生成令牌并返回给前端。
    密码安全
    使用 MD5 加密算法对明文密码加密
password = DigestUtils.md5DigestAsHex(password.getBytes());  // 加密后比对
5. 什么是 Knife4j?如何配置接口文档?

Knife4j
是 Swagger 的增强解决方案,用于自动生成、可视化 RESTful 接口文档,简化前后端协作。
配置步骤

  1. 导入依赖:
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
  1. 配置 Docket Bean(如 WebMvcConfiguration):
@Bean
public Docket docket() {
    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(new ApiInfoBuilder().title("接口文档").build())
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
            .build();
}
  1. 访问地址:http://localhost:8080/doc.html
6. JWT 令牌在登录功能中的作用是什么?如何生成?

作用

  • 作为用户身份凭证,避免每次请求都查询数据库;
  • 实现无状态认证,减轻服务器负担。
// 基于员工ID生成JWT令牌
Map<String, Object> claims = new HashMap<>();
claims.put("empId", employee.getId());
String token = JwtUtil.createJWT(
    secretKey,          // 签名密钥
    ttlMillis,          // 过期时间
    claims              // 载荷数据
);

网站公告

今日签到

点亮在社区的每一天
去签到