天机项目(使用Jenkins实现自动化部署,开发的一些规范,修改bug的流程,项目环境的搭建,git分支管理规范,享元模式)

目录

一、引言

二、项目介绍

1. 系统功能演示

1.1 在线演示系统地址

1.2 系统功能介绍

1.2.1 老师核心业务

2.3.2 学员核心业务

2. 系统架构介绍

2.1 系统架构

2.2 技术架构​编辑

3. 小结

三、项目环境搭建

1. 导入虚拟机

2. 配置本机hosts

3. 部署启动服务

3.1 虚拟机部署公用服务

部署步骤说明

1. 打开Jenkins管理界面

2. 部署微服务

3. 验证微服务是否成功启动

启动必要的微服务

3.2 本地部署自己的服务

克隆项目代码并创建dev分支

如何在本地启动服务

4. 小结

四、项目开发规范

1.项目结构规范

2.代码开发规范

2.1 实体类规范

2.2 入参校验规范

2.3 接口文档规范

2.4 依赖注入规范

3. 配置文件规范

4.异常和日志规范

4.1 异常规范

4.2 日志规范

5. 小结

五、缺陷修复验证

1. 阅读项目源码

1.1 查看请求接口

1.2 梳理请求链路

1.3 远程DEBUG

1.3.1 idea里本地配置

1.3.2 启动线上服务允许远程debug

1.3.3 开始远程debug调试

2. 缺陷定位修复

2.1 debug调试定位问题

2.2 企业分支管理模式

2.3 创建bug修复分支并修复bug

2.4 本地验证bug是否成功修复

2.5 合并代码

3. 代码测试验证

3.1 本地接口测试

3.2 组件测试

3.3 部署联调

3.3.1 修改Jenkins监听dev分支代码(已经配置好了)

3.3.2 把dev分支推送到gogs里

3.3.3 等待tjxt-dev-build任务执行完毕

3.3.4 重新部署tj-trade服务

3.3.5 将tj-trade服务实例权重修改回1

3.3.6 测试删除订单

4. 小结

常见问题

1. maven导依赖失败,爆红

原因

解决

第1步:确认maven正确配置

1) 要保证已正确配置maven

2) 检查maven是否使用了jdk11

3) 检查是否配置了阿里云的maven仓库镜像

第2步:处理导依赖报错的问题

1) 先清理本地仓库的残留文件然后刷新一下

2) 清理idea缓存并重启idea

3) 执行命令强制更新依赖

今天内容回顾


一、引言

过去一段时间的学习,大家对于框架、微服务、数据库等都有了一定的练习和认知。但是一个真实的开发流程到底是什么样?到公司了真实的微服务又是什么样子呢?我们内心一定会有这样几个问题:

  • 微服务技术如何在企业真实落地

  • 刚入职如何快速上手微服务项目

  • 新到一家公司应该从哪拉取代码

  • 标准的开发流程和规范是怎么样

因此今天开始我们将借助于“天机学堂” 这个微服务实战,带领大家完成下面知识点的学习:

  • 模拟入职代码拉取、阅读代码、修复缺陷的流程

  • 搭建开发环境,优化项目代码,开发新的业务代码

  • 了解代码自动化部署,CI/CD

二、项目介绍

天机学堂是一个基于微服务架构的在线教育项目,核心用户不是K12群体,而是面向成年人的非学历职业技能培训平台。

随着2021年7月双减政策的颁布,我国的教育培训(K12)课外辅导遭受到了毁灭性的打击。相对的,职业教育培训市场规模持续增长。

疫情导致的经济形式加剧变化,也让越来越多的人选择了在线教育,由于其成本更低、学习时间更碎片化、教育资源更能充分利用的多重优势,在线教育市场规模也在不断增长,并且前景巨大。

1. 系统功能演示

1.1 在线演示系统地址

1.2 系统功能介绍

天机学堂分为两部分:

  • 学生端(用户端):其核心业务主体就是学员,所有业务围绕着学员的展开

  • 管理端(平台运营人员和老师端):其核心业务主体包括老师、管理员、其他员工,核心业务围绕着老师展开

1.2.1 老师核心业务

例如,老师的核心业务流程有:

虽然流程并不复杂,但其中包含的业务繁多,例如:

  • 课程分类管理:课程分类的增删改查

  • 媒资管理:媒资的增删改查、媒资审核

  • 题目管理:试题的增删改查、试题批阅、审核

  • 课程管理:课程增删改查、课程上下架、课程审核、发布等等

2.3.2 学员核心业务

学员的核心业务就是买课、学习,基本流程如下:


2. 系统架构介绍

了解系统架构设计,是快速了解系统所用技术栈、部署情况、数据库设计的一种方式,下面我们将了解一下天机学堂所涉及的相关架构。

2.1 系统架构

天机学堂目前是一个B2C(Business To Custom,平台对个人)类型的教育网站,因此分为两个端:

  • 后台管理端

  • 用户端(PC网站)

其它的了解:

  • O2O:Online To Offline,线上对线下,美团外卖,猫眼订票

  • B2B:Business To Business,商家对商家,比如一些批发平台

整体架构如下:

2.2 技术架构

3. 小结

天机学堂是一个基于微服务架构的在线教育B2C项目,核心用户不是K12群体,而是面向成年人的非学历职业技能培训平台。整个系统有2个端,分别是:

  • 学员用户端:主要是给学员使用的,学员可以注册帐号并登录到系统里,然后就可以搜索浏览课程、购买课程开始学习。为了提升用户学习的积极性,我们系统还提供了一些激励和互动措施,比如:用户在学习过程中可以记录笔记、互动问答,这些互动的活动还可以累计积分,系统会根据积分形成排行榜。另外,系统还会根据排行榜给用户发放一些优惠券,进一步增加用户的粘性。

  • 平台管理端:主要是给平台运营人员和老师使用的。老师可以在管理端发布课程并进行回答学员的提问;平台运营人员可以对所有的数据进行管理和统计,特别是增加各种活动,并发放优惠券。

项目的技术主要是SpringCloud和MybatisPlus技术体系,还用到了xxl-job分布式定时任务调度,基于Redisson实现的分布式锁,rabbitMQ消息队列;在数据存储上,以MySQL为主,一些非关键但是数据量很大的数据使用MongoDB存储。搜索功能使用ElasticSearch

三、项目环境搭建

为了模拟真实的开发场景,我们设定的场景是这样的:天机学堂项目已经完成1.0.0版本60%的功能开发,能够实现项目的课程管理、课程购买等业务流程。现在需要加入课程学习、优惠促销、评价等功能。

相关微服务及1.0.0版本的完成状态如下:

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

部署环境如下:

1. 导入虚拟机

我们利用虚拟机,模拟真实的企业环境,搭建了一套开发环境,其中部署了开发常用的组件:

  • Git私服(gogs):代码全部提交带了自己的Git私服,模拟企业开发的代码管理,大家也需要自行到私服拉取代码

  • jenkins:持续集成,目前已经添加了所有部署脚本和Git钩子,代码推送会自动编译,可以根据需求手动部署

  • nacos:服务注册中心、统一配置管理,大多数共享的配置都已经交给nacos处理

  • seata:分布式事务管理

  • xxl-job:分布式任务系统

  • es:索引库

  • redis:缓存库

  • mysql:数据库

  • kibana:es控制台

如图:

导入方式参考资料里的《虚拟机运行说明.md》

ip地址:192.168.150.101 帐号:root 密码:123321

注意:

  • 导入虚拟机后所有软件即可使用,无需重复安装

  • VMware一定要按照文档中设置IP段为192.168.150.0,不要私自修改。

  • 一定要关闭windows防火墙。

  • 启动虚拟机的时候,你的电脑要联网(虚拟机里nginx 需要联网访问腾讯云)

2. 配置本机hosts

可以 利用SwitchHosts等软件,更方便的管理本机hosts文件。

为了模拟使用域名访问,我们需要在本地配置hosts:C:\Windows\System32\drivers\etc\hosts

192.168.150.101 git.tianji.com
192.168.150.101 jenkins.tianji.com
192.168.150.101 mq.tianji.com
192.168.150.101 nacos.tianji.com
192.168.150.101 xxljob.tianji.com
192.168.150.101 es.tianji.com
192.168.150.101 api.tianji.com
192.168.150.101 www.tianji.com
192.168.150.101 manage.tianji.com

当我们访问上述域名时,请求实际是发送到了虚拟机,而虚拟机中的Nginx会对这些域名做反向代理,这样我们就能请求到对应的组件了:

在浏览器中输入对应域名,即可查看到对应服务,例如Git私服地址:http://git.tianji.com

每个域名对应的服务列表如下:

名称域名账号端口
Git私服http://git.tianji.comtjxt/12332110880
Jenkins持续集成http://jenkins.tianji.comroot/12318080
RabbitMQhttp://mq.tianji.comtjxt/12332115672
Nacos控制台http://nacos.tianji.comnacos/nacos8848
xxl-job控制台http://xxljob.tianji.comadmin/1234568880
ES的Kibana控制台http://es.tianji.com-5601
微服务网关http://api.tianji.com-10010
用户端入口http://www.tianji.com-18081
管理端入口http://manage.tianji.com-18082

同样,我们访问用户端或者管理端页面时,也会被Nginx反向代理:

当我们访问www.tianji.com时,请求会被代理到虚拟机中的/usr/local/src/tj-portal目录中的静态资源

当页面访问api.tianji.com时,请求会被代理到虚拟机中的网关服务。

3. 部署启动服务

在企业开发中,微服务项目非常庞大,往往有十几个,甚至数十个,数百个微服务。而这些微服务也会交给不同的开发组去完成开发。你可能只参与其中的某几个微服务开发,那么问题来了:

如果我的微服务需要访问其它微服务怎么办?

难道说我需要把所有的微服务都部署到自己的电脑吗?

很明显,这样做是不现实的。

  • 第一,不是所有的代码你都有访问的权限;

  • 第二,你的电脑可能无法运行这数十、数百的微服务。

因此,公司往往会给我们部署一套开发环境,在上面运行那些经过测试的可部署的微服务。而多数情况下我们是面向接口编程,功能自测完成后再与公司开发环境的其它微服务联调。

为了模拟真实环境,我们在虚拟机中已经提供了一套持续集成的开发环境,代码一旦自测完成,push到Git私服后即可自动编译部署。

而开发我们负责的微服务时,则需要在本地启动运行部分微服务。

3.1 虚拟机部署公用服务

部署步骤说明

项目已经基于Jenkins实现了持续集成,每当我们push代码时,就会触发项目完成自动编译和打包,制作成docker镜像,启动docker容器,然后就可以访问这个服务了。

如果要部署某个微服务时,我们只需要:

1. 打开Jenkins管理界面

访问jenkins控制台:http://jenkins.tianji.com (账号:root/123)

2. 部署微服务

点击对应微服务后面的运行按钮

构建过程中,可以在页面左侧看到构建进度,如果没有说明构建已经结束了(你的机器速度太快了!):

完成后,点击对应的微服务名称【例如tj-gateway】,即可进入构建任务的详情页面,在页面左侧可以看到构建历史:

其中#1代表第一次构建,点击前面的√即可查看构建日志:

看到上面的日志,说明构建已经成功,容器也成功运行了。

3. 验证微服务是否成功启动

可以打开Nacos查看服务是否注册成功

也可以使用FinalShell或者WindTerm连接虚拟机

  • 执行命令:dps (或 docker ps),如果能看到刚刚部署的容器,说明部署已经成功

  • 执行命令:docker logs -f 容器名查看容器日志,如果看到以下结果说明服务已经成功启动

    按Ctrl+C退出查看

启动必要的微服务

我们需要分别启动几个开发完成的微服务(我们开发中为了节省内存资源,可以只启动我们所依赖的服务):

  • tj-user

  • tj-auth

  • tj-gateway

  • tj-course

  • tj-media

  • tj-search

  • tj-exam

  • tj-data

  • tj-message

  • tj-trade

  • tj-pay

此时访问Nacos控制台,可以看到微服务都成功注册了:

此时访问 http://www.tianji.com 即可看到用户端页面:

此时访问 http://manage.tianji.com 即可看到管理端页面:

如果想要知道微服务具备哪些API接口,可以访问网关中的swagger页面,路径如下:

http://api.tianji.com/doc.html

其中可以查看所有微服务的接口信息

3.2 本地部署自己的服务

对于需要开发功能的微服务,则需要在本地部署,不过首先我们要把代码拉取下来。

克隆项目代码并创建dev分支

查看Git私服的代码:http://git.tianji.com/tjxt/tianji

利用命令将代码克隆到你的IDEA工作空间中:

git clone http://192.168.150.101:10880/tjxt/tianji.git -b lesson-init

注意,开发时需要使用dev分支,因此我们需要创建新的分支:

# 进入项目目录
cd tianji
# 创建新的分支
git checkout -b dev

为了方便我们教学,目前所有微服务代码都聚合在了一个Project中,如图:

如何在本地启动服务

在默认情况下,微服务启用的是dev配置,如果要在本地运行,需要设置profile为local:

可以在本地启动ExamApplication,然后我们去Nacos控制台查看exam-service,可以看到有两个实例,分别是虚拟机IP和宿主机IP:

本地启动服务的操作步骤演示完成后,关闭掉Exam服务即可

4. 小结

第1步:启动虚拟机
    1. 打开虚拟机里.vmx文件
    2. 修改VMWare的网卡配置,把VMnet8的ip段设置为192.168.150.0
    3. 再启动虚拟机
第2步:配置域名映射
    1. 打开本机C:\Windows\System32\drivers\etc\hosts
    2. 把笔记里的域名映射添加到文件的最后,保存文件
    3. 验证基础环境是否成功。可能需要等待一会
        打开Nacos:http://nacos.tianji.com
        打开RabbitMQ:http://mq.tianji.com
        ...
第3步:部署启动公用服务
    1. 打开Jenkins管理界面 http://jenkins.tianji.com,输入帐号密码并登录
    2. 找到tj-auth, tj-user, tj-gateway, ……任务,点击运行按钮
        不要启动 xxx-debug和 tjxt-dev-build两个任务
    3. 打开Nacos,验证一下服务是否启动成功
        http://nacos.tianji.com,找到服务列表,刷新几次
        需要耐心等待,可能比较慢
        最终会有11个服务注册到Nacos里来
第4步:本地服务的拉取和启动
    1. 从Gogs里克隆项目代码,并直接检出lesson-init分支
    2. 使用idea打开项目代码,然后创建dev分支
    3. 修改idea里的启动链接,激活local环境
    4. 运行启动链接,在本机启动自己负责的服务

四、项目开发规范

开发规范是体现一位程序员经验、技能水准的最直接的地方,以下总结并归纳了一部分,但也不是很全(如代码封装、重构、命名、重复代码、正交性、解耦等),建议朗读并背诵全文。如有精力和时间推荐阅读:《程序员修炼之路》、《代码简洁之道》

1.项目结构规范

在微服务架构中,工程不再是在一个工程目录中,这个我们在微服务学习阶段已经深有感知,如cloud-demo中的order-service、user-service、feign-api、gateway等。因此对于企业开发其目录结构也一般有两种:

  • 一个工程多个Project,可以理解为上述的:oder-service、user-service等都是单独工程,这个耦合度降低了,但是不便于维护管理

  • 一个工程只有一个project,其中有多个Module,跟我们微服务学习阶段的工程目录结构一致,此时方便管理,也利于公共代码的使用,降低代码重复率

因此我们的工程目录结构如下:

但是也存在部分模块,如权限服务:tj-auth,会在多个微服务中共享使用,此时我们会针对module再封装子模块,变成:

此时,右侧多个module职责如下:

  • tj-auth-common:权限公共代码模块,tj-auth自身代码封装、别的模块也可pom直接引入使用

  • tj-auth-resource-sdk:给其余微服务使用的代码模块

  • tj-auth-gateway-sdk:给网关单独使用的核心模块

  • tj-auth-service:权限自身业务逻辑处理层

2.代码开发规范

2.1 实体类规范

在微服务开发中,我们实体类按照不同职责会分成下面几种类型:

  • DTO:Data Transfer Object,数据传输对象,在客户端与服务端间传递数据,通常是新增或修改时的参数

    例如微服务之间的请求参数和返回值、前端提交的表单

  • PO:Persistent Object, 持久层对象,与数据库表一一对应,作为查询数据库时的返回值【数据库映射对象】

  • VO:View Object,视图对象,返回给前端用于封装页面展示的数据【前端返回】

  • QUERY:查询对象,一般是用于封装复杂查询条件【前端查询入参】

如我们的工程目录中的代码规范:

2.2 入参校验规范

在微服务中,对于客户端(前端http、跨服务调用rpc)的调用,我们对于接口的入参往往也会做基础校验:非空、字段长度、日期格式等。以前端请求为例,假设做下单请求,我们针对不想为空的字段一般会这么来做:

  • 请求入参增加注解:@NotNull做非空校验、@Size做数据大小校验等

    下图中@ApiModel、@ApiModelProperty是给swagger接口做中文说明使用的,与校验无关,这样工程发布后前端就可以知道每个字段的含义

  • controller接口增加注解:@Validated, 或者 @Valid

2.3 接口文档规范

接口设计无外乎:接口地址、入参、出参,因此我们需要将所涉及的类中都增加swagger注解,前端就可以快速知晓含义,便于我们的联调测试。

2.4 依赖注入规范

为了方便管理各种Bean,Spring提供了依赖注入的方式,常见的有:

  • 字段成员变量注入(@Autowired 或 @Resource,工作最常用)

  • 构造函数注入(Spring推荐)

  • set方法注入

对于字段注入的方式大家都非常、非常的熟悉了,我们也建议大家在工作中直接这样使用。为了多一种技艺的学习掌握,这里我们给大家实战另一种方式:构造函数注入。而要实现构造函数注入,就意味着需要我们去每一个使用的地方编写一个构造器,显然太过麻烦,但Lombok已经帮我们做好了,我们只需像下面这样就可以优雅的实现Spring Bean的注入和使用。

3. 配置文件规范

对于一个项目而言,前面我们也提到开发环境众多,包含local、dev、test、pre、prod等多个环境,项目配置文件往往也是多个,这里我们准备如下:

文件描述说明
bootstrap.yml通用配置属性,包含服务名、端口、日志等等各环境通用信息
bootstrap-dev.yml线上开发环境配置属性,虚拟机中部署使用, 这里平级的也有bootstrap-test、bootstrap-pre、bootstrap-prod的环境文件
bootstrap-local.yml本地开发环境配置属性,本地开发、测试、部署使用

配置文件之间主要的差别是:nacos地址、数据库地址、mq地址等。因为不同的环境中相关的中间件都是独立部署的:即有开发环境的nacos、测试环境的nacos、预发环境nacos、生产nacos,都是独立的部署信息,因此我们多环境配置文件也主要是修改这些信息。

除了基本配置以外,其它的配置都放在了Nacos中共享,包括两大类:


  • 共享的配置(在命名空间public下查看):

在nacos中查看主要如下:

主要存放一些公用配置项如日志级别、ID生成策略等。同时会发现上图中的语法是:默认写死IP、端口,但同时支持用户通过选中的key进行覆盖,从而实现多环境IP、端口等的差异化配置。上图中也会发现只有数据库名没有默认值,是因为我们在工程代码中声明了(并非必须,是为了演示这种覆盖的效果)


  • 微服务中可能会根据业务变化的配置,如nacos访问后(命名空间为public)发现有一个:search-service.yaml,就是特属于某个微服务的

更多配置信息,登录 http://nacos.tianji.com 即可看到所有被管理的配置信息

4.异常和日志规范

4.1 异常规范

在微服务项目中,我们可不能随便的:throw new RuntimeException了。正规的项目中都应该有针对于当前业务工程的异常类封装,我们提供了常见的几种:

异常类状态码描述
CommonException--所有异常父类
BadRequestException400参数有误异常
UnauthorizedException401未登录异常
ForbiddenException403没有权限访问的异常
BizIllegalException500业务状态异常
DbException500数据库异常

同时在tj-common模块中,我们利用@RestControllerAdvice对异常做了统一处理,可参照:com.tianji.common.mvc.advice.CommonExceptionAdvice。这些异常处理类一般的项目中都已经定义好,对于研发人员来说,只需要在对应的异常路径抛出对应的异常即可,如:throw new DbException,就会被自动拦截,并完成响应体的封装,最终返回给前端。

4.2 日志规范

在springboot框架中,其已经完成了Slf4j + logback实现,并且有自动装配,比如我们想输出到指定文件、格式,才需要修改配置项,如下:

logging:
    pattern:    
        dateformat: HH:mm:ss.SSS # 日志中的日期格式  
        console: "[%X{requestId:-sys}] ... %m%n " # 输出到控制台的日志模板
        file: " [%X{requestId:-sys}] ... %m%n " # 输出到文件的日志模板 
    file:   
        path: "logs/${spring.application.name}" # 这里定义了日志保存在项目的logs目录

当然细心的读者应该发现了,这部分配置跟工程无关,因此我们可以解耦到nacos配置中心中,我们已经封装到了:share-logs.yaml。并且springboot已经实现了日志的滚动记录,名称默认为:spring.log.${date}.${index}.gz ,根据日期、文件大小自动拆分,形成新的日志文件。

5. 小结

实体类的划分:

  • DTO:客户端要新增或修改,提交的参数封装成DTO;其它服务远程调用时传的参数是DTO

  • Query:客户端要查询,查询条件封装成Query

  • PO:对应数据库表的实体类

  • VO:给客户端返回的结果对象

入参校验规范:

  • 参数实体类里的属性上加校验注解:@NotNull, @Min, Max,...

  • Controller里方法的形参上要加注解:@Validated 或者 @Valid

依赖注入规范:

  • 可以使用 private ICourseService courseService; + @Autowried

  • 可以使用 private ICourseService courseService; + @Resource

  • 可以使用 private final ICourseService courseService; 然后在类上加 @RequiredArgsConstructor

接口文档规范:Swagger的使用规范,略

配置文件规范:在Nacos里有配置文件,如果要修改配置,可以使用 tj.xxxx配置参数

异常处理规范:

  • 天机学堂项目里的异常,抛出来。要抛出的是CommonException的子类,是自定义的异常类

  • 项目里有统一异常处理器,可以抓取CommonException异常,给客户端返回友好提示

五、缺陷修复验证

“写代码5min、改BUG 2小时”、“代码都是百度CV的怎么会报错呢?”、“哎一样的代码,test环境是好的,生产怎么就不行呢?”,诸如此类的场景大家一定不陌生吧,作为一名刚到公司的新人,公司也一般不会让新人直接设计表、架构搭建,往往是从一个小bug开始逐步熟悉业务。接下来我们复现一下这个缺陷:

  • 分别用杰克(jack/123)、萝丝(rose/123456)登录,并进入“我的订单模块”

  • 杰克删除自己的订单信息,发现删除正常

  • 萝丝删除自己第一个订单信息,系统异常,并提示:不能删除他人订单

这明明是我萝丝登录进来,我自己的订单,为什么不让我删除呢?这时候最快的方式是不是就是要去阅读代码了呀。当然如果写这个代码的人有相关的日志打印,优先排查日志才是最优解。日志没有的情况下再去查看对应的代码业务逻辑。因此我们往往遵循下面的路径:

1. 阅读项目源码

1.1 查看请求接口

快速定位问题,一般都是F12-Network,获取对应的接口,因此我们知道是:http://api.tianji.com/ts/orders/159750267824138305

1.2 梳理请求链路

  • 当前端请求的时候,根据前面的分析我们知道会进入网关,而能进入的原因是我们在nginx中的配置项

  • 此时我们进入网关服务,其对应的规则如下,因此可以知道接下来进入的微服务:trade-service

  • 在trade-service服务中找到对应的代码,这块就完成了代码的定位了

1.3 远程DEBUG

如果部署的微服务不在本地,我们可以采用Idea的远程调试功能(此功能一般仅dev环境才会开启,test-pre-prod会做端口禁用,禁止debug,因为一旦debug就会阻塞用户请求了,这点需要意识到)。

1.3.1 idea里本地配置
  • 打开启动链接配置窗口

  • 创建Remote JVM Debug链接

  • 输入远程debug相关信息。

  • 其中jvm命令行参数的作用,是在启动时追加上我们上面的指令:

    默认启动指令:java -jar **.jar

    追加后变成:java -jar **-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005** xxx.jar

1.3.2 启动线上服务允许远程debug

要给线上服务完成这个参数的追加,我们已经给大家提前写好了,只要:

  1. 在虚拟机里停止tj-trade服务docker stop tj-trade, 因为这个服务里没有添加参数,不允许远程debug,所以关掉

  2. 在Jenkins里运行任务 tj-trade-debug,会启动一个新的tj-trade服务,这个服务是允许远程debug的,暴露了5005端口

如果对配置过程感兴趣,可以参考以下步骤:

  • 访问路径:

  • 进来后点击:配置

通过这种方式我们就可以远程debug 5005端口了,但是这些配置一般公司里运维都帮我们做好了,无需我们再操作。

我们点击启动执行

此时去docker中查看,执行指令dps就会发现增加了一个5005的端口暴露

也可使用日志查看命令:dlog -f tj-trade 或者(docker logs -f tj-trade),验证服务启动成功,启动成功后idea开始debug,有下述指令验证OK

1.3.3 开始远程debug调试

在idea里运行刚刚配置好的Remote JVM Debug,然后就可以在idea里打断点,当程序执行到断点位置时就会暂停。

注意:

虽然我们的断点是打在本地idea里,但是实际调试的是虚拟机里的服务程序

所以你本地idea里的代码 和 虚拟机里的运行的服务代码要相同

2. 缺陷定位修复

2.1 debug调试定位问题

  • 我们需要增加一个debug断点

  • 用杰克删除订单,注意swagger请求头应该是2且订单应该是jack的订单,如:1564890487310106626

  • 发现用户id和订单中用户id都是2,满足判断逻辑所以可以正常删除

  • 操作萝丝进行订单的删除,注意swagger请求头应该是2且订单应该是jack的订单,如:1597502678241378305

  • 会发现虽然数值都是129,但却是两个对象,这是什么原因呢?

细心的读者一定会发现,我们的判断逻辑是:!=,即不仅判断包装类型Long数值是否相等、还会判断内存地址是否一样(常见的面试题:==和equals区别 还有印象吗),而两者的内存地址在上面的截图中我们可以看到并不相同,这是为什么呢?

享元模式:用于减少创建对象的数量,以减少内存占用和提高性

这是因为Long这个包装类型也采用了Java设计模式中的享元模式,用于减少对象创建的性能开销,查看源码也可以看到了:

2.2 企业分支管理模式

一般我们不建议大家直接在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分支。

2.3 创建bug修复分支并修复bug

  1. 创建一个名称为 bugfix-order-delete-error的分支,切到这个分支里边

  2. 在这个分支里修复bug并提交:tj-trade服务里,找到OrderServiceImpl类里的deleteOrder方法,修改以下内容:

2.4 本地验证bug是否成功修复

  • 修改代码后重新启动本地服务:tj-trade,访问swagger地址:http://localhost:8088/doc.html,填入参数后点击发送,去控制台会发现成功删除

这里也会发现,真正工作中的删除都不会删除真实数据,而是采用更新

2.5 合并代码

bugfix-order-delete-error分支的代码,合并到dev分支里边

可以删除bugfix-order-delete-error分支

git push -f origin dev

强制推送到git仓库里

3. 代码测试验证

一般的测试步骤是这样的:

我们刚刚的代码比较简单,所以就不做本地单元测试了

3.1 本地接口测试

启动本地的trade服务,然后打开Swagger界面: http://localhost:8088/doc.html

由于删除订单时需要对登录用户做校验,因此需要先设置用户id的全局参数。注意:配置好以后要刷新页面

找到删除订单接口,设置id为:1597502678241378305,然后发送请求。如果能删除成功,说明bug已经修复

        

3.2 组件测试

接下来将我们的服务与网关进行联调,再次测试。

但是:我们本地启动了trade服务,虚拟机里也启动了trade服务。怎么保证通过网关访问的一定是我们本地服务呢?

  • 方式1:将虚拟机里的trade服务实例关闭,则trade服务只有本地一个实例可用,自然会访问到本地的trade服务

  • 方式2:打开Nacos的服务列表,找到虚拟机里的trade服务实例,设置权重为0,则虚拟机里的服务不会被访问到

2种方式均可,方式1比较简单,不需要多说。我们这里演示一下方式2

然后通过浏览器访问前端页面,登录rose/123456,然后点击删除订单,如果能删除成功,说明组件测试通过。

3.3 部署联调

3.3.1 修改Jenkins监听dev分支代码(已经配置好了)

在上面我们实际都还只是完成了本地的代码测试,代码并没有真正推送到远程仓库,接下来我们做下这部分工作,而要做到这步,前面我们提到的自动化部署就可以帮我们做到。但是在代码提交之前,我们会发现我们目前的开发分支是在:dev,而自动化部署的配置可不是,因此我们需要调整下面两个地方:

3.3.2 把dev分支推送到gogs里

更改后,我们再去提交代码,就会帮我们自动构建

注意,提交代码是需要填写此时git仓库对应账户信息:tjxt/123321

3.3.3 等待tjxt-dev-build任务执行完毕

去jenkins观察自动构建任务tjxt-dev-build,可以点击进去查看一下编译信息

3.3.4 重新部署tj-trade服务

等待构建完成后(上图进度条走完),进入dev菜单点击重新部署

  • 进入linux操作终端,通过:dlogs -f tj-trade,查看启动日志,直至tj-trade真正启动完成

  • 将剩余微服务都启动,验证一下(内存不够的可不操作,建议8核8G运行)

    docker start tj-auth tj-gateway tj-course tj-exam tj-search tj-user tj-data tj-media

    或者启动所有微服务:

    docker start $(docker ps -a | grep tj- | awk '{print $1}')

3.3.5 将tj-trade服务实例权重修改回1

打开Nacos界面,找到trade服务,将虚拟机里的服务实例的权重值修改回1。然后可以把本地的trade服务关闭掉

3.3.6 测试删除订单

注意:本机的服务要关闭掉,再测试部署到服务器上的trade服务是否正确

  • 访问地址:http://www.tianji.com/,账户:rose/123456

  • 将本地单测删除的订单还原,删除订单验证,如果没有可删除的订单,可以在数据库将订单数据恢复一下也可(deleted字段值修改回0即可)

至此,我们基本完成新人第一周的事情了:环境配置、代码拉取、紧急缺陷修复,学有余力的同学可以再操作一遍今日关键点:代码拉取-创建本地开发分支-提交代码-部署验证。

4. 小结

常见问题

1. maven导依赖失败,爆红

原因

导入依赖失败,maven没有下载成功jar包。或者idea没有成功加载到依赖

解决

第1步:确认maven正确配置
1) 要保证已正确配置maven

确认一下,idea已经正确配置了maven

2) 检查maven是否使用了jdk11

3) 检查是否配置了阿里云的maven仓库镜像

在settings.xml文件里配置阿里云仓库镜像

 <mirror>
     <id>aliyunmaven</id>
     <mirrorOf>*</mirrorOf>
     <name>阿里云公共仓库</name>
     <url>https://maven.aliyun.com/repository/public</url>
</mirror>

第2步:处理导依赖报错的问题
1) 先清理本地仓库的残留文件然后刷新一下
  • 清理maven仓库里的残留,然后在idea里重新clean,然后编译

    每次下载依赖失败,在本地仓库里都会有残留文件,后缀名是 .lastUpdated

    可以去本地仓库文件夹里,搜索 *.lastUpdated,把搜索结果的所有文件全选中,删除掉

  • 清理maven仓库里的残留,然后

    1. 在idea里打开pom.xml, 把出错的依赖剪切掉,刷新;重新粘贴回去,再刷新

    2. 如果还不行,就执行maven的clean,然后compile

2) 清理idea缓存并重启idea
  • 如果上一步完成之后,依赖问题还没有解决,就清理idea缓存然后重启一下idea

3) 执行命令强制更新依赖
  • 如果上一步完成之后,依赖问题还没有解决,就执行命令强制更新依赖

  • 在idea里执行命令:mvn -U idea:idea,表示要强制更新下载依赖,刷新一下;然后再clean、重新编译

今天内容回顾

  1. 请介绍一下天机学堂项目

        

天机学堂是一个基于微服务架构的在线教育B2C项目,核心用户不是K12群体,而是面向成年人的**非学历职业技能培训**平台。整个系统有2个端,分别是:
    * 学员用户端:主要是给学员使用的,学员可以注册帐号并登录到系统里,然后就可以搜索浏览课程、购买课程开始学习。为了提升用户学习的积极性,我们系统还提供了一些激励和互动措施,比如:用户在学习过程中可以记录笔记、互动问答,这些互动的活动还可以累计积分,系统会根据积分形成排行榜。另外,系统还会根据排行榜给用户发放一些优惠券,进一步增加用户的粘性。
    * 平台管理端:主要是给平台运营人员和老师使用的。老师可以在管理端发布课程并进行回答学员的提问;平台运营人员可以对所有的数据进行管理和统计,特别是增加各种活动,并发放优惠券。

项目的技术主要是SpringCloud和MybatisPlus技术体系,还用到了xxl-job分布式定时任务调度,基于Redisson实现的分布式锁,rabbitMQ消息队列;在数据存储上,以MySQL为主,一些非关键但是数据量很大的数据使用MongoDB存储。搜索功能使用ElasticSearch

项目包含14个服务,其中我负责的部分有 学习服务,促销服务,点赞评价服务,考试服务

2. 今天的天机学堂项目环境的搭建

第1步:启动虚拟机
    1. 打开虚拟机里.vmx文件
    2. 修改VMWare的网卡配置,把VMnet8的ip段设置为192.168.150.0
    3. 再启动虚拟机
第2步:本机配置域名映射
    1. 打开本机C:\Windows\System32\drivers\etc\hosts
    2. 把笔记里的域名映射添加到文件的最后,保存文件
    3. 验证基础环境是否成功。可能需要等待一会
        打开Nacos:http://nacos.tianji.com
        打开RabbitMQ:http://mq.tianji.com
        ...
第3步:部署启动公用服务
    1. 打开Jenkins管理界面 http://jenkins.tianji.com,输入帐号密码并登录
    2. 找到tj-auth, tj-user, tj-gateway, ……任务,点击运行按钮
        不要启动 xxx-debug和 tjxt-dev-build两个任务
        不要启动 我们自己负责的服务:tj-learning,tj-promotion,tj-remark,因为我们还没有开发
    3. 打开Nacos,验证一下服务是否启动成功
        http://nacos.tianji.com,找到服务列表,刷新几次
        需要耐心等待,可能比较慢
        最终会有11个服务注册到Nacos里来
第4步:本地服务的拉取和启动
    1. 从Gogs里克隆项目代码,并直接检出lesson-init分支
    2. 使用idea打开项目代码,然后创建dev分支
    3. 修改idea里的启动链接,激活local环境
    4. 运行启动链接,在本机启动自己负责的服务
第5步:访问测试。也需要耐心等待
    打开浏览器,输入地址
    用户端 http://www.tianji.com
    管理端 http://manage.tianji.com

3. 天机学堂项目开发的一些规范

项目结构规范:
    通常情况下,企业里的微服务项目结构有2种情况:
        一个项目,是一个Project,里边有多个Module。适合于中小型的微服务项目
        一个项目,是多个project,每个project是一个服务。适合于大型的微服务项目
    天机学堂的项目结构:一个project,里边有多个Module。每个Module是一个服务
实体类规范:
    DTO:用于封装新增或修改的参数;其它服务调用时传递的数据
    Query:用于封装查询条件
    PO:用于对应数据库表的实体类
    VO:用于封装 给客户端返回的结果
入参校验规范:
    常用校验注解:@NotNull,@Min, @Max, @Size,……
    在Controller里方法形参上加 @Validated或者@Valid
接口文档规范:
    使用Swagger的注解
    @Api:加在Controller类上,对类进行说明
    @ApiOperation:加在Controller里方法上,对方法进行说明
    @ApiParam:加在Controller里方法参数上,对参数进行说明
    @ApiModel:加在实体类上,对实体类进行说明
    @ApiModelProperty:加在实体类里属性上,对属性进行说明
依赖注入规范:
    成员变量直接加@Autowired
    成员变量直接加@Resource
    final类型的成员变量,并在类上加@RequiredArgsConstructor:通过构造方法注入的
配置文件规范:
    不变的参数在项目里配置了,可能会变的参数在配置中心Nacos里托管了
    项目相关的参数名,通常是 tj.xxx
异常处理规范:
    如果功能里出现问题,就抛出CommonException的子类异常对象
    项目里已经提供了异常处理器,统一抓取异常,给客户端返回友好提示
    

4. 修改bug的流程

如果是紧急bug:需要立即想办法暂时让数据恢复正常,让用户可以继续使用。争取一些时间,然后再找到问题的根源处理
正常处理bug:
    复现bug:根据测试人员提供复现的操作步骤,我们要亲自重现这个bug
    定位bug:F12的Network网络抓包,梳理请求链路,查看报错信息,debug查看跟踪源码
    解决bug:根据找到的问题根源,把bug解决掉
    验证bug:
        单元测试:最小的可测试单元,比如针对一个方法进行测试
        本地接口测试:通过Swagger或者Postman,直接测试这个服务里的功能是否正常
        组件测试:微服务要联合其它服务进行测试,比如我们今天是测试tr-trade和网关的测试
        部署测试:把修复后的代码提交push,然后部署到服务器上,再进行测试    

5. git分支管理规范

企业项目的分支,通常有:
* Master:主分支代码。
    不允许随便动。因为Master里的代码必须保证是稳定的可靠的。
    一旦线上环境出现问题,Master里随意有可用的代码
* dev:开发分发
    项目组里所有开发人员,在进行功能开发时公用的分支
    这里的代码都是新功能代码,不稳定,因为可能还没有经过测试
* 功能分支:开发一个功能,就从dev里拉一个新的功能分支
    在这个功能分支时编写代码实现功能。每次提交、推送都到这个分支里
    不同开发人员有各自的分支,开发过程中互不影响
    开发完成以后,要把分支的代码合并到dev分支里

大家进公司以后,需要先确定一下,公司的项目分支是怎么管理的。可能按规范,也可能不按规范,也可能有自己的一套规范。我们要跟随公司项目组的要求去做

6. 项目部署的流程:使用Jenkins实现自动化部署

1. 可以创建任务,提前预设好 要部署项目的每一步操作:从git仓库里拉取项目代码、使用maven进行编译打包、制作成docker镜像、创建容器并运行起来
2. 开发人员把编写好的代码提交并push到git仓库(我们天机学堂使用的Gogs);Jenkins会监听dev分支的代码,一旦有新的推送,就会自动拉取项目代码,进行编译
3. 再去Jenkins里运行部署某服务的任务,就可以把项目部署起来了

如果面试官问到:你有没有使用Jenkins进行过项目构建和部署?
答:我们公司有个运维,项目的部署、各种环境的搭建处理都是他负责的。我使用过他搭建好的Jenkins做过项目部署,但是没有自己构建过Jenkins的任务。 不过Jenkins只是实现自动化部署,提前预设一些部署的操作,各个环节的操作我很多都了解过,如果需要我创建任务的话,我觉得学习起来问题不大。

7. 开发时代码技巧:享元模式

Long,Integer等等都会把 -128~127数字对象缓存起来。所以
* 如果有两个Integer类型的变量,值都是这个范围内的,两个变量其实指向中是同一个缓存对象,使用==判断为true
* 如果有两个Integer类型的变量,值不在-128~127之间,两个变量指向2个对象,使用==判断为false

判断两个变量是否相等时:
    基本类型的判断:使用==
    引用类型的判断:使用equals方法

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值