本文你将学到什么?
本文将以原理+实战的方式,首先对“微服务”相关的概念进行知识点扫盲,然后开始手把手教你搭建这一整套的微服务系统。
这套微服务框架能干啥?
这套系统搭建完之后,那可就厉害了:
请点击此处输入图片描述
开始动手
3.1 创建Project
请点击此处输入图片描述
请点击此处输入图片描述
请点击此处输入图片描述
请点击此处输入图片描述
3.3 构建模块的依赖关系
目前为止,模块之间没有任何联系,下面我们要通过pom文件来指定它们之间的依赖关系,依赖关系如下图所示:
请点击此处输入图片描述
Gaoxi-User、Gaoxi-Analysis、Gaoxi-Product、Gaoxi-Order这四个系统相当于以往三层结构的Service层,提供系统的业务逻辑,只不过在微服务结构中,Service层的各个模块都被抽象成一个个单独的子系统,它们提供RPC接口供上面的Gaoxi-Controller调用。它们之间的调用由Dubbo来完成,所以它们的pom文件中并不需要作任何配置。而这些模块和Gaoxi-Common-Service-Facade之间是本地调用,因此需要将Gaoxi-Common-Service-Facade打成jar包,并让这些模块依赖这个jar,因此就需要在所有模块的pom中配置和Gaoxi-Common-Service-Facade的依赖关系。
此外,为了简化各个模块的配置,我们将所有模块的通用依赖放在Project的pom文件中,然后让所有模块作为Project的子模块。这样子模块就可以从父模块中继承所有的依赖,而不需要自己再配置了。
下面开始动手:
class="java"><groupId>com.gaoxi</groupId><artifactId>gaoxi-common-service-facade</artifactId><version>0.0.1</version><packaging>jar</packaging>
<groupId>com.gaoxi</groupId><artifactId>gaoxi-user</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging>
<modules> <module>Gaoxi-Analysis</module> <module>Gaoxi-Order</module> <module>Gaoxi-Product</module> <module>Gaoxi-User</module> <module>Gaoxi-Redis</module> <module>Gaoxi-Controller</module> <module>Gaoxi-Common-Service-Facade</module></modules>
?
?<parent> <groupId>com.gaoxi</groupId> <artifactId>gaoxi</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath>../pom.xml</relativePath></parent>
?
? 到此为止,模块的依赖关系配置完毕!但要注意模块打包的顺序。由于所有模块都依赖于Gaoxi-Common-Servie-Facade模块,因此在构建模块时,首先需要编译、打包、安装Gaoxi-Common-Servie-Facade,将它打包进本地仓库中,这样上层模块才能引用到。当该模块安装完毕后,再构建上层模块。否则在构建上层模块的时候会出现找不到Gaoxi-Common-Servie-Facade中类库的问题。 3.4 在父模块的pom中添加所有子模块公用的依赖 ?<dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- Spring MVC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <!-- Mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- Dubbo --> <dependency> <groupId>io.dubbo.springboot</groupId> <artifactId>spring-boot-starter-dubbo</artifactId> <version>1.0.0</version> </dependency> <!-- gaoxi-common-service-facade --> <dependency> <groupId>com.gaoxi</groupId> <artifactId>gaoxi-common-service-facade</artifactId> <version>0.0.1</version> </dependency> <!-- AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- guava --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.3-jre</version> </dependency></dependencies>
?
? 当父模块的pom中配置了公用依赖后,子模块的pom文件将非常简洁,如下所示: ?<groupId>com.gaoxi</groupId><artifactId>gaoxi-user</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>gaoxi-user</name><parent> <groupId>com.gaoxi</groupId> <artifactId>gaoxi</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath>../pom.xml</relativePath></parent>
?
? 当项目的结构搭建完成之后,接下来你需要配置Docker环境,并将这些项目打包进容器中,验证下是否能正常启动。 4. 创建Docker容器 4.1 安装Docker 在使用Docker之前,你当然先要安装Docker,安装过程较为简单,基本上就是傻瓜式操作,这里就不作过多介绍了,你可以在Docker的官网下载相应系统的安装包。 https://www.docker.com/ 4.2 获取Tomcat镜像 在微服务架构中,一个完整的系统被拆分成了多个被称为“微服务”的子系统,这些子系统可以独立运行在Web容器中。所以我们需要为这些系统提供运行的Web容器,这里我们选择大家较为熟悉的Tomcat。 我们知道,Tomcat依赖于Java环境,安装Tomcat之前要进行一系列环境的配置:安装Java、配置环境变量、安装Tomcat等等。这些操作还是有些繁琐的。不过没关系,当使用了Docker之后,这些过程都可以轻而易举地完成。 我们只需从Docker Hub上找到Tomcat的镜像资源,然后从上面拉取下来就可以使用。你可以使用Tomcat官方的镜像,也可以使用我发布在Docker Hub上的Tomcat镜像。 注意点:推荐使用我的Tomcat镜像资源chaimm/tomcat,因为这个镜像中除了配置Tomcat的安装环境以外,还有一些本项目中要用到的Jenkins相关的配置。 采用如下命令从Docker Hub上拉取镜像: docker?pull?chaimm/tomcat:1.1 简单解释下,docker pull是从从Docker Hub上拉取镜像的命令,后面的chaimm/tomcat是镜像的名称,:1.1是镜像的版本号。目前这个镜像的最新版本号是1.1,推荐大家拉取这个。 4.3 创建Tomcat容器 这里再简单介绍下“镜像”和“容器”的关系。 “镜像”就好比是面向对象中的“类”,“容器”就好比“类”创建的“对象”。在面向对象中,“类”定义了各种属性,“类”可以实例化出多个“对象”;而在Docker中,“镜像”定义了各种配置信息,它可以实例化出多个“容器”。“容器”就是一台可以运行的“虚拟机”。 接下来我们需要为所有的微服务创建各自的容器:
请点击此处输入图片描述
接下来,你需要按照上面的方法,给剩下几个系统创建好Tomcat容器。
注意点:这里要注意的是,你需要给这些Tomcat容器指定不同的端口号,防止端口号冲突。当然,在实际开发中,你并不需要将容器的8080端口映射到宿主机上,这里仅仅是为了验证容器是否启动成功才这么做的。文章中涉及到的微服务、dubbo,等技术均分享在群619881427已录制成视频,可免费下载。如有需要可以加入进来一起学习交流
5. 整合Dubbo
5.1 创建zookeeper容器
Dubbo一共定义了三种角色,分别是:服务提供者、服务消费者、注册中心。注册中心是服务提供者和服务消费者的桥梁,服务消费者会在初始化的时候将自己的IP和端口号发送给注册中心,而服务消费者通过注册中心知道服务提供者的IP和端口号。
在Dubbo中,注册中心有多种选择,Dubbo最为推荐的即为ZooKeeper,本文采用ZooKeepeer作为Dubbo的注册中心。
创建ZooKeeper容器也较为简单,大家可以直接使用我创建的ZooKeeper镜像,通过如下命令即可下载镜像:
docker?pull?chaimm/zookeeper-dubbo:1.0
该镜像中不仅运行了一个zookeeper,还运行了一个拥有dubbo-admin项目的tomcat。dubbo-admin是Dubbo的一个可视化管理工具,可以查看服务的发布和引用的情况。
使用如下命令启动容器:
docker?run?--name?zookeeper-debug?-p?2182:2181?-p?10000:8080?chaimm/zookeeper-dubbo:1.0
请点击此处输入图片描述
5.2 父pom文件中引入dubbo依赖
?
<!-- Spring Boot Dubbo 依赖 --><dependency> <groupId>io.dubbo.springboot</groupId> <artifactId>spring-boot-starter-dubbo</artifactId> <version>1.0.0</version></dependency>
?
? 5.3 发布服务 假设,我们需要将Gaoxi-User项目中的UserService发布成一项RPC服务,供其他系统远程调用,那么我们究竟该如何借助Dubbo来实现这一功能呢?public interface UserService { public UserEntity login(LoginReq loginReq);
}
?
?@Service(version = "1.0.0")public class UserServiceImpl implements UserService { @Override
public UserEntity login(LoginReq loginReq) { // 具体的实现代码
}
}
?
?@RestControllerpublic 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);
}
}
?
?
请点击此处输入图片描述
请点击此处输入图片描述
请点击此处输入图片描述
请点击此处输入图片描述
请点击此处输入图片描述
请点击此处输入图片描述
OK,Gaoxi-User的构建过程就配置完成了。当我们点击“立即构建”按钮时,Jenkins首先会从我们指定的Git仓库中拉取代码,然后执行Pre Step中的Maven命令,将Gaoxi-Common-Serivce-Facade打包安装到本地仓库。然后执行Build过程,将Gaoxi-User进行编译打包。
6.3 远程部署
<plugin> <groupId>org.codehaus.cargo</groupId> <artifactId>cargo-maven2-plugin</artifactId> <version>1.6.5</version> <configuration> <container> <!-- 指明使用的tomcat服务器版本 --> <containerId>tomcat8x</containerId> <type>remote</type> </container> <configuration> <type>runtime</type> <cargo.remote.username>Tomcat的用户名</cargo.remote.username> <cargo.remote.password>Tomcat的密码</cargo.remote.password> </configuration> </configuration> <executions> <execution> <phase>deploy</phase> <goals> <goal>redeploy</goal> </goals> </execution> </executions></plugin>
?
?
请点击此处输入图片描述
注意:如果你使用了chaimm/tomcat镜像,那么其中Tomcat配置都已经完成,默认用户名:admin、默认密码:jishimen2019。强烈建议修改用户名和密码。
请点击此处输入图片描述
-?WAR/EAR?files:表示你需要发布的war包-?Containers:配置目标Tomcat的用户名和密码
7. Maven的profile功能
在实际开发中,我们的系统往往有多套环境构成,如:开发环境、测试环境、预发环境、生产环境。而不同环境的配置各不相同。如果我们只有一套配置,那么当系统从一个环境迁移到另一个环境的时候,就需要通过修改代码来更换配置,这样无疑增加了工作的复杂度,而且易于出错。但好在Maven提供了profile功能,能帮助我们解决这一个问题。
<profiles> <profile> <id>dev</id> <properties> <profileActive>dev</profileActive> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <id>test</id> <properties> <profileActive>test</profileActive> </properties> </profile> <profile> <id>prod</id> <properties> <profileActive>prod</profileActive> </properties> </profile></profiles>
?
?<resources>
<resource>
<!-- 标识配置文件所在的目录 -->
<directory>src/main/resources</directory>
<filtering>true</filtering>
<!-- 构建时将这些配置文件全都排除掉 -->
<excludes>
<exclude>application.properties</exclude>
<exclude>application-dev.properties</exclude>
<exclude>application-test.properties</exclude>
<exclude>application-prod.properties</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<!-- 标识构建时所需要的配置文件 -->
<includes>
<include>application.properties</include>
<!-- ${profileActive}这个值会在maven构建时传入 -->
<include>application-${profileActive}.properties</include>
</includes>
</resource></resources>
?
?<plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> <configuration> <delimiters> <delimiter>@</delimiter> </delimiters> <useDefaultDelimiters>false</useDefaultDelimiters> </configuration></plugin>
?
?
请点击此处输入图片描述
spring.profiles.active=@profileActive@?
请点击此处输入图片描述
8.2 开发登录服务
首先需要在Gaoxi-Common-Service-Facade中创建UserService接口,并在其中声明登录的抽象函数。
?
public interface UserService { public UserEntity login(LoginReq loginReq);
}
?
? PS:为什么要将UserService放在Gaoxi-Common-Service-Facade中? 然后在Gaoxi-User中开发UserService的实现——UserServiceImpl。 UserServiceImpl上必须要加上Dubbo的@Service注解,从而告诉Dubbo,在本项目初始化的时候需要将这个类发布成一项服务,供其他系统调用。 ?@Service(version = "1.0.0")@org.springframework.stereotype.Servicepublic class UserServiceImpl implements UserService { @Autowired
private UserDAO userDAO; @Override
public UserEntity login(LoginReq loginReq) { // 校验参数
checkParam(loginReq); // 创建用户查询请求
UserQueryReq userQueryReq = buildUserQueryReq(loginReq); // 查询用户
List<UserEntity> 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和端口号,并发送调用请求。但这一切对于程序猿来说是完全透明的。 ?@RestControllerpublic class UserControllerImpl implements UserController { @Reference(version = "1.0.0")
private UserService userService;
/**
* 登录
* @param loginReq 登录请求参数
* @param httpRsp HTTP响应
* @return 登录是否成功
*/
@GetMapping("/login")
@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. 总结
总结一下,这套框架有如下优势: