day01-修复BUG、分支管理

修复BUG

在刚刚进入项目组后,一般不会布置开发任务,而是先熟悉项目代码。为了帮助大家熟悉整个项目,我们预留了一个BUG,让大家在修复BUG的过程中熟悉项目代码。
一般修复BUG的过程是这样的:

  • 熟悉项目
  • 阅读源码
  • 分析解决
  • 测试部署
    因此,解决BUG的过程,就是熟悉项目的过程。

项目结构

我们先来看看项目结构,目前企业微服务开发项目结构有两种模式:

  • 1)项目下的每一个微服务,都创建为一个独立的Project,有独立的Git仓库,尽可能降低耦合
  • 2)项目创建一个Project,项目下的每一个微服务都是一个Module,方便管理

方案一更适合于大型项目,架构更为复杂,管理和维护成本都比较高;
方案二更适合中小型项目,架构更为简单,管理和维护成本都比较低;

我们采用的正是第二种模式,结构如图:
在这里插入图片描述
对应到我们项目中每个模块及功能如下:
在这里插入图片描述
当我们要创建新的微服务时,也必须以tjxt为父工程,创建一个子module. 例如交易微服务:
在这里插入图片描述
微服务module中如果有对外暴露的Feign接口,需要定义到tj-api模块中:
在这里插入图片描述

实体类规范

所有实体类按照所处领域不同,划分为4种不同类型:

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

依赖注入

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

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

在以往代码中,我们经常利用Spring提供的@Autowired注解来实现依赖注入:
在这里插入图片描述
不过,这种模式是不被Spring推荐的,Spring推荐的是基于构造函数注入,像这样:
在这里插入图片描述
但是,如果需要注入的属性较多,构造函数就会非常臃肿,代码写起来也比较麻烦。

好在Lombok提供了一个注解@RequiredArgsConstructor,可以帮我们生成构造函数,简化代码:
在这里插入图片描述
这样一来,不管需要注入的字段再多,我们也只需要一个注解搞定:
在这里插入图片描述

异常处理

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

当微服务抛出这些异常时,需要一个统一的异常处理类,同样在tj-common模块中定义了:

@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);
    }
}

配置文件

SpringBoot的配置文件支持多环境配置,在天机学堂中也基于不同环境有不同配置文件:
在这里插入图片描述
在这里插入图片描述
项目中的很多共性的配置都放到了Nacos配置中心管理:
在这里插入图片描述
例如mybatis、mq、redis等,都有对应的shared-xxx.yaml共享配置文件。在微服务中如果用到了相关技术,无需重复配置,只要引用上述共享配置即可:
在这里插入图片描述

bootstrap.yml

在这里插入图片描述

阅读源码

阅读源码也不是闷头乱找,而是有一定的技巧。一般阅读源码的流程如下:
在这里插入图片描述

分支管理

一般我们不建议大家直接在Dev分支直接修改代码。在企业中都有一套分支管理机制,称为GitFlow,大概如图所示:
在这里插入图片描述
说明:

  • Master:主分支,用于正式发布的分支。不可直接基于该分支提交。只有经过严格审核测试后的Develop或Hotfix分支可以合并到master
  • Develop:开发分支,从Master创建得来。功能开发的基础分支。
  • Feature:功能分支,从Develop分支创建得来。开发测试完成后会合并到Develop分支。
  • Release:预发布分支,当Develop上积累了一定的功能特性后,从Develop分支创建一个Release分支,做一些发布前的准备工作,不可开发功能。最终合并到Master分支和Develop分支。
  • Hotfix:热修复分支,当Master出现紧急BUG时,基于Master临时创建的分支,修复完成后合并到Develop和Master分支。

在咱们项目中,master分支用来给大家提供完整版本代码了,而lesson-init分支作为初始化分支。因此一般不使用master分支,而是把lesson-init当做master分支来用。开发用的dev分支就等于GitFlow中的Develop分支。
因此,这里建议大家在dev分支基础上创建一个Hotfix分支,用以修改BUG,可以通过命令来创建该分支:

git checkout -b hotfix-delete-order-error

也可以在idea中通过点击右下角new branch 新建分支
在这里插入图片描述
修改代码后 接下来,提交代码:
在这里插入图片描述
然后切换Dev分支,并将hotfix-delete-order-error分支合并到dev分支,然后删除:
在这里插入图片描述

测试部署

在这里插入图片描述
由于我们这里的修改比较简单,这里就不做单元测试了。

接口测试

我们首先基于swagger做本地接口测试,在本地启动tj-trade项目,然后访问swagger页面:
http://localhost:8088/doc.html ,找到删除订单接口:
在这里插入图片描述
由于删除订单时需要对登录用户做校验,因此需要先设置用户id的全局参数:
在这里插入图片描述
在这里插入图片描述
微服务获取用户是基于请求头来传递的,因此我们设置全局参数时添加一个user-info的请求头参数即可。

然后刷新页面,来再次找到删除订单接口,进行调试,发现当用户id不对时,删除会失败:
在这里插入图片描述
当用户id正确时,删除成功:
在这里插入图片描述

组件测试

接下来让我们的服务与网关联调,再次测试。
不过问题来了,现在我们在本地启动了交易服务,而虚拟机中也启动了交易服务:
在这里插入图片描述
这里有两种办法:

  • 关停虚拟机中启动的交易服务
  • 将虚拟机中启动的交易服务权重设置为0

接下来,通过浏览器访问前端页面,然后点击删除订单测试即可。

部署联调

最后,测试没有问题,我们就可以将代码部署到开发环境去了。
我们在Jenkins中配置了web钩子,代码推送后自动触发构建。不过需要注意的是,默认情况下我们推送的代码不管是哪个分支都会触发构建,而且构建默认是基于lesson-init分支,需要重新配置。

我们找到Jenkins控制台中的tjxt-dev-build任务:
在这里插入图片描述
修改其中的配置。
第一个是哪些分支变化以后触发构建:
在这里插入图片描述
第二个是构建时基于哪个分支构建:
在这里插入图片描述
然后选择提交dev分支,并push到远端仓库:
在这里插入图片描述
在这里插入图片描述
然后到控制台,重新构建tj-trade服务:
在这里插入图片描述将本地服务停止,修改nacos中的虚拟机中的tj-trade实例权重为1:
在这里插入图片描述

再次测试即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值