小蔡学Java

项目一总结:(一)项目架构介绍及核心流程

2024-01-03 21:43 1108 0 项目 Java远程DeBug项目实战

项目介绍:

项目一:是一个基于微服务架构的生产级在线教育项目,是一个B2C类型的教育网站,分为两个端:后台管理端、 用户端(PC网站)。主要包含了在线教育中核心的学习辅助系统、考试系统,电商类项目的促销优惠系统;项目中涉猎技术广泛,涉及到SpringCloud Alibaba组件非关系型数据库mongoDB以及最常见的Redis,集成了ElasticSearch搜索技术,还有消息中间件RabbitMQ及定时任务组件Xxl-Job分布式锁组件redisson。最终项目基于docker容器搭建使用Graylog日志SKywalking进行分布式链路追踪;

整体架构

技术架构

项目核心流程

模块情况

微服务名称 功能描述
tj-parent 父工程
tj-common 通用工程
tj-message 消息中心
tj-gateway 网关
tj-auth 权限服务
tj-user 用户服务
tj-pay 支付服务
tj-course 课程服务
tj-exam 考试服务
tj-search 搜索服务
tj-trade 交易服务
tj-learning 学习服务
tj-promotion 促销服务
tj-media 媒资服务
tj-data 数据服务
tj-remark 评价服务

实体类规范

  • DTO:数据传输对象,在客户端与服务端间传递数据,例如微服务之间的请求参数和返回值、前端提交的表单
  • PO:持久层对象,与数据库表一一对应,作为查询数据库时的返回值
  • VO:视图对象,返回给前端用于封装页面展示的数据
  • QUERY:查询对象,一般是用于封装复杂查询条件

依赖注入(spring推荐的构造器注入方式)

Spring提供了依赖注入的功能,方便我们管理和使用各种Bean,常见的方式有:

  • 字段注入(@Autowired 或 @Resource
  • 构造函数注入
  • set方法注入

在以往代码中,我们经常利用Spring提供的@Autowired注解来实现依赖注入:

Spring推荐的是基于构造函数注入 Lombok提供了一个注解@RequiredArgsConstructor,可以帮我们生成构造函数。代码不会那么臃肿

全局异常处理

在项目运行过程中,或者业务代码流程中,可能会出现各种类型异常,为了加以区分,我们定义了一些自定义异常对应不同场景:在开发业务的过程中,如果出现对应类型的问题,应该优先使用这些自定义异常。

@RestControllerAdvice
@Slf4j
public class CommonExceptionAdvice {

    @ExceptionHandler(DbException.class)
    public Object handleDbException(DbException e) {
        log.error("mysql数据库操作异常 -> ", e);
        return processResponse(e.getStatus(), e.getCode(), e.getMessage());
    }

    @ExceptionHandler(CommonException.class)
    public Object handleBadRequestException(CommonException e) {
        log.error("自定义异常 -> {} , 状态码:{}, 异常原因:{}  ",e.getClass().getName(), e.getStatus(), e.getMessage());
        log.debug("", e);
        return processResponse(e.getStatus(), e.getCode(), e.getMessage());
    }

    @ExceptionHandler(FeignException.class)
    public Object handleFeignException(FeignException e) {
        log.error("feign远程调用异常 -> ", e);
        return processResponse(e.status(), e.status(), e.contentUTF8());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        String msg = e.getBindingResult().getAllErrors()
                .stream().map(ObjectError::getDefaultMessage)
                .collect(Collectors.joining("|"));
        log.error("请求参数校验异常 -> {}", msg);
        log.debug("", e);
        return processResponse(400, 400, msg);
    }
    @ExceptionHandler(BindException.class)
    public Object handleBindException(BindException e) {
        log.error("请求参数绑定异常 ->BindException, {}", e.getMessage());
        log.debug("", e);
        return processResponse(400, 400, "请求参数格式错误");
    }

    @ExceptionHandler(NestedServletException.class)
    public Object handleNestedServletException(NestedServletException e) {
        log.error("参数异常 -> NestedServletException,{}", e.getMessage());
        log.debug("", e);
        return processResponse(400, 400, "请求参数异常");
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public Object handViolationException(ConstraintViolationException e) {
        log.error("请求参数异常 -> ConstraintViolationException, {}", e.getMessage());

        return processResponse( HttpStatus.OK.value(), HttpStatus.BAD_REQUEST.value(),
                e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).distinct().collect(Collectors.joining("|"))
                );
    }

    @ExceptionHandler(Exception.class)
    public Object handleRuntimeException(Exception e) {
        log.error("其他异常 uri : {} -> ", WebUtils.getRequest().getRequestURI(), e);
        return processResponse(500, 500, "服务器内部异常");
    }

    private Object processResponse(int status, int code, String msg){
        // 1.标记响应异常已处理(避免重复处理)
        WebUtils.setResponseHeader(Constant.BODY_PROCESSED_MARK_HEADER, "true");
        // 2.如果是网关请求,http状态码修改为200返回,前端基于业务状态码code来判断状态
        // 如果是微服务请求,http状态码基于异常原样返回,微服务自己做fallback处理
        return WebUtils.isGatewayRequest() ?
                R.error(code, msg).requestId(MDC.get(Constant.REQUEST_ID_HEADER))
                : ResponseEntity.status(status).body(msg);
    }
}

技能掌握:(远程调试)

添加一个新的启动项:

在新建的Configuration中填写信息: 远程调试的部署脚本

  • 仅仅本地配置还不够,我们还需要在虚拟机中部署时,添加一段配置到部署脚本中,这段配置IDEA已经提供给我们了
java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 xx.jar

运行Jenkins部署脚本

虚拟机中暴露远程调试的端口

debug打断点调试

评论( 0 )

  • 博主 Mr Cai
  • 坐标 河南 信阳
  • 标签 Java、SpringBoot、消息中间件、Web、Code爱好者

文章目录