本文将以原理 实战的方式,首先对“微服务”相关的概念进行知识点扫盲,然后开始手把手教你搭建一整套微服务系统。 这套微服务框架能干啥? 这套系统搭建完之后,可以实现
知识点扫盲篇 咳咳,敲黑板啦!笔记赶紧记起来,课后我要检查的!检查不合格的同学放学后留下来! 知识点 1:微服务 微服务一词近几年相当火,成为程序猿装逼热门词汇,你不对它有所了解如何在程序猿装逼圈子里混?下面我用最为通俗易懂的语言介绍它。要讲清楚微服务,我先要从一个系统架构的演进过程讲起。 单机结构 我想大家最最最熟悉的就是单机结构,一个系统业务量很小的时候所有的代码都放在一个项目中,然后这个项目部署在一台服务器上就好了。整个项目所有的服务都由这台服务器提供。这就是单机结构。 那么,单机结构有啥缺点呢?单机的处理能力毕竟有限,当你的业务增长到一定程度的时候,单机的硬件资源将无法满足你的业务需求。此时便出现了集群模式。 集群结构 集群模式在程序猿界由各种装逼解释,有的让你根本无法理解,其实就是一个很简单的玩意儿,且听我一一道来。 单机处理到达瓶颈的时候,你就把单机复制几份,这样就构成了一个“集群”。集群中每台服务器就叫做这个集群的一个“节点”,所有节点构成了一个集群。 每个节点都提供相同的服务,那么这样系统的处理能力就相当于提升了好几倍(有几个节点就相当于提升了这么多倍)。 但问题是用户的请求究竟由哪个节点来处理呢?最好能够让此时此刻负载较小的节点来处理,这样使得每个节点的压力都比较平均。 要实现这个功能,就需要在所有节点之前增加一个“调度者”的角色,用户的所有请求都先交给它,然后它根据当前所有节点的负载情况,决定将这个请求交给哪个节点处理。这个“调度者”有个牛逼的名字——负载均衡服务器。 集群结构的好处就是系统扩展非常容易。随着你们系统业务的发展,当前的系统又支撑不住了,那么给这个集群再增加节点就行了。 但是,当你的业务发展到一定程度的时候,你会发现一个问题——无论怎么增加节点,貌似整个集群性能的提升效果并不明显了。这时候,你就需要使用微服务结构了。 微服务结构 先来对前面的知识点做个总结。从单机结构到集群结构,你的代码基本无需要作任何修改,你要做的仅仅是多部署几台服务器,没台服务器上运行相同的代码就行了。 但是,当你要从集群结构演进到微服务结构的时候,之前的那套代码就需要发生较大的改动了。 所以对于新系统我们建议,系统设计之初就采用微服务架构,这样后期运维的成本更低。 但如果一套老系统需要升级成微服务结构的话,那就得对代码大动干戈了。所以,对于老系统而言,究竟是继续保持集群模式,还是升级成微服务架构,这需要你们的架构师深思熟虑、权衡投入产出比。 下面开始介绍所谓的微服务。 微服务就是将一个完整的系统,按照业务功能,拆分成一个个独立的子系统,在微服务结构中,每个子系统就被称为“服务”。这些子系统能够独立运行在 Web 容器中,它们之间通过 RPC 方式通信。 举个例子,假设需要开发一个在线商城。按照微服务的思想,我们需要按照功能模块拆分成多个独立的服务,如:用户服务、产品服务、订单服务、后台管理服务、数据分析服务等等。 这一个个服务都是一个个独立的项目,可以独立运行。如果服务之间有依赖关系,那么通过 RPC 方式调用。 这样的好处有很多
那么问题来了,当采用微服务结构后,一个完整的系统可能由很多独立的子系统组成,当业务量渐渐发展起来之后,这些子系统之间的关系将错综复杂。 而且为了能够针对性地增加某些服务的处理能力,某些服务的背后可能是一个集群模式,由多个节点构成,这无疑大大增加了运维的难度。 微服务的想法好是好,但开发、运维的复杂度实在是太高。为了解决这些问题,阿里巴巴的 Dubbo 就横空出世了。 知识点 2:Dubbo Dubbo 是一套微服务系统的协调者,在它这套体系中,一共有三种角色
你在使用的时候需要将 Dubbo 的 jar 包引入到你的项目中,也就是每个服务都要引入 Dubbo 的 jar 包。 然后当这些服务初始化的时候,Dubbo 就会将当前系统需要发布的服务、以及当前系统的 IP 和端口号发送给注册中心,注册中心将其记录下来。这就是服务发布的过程。 与此同时,也是在系统初始化的时候,Dubbo 还会扫描一下当前系统所需要引用的服务,然后向注册中心请求这些服务所在的 IP 和端口号。 接下来系统就可以正常运行了。当系统 A 需要调用系统 B 的服务的时候,A 就会与 B 建立起一条 RPC 信道,然后再调用 B 系统上相应的服务。 这,就是 Dubbo 的作用。 知识点 3:容器化部署 当我们使用了微服务架构后,我们将一个原本完整的系统,按照业务逻辑拆分成一个个可独立运行的子系统。 为了降低系统间的耦合度,我们希望这些子系统能够运行在独立的环境中,这些环境之间能够相互隔离。 在 Docker 出现之前,若使用虚拟机来实现运行环境的相互隔离的话成本较高,虚拟机会消耗较多的计算机硬件/软件资源。 Docker 不仅能够实现运行环境的隔离,而且能极大程度的节约计算机资源,它成为一种轻量级的“虚拟机”。 知识点 4:自动化构建 当我们使用微服务架构后,随着业务的逐渐发展,系统之间的依赖关系会日益复杂,而且各个模块的构建顺序都有所讲究。 对于一个小型系统来说,也许只有几个模块,那么你每次采用人肉构建的方式也许并不感觉麻烦。 但随着系统业务的发展,你的系统之间的依赖关系日益复杂,子系统也逐渐增多,每次构建一下你都要非常小心谨慎,稍有不慎整个服务都无法正常启动。 而且这些构建的工作很 Low,却需要消耗大量的精力,这无疑降低了开发的效率。不过没关系,Jenkins 就是来帮助你解决这个问题的。 我们只需在 Jenkins 中配置好代码仓库、各个模块的构建顺序和构建命令,在以后的构建中,只需要点击“立即构建”按钮,Jenkins 就会自动到你的代码仓库中拉取最新的代码,然后根据你事先配置的构建命令进行构建,最后发布到指定的容器中运行。 你也可以让 Jenkins 定时检查代码仓库版本的变化,一旦发现变动就自动地开始构建过程,并且让 Jenkins 在构建成功后给你发一封邮件。这样你连“立即构建”的按钮也不需要按,就能全自动地完成这一切构建过程。 实战动手篇 学习目标 接下来我会带着大家,以一个在线商城为例,搭建一套能够自动化部署的微服务框架。 这个框架能做如下几件事情
项目背景介绍 本文我以一个在线商城作为例子,一步步教大家如何搭建微服务框架,它有如下功能
注意本文的 IDE 使用的是 intelliJ IDEA,推荐大家也用这个,用了都说好,用了你就会爱上它。 创建项目的组织结构 在动手之前,我先来说一说这一步的目标
每个自模块都是一个独立的 Spring Boot 项目
下面开始动手。 创建 Project 首先 New 一个 Project,如下图 然后选择 Spring Initializr,如下图所示 设置 groupId、artifactId、version,代码如下 <groupId>com.gaoxi</groupId> Project 创建完毕!接下来在 Project 下面创建 Module。 创建 Module 在 Project 上 New Module,如下图 和刚才一样,选择 Spring Initializr,设置 groupId、artifactId、version。 依次创建好所有的 Module,如下图所示 构建模块的依赖关系 目前为止,模块之间没有任何联系,下面我们要通过 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 的子模块。这样子模块就可以从父模块中继承所有的依赖,而不需要自己再配置了。 下面开始动手 首先将 Common-Service-Facade 的打包方式设成 jar。 当打包这个模块的时候,Maven 会将它打包成 jar,并安装在本地仓库中。这样其他模块打包的时候就可以引用这个 jar。 <groupId>com.gaoxi</groupId> 将其他模块的打包方式设为 war。除了 Gaoxi-Common-Service-Facade 外,其他模块都是一个个可独立运行的子系统,需要在 Web 容器中运行,所以我们需要将这些模块的打包方式设成 war。 <groupId>com.gaoxi</groupId> 在总 pom 中指定子模块。modules 标签指定了当前模块的子模块是谁,但是仅在父模块的 pom 文件中指定子模块还不够,还需要在子模块的 pom 文件中指定父模块是谁。 <modules> 在子模块中指定父模块。 <parent> 到此为止,模块的依赖关系配置完毕!但要注意模块打包的顺序。 由于所有模块都依赖于 Gaoxi-Common-Servie-Facade 模块,因此在构建模块时,首先需要编译、打包、安装 Gaoxi-Common-Servie-Facade,将它打包进本地仓库中,这样上层模块才能引用到。当该模块安装完毕后,再构建上层模块。 否则在构建上层模块的时候会出现找不到 Gaoxi-Common-Servie-Facade 中类库的问题。 在父模块的 pom 中添加所有子模块公用的依赖<dependencies> 当父模块的 pom 中配置了公用依赖后,子模块的 pom 文件将非常简洁,如下所示 <groupId>com.gaoxi</groupId> 当项目的结构搭建完成之后,接下来你需要配置 Docker 环境,并将这些项目打包进容器中,验证下是否能正常启动。 创建 Docker 容器 安装 Docker 在使用 Docker 之前,你当然先要安装 Docker,安装过程较为简单,基本上就是傻瓜式操作,这里就不作过多介绍了,你可以在 Docker 的官网下载相应系统的安装包。 获取 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,推荐大家拉取这个。 创建 Tomcat 容器 这里再简单介绍下“镜像”和“容器”的关系。 “镜像”就好比是面向对象中的“类”,“容器”就好比“类”创建的“对象”。 在面向对象中,“类”定义了各种属性,“类”可以实例化出多个“对象”;而在 Docker 中,“镜像”定义了各种配置信息,它可以实例化出多个“容器”。“容器”就是一台可以运行的“虚拟机”。 接下来我们需要为所有的微服务创建各自的容器
以创建 gaoxi-user 容器为例,采用如下命令创建容器 docker run --name gaoxi-user-1 -p 8082:8080 -v /usr/web/gaoxi-log:/opt/tomcat/gaoxi-log chaimm/tomcat:1.1
这条命令执行成功后,你就可以通过你的 IP:8082 访问到 gaoxi-user-1 容器的 Tomcat 了。如果你看到了那只眼熟的猫,那就说明容器启动成功了! 接下来,你需要按照上面的方法,给剩下几个系统创建好 Tomcat 容器。 注意点你需要给这些 Tomcat 容器指定不同的端口号,防止端口号冲突。当然,在实际开发中,你并不需要将容器的 8080 端口映射到宿主机上,这里仅仅是为了验证容器是否启动成功才这么做的。 整合 Dubbo 创建 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
启动成功后,你就可以通过你的 IP:10000/dubbo-admin-2.8.4/ 访问到 Dubbo-Admin,如下图所示 父 POM 文件中引入 Dubbo 依赖 <!-- Spring Boot Dubbo 依赖 --> 发布服务 假设,我们需要将 Gaoxi-User 项目中的 UserService 发布成一项 RPC 服务,供其他系统远程调用,那么我们究竟该如何借助 Dubbo 来实现这一功能呢? 在 Gaoxi-Common-Service-Facade 中定义 UserService 的接口。 由于服务的发布和引用都依赖于接口,但服务的发布方和引用方在微服务架构中往往不在同一个系统中,所以需要将发布和引用的接口放在公共类库中,从而双方都能够引用。 接口如下所示 public interface UserService { 在 Gaoxi-User 中定义接口的实现。在实现类上需要加上 Dubbo 的 @Service 注解,从而 Dubbo 会在项目启动的时候扫描到该注解,将它发布成一项 RPC 服务。 @Service(version = '1.0.0') 在 Gaoxi-User 的 application.properties 中配置服务提供者的信息。 spring.dubbo.application.name=user-provider # 本服务的名称 按照上面配置完成后,当 Gaoxi-User 系统初始化的时候,就会扫描 spring.dubbo.scan 所指定的路径下的 @Service 注解,该注解标识了需要发布成 RPC 服务的类。 Dubbo 会将这些类的接口信息 本服务器的 IP spring.dubbo.protocol.port 所指定的端口号发送给 ZooKeeper,Zookeeper 会将这些信息存储起来。 这就是服务发布的过程,下面来看如何引用一项 RPC 服务。 引用服务 假设,Gaoxi-Controller 需要调用 Gaoxi-User 提供的登录功能,此时它就需要引用 UserService 这项远程服务。下面来介绍服务引用的方法。 声明需要引用的服务。引用服务非常简单,你只需要在引用的类中声明一项服务,然后用 @Reference 标识,如下所示 @RestController 在 Gaoxi-Controller 的 application.properties 中配置服务消费者的信息。 spring.dubbo.application.name=controller-consumer # 本服务的名称 上述操作完成后,当 Gaoxi-Controller 初始化的时候,Dubbo 就会扫描 spring.dubbo.scan 所指定的路径,并找到所有被 @Reference 修饰的成员变量;然后向 Zookeeper 请求该服务所在的 IP 和端口号。 当调用 userService.login() 的时候,Dubbo 就会向 Gaoxi-User 发起请求,完成调用的过程。 这个调用过程是一次 RPC 调用,但作为程序猿来说,这和调用一个本地函数没有任何区别,远程调用的一切都由 Dubbo 来帮你完成。这就是 Dubbo 的作用。 自动化构建 Jenkins 是一个自动化构建工具,它可以帮助我们摆脱繁琐的部署过程,我们只需要在一开始配置好构建策略,以后部署只需要一键完成。 创建 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 会带着你进行一系列的初始化设置,你只要跟着它一步步走就行了,比较傻瓜式。 在 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。 远程部署 首先你需要下载 Deploy Plugin 插件。 下载地址:https://wiki./display/JENKINS/Deploy Plugin 安装插件,在系统管理–>插件管理–>高级上传 deploy.hpi 进行安装。 在父项目的 pom 文件中增加远程部署插件 <plugin> 为 Tomcat 设置用户名和密码,修改 gaoxi-user 容器中 Tomcat 的 tomcat-users.xml 文件,增加 Tomcat 的 manager 用户。 注意如果你使用了 chaimm/tomcat 镜像,那么其中 Tomcat 配置都已经完成,默认用户名:admin、默认密码:jishimen2019。强烈建议修改用户名和密码。 修改 Jenkins 中 gaoxi-user 的配置,在“构建后操作”中增加如下配置
Maven 的 profile 功能 在实际开发中,我们的系统往往有多套环境构成,如:开发环境、测试环境、预发环境、生产环境。而不同环境的配置各不相同。 如果我们只有一套配置,那么当系统从一个环境迁移到另一个环境的时候,就需要通过修改代码来更换配置,这样无疑增加了工作的复杂度,而且易于出错。但好在 Maven 提供了 profile 功能,能帮助我们解决这一个问题。 父项目的 pom 中添加 profile 元素,首先,我们需要在总 pom 的中添加多套环境的信息,如下所示 <profiles> 父项目的 pom 中添加 resource 元素,resource 标识了不同环境下需要打包哪些配置文件。 <resources> 父项目的 pom 中添加插件 maven-resources-plugin,该插件用来在 Maven 构建时参数替换。 <plugin> 在子项目中创建配置,分别为 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 文件。 开发流程 到此为止,所有准备工作都已经完成,接下来就可以进入代码开发阶段。下面我以一个例子,带着大家感受下有了这套微服务框架后,我们的开发流程究竟有了哪些改变? 下面以开发一个用户登录功能为例,介绍下使用本框架之后开发的流程。 开发目标 有如下两个开发目标
开发登录服务 首先需要在 Gaoxi-Common-Service-Facade 中创建 UserService 接口,并在其中声明登录的抽象函数。 public interface UserService { 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') 引用登录服务 当 UserService 开发完毕后,接下来 Gaoxi-Controller 需要引用该服务,并向前端提供一个登录的 REST 接口。 若要使用 userService 中的函数,仅需要在 userService 上添加 @Reference 注解,然后就像调用本地函数一样使用 userService Dubbo 会帮你找到 UserService 服务所在的 IP 和端口号,并发送调用请求。但这一切对于程序猿来说是完全透明的。 @RestController 自动构建服务 上面的代码完成后,你需要将代码提交至你的 Git 仓库,接下来就是自动化部署的过程了。 你需要进入 Jenkins,由于刚才修改了 Gaoxi-User 和 Gaoxi-Controller 的代码,因此你需要分别构建这两个项目。 接下来 Jenkins 会自动从你的 Git 仓库中拉取最新的代码,然后依次执行 Pre Step、Build、构建后操作的过程。 由于我们在 Pre Step 中设置了编译 Gaoxi-Common-Service-Facade,因此 Jenkins 首先会将其安装到本地仓库;然后再执行 Build 过程,构建 Gaoxi-User,并将其打包成 war 包。 最后将执行“构建后操作”,将 war 包发布到相应的 Tomcat 容器中。 至此,整个发布流程完毕! 查看服务的状态 当 Jenkins 构建完成后,我们可以登录 Dubbo-Admin 查看服务发布和引用的状态。 当我们搜索 UserService 服务后,可以看到,该服务的提供者已经成功发布了服务 点击“消费者”我们可以看到,该服务已经被 controller-consumer 成功订阅 总结 总结一下,这套框架有如下优势
作者:大闲人柴毛毛 |
|
来自: 昵称48052010 > 《待分类》