最近微服务很火,不管去面试也好还是实际工作中,大家多多少少会用到,微服务的框架也有很多(目前我知道的就有Dubbo、spring cloud、Motan),但使用比较多的个人感觉还是spring cloud这套,趁着最近有时间我把微服务搭建的整个过程给写下来供自己后续参考也算是把一些坑都给大家先挖出来,避免大家在搭建的时候遇到重复的问题。
在这之前先简单的聊聊微服务和传统的单体服务的一个区别:
单体服务:一个war包里面包含了所有的功能模块、有的甚至前后台都写在一起了,时间越久,工程越发的臃肿(我有碰到过一个工程启动要一个小时的,我当时内心都崩溃了。。),扩展性不好、不太灵活、持续交付很差,如果只是做个很简单的工程,为了快速完成任务那还是很适合单体的。
微服务:其实就把以前单体服务里面的功能进行拆分,然后各自组建服务,当然不是要你们把每个功能都拆成一个服务,这个拆分服务是根据当前的业务功能进行拆分的。微服务的有点在于职责单一,每个服务负责该功能一个单独的部分,每个服务都可以单独部署及重新部署而不影响整个系统,各个微服务之间是松耦合的,服务之间是通过REST API来进行通信的。
正式开工之前我先介绍下等会我们会用到的几个spring cloud 组件说明:
Eureka:服务注册中心,提供服务注册与发现的功能,服务端提供服务注册,当客户端服务启动的时候,会主动向服务端进行注册,服务端会存储所有已经注册服务节点信息。服务端会管理这些节点信息,并且会将异常的节点移除服务列表,服务之间的相互调用由注册中心来统一管理。
Gateway:服务网关,外部请求唯一的入口,可以对所有请求进来的服务做统一的策略管理,提供统一的路由方式,并且提供了基本的功能,例如:安全,监控/埋点,和限流等。
Spring boot:一个开箱即用的框架,相比较以前的Spirng mvc要配置各种xml文件,Spirng boot按照约定大于配置,简化spring的xml配置流程,做到自动化配置真正的开箱即用(下面你们会看到spring boot只需要配置几个参数,都不需要部署到tomcat容器里面即可启动,简直太爽了)。
版本组件:
组件名称 | 版本 | 备注 |
spring-cloud-dependencies | Hoxton.SR4 | spirng cloud依赖 |
spring-boot-dependencies | 2.3.0.RELEASE | spring boot依赖 |
spring-cloud-starter-netflix-eureka-server | 2.2.2.RELEASE | eureka服务依赖 |
spring-cloud-starter-netflix-eureka-client | 2.2.2.RELEASE | eureka客户端依赖 |
spring-cloud-starter-gateway | 2.2.2.RELEASE | 网关依赖 |
spring-boot-starter-actuator | 2.3.0.RELEASE | spring监控依赖 |
spring-boot-starter-web | 2.3.0.RELEASE | WEB依赖 |
spring-boot-devtools | 2.3.0.RELEASE | 热部署依赖 |
正所谓工欲善其事,必先利其器 ,我们正式搭建微服务之间先把环境弄弄,环境都准备好的小伙伴就直接跳过这段。
很多小伙伴可能都是用的公司电脑,公司的电脑怎么说呢,很多的时候都帮大家把开发的环境都整理好了,就只需要写写代码就行了,但实际要自己重新去弄的时候发现本地环境各种跑不通,所以我这边正好趁着早几天把电脑重装干脆就从0开始弄吧。
第一步安装 java的IDE工具,这个没什么好说的,萝卜青菜各有所爱,我就不评价了(笔者用的是Eclipse IDE,用习惯了,懒得换)。
第二部安装jdk,去官网下载自己需要的版本(https://www.oracle.com/java/technologies/javase-downloads.html),下载完成之后直接安装,安装好了之后开始配置环境变量,右键“计算机-属性-高级系统设置”
点击环境变量,点击新建:变量名:“JAVA_HOME”,变量值:“D:\Program Files\Java\jdk1.8.0_202(刚刚安装好jdk的路劲)”,在找到变量名:“Path”,在变量值里面添加:“%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;”。弄完之后打开cmd命令窗口输入“java -version”能显示出版本就证明环境配置ok。
第三步开始配置maven,直接在官网下载(https://maven.apache.org/download.cgi),下载好了之后解压出来先配置环境变量,同jdk配置一样,在环境变量里面新增:变量名:“MAVEN_HOME”,变量值:“D:\Program Files\apache-maven-3.2.1(maven解压路劲)”,然后在“Path”变量名添加:“%MAVEN_HOME%\bin\;”。弄完之后打开cmd命令窗口输入“mvn -v”能显示出版本就证明环境配置ok。
maven环境变量完成之后需要在apache-maven-3.2.1\conf下面找到settings文件打开编辑,配置本地jar包存储的地址路劲(不配置也没问题,默认地址:Default: ${user.home}/.m2/repository)
我们这里使用的是阿里的远程仓库。
第四步我们还需要在eclipse里面配置下maven和jdk
jdk的配置简单,我就直接上图了
点击“Window-pareferences”输入“jdk”
点击director选择jdk安装的目录,最后在点击finish
选择自己的jdk,最后保存并关闭,eclipse与jdk的配置就完成了。
我们在来配置下eclipse与maven,同样的操作Window-pareferences”输入“maven”
选择添加自己的maven之后在user Settings里面配置好自己的settings文件。
至此我们的环境、工具都准备好了,开始进入spring cloud的搭建(中途会用到一些插件,至于插件怎么下载怎么安装笔者也会一步一步教大家的)
万里长城要开始第一步操作了,这里会快速告诉大家怎么搭建spring cloud微服务,很多细节可能不会讲那么明白,不过没关系,笔者会在后面单独开篇章单独介绍,这里更多可能是教会大家搭建,然后用起来(毕竟spring cloud家族那么多东西,我不可能一口气都讲完,而且很多笔者也可能没用到!)。
工程采取maven父子模块的方式进行搭建,所以大家在第一步直接在ide里面创建一个maven project工程
这里的packging要选择pom,不要搞错了,然后artifact Id是你的工程名,group Id是唯一标识名称
完成之后在pom文件里面新增spring boot和spring cloud 的依赖声明,spring boot和spirng cloud对应的版本大家可以直接上官网查看(地址:https://spring.io/projects/spring-cloud/)
话不多说,直接上最新最稳定的版本吧
org.springframework.cloud
spring-cloud-dependencies
Hoxton.SR4
pom
import
org.springframework.boot
spring-boot-dependencies
2.3.0.RELEASE
pom
import
在父POM定义spring cloud和spring boot的版本之后我们开始创建子工程(在这个之前我建议大家在IDE里面安装spring boot的插件)
eclipse的话直接在help-eclipse marketplace里面选择popular就能看到我上面的截图了,然后在线下载,下载好了之后重启eclipse就可以用啦。其它请参考官网(https://spring.io/tools)
先创建“Eureka”服务于注册中心,这个主要是用来治理服务的,说的通俗点就是几个Eureka在一起搞了个大家庭,然后服务主动往上面进行注册,告知eureka好了,我的身份id是什么,以后别人也注册上来想来我家串门你就告诉他我的身份id就行了,当然Eureka还有其它功能,等用到了在说。
在刚刚创建好的父工程右键创建maven module
点击next,输入自己的工程名,勾上create a simple project
再次点击下一步,记得packaging要选择jar或者war,我们这里选择jar包的形式,直接finish
创建好了之后我们spring-eureka这个工程的pom添加eureka-server的引用。
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.boot
spring-boot-devtools
在工程里面创建EurekaClientApplication类,@EnableEurekaServer这个注解是开启eureka服务的
package sh.cn.com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
在添加application.yml的配置文件,配置的类容如下,弄完之后我们在EurekaClientApplication里面直接运行main方法启动(原理请小伙伴们自行百度)
#服务注册中心端口号
server:
port: 9002
#服务注册中心实例的主机名
spring:
application:
name: spring-eureka-jq
eureka:
instance:
hostname: localhost
client:
#是否向服务注册中心注册自己
register-with-eureka: true
#是否检索服务
fetch-registry: false
service-url:
#服务注册中心的配置内容,指定服务注册中心的位置
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
在浏览器输入http://localhost:9002/,看到下面这个界面。
ok.第一步算是跨出去了,但是生产肯定不能这么搞,就一台eureka,如果挂了,那服务不都挂了。。。所以我们要在新建个eureka2,然后这2个集群,让他们变成高可用服务注册中心,前面的步骤就不说了,大家在自己新建一个eureka,然后修改下application.yml配置的 defaultZone
eureka1的application.yml
#服务注册中心端口号
server:
port: 9001
#服务注册中心实例的主机名
spring:
application:
name: spring-eureka
eureka:
instance:
hostname: spring-eureka
client:
#是否向服务注册中心注册自己
register-with-eureka: true
#是否检索服务
fetch-registry: false
service-url:
#服务注册中心的配置内容,指定服务注册中心的位置
defaultZone: http://localhost:9001/eureka/,http://localhost:9002/eureka/
eureka2的application.yml
#服务注册中心端口号
server:
port: 9002
#服务注册中心实例的主机名
spring:
application:
name: spring-eureka-jq
eureka:
instance:
hostname: spring-eureka
client:
#是否向服务注册中心注册自己
register-with-eureka: true
#是否检索服务
fetch-registry: false
service-url:
#服务注册中心的配置内容,指定服务注册中心的位置
defaultZone: http://localhost:9001/eureka/,http://localhost:9002/eureka/
然后访问http://localhost:9002/或者http://localhost:9001/就可以看到2个eureka互相注册达到了高可用的地步,就算其中一个挂了,另外一个也可以用。(单独启动第一个的时候会报另外一个连接不上,这个是正常的,等另外一个启动之后就不会在出现连接不上的问题了)
接下来开始创建网关gateway,还是一样的新建一个子工程spring-gateway,在pom文件里面引入gateway的依赖和eureka的客服端,等会要注册到eureka里面去
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-devtools
gateway的application.yml如下
server:
port: 9003
spring:
cloud:
gateway:
discovery:
locator:
#访问serviceId用小写进行配置,默认是false,需要大写
lower-case-service-id: true
#根据服务在eureka的serviceId,来将请求路由到不同的服务,默认是false
enabled: true
application:
name: spring-gateway
eureka:
client:
service-url:
defaultZone: http://localhost:9001/eureka/
还是一样的,在sh.cn.com的包下新建一个GatewayServiceApplication类,代码如下,然后启动程序
package sh.cn.com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class GatewayServiceApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServiceApplication.class, args);
}
}
在eureka界面看看,就会发现网关已经注册进来了
网关的东西比较多,责任也比较大,毕竟第一道门就是网关在把守,我这里就简单的创建一个,等会演示其它工程通过网关怎么去访问微服务,其它的我后面单独写这个网关,这章主要的目的还是快速搭建整套spring cloud。
现在大家的目录结构应该是这样的
我们接下来创建A和B2个服务,目录结构如下
我们在A和B服务的pom里面添加依赖,具体包的作用就不解释了,我这边写注释,自己看,不懂百度。
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
A和B的application.yml如下
server:
port: 9004
spring:
application:
name: spring-service-a
eureka:
client:
service-url:
defaultZone: http://localhost:9001/eureka/
server:
port: 9005
spring:
application:
name: spring-service-b
eureka:
client:
service-url:
defaultZone: http://localhost:9001/eureka/
我们在serviceA里面添加RestTemplate的配置
package sh.cn.com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
public class SpringServiceAApplication {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory){
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(15000);
factory.setReadTimeout(5000);
return factory;
}
public static void main(String[] args) {
SpringApplication.run(SpringServiceAApplication.class, args);
}
}
然后在A和B写个ControllerA和ControllerB
ControllerA的代码:
package sh.cn.com.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("spring")
public class ControllerA {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("queryServiceA")
public String queryServiceA() {
String str = restTemplate.postForObject("http://usermic-lir5kbp:9005/spring/queryServiceB", null, String.class);
return str;
}
}
ControllerB的代码:
package sh.cn.com.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("spring")
public class ControllerB {
@RequestMapping("queryServiceB")
public String queryServiceB() {
return "我是ServiceB";
}
}
目录结构:
把eureka、serviceA和serviceB都启动,打开eureka界面如下:
访问http://usermic-lir5kbp:9004/spring/queryServiceA
我们现在把网关也开启,其它外部工程通过网关的地址进行访问:http://usermic-lir5kbp:9003/spring-service-a/spring/queryServiceA
到这里就差不多完成了,接下来我单独说下微服务之间是怎么用“feign”来进行通信、怎么使用spring cloud config配置中心来管理配置(这里还会把携程研发的apollo配置中心一起讲讲)、在高并发的情况下用熔断(Hystrix)这些都单独写出来。
最后我把相关的pom文件都贴出来
spirng-colud-demo(父pom文件内容)
4.0.0
sh.pd.com
spirng-colud-demo
0.0.1-SNAPSHOT
pom
spring-eureka
spring-gateway
spring-eureka-jq
spring-service-A
spring-service-B
org.springframework.cloud
spring-cloud-dependencies
Hoxton.SR4
pom
import
org.springframework.boot
spring-boot-dependencies
2.3.0.RELEASE
pom
import
org.springframework.boot
spring-boot-maven-plugin
2.3.0.RELEASE
repackage
spring-eureka(子pom文件)
4.0.0
sh.pd.com
spirng-colud-demo
0.0.1-SNAPSHOT
spring-eureka
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.boot
spring-boot-devtools
spring-eureka-jq(子pom文件)
4.0.0
sh.pd.com
spirng-colud-demo
0.0.1-SNAPSHOT
spring-eureka-jq
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.boot
spring-boot-devtools
spring-gateway(子pom文件)
4.0.0
sh.pd.com
spirng-colud-demo
0.0.1-SNAPSHOT
spring-gateway
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-devtools
org.springframework.boot
spring-boot-maven-plugin
spring-service-A(子pom文件)
4.0.0
sh.pd.com
spirng-colud-demo
0.0.1-SNAPSHOT
spring-service-A
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
spring-service-B(子pom文件)
4.0.0
sh.pd.com
spirng-colud-demo
0.0.1-SNAPSHOT
spring-service-B
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
至此所有的步骤全部完成了。