Nacos注册中心
Nacos是目前国内企业中占比最多的注册中心组件。它是阿里巴巴的产品,目前已经加入SpringCloudAlibaba中。
服务提供者将自己的信息包括ip端口,注册到注册中心上。服务调用者去注册中心订阅信息,注册中心就会把实例提供给服务调用者。提供服务的可能有多个实例,这里使用负载均衡(轮询,随机,hash)来选择合适的实例,然后再远程调用。
服务提供者与注册中心之间还有一个心跳机制,如果有服务挂掉,注册中心就会在服务列表中将服务删掉,并且给服务调用者进行推送和变更。
准备工作步骤如下:
在linux操作系统中,创建一个naocs文件夹,然后在里面创建一个文件名称为:custom.env
文件里面的内容为:
PREFER_HOST_MODE=hostname
MODE=standalone
SPRING_DATASOURCE_PLATFORM=mysql
MYSQL_SERVICE_HOST=192.168.248.133
MYSQL_SERVICE_DB_NAME=nacos
MYSQL_SERVICE_PORT=3306
MYSQL_SERVICE_USER=root
MYSQL_SERVICE_PASSWORD=123
MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
然后在linux系统下下载nacos镜像,之后进行部署。
部署的文件为:
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim
服务注册
提供自己的信息到nacos
后端:
1.引入Nacos discovery依赖:
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.配置Nacos地址:
spring:
application:
name: item-service # 服务名称
cloud:
nacos:
server-addr: 192.168.150.101:8848 # nacos地址
多实例部署,在配置一份实例部署,并且更改端口号,防止冲突
服务发现
调用者去注册中心拉去服务列表
要想调用服务是不知道该服务的地址的,因此必须去Nacos中拉去服务的信息。
负载均衡实现:
private final Discoveryclient discoveryclient;
private void handleCartItems(List<CartV0> vos){
//.根据服务名称,拉取服务的实例列表
List<ServiceInstance>instances = discoveryclient,getInstances("item-service");
//2.负载均衡,挑选一个实例
ServiceInstance instance = instances.get(RandomUtil,randomInt(instances.size()));
//3.获取实例的TP和端口
URI uri = instance.getUri();
这样就不需要记住IP的端口信息了,可以通过实例名来获取。如果有一个服务挂了,就不会被调用,服务回复也会被重新调用。
代码如图:
之前的远程调用为
实现效果:
OpenFeign
我们利用Nacos实现了服务的治理,利用RestTemplate实现了服务的远程调用。但是远程调用的代码太复杂了。而且这种调用方式,与原本的本地方法调用差异太大,编程时的体验也不统一,一会儿远程调用,一会儿本地调用。因此,我们必须想办法改变远程调用的开发模式,让远程调用像本地方法调用一样简单。而这就要用到OpenFeign组件了。
其实远程调用的关键点就在于四个:
-
请求方式
-
请求路径
-
请求参数
-
返回值类型
所以,OpenFeign就利用SpringMVC的相关注解来声明上述4个参数,然后基于动态代理帮我们生成远程调用的代码,而无需我们手动再编写,非常方便。
OpenFeign已经被SpringCloud自动装配,实现起来非常简单。
1.引入依赖,包括负载均衡组件SpringCloudLoadBalancer(在老的版本里负载均衡用的Ribbon)
<!--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>
2.在启动类上添加@EnableClients注解,来启动OpenFeign功能。
3.编写OpenFeign客户端
@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>
。
我们只需要直接调用这个方法,即可实现远程调用了。
4.使用FeignClient,实现远程调用
List<ItemTO> items = itemClient.quertItemByIds(List.of(1,2,3));
连接池
OpenFeign发起请求用的是Client 每次调用都要重写创建连接,对性能不友好
Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现包括:
-
HttpURLConnection:默认实现,不支持连接池
-
Apache HttpClient :支持连接池
-
OKHttp:支持连接池
因此我们通常会使用带有连接池的客户端来代替默认的HttpURLConnection。比如,我们使用OK Http
1.引入依赖
<!--OK http 的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
2.开启连接池功能
feign:
okhttp:
enabled: true # 开启OKHttp功能
最佳使用方式
避免重复编码的办法就是抽取。不过这里有两种抽取思路:
-
思路1:抽取到微服务之外的公共module
-
思路2:每个微服务自己抽取一个module
-
方案1抽取更加简单,工程结构也比较清晰,但缺点是整个项目耦合度偏高。
-
方案2抽取相对麻烦,工程结构相对更复杂,但服务之间耦合度降低。
在运行之前还需要添加扫描包
-
方式1:声明扫描包:@EnableFeginClients(basePackages = “”)
-
方式2:声明要用的FeignClient:@EnableFeginClients(Clients = {UserClient.class})
日志输出
OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:
-
NONE:不记录任何日志信息,这是默认值。
-
BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
-
HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
-
FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。
接下来,要让日志级别生效,还需要配置这个类。有两种方式:
-
局部生效:在某个
FeignClient
中配置,只对当前FeignClient
生效
@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
-
全局生效:在
@EnableFeignClients
中配置,针对所有FeignClient
生效。
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)
只有在调试的时候才需要配置