目录
一、服务注册和发现
1、注册中心原理
2、nacos注册中心
3、服务注册
3.1 添加依赖
3.2 配置nacos
3.3 服务的发现
二、openfeign
1、快速入门
1.1 引入依赖
1.2 启用openfeign
1.3 编写openfeign客户端
2、连接池
2.1 引入依赖
2.2 开启连接池
3、 最佳实践
3.1 思路分析
3.2 扫描包
3.3 日志配置
3.1 定义日志级别
3.2 配置
一、服务注册和发现
1、注册中心原理
在微服务远程调用的过程中,包括两个角色:
-
服务提供者:提供接口供其它微服务访问
-
服务消费者:调用其它微服务提供的接口
在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心的概念。注册中心、服务提供者、服务消费者三者间关系如下:
流程如下:
-
服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心
-
服务的调用者会从注册中心订阅自己想要的服务,并从服务中心拉取订阅服务列表,此处的服务列表也可能不止一个(一个服务可以创建多个实例,也就是部署到多个服务器)
-
调用者自己对服务实现负载均衡(通俗来讲也就是按照一定的算法从多个实例中挑选出一个要调用的服务器)
-
调用者向该实例发送远程调用
当服务提供者的实例宕机或者启动新实例时,调用者如何得知呢?
-
服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求:就是每隔一段时间向注册中心发送自己的状态)
-
当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除
-
当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表
-
当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表
2、nacos注册中心
目前开源的注册中心框架有很多,国内比较常见的有:
-
Eureka:Netflix公司出品,目前被集成在SpringCloud当中,一般用于Java应用
-
Nacos:Alibaba公司出品,目前被集成在SpringCloudAlibaba中,一般用于Java应用
-
Consul:HashiCorp公司出品,目前集成在SPringCloud中,不限制微服务语言
以上几种注册中心都遵循SpringCloud中的API规范,因此在业务开发使用上没有太大差异。由于Nacos是国内产品,中文文档比较丰富,而且同时具备配置管理功能(后面会学习),因此在国内使用较多。
使用docker部署nacos需要提供nacos数据库用来保存nacos产生的数据,由于是docker部署所以需要将数据库放到docker容器中的mysql中,数据库表如下:
然后将nacos放到/root根目录下,可以使用如下命令来创建一个docker容器
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
--network hmall \
nacos/nacos-server:v2.1.0-slim
启动完之后使用虚拟机的ip:8848/nacos就可以访问到nacos了,示例如下
3、服务注册
3.1 添加依赖
在需要注册到nacos注册中心的服务pom文件中添加如下依赖
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
3.2 配置nacos
在要注册到nacos注册中心的项目的yaml文件中添加如下配置
spring:
application:
name: item-service # 服务名称
cloud:
nacos:
server-addr: 192.168.150.101 # nacos地址
name处事自己的服务名字,nacos地址是虚拟机地址。
然后将配置了nacos配置的服务重启一下,启动的时候注意端口的修改防止发生冲突。
重启之后进入到nacos注册中心的控制台就会发现服务注册成功,示例如下:
点击详情可以查看服务的详细信息
3.3 服务的发现
服务的消费者要去nacos订阅服务,这个过程就是服务发现,步骤如下:
-
引入依赖
-
配置Nacos地址
-
发现并调用服务
服务发现除了要引入nacos依赖以外,由于还需要负载均衡,因此要引入SpringCloud提供的LoadBalancer依赖。
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
可以发现,这里Nacos的依赖于服务注册时一致,这个依赖中同时包含了服务注册和发现的功能。因为任何一个微服务都可以调用别人,也可以被别人调用,即可以是调用者,也可以是提供者。
ymal文件中需要配置nacos与服务注册一样,此处省略。
二、openfeign
在上一章,我们利用Nacos实现了服务的治理,利用RestTemplate实现了服务的远程调用。但是远程调用的代码太复杂了:
而且这种调用方式,与原本的本地方法调用差异太大,编程时的体验也不统一,一会儿远程调用,一会儿本地调用。
因此,我们必须想办法改变远程调用的开发模式,让远程调用像本地方法调用一样简单。而这就要用到OpenFeign组件了。
其实远程调用的关键点就在于四个:
-
请求方式
-
请求路径
-
请求参数
-
返回值类型
所以,OpenFeign就利用SpringMVC的相关注解来声明上述4个参数,然后基于动态代理帮我们生成远程调用的代码,而无需我们手动再编写,非常方便。接下来,我们就通过一个快速入门的案例来体验一下OpenFeign的便捷吧。
1、快速入门
1.1 引入依赖
<!--openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--负载均衡器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
1.2 启用openfeign
在服务的启动类上添加注解,启动OpenFeign功能:
1.3 编写openfeign客户端
新建一个client包,在其中编写client客户端,其中的代码如下:
package com.hmall.cart.client;
import com.hmall.cart.domain.dto.ItemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient("item-service")
public interface ItemClient {
@GetMapping("/items")
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
这里只需要声明接口,无需实现方法。接口中的几个关键信息:
@FeignClient("item-service")
:声明服务名称
@GetMapping
:声明请求方式
@GetMapping("/items")
:声明请求路径
@RequestParam("ids") Collection<Long> ids
:声明请求参数
List<ItemDTO>
:返回值类型
有了上述信息,OpenFeign就可以利用动态代理帮我们实现这个方法,并且向http://item-service/items
发送一个GET
请求,携带ids为请求参数,并自动将返回值处理为List<ItemDTO>
。
我们只需要直接调用这个方法,即可实现远程调用了。
2、连接池
在微服务架构中,服务之间的通信通常通过 HTTP 或 RPC 进行。为了提高性能和资源利用率,通常会使用连接池来管理这些连接。
Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现包括:
-
HttpURLConnection:默认实现,不支持连接池
-
Apache HttpClient :支持连接池
-
OKHttp:支持连接池
因此我们通常会使用带有连接池的客户端来代替默认的HttpURLConnection。比如,我们使用OK Http.
2.1 引入依赖
<!--OK http 的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
2.2 开启连接池
在项目中的yaml文件中添加以下配置
feign:
okhttp:
enabled: true # 开启OKHttp功能
重启服务,连接池就生效了。
3、 最佳实践
上面了解了openfeign如何使用之后,我们来了解如何在项目中集成openfeign。
3.1 思路分析
在我们了解过如何编写openfeign客户端以及其使用之后,我们就需要知道如何在我们的项目中集成这个功能完成不同服务之间的异步调用了。
目前有两种方法来实现,如图所示:
第一种是直接在我们的项目中创建一个新的模块来专门写这些Oppenfeign客户端,方案1抽取更加简单,工程结构也比较清晰,但缺点是整个项目耦合度偏高。
第二种是直接在我们得每个服务中直接新建一个包来编写每个服务的api模块。方案2抽取相对麻烦,工程结构相对更复杂,但服务之间耦合度降低。
3.2 扫描包
使用方式一来集成到项目中openfeign会出现一个问题,你启动项目的时候可能会报错。原本因也很简单,因为你调用client的那个服务并不能扫描到你编写的client所在的包。解决办法也有两种,只需要在对应的服务启动类上加上扫描包的注解即可。
-
方式1:声明扫描包:
-
方式2:声明要用的FeignClient
3.3 日志配置
OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:
-
NONE:不记录任何日志信息,这是默认值。
-
BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
-
HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
-
FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。
3.1 定义日志级别
在创建的api模块下新建一个config包编写如下配置:
package com.hmall.api.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfig {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.FULL;
}
}
3.2 配置
接下来,要让日志级别生效,还需要配置这个类。有两种方式:
-
局部生效:在某个
FeignClient
中配置,只对当前FeignClient
生效
@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
-
全局生效:在
@EnableFeignClients
中配置,针对所有FeignClient
生效。
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)