原标题:如何零基础搭建一套微服务框架(Spring Boot + Dubbo + Docker + Jenkins)
6. 自动化构建
Jenkins是一个自动化构建工具,它可以帮助我们摆脱繁琐的部署过程,我们只需要在一开始配置好构建策略,以后部署只需要一键完成。
6.1 创建Jenkins容器
Jenkins采用Java开发,也需要Java环境,但我们使用Docker后,一切都采用容器化部署,Jenkins也不例外。
拉取镜像
这里我们使用Jenkins官方提供的镜像,大家只需执行如下命令拉取即可:
docker pull docker.io/jenkins/jenkins
启动容器
由于Jenkins运行在Tomcat容器中,因此我们将容器的8080端口映射到宿主机的10080端口上:
docker run --name jenkins -p 10080:8080 docker.io/jenkins/jenkins
初始化Jenkins
然后你需要访问IP:10080,Jenkins会带着你进行一系列的初始化设置,你只要跟着它一步步走就行了,比较傻瓜式。
6.2 在Jenkins中创建项目
接下来我们要做的是,在Jenkins中为每一个服务创建一个项目,每个项目中定义了构建的具体流程。由于我们将整个项目分成了6个微服务,所以我们需要在Jenkins中分别为这6个服务创建项目。那句开始吧~
点击页面左侧的“新建”按钮:
输入项目名称gaoxi-user,选择“构建一个Maven项目”,然后点击“OK”:
配置Git仓库
选择Git,然后输入本项目Git仓库的URL,并在Credentials中输入Git的用户名和密码,如下图所示:
构建触发器
选择第一项,如下图所示:
Pre Step
Pre Step会在正式构建前执行,由于所有项目都依赖于Gaoxi-Common-Service—Facade,因此在项目构建前,需要将它安装到本地仓库,然后才能被当前项目正确依赖。 因此,在Pre Step中填写如下信息:
Build
然后就是正式构建的过程,填写如下信息即可:
OK,Gaoxi-User的构建过程就配置完成了。当我们点击“立即构建”按钮时,Jenkins首先会从我们指定的Git仓库中拉取代码,然后执行Pre Step中的Maven命令,将Gaoxi-Common-Serivce-Facade打包安装到本地仓库。然后执行Build过程,将Gaoxi-User进行编译打包。 但此时Gaoxi-User仍然只是一个本地war包,并没有部署到Tomcat容器中,而我们采用了容器化部署后,Jenkins服务和Gaoxi-User服务并不在同一个Docker容器中,那么究竟该如何才能将Jenkins本地编译好的war包发送到Gaoxi-User容器中呢?这就需要使用Jenkins的一个插件——Deploy Plugin。
6.3 远程部署
安装插件
在系统管理–>插件管理–>高级上传deploy.hpi进行安装。
在父项目的pom文件中增加远程部署插件:
org.codehaus.cargo
cargo-maven2-plugin
1.6.5
tomcat8x
remote
runtime
Tomcat的用户名
Tomcat的密码
deploy
redeploy
为Tomcat设置用户名和密码
修改gaoxi-user容器中tomcat的tomcat-users.xml文件,增加tomcat的manager用户
注意:如果你使用了chaimm/tomcat镜像,那么其中Tomcat配置都已经完成,默认用户名:admin、默认密码:jishimen2019。强烈建议修改用户名和密码。
修改Jenkins中gaoxi-user的配置
在“构建后操作”中增加如下配置:
WAR/EAR files:表示你需要发布的war包
Containers:配置目标Tomcat的用户名和密码
7. Maven的profile功能
在实际开发中,我们的系统往往有多套环境构成,如:开发环境、测试环境、预发环境、生产环境。而不同环境的配置各不相同。如果我们只有一套配置,那么当系统从一个环境迁移到另一个环境的时候,就需要通过修改代码来更换配置,这样无疑增加了工作的复杂度,而且易于出错。但好在Maven提供了profile功能,能帮助我们解决这一个问题。
父项目的pom中添加profile元素
首先,我们需要在总pom的中添加多套环境的信息,如下所示:
dev
dev
true
test
test
prod
prod
- 父项目的pom中添加resource元素
resource标识了不同环境下需要打包哪些配置文件。
src/main/resources
true
application.properties
application-dev.properties
application-test.properties
application-prod.properties
src/main/resources
true
application.properties
application-${profileActive}.properties
- 父项目的pom中添加插件maven-resources-plugin
该插件用来在Maven构建时参数替换
maven-resources-plugin
3.0.2
@
false
在子项目中创建配置
分别为dev环境、test环境、prod环境创建三套配置,application.proerpties中存放公用的配置。
在application.properties中添加spring.profiles.active=@profileActive@
spring.profiles.active=@profileActive@
修改Jenkins的配置
在所有Jenkins中所有Maven命令的末尾添加-P test,在打包的时候-P后面的参数将会作为@profileActive@的值传入系统中,从而根据该值打包相应的application-{profileActive}.properties文件。
8. 开发流程
到此为止,所有准备工作都已经完成,接下来就可以进入代码开发阶段。下面我以一个例子,带着大家感受下有了这套微服务框架后,我们的开发流程究竟有了哪些改变?下面以开发一个用户登录功能为例,介绍下使用本框架之后开发的流程。
8.1 开发目标
在Gaoxi-User系统中实现登录的业务逻辑,并发布成RPC服务
在Gaoxi-Controller中远程调用登录服务,并向前端提供登录的REST接口
8.2 开发登录服务
首先需要在Gaoxi-Common-Service-Facade中创建UserService接口,并在其中声明登录的抽象函数。
public interface UserService {
public UserEntity login(LoginReq loginReq);
}
PS:为什么要将UserService放在Gaoxi-Common-Service-Facade中? 在这个项目中,Gaoxi-User是UserService服务的提供方,Gaoxi-Controller是UserService服务的引用方。由于二者并不在同一个系统中,所以必须要借助于Dubbo来实现远程方法调用。而Dubbo发布服务和引用服务的时候,都是根据服务的接口标识服务的,即服务引用方和发布方都需要使用服务的接口,因此需要将服务的接口放在所有项目共同依赖的基础模块——Gaoxi-Common-Service-Facade中。
然后在Gaoxi-User中开发UserService的实现——UserServiceImpl。 UserServiceImpl上必须要加上Dubbo的@Service注解,从而告诉Dubbo,在本项目初始化的时候需要将这个类发布成一项服务,供其他系统调用。
@Service(version = "1.0.0")
@org.springframework.stereotype.Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
@Override
public UserEntity login(LoginReq loginReq) {
// 校验参数
checkParam(loginReq);
// 创建用户查询请求
UserQueryReq userQueryReq = buildUserQueryReq(loginReq);
// 查询用户
List userEntityList = userDAO.findUsers(userQueryReq);
// 查询失败
if (CollectionUtils.isEmpty(userEntityList)) {
throw new CommonBizException(ExpCodeEnum.LOGIN_FAIL);
}
// 查询成功
return userEntityList.get(0);
}
}
8.3 引用登录服务
当UserService开发完毕后,接下来Gaoxi-Controller需要引用该服务,并向前端提供一个登录的REST接口。 若要使用userService中的函数,仅需要在userService上添加@Reference注解,然后就像调用本地函数一样使用userService即可。Dubbo会帮你找到UserService服务所在的IP和端口号,并发送调用请求。但这一切对于程序猿来说是完全透明的。
@RestController
public class UserControllerImpl implements UserController {
@Reference(version = "1.0.0")
private UserService userService;
@Override
public Result login(LoginReq loginReq, HttpServletResponse httpRsp) {
// 登录鉴权
UserEntity userEntity = userService.login(loginReq);
// 登录成功
doLoginSuccess(userEntity, httpRsp);
return Result.newSuccessResult();
}
}
8.4 自动构建服务
上面的代码完成后,接下来你需要将代码提交至你的Git仓库。接下来就是自动化部署的过程了。
你需要进入Jenkins,由于刚才修改了Gaoxi-User和Gaoxi-Controller的代码,因此你需要分别构建这两个项目。 接下来Jenkins会自动从你的Git仓库中拉取最新的代码,然后依次执行Pre Step、Build、构建后操作的过程。由于我们在Pre Step中设置了编译Gaoxi-Common-Service-Facade,因此Jenkins首先会将其安装到本地仓库;然后再执行Build过程,构建Gaoxi-User,并将其打包成war包。最后将执行“构建后操作”,将war包发布到相应的tomcat容器中。 至此,整个发布流程完毕!
8.5 查看服务的状态
当Jenkins构建完成后,我们可以登录Dubbo-Admin查看服务发布和引用的状态。
当我们搜索UserService服务后,可以看到,该服务的提供者已经成功发布了服务:
点击“消费者”我们可以看到,该服务已经被controller-consumer成功订阅:
9. 总结
总结一下,这套框架有如下优势:
微服务架构
我们借助于SpringBoot和Dubbo实现了微服务架构。微服务架构的理念就是将一个原本庞大、复杂的系统,按照业务功能拆分成一个个具有独立功能、可以独立运行的子系统,系统之间若有依赖,则通过RPC接口通信。从而最大限度地降低了系统之间的耦合度,从而更加易于扩展、更加易于维护。
容器化部署
我们借助于Docker实现了容器化部署。容器能够帮助我们屏蔽不同环境下的配置问题,使得我们只需要有一个Dockerfile文件,就可以处处运行。和虚拟机一样,Docker也拥有环境隔离的能力,但比虚拟机更加轻量级,由于每个容器仅仅是一条进程,因此它可以达到秒级的启动速度。
自动化构建
我们借助于Jenkins实现了所有项目的自动化构建与部署。我们只需要点击“立即构建”这个按钮,Jenkins就可以帮助我们梳理好错综复杂的项目依赖关系,准确无误地完成构建,并将war包发送到相应的web容器中。在启动的过程中,Dubbo会扫描当前项目所需要发布和引用的服务,将所需要发布的服务发布到ZooKeeper上,并向ZooKeeper订阅所需的服务。 有了Jenkins之后,这一切都是自动化完成。也许你并没有太强烈地感受到Jenkins所带来的便利。但是你想一想,对于一个具有错综复杂的依赖关系的微服务系统而言,如果每个服务的构建都需要你手动完成的话,你很快就会崩溃,你大把的时间将会投入在无聊但又容易出错的服务构建上。而Jenkins的出现能让这一切自动化完成。