微服务入门(RestTemplateEurekaNacosFeignGateway)
Posted 吞吞吐吐大魔王
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微服务入门(RestTemplateEurekaNacosFeignGateway)相关的知识,希望对你有一定的参考价值。
文章目录
- 1. 框架介绍
- 2. 认识微服务
- 3. Eureka 注册中心
- 4. Ribbon 负载均衡
- 5. Nacos 注册中心
- 6. Nacos 配置管理
- 7. HTTP 客户端 Feign
- 8. 统一网关 Gateway
1. 框架介绍
1.1 单体架构
基本介绍: 将业务的所有功能集中在一个项目中开发,打成一个包部署。
优点: 架构简单、部署成本低
缺点: 耦合度高
1.2 分布式架构
基本介绍: 根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。
优点: 降低服务耦合、有利于服务升级拓展
缺点: 架构复杂、难度大
分布式架构的要考虑的问题:
- 服务拆分粒度如何?
- 服务集群地址如何维护?
- 服务之间如何实现远程调用?
- 服务健康状态如何感知?
1.3 微服务架构
基本介绍: 微服务是一种经过良好架构设计的分布式架构方案。
特征:
- 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
- 面向服务:微服务对外暴露业务接口
- 自治:团队独立、技术独立、数据独立、部署独立
- 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题。
优点: 拆分粒度更小、服务更独立、耦合度更低
缺点: 架构非常复杂,运维、监控、部署难度提高
2. 认识微服务
2.1 SpringCloud
-
SpringCloud 是目前国内使用最广泛的微服务框架。官网地址:https://spring.io/projects/spring-cloud
-
SpringCloud 集成了各种微服务功能组件,并基于 SpringBoot 实现了这些组件的自动装配,从而提供了良好的开箱即用体验
- 服务注册发现:Eureka、Nacos、Consul
- 统一配置管理:SpringCloudConfig、Nacos
- 服务远程调用:OpenFegin、Dubbo
- 统一网关路由:SpringCloudGateway、zuul
- 服务链路监控:Zipkin、Sleuth
- 流程、降级、保护:Hystix、Sentinel
-
SpringCloud 与 SpringBoot 的版本兼容关系:
Realesase Train(版本序列) Boot Version 2020.0.x aka Ilford 2.4.x Hoxton 2.2.x, 2.3.x Greenwich 2.1.x Finchley 2.0.x Edgware 1.5.x Dalston 1.5.x
2.2 微服务拆分注意事项
- 不同微服务,不要重复开发相同业务
- 微服务数据独立,不要访问其它微服务的数据库
- 微服务可以将自己的业务暴露为接口,供其它微服务调用
2.3 微服务远程调用方式
2.3.1 提供者与消费者
- 服务提供者:一次业务中,被其他微服务调用的服务。(提供接口给其它微服务)
- 服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
- 一个服务既可以是提供者又可以是消费者。
2.3.2 RestTemplate
基本介绍:
当我们需要远程调用一个 HTTP 接口时,我们经常会用到 RestTemplate 这个类,这个类是 Spring 框架提供的一个工具类。它提供了常见的 REST 请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。
常用方法:
方法 | 描述 |
---|---|
getForObject | 通过 GET 请求获得响应结果 |
getForEntity | 通过 GET 请求获取 ResponseEntity 对象,包括状态码、响应头和响应数据等 |
headForHeaders | 以 HEAD 请求资源返回所有响应头信息 |
postForLocation | 用 POST 请求创建资源,并返回响应数据中响应的字段 Location 的数据 |
postForObject | 通过 POST 请求创建资源,获得响应结果 |
put | 通过 PUT 请求来创建或更新资源 |
patchForObject | 通过 PATH 请求来更新资源,并获得响应结果 |
delete | 通过 DELETE 请求来删除资源 |
optionsForAllow | 通过 ALLOW 请求来获得资源所允许访问的所有 HTTP 方法,可用来查看某个请求支持哪些请求方式 |
exchange | 更通过版本的请求处理方法,接受一个 RequestEntity 对象,可以设置路径、请求头、请求信息等,最后返回一个 ResponseEntity 实体 |
execute | 最通用的执行 HTTP 请求方法,上面所有方法都是基于 execute 的封装,可以全面控制请求信息,并通过回调接口获得响应数据 |
简单使用:
-
首先将 RestTemplate 在配置类中进行注入(启动类也属于配置类,可以直接在启动类中使用
@Bean
进行注入) -
通过 RestTemplate 对象的
getForObject(url, Class<T> responseType)
方法,就能够通过 get 方法向其它微服务发送对应的请求去获取数据。
RestTamplate 远程调用出现的问题:
- 服务消费者该如何获取服务提供者的地址信息?
- 如果有多个服务提供者,消费者该如何选择?
- 消费者如何得知服务提供者的健康状态?
- 代码可读性差,编程体验不统一
- 参数复杂 URL 难以维护
3. Eureka 注册中心
3.1 基本介绍
在 Eureka 框架中,微服务角色有两类:
-
EurekaServer(服务端,注册中心):用于记录服务信息,进行心跳监控
-
EurekaClient(客户端):包括服务提供者和消费者
- 服务提供者能够将注册自己的信息到 EurekaServer 中,并且每隔 30s 向 EurekaServer 发送心跳
- 服务消费者能够根据服务名称从 EurekaServer 中拉取服务列表,并基于服务列表做负载均衡,选中一个微服务后发起远程调用
在进行之后的步骤之前,需要在父工程中引入 spring-cloud-dependencies
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR10</version>
<type>pom</type>
<scope>import</scope>
</dependency>
3.2 搭建 EurekaServer
-
创建项目,引入
spring-cloud-starter-netflix-eureka-server
eureka 服务端依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
-
编辑启动类,添加
@EnableEurekaServer
注解@SpringBootApplication @EnableEurekaServer public class EurekaApplication public static void main(String[] args) SpringApplication.run(EurekaApplication.class, args);
-
添加 application.yml 文件,编写下面的配置:
server: port: 10086 # 服务端口 spring: application: name: eurekaserver # eureka 的服务名称 eureka: client: service-url: # eureka 的地址信息 defaultZone: http://127.0.0.1:10086/eureka
-
通过配置的 eureka 的地址信息就能够访问到 eureka 注册中心的状况
3.3 服务注册
-
在要注册的服务中引入
spring-cloud-starter-netflix-eureka-server
eureka 客户端依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
在 application.yml 文件中编写下面的配置:
server: port: 8080 # 服务端口 spring: application: name: aservice # 注册到 eureka 中的服务名称 eureka: client: service-url: # 配置 eureka 的地址信息 defaultZone: http://127.0.0.1:10086/eureka
-
通过配置的 eureka 的地址信息就能够查看注册到 eureka 中的实例
-
以上是对某个服务进行一个实例的部署,如果要部署多个实例,则为了避免端口冲突,需要修改端口配置
-
右击要部署多个的服务,点击 Copy Configuration…
-
修改配置的名字,并在 VM options 这栏加上
-Dserver.port=新的端口号
却修改配置的端口号 -
启动项目后,在 eureka 的注册中心就能看到 aservice 服务注册了两个实例
-
3.4 服务拉取(含负载均衡)
-
修改 RestTemplate 的 url 路径,用服务名代替 ip 和端口号
-
在 RestTemplate 注册到 Spring 容器的位置加上
@LoadBalanced
负载均衡注解 -
启动项目,通过 bservice 服务访问 aservice 服务中的方法
4. Ribbon 负载均衡
4.1 负载均衡流程
通过浏览器直接访问 http://aservice/say/dog 是访问不到的,因为 aservice 不是正确的 ip 和端口号,而加上负载均衡的注解 @LoadBalanced 注解就能够对这个请求进行拦截,在 eureka-server 中找到 aservice 对应的 ip 和端口号,最终返回请求的数据。
加上 @LoadBalanced 就能就行拦截,那么拦截的动作是谁处理的呢?
请求会被一个叫 LoadBalancerInterceptor 的负载均衡拦截器给拦住,它能获取拦截请求中的主机名,即上述请求中的 aservice,并把它交给 RibbonLoadBalancerClient。RibbonLoadBalancerClient 又会把服务名交给 DynamicServerListLoadBalancer。DynamicServerListLoadBalancer 就会到 eureka-server 中获取到对应服务的服务列表并交给 IRule。IRule 会从服务列表中基于规则选取其中一个服务,将 ip 和端口号返回给 RibbonLoadBalancerClient。RibbonLoadBalancerClient 就会将请求修改成正确的地址并发送出去,最终获取到数据。
4.2 负载均衡策略
Ribbon 的负载均衡规则是一个叫做 IRule 的接口来定义的,它的每一个子接口都是一种规则
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是 Ribbon 默认的负载均衡规则。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,但权重值会影响服务器的选择。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用 Zone 对服务器进行分类,这个 Zone 可以理解为一个机房、一个机架。而后再对 Zone 内的的多个服务做轮询。 |
AvailabilityFilteringRule | 1. 在默认情况下,这台服务器如果3次连接失败,就会被设置为“短路”状态。短路状态将持续30s,如果再次连接失败,短路的时间就会几何级地增加。2. 并发数过高的服务器,如果配置了 AvailabilityFilteringRule 规则,客户端就会将其忽略。 |
修改负载均衡规则方式:
-
直接在配置类或启动类中定义一个新的 IRule(定义后,该服务调用其它微服务都会使用如下规则)
// 将规则修改成随机选择 @Bean public IRule randomRule() return new RandomRule();
-
在 application.yml 文件中,添加新的配置(配置后,该服务会对指定要调度的服务进行规则配置)
aservice: # 要调度的服务名 ribbon: # 负载均衡规则,修改为要使用的负载均衡的类的名称 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
4.3 饥饿加载
Ribbon 默认是采用懒加载,即第一次访问时会去创建 LoadBalanceCilent,请求时间很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
ribbon:
eager-load:
enabled: true # 开启饥饿加载
clients: # 指定饥饿加载的服务名称
- aservice # 指定对 aservice 服务开启饥饿加载
5. Nacos 注册中心
5.1 安装 Nacos
Nacos 是阿里巴巴的产品,现在是 SpringCloud 中的一个组件,相比于 Eureka 功能更加丰富。
Nacos 的安装方式如下:
-
下载 Nacos 的安装包,并解压好(下载地址为:https://github.com/alibaba/nacos/tags)
-
启动 Nacos(进入解压好的 bin 目录,通过控制台执行下面的命令)
# 单机启动 Nacos startup.cmd -m standalone
-
Console 后面的地址就是 Nacos 的控制台地址,Ctrl + 鼠标左键就能够直接打开
-
登录 Nacos(默认账号和密码都是 nacos),登录成功后就会跳转到控制台页面
5.2 服务注册
-
在父工程中添加
spring-cloud-alilbaba
的管理依赖:<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.5.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency>
-
在指定的服务中添加 nacos 的客户端依赖:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
-
配置 application.yml 文件
spring: application: name: aservice # 服务名 cloud: nacos: server-addr: localhost:8848 # nacos 服务地址
-
运行项目后,指定服务就会注册到 nacos 中。在 nacos 的控制台就可以看到具体的信息。
-
通过 RestTemplate 就可以对注册到 nacos 上的服务进行调度。
5.3 Nacos 服务分级存储模型
一个服务可以分成多个集群,每个集群中又可以包括多个实例。
服务跨集群调用问题:
服务调用尽可能选择本地集群的服务,跨集群调用延迟较高。当本地集群不可访问时,再去访问其它集群。
集群的配置方式:
当我们不配置集群时,Nacos 中集群显示为 DEFAULT,即表示没有集群
可以在 application.yml 文件中编辑如下配置,去配置集群
spring:
cloud:
nacos:
discovery:
cluster-name: CS # 集群名称
在 aservice 服务中设置好了集群名为 CS 后启动 aservice 的 8080 实例。然后再修改 aservice 的集群名为 HZ 并启动 aservice 的 8082 实例,Nacos 中的集群就会显示下面结果:
5.4 根据集群负载均衡
上面已经修改了 aservice 服务的配置,将 aservice 的两个实例分别配置到了两个集群中。不过将 bservice 设置集群名为 CS,当调用 asevice 实例时并不是优先调用的同集群中的 aservice 实例。这是因为服务在选择实例时是通过负载均衡的规则去选择的,而负载均衡的默认规则是轮询调度。因此想要优先调度同集群的实例,就要修改负载均衡的规则。
修改方式如下:
-
先设置好调度者的服务的集群名
-
在该服务中设置负载均衡的 IRule 为 NacosRule,这个规则优先会寻找与自己同集群的服务(不过是同集群中随机调度)
aservice: # 要调度的服务名 ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
5.5 根据权重负载均衡
Nacos 提供了权重配置来控制实例的访问频率,权重越大者访问频率越大。
应用场景:
将性能好的机器设置权重多一点,将性能差的机器设置权重少一点。
修改方式如下:
-
在 Nacos 控制台就可以设置实例的权重值,首先点击实例的编辑按钮
-
将权重修改为你要的值即可(权重值在 0~1 之间,0 表示不会被访问到)
5.6 环境隔离 namespace
Nacos 中服务存储和数据存储的最外层都是一个名为 namespace 的东西,用来做最外层的隔离。每个 namespace 都有唯一 ID,不同 namespace 下的服务是不可见的。
在 Nacos 控制台我们可以看到,我们注册的实例都默认存放在一个叫 public 的命名空间中
配置新的命名空间的方式:
-
在 Nacos 的控制台新建一个命名空间
-
创建好了之后,在服务列表就能看到新创建的命名空间了。
-
修改服务的 application.yml 文件,添加如下配置:
spring: cloud: nacos: discovery: namespace: a6fedd4e-c8fc-4695-b5d1-60bf72205819 # 命名空间的ID
-
重启服务
5.7 临时实例和非临时实例
Nacos 支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式。默认采用临时实例。
配置非临时实例方式:
spring:
cloud:
nacos:
discovery:
ephemeral: false # 设置非临时实例
5.8 Nacos 和 Eureka 的差别
共同点:
- 都支持服务注册和服务拉取
- 都支持服务提供者心跳方式做健康检测
区别:
- Nacos 支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式。
- 临时心跳不正常会被剔除,非临时实例则不会被剔除。
- Nacos 支持服务列表变更的消息推送模式,服务列表更新更及时。
- Nacos 集群默认采用 AP 模式,当集群中存在非临时实例时,采用 CP 模式;Eureka 采用 AP 模式
6. Nacos 配置管理
6.1 Nacos 进行配置管理
-
在 Nacos 控制台中添加配置信息
-
在弹出的表单中填写配置信息
6.2 微服务获取 Nacos 中的配置
当只读取本地配置文件时,步骤如下:
当需要读取 nacos 控制台中的配置文件时,步骤如下:
-
引入 Nacos 的配置管理客户端依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
-
在服务中的 resources 目录中添加一个
bootstrap.yml
文件(这个文件是引导文件,优先级高于 application.yml)spring: application: name: aservice # 服务名称(与 nacos 控制台配置文件名对应) profiles: active: dev # 开发环境,这里是 dev(与 nacos 控制台配置文件名对应) cloud: nacos: server-addr: localhost:8848 # Nacos 地址 config: file-extension: yaml # 文件后缀名(与 nacos 控制台配置文件名对应)
6.3 配置热更新
Nacos 中的配置文件变更后,微服务无需重启就可以感知。不过需要通过下面两种配置实现:
-
方式一:在
@Value
注入的变量所在类上添加注解@RefreshScope
@Value
该注解的作用是将配置文件的属性读出来,有@Value("$")
和@Value("#")
两种方式 -
方式二:使用
@ConfigurationProperties
注解上面的例子将会读取配置文件中所有以 pattern 开头的属性,并和 bean 中的字段进行匹配。
6.4 多种配置的优先级
微服务从 nacos 读取配置文件时会读取两个配置:
[服务名]-[spring.profile.active].yaml
环境配置[服务名].yaml
默认配置,多环境共享
优先级:
nacos 中的当前环境配置(服务名-环境.yaml)> nacos 中共享配置(服务名.yaml)> 本地配置
7. HTTP 客户端 Feign
7.1 定义和使用 Feign
基本介绍:
Feign 是一个声明式的 HTTP 客户端,能够帮助我们优雅地实现 HTTP 请求的发送。官方地址:https://github.com/OpenFeign/feign
使用 Feign 步骤如下:
-
引入依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
在消费者的启动类中添加
@EnableFeignClients
注解开启 Feign 的功能:@SpringBootApplication @EnableFeignClients public class BApplication public static void main(String[] args) SpringApplication.run(BApplication.class, args);
-
编写 FeignClient 接口:
@FeignClient("aservice") // 要调度的微服务 public interface AClient // 该接口中包含和 aservice 服务中的多有需要调度的方法 @GetMapping("say/dog") public String dogSay();
-
使用编写好的 FeignClient 中定义的方法
@RestController @RequestMapping("b") public class BController @Autowired private AClient aClient; @GetMapping("say") public String bSay() return aClient.dogSay();
7.2 自定义 Feign 的配置
Feign 运行自定义配置来覆盖默认配置,可以修改的配置如下:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE(默认)、BASIC、HEADERS、FULL |
feign.coder.Decoder | 响应结果的解析器 | HTTP 远程调用的结果做解析,例如解析 json 字符串为 java 对象 |
feign.coder.Encoder | 请求参数编码 | 将请求参数编码,便于通过 HTTP 请求发送 |
feign.Contract | 支持的注解格式 | 默认是 SpringMVC 的注解 |
feign.Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用 Ribbon 的重试 |
配置 Feign 日志有两种方式:
-
方式一:配置文件方式
-
全局生效
feign: client: config: default: # default 表示全局配置,即对调度的所有微服务都作如下配置 loggerLevel: Full # 日志级别
-
局部生效
feign: client: config: aservice: # 具体服务名称,针对某个要被调度的微服务进行配置 loggerLevel: Full # 日志级别
-
-
方式二:java 代码方式,需要先声明一个 Bean
public class FeignClientConfiguration @Bean public Logger.Level feignLogLevel() return Logger.Level.BASIC;
-
而后如果是全局配置,则把它放到
@EnableFeignClients
这个注解中@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class);
-
如果是局部配置,则把它放到
@FeignClient
这个注解中@FeignClient(value = "aservice", configuration = FeignClientConfiguration.class);
-
7.3 Feign 的性能优化
Feign 底层的客户端实现:
- URLConnection:默认实现,不支持连接池
- Apache HttpClient:支持连接池
- OKHttp:支持连接池
优化 Feign 的性能主要包括:
- 使用连接池代替默认的 URLConnection,可以减少连接创建和销毁的性能损耗
- 日志级别最好使用 basic 或 none,级别低的话性能更高
Feign 添加 HttpClient 的支持方式:
-
引入依赖
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
-
配置连接池:
feign: httpclient: enabled: true # 开启 feign 对 httpClient 的支持 max-connections: 200 # 最大连接数 max-connections-per-route: 50 # 每个路径的最大连接数
7.4 Feign 的最佳实现
方式一(继承):给消费者的 FeignClient 和提供者的 controller 定义统一的父接口作为标准
方式二(抽取):
-
首先创建一个 module,命名为 feign-api,然后引入 feign 的 starter 依赖
-
在 feign-api 中编写提供者的 FeignClient、POJO 和 Feign 的默认配置
-
在消费者中引入 feign-api 的依赖
-
重启项目
-
当定义的 FeignClient 不在 SpringBootApplication 的扫描包范围时,这些 FeignClient 无法使用。有两种方式解决:
-
方式一:指定 FeignClient 所在包
@EnableFeignClients(basePackages = "com.mmr.feign.clients")
-
方式二:指定 FeignClient 字节码
@EnableFeignClients(clients = AClient.class)
-
8. 统一网关 Gateway
8.1 认识网关
网关的 以上是关于微服务入门(RestTemplateEurekaNacosFeignGateway)的主要内容,如果未能解决你的问题,请参考以下文章