目录
先大声BB几句
项目源码地址及导入进IDEA时可能会遇到的问题
第一步——创建maven项目
第二步——搭建服务注册中心
第三步——搭建服务提供者
第四步——搭建第二个服务提供者
第五步——搭建Gateway网关
第六步——在网关中加入熔断器Hystrix
小结
首先告诉大家,这不只是一篇简单的教程!!!
首先告诉大家,这不只是一篇简单的教程!!!
首先告诉大家,这不只是一篇简单的教程!!!
重要的事说三遍,为什么这么说呢?因为笔者在这篇文章里的示例,可以说是一个简单的微服务开发脚手架,你可以存起来,做微服务项目的时候把这个架子拉过去,直接用,节省你搭项目的时间。虽然说是简单的脚手架,但是也满足了大部分的开发场景,因为在这篇文章的示例中,我集成了Gateway网关、Hystrix熔断器、Eureka注册中心、两个服务提供者(达到了负载均衡的效果,可水平扩展)。
可能有的人会说,你这东西也太少了,像什么OAuth 2.0、Redis、MQ、ElasticSearch等等都没有集成。呃,我想说两点,第一点就是这篇文章的定位还是一篇初级教程,重点是带领对Spring Cloud零基础的朋友搭建微服务开发环境;第二点是,没有哪个框架可以完全满足项目的架构,只能最大限度的去搭建一个可以共用的架子,至于里面具体的需求,需要你去实现定制化开发,去填充这个架子。
源码请从百度网盘下载,无提取码,永久有效:
https://pan.baidu.com/s/173Ahef8-fMz9ncBd0vOShA
因为我的项目是一个大的maven项目下保护几个小的maven项目,所以导入IDEA时需要动点手脚,你导入进去可能是这样的:
它只识别了最外层的maven项目,并没有把里面的几个小项目当作maven项目处理 ,怎么办?看下面几张图:
在弹出的页面里选中这几个小项目的pom.xml文件,点击OK即可:
我们只是学习而非生成环境,没必要弄好几台机器去模拟,直接在本地创建几个项目就好。
同时又为了不打开好多个IDEA窗口,达到省事、看着心不烦的效果,我们先创建一个大的maven空项目,再在里面去添加若干个子项目。
首先我们点击新建项目:
然后选择Spring Initializr(这里提一句,IDEA是最好的Java开发工具!):
然后创建项目的maven坐标:
小声BB:我这里的意思是,com.blog.wang说明这是我的一个博客项目(本人姓王),cloud说明这是一个Spring Cloud项目,你们没必要按着我的来,它就是一个项目名而已。
然后选择依赖,因为我们目前只是创建一个项目文件夹,跳过,直接下一步:
然后设置项目的文件夹名称和路径,名称一般默认即可,路径自选:
然后等待IDEA加载项目,加载完成之后是如下效果:
由于我们这个项目文件夹并不保存实质的代码,只是作为包装其他子项目的容器,删除没用的东西,最终效果如下:
没错,你没看错,src都不要!就剩这三个就行!
微服务架构下,我们将项目按照功能或者业务拆分为若干个小的服务,那么这些服务应该如何进行管理?这就需要一个服务注册中心,可以把它理解为一个管理员,我们将所有的服务注册到这个管理员,由它去进行维护和管理。
注册到注册中心的每一个服务,都会有一个服务名称,如果出现了相同的服务名称,则认为是两个相同的服务,那么当客户端在调用这些服务的时候,会自动以负载均衡的方式去调用,将压力分担到不同的服务上,功能上类似于Nginx。此外,注册中心和注册中心之间也可以相互注册,达到高可用的服务集群效果。
服务注册中心有很多种,Dubbo,Eureka,Nacos……,本篇文章采用较简单的Eureka来搭建注册中心,在以后的博文中会陆续介绍其他的几个注册中心。
废话不多说,开始干!
首先,选中cloud文件夹,右键,new,module:
然后是熟悉的场景,不用多说了吧?下一步就好:
然后设置maven坐标:
这里的意思是,表面这个子项目是一个注册中心
然后选择依赖:
因为我们这是一个Eureka的服务注册中心,所以我们要选中这个依赖。
然后到了这一步,默认就好,点击完成:
然后等IDEA加载项目,加载完毕之后的效果:
然后我们对服务注册中心做一下配置,配置什么呢?注册中心在启动时,会寻找另一个注册中心进行注册,但是因为我们这是单机项目,不是集群式的,没必要搭建其他注册中心,所以我们要让这个注册中心扛起大旗,独立起来,做一个成熟的注册中心。也就是说,我们要禁止它注册它自己(另外,我把配置文件的格式改成.yml了):
# 端口号为8080(默认也是8080)
server:
port: 8080
# 禁止注册自己
eureka:
client:
register-with-eureka: false
fetch-registry: false
然后我们在启动类上加@EnableEurekaServer注解,表示将这个项目作为 服务注册中心:
然后我们启动项目(注意application.yml文件的编码格式需要是UTF-8):
另外,这里可能有坑,如果你出现了以下错误:
Caused by: java.nio.charset.MalformedInputException: Input length = 1
说明你的application.yml文件的编码格式不是UTF-8!!!解决办法:
然后把application.yml文件删了,重新创建,内容复制一遍……我也没找到什么更好的办法。。。
如果启动成功,如下:
我们打开浏览器,访问8080,可以看到以下页面,说明注册中心已经启动成功了,只不过没有任何服务注册进来:
到了这一步,服务提供者,往往就是我们项目里真正要提供API接口的部分,也就是我们的业务代码。
由于我们这里只是示例,所以不牵扯数据库,controller层直接返回模拟数据。
步骤跟上边创建注册中心一样,只贴出几个不一样的地方。
我们把项目叫做cloud-biz-1,表示这是一个服务提供者端点:
这里主要选择以下几个依赖:
Lombok,可以简化我们的开发,最主要的用途是省略getter和setter方法,减少代码篇幅。
另外,要在IDEA使用Lombok插件,需要进行安装,安装步骤:
Spring Web,选了这个依赖,我们的项目就具有提供web服务的能力了,主要是提供controller控制器。
Eureka Discovery Client,可以使我们的项目拥有注册到Eureka服务中心的能力。
选了以上3个依赖,我们直接点击下一步,再点击完成,IDEA加载项目完毕之后:
然后,我们创建一个实体类和一个Controller控制器:
package com.blog.wang.cloudbiz1.entity;
import lombok.Data;
/**
*
* 学生实体类
*
*
* @author 秋枫艳梦
* @date 2019-12-08
* */
@Data
public class StudentEntity {
//姓名
private String name;
//年龄
private int age;
}
package com.blog.wang.cloudbiz1.controller;
import com.blog.wang.cloudbiz1.entity.StudentEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* 学生模块,控制器
*
*
* @author 秋枫艳梦
* @date 2019-12-08
* */
@RestController
@RequestMapping(value = "/student")
public class StudentController {
/**
* 获取一个学生
* @return JSON对象
* */
@GetMapping(value = "/detail")
public Object getStudent () {
StudentEntity student = new StudentEntity();
//可以看到,我们没有在实体类写get和set方法,但是我们依然可以调用这些方法
//这就是Lombok插件的好处,只需要在实体类加一个@Data注解
student.setName("张三");
student.setAge(18);
return student;
}
}
然后我们修改一下这个项目的application.yml:
server:
port: 8081
# 应用名称:client
spring:
application:
name: client
# 注册到我们本地的eureka注册中心,端口是8080
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka/
然后在启动类上加@EnableEurekaClient注解,表面这是一个服务提供者:
然后启动这个服务提供者,成功之后刷新注册中心页面,可以看到名叫client的服务已经注册进来(只有一个节点):
我们访问localhost:8081/student/list,如下:
可能有同学要说了,你不是都微服务了吗,怎么还是直接调8081端口?为什么不走服务中心啊?这样还不如我直接创建一个Spring Boot项目呢?
我们先不通过服务中心调接口,因为服务注册中心是根据服务名称去选择要进行业务处理的服务器,而通过服务名称调用接口,要么走Fegin,Ribbon的RestTemplate,这些都是需要用Java代码实现的,适用于系统内部之间的调用,不适用于直接访问的情况。如果你想直接以API接口的方式访问服务注册中心的服务并且达到负载均衡的效果,只有通过网关。网关的内容在下面,先不着急。
为了实现通过网关访问API接口,并达到负载均衡的效果,我们需要创建第二个服务节点,步骤和上面的一样,我就不细说了,唯一不一样的地方就是项目名称叫cloud-biz-2,里面的任何东西都一样,你可以创建完之后从cloud-biz-1复制一份。
但是请注意,有以下两点不一样的地方。
第一个不一样的地方是服务的端口号是8082,不能冲突:
第二个地方是controller里返回的数据不一样,因为我们要测试负载均衡吗,需要验证访问的是不同的服务器:
在启动类加上@EnableEurekaClient注解之后,启动项目,可以发现服务注册中心有两个client服务节点:
访问8082,没毛病:
网关,往往是我们真正暴露给用户进行访问的,用户调用网关,网关再去调用内部的各个服务。这样有什么好处呢?
第一,减轻业务服务器的压力。有人要说了,你特么只是通过网关转发了一下,最后不还是请求到了业务服务器么?怎么就减轻压力了?同学你想想,假如你的系统里对每一个API接口都有权限验证,如果没有网关的话,权限验证这件事也需要在业务服务器上进行处理,是不是所有请求都直接落在了你的业务服务器?但是如果你加一个网关,你就可以在网关层过滤一些请求,保证只有合法、有效的请求才落实到业务服务器,这样一来,权限验证这个事就从业务服务器转移到了网关服务器,难道不是减轻压力吗?
第二,保护业务服务器的安全。在部署这些微服务时,我们可以将所有的服务都部署在一个局域网里,网关和服务之间只通过内网调用,只把网关暴露出去,这样一来,即使网关的域名或者地址被攻破了,你的业务服务器还是安全的。此外,网关还可以在大量流量突然涌入时,做限流,一样保护了业务服务器的安全。
第三,可以配合Nginx搭建高可用的服务。在一个微服务系统中,往往一个网关是不够的,为什么?假如你的业务服务器一共有10个节点,但是你又不想暴露出去,只想把网关暴露给用户,那么所有的流量压力其实都到了网关身上,这时候我们可以注册多个网关,在网关的上层再去加一个Nginx,由Nginx转发到网关集群,再由不同的网关去转发到不同的业务节点,达到高可用、高性能的服务器。
话不多说,接下来我们就开始。这里采用目前比较流行的Gateway网关,这是Spring Cloud团队自己研发的网关,功能很强大,由于篇幅原因,此处博主就带领大家体验基本的功能。
依然是创建子项目,跟上边创建服务提供者一样,我就不一一罗列了,只贴出不一样的地方:
首先项目名称叫cloud-gateway,不贴图了。
然后是依赖:
在网关项目的配置文件添加如下内容:
server:
#网关端口8083
port: 8083
spring:
application:
#在服务中心的应用名称
name: gateway
cloud:
gateway:
discovery:
locator:
#自动映射eureka下的服务路由
enabled: true
#开启服务名称小写
lower-case-service-id: true
# 注册到服务中心
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka/
然后在启动类加上@EnableEurekaClient注解,启动项目,可以看到已经注册到服务中心了:
接下来我们测试一下通过网关访问我们的client服务,如何访问呢?
前面我们说到网关是通过服务名,去注册中心寻找相应的节点的,假如我们访问localhost:8083/client/student/detail,那么它就会去服务注册中心寻找叫做client的服务节点,并将后面的/student/detail转发到对应的节点,所以最后访问的真正地址也就是我们的localhost:8081/student/list。如果有多个相同名称的节点,则会以负载均衡的方式去分配到不同的服务节点。当然了,这都是gateway默认的转发方式,它有很多灵活的转发方式,都可以配置,比如忽略服务名前缀啊、从端口号后的第几个路径开始截图转发等等。
也就是说,网关默认的访问方式是:主机名(或者IP、域名):网关端口号/服务名称/请求的url
落实到我们这个例子里,就是localhost:8083/client/student/list,访问效果:
可以看到,张三和李四是交替出现的,Gateway网关为我们实现了负载均衡。
Gateway的玩法有很多,由于篇幅原因以后再给大家介绍。
在微服务中,各个系统之间相互调用,难免出现某个服务挂掉的情况,此时熔断器机制就很重要,我们可以利用熔断器,将超时的请求予以异常处理,避免形成死循环的调用链。
由于我们刚才在搭建网关时没用选择熔断器依赖,所以在网关项目的pom.xml手动添加以下依赖:
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
然后修改配置文件:
server:
#网关端口8083
port: 8083
spring:
application:
#在服务中心的应用名称
name: gateway
cloud:
gateway:
discovery:
locator:
#自动映射eureka下的服务路由
enabled: true
#开启服务名称小写
lower-case-service-id: true
#服务熔断,降级
default-filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka/
# hystrix熔断器,3秒后自动超时
hystrix:
command:
fallbackcmd:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
上面配置里的fallbackUri,是指熔断时的回调地址,也就是说一旦服务响应超过了3秒,会转发到这个地址,执行自定义的业务操作,我们往往是返回一个错误码。注意,这个回调的controller,是要写到网关里的,所以我们在网关项目里新建如下:
package com.blog.wang.cloudgateway.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class ErrorController {
/**
* 服务响应超时的回调
* @return 错误码
* */
@RequestMapping(value = "/fallback")
public Object fallback () {
Map result = new HashMap<>();
result.put("code" , 0);
result.put("msg" , "服务器繁忙");
result.put("state" , false);
return result;
}
}
然后我们在第一个服务节点里,添加如下代码,模拟超时:
再通过我们的网关访问API接口,发现每当请求到达第一个服务节点时,就会提示服务器繁忙:
以上就是基本的Spring Cloud脚手架,相信还是可以满足大多数人的开发需求的。不足的地方有很多,比如处理跨域、线上环境的注册中心配置、Gateway网关的多种配置、熔断器的各种配置,以后都会慢慢地补上,还请大家海涵吧!
码字不易,欢迎各位的转发、分享、支持!