远程调用的作用
在原来的springboot项目中通常是通过调用业务层来实现一个功能。在微服务中由于我们将许多业务进行一个拆开,但是我们需要其他业务层的数据所以就需要通过发送请求的方式来获取数据
实现注册中心nacos(第一步)
我们基于Docker来部署Nacos的注册中心,首先我们要准备MySQL数据库表,用来存储Nacos的数据。由于是Docker部署,所以大家需要将资料中的SQL文件导入到你Docker中的MySQL容器中:
其中的nacos/custom.env
文件中,有一个MYSQL_SERVICE_HOST也就是mysql地址,需要修改为你自己的虚拟机IP地址:
然后,将课前资料中的nacos
目录上传至虚拟机的/root
目录。
进入root目录,然后执行下面的docker命令:
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
值得注意的是:当我们电脑的ip地址改变的时候 需要通过将nacos从docker中进行移除,重新通过docker进行run,同时别忘忘记将nacos/custom.env中的ip进行修改
启动完成后,访问下面地址:http://192.168.150.101:8848/nacos/,注意将192.168.150.101
替换为你自己的虚拟机IP地址。
首次访问会跳转到登录页,账号密码都是nacos
服务注册
将一个模块注册到nacos注册中心的步骤如下
1.引入依赖
2.配置nacos地址
添加依赖
在需要实现nacos的模块中的pom文件中添加一下依赖
<!--nacos 服务注册发现-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
配置Nacos
在yml文件添加nacos的地址配置
spring:application:name: item-service # 服务名称cloud:nacos:server-addr: 192.168.150.101:8848 # nacos地址
然后我们访问 http:虚拟机地址:8848/nacos/
看是否可以登录进去,如果可以登录进去就代表成功
服务发现
服务的消费者要去nacos订阅服务,这个过程就是服务发现,步骤如下:
-
引入依赖
-
配置Nacos地址
-
发现并调用服务
引入依赖
服务发现除了要引入nacos依赖以外,由于还需要负载均衡,因此要引入SpringCloud提供的LoadBalancer依赖。
在消费者中模块中添加一下依赖
<!--nacos 服务注册发现-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
这样消费者还有生产者都全部确定
OpenFegin(第二步)
快速使用
引入依赖
在微服务的pom.xml中引入OpenFeign
的依赖和loadBalancer
依赖:
<!--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>
启用OpenFegin
接下来,我们在cart-service
的CartApplication
启动类上添加注解,启动OpenFeign功能:
"@EnableFeignClients" 这个注解就是气筒OpenFegin服务的注解
package com.hmall.cart;import com.hmall.api.config.DefaultFeignConfig;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;@EnableFeignClients(basePackages = "com.hmall.api.client",defaultConfiguration = DefaultFeignConfig.class)
@MapperScan("com.hmall.cart.mapper")
@SpringBootApplication
public class CartApplication {public static void main(String[] args) {SpringApplication.run(CartApplication.class, args);}
}
编写OpenFeign客户端
在cart-service(这个服务是一个例子)
中,定义一个新的接口,编写Feign客户端(现在是在cart包中,后面需要将统一写在api模块中):
其中代码如下:
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>
:返回值类型
在这里我们需要搞清楚消费者和生产者:
我们是将item-service中的服务进行注册,也就是item-service是生产者
cart-service是消费者
有了上述信息,OpenFeign就可以利用动态代理帮我们实现这个方法,并且向http://item-service/items
发送一个GET
请求,携带ids为请求参数,并自动将返回值处理为List<ItemDTO>
。
我们只需要直接调用这个方法,即可实现远程调用了。
使用FeignClient
最后,我们在cart-service
的com.hmall.cart.service.impl.CartServiceImpl
中改造代码,直接调用ItemClient
的方法:
连接池
Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现包括:
-
HttpURLConnection:默认实现,不支持连接池
-
Apache HttpClient :支持连接池
-
OKHttp:支持连接池
因此我们通常会使用带有连接池的客户端来代替默认的HttpURLConnection。比如,我们使用OK Http.
引入依赖
在cart-service
的pom.xml
中引入依赖(值得注意的是我们是在消费者这边引入依赖):
<!--OK http 的依赖 -->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId>
</dependency>
开启连接池
在cart-service
的application.yml
配置文件中开启Feign的连接池功能(值得注意的是我们也是在消费者这边开启连接池功能):
feign:okhttp:enabled: true # 开启OKHttp功能
重启服务,连接池就生效了。
最佳实践(其就是将消费者client转移统一的模块)
相信大家都能想到,避免重复编码的办法就是抽取。不过这里有两种抽取思路:
-
思路1:抽取到微服务之外的公共module
-
思路2:每个微服务自己抽取一个module
如图:
方案1抽取更加简单,工程结构也比较清晰,但缺点是整个项目耦合度偏高。
方案2抽取相对麻烦,工程结构相对更复杂,但服务之间耦合度降低。
由于item-service已经创建好,无法继续拆分,因此这里我们采用方案1.
抽取Fegin客户端
在自己的父项目定义新的的moduie,命名为xxx-api (由于在黑马上学习的,所以名为hm-api)
其依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>hmall</artifactId><groupId>com.heima</groupId><version>1.0.0</version></parent><modelVersion>4.0.0</modelVersion><artifactId>hm-api</artifactId><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties><dependencies><!--open feign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!-- load balancer--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!-- swagger 注解依赖 --><dependency><groupId>io.swagger</groupId><artifactId>swagger-annotations</artifactId><version>1.6.6</version><scope>compile</scope></dependency></dependencies>
</project>
现在,任何微服务要调用item-service(原来的没有进行拆解的项目)
中的接口,只需要引入hm-api
模块依赖即可,无需自己编写Feign客户端了。
扫描包
接下来,我们在cart-service(消费者)
的pom.xml
中引入hm-api
模块:
<!--feign模块--><dependency><groupId>com.heima</groupId><artifactId>hm-api</artifactId><version>1.0.0</version></dependency>
在这里需要注意的是由于我们将client(装有可以远程调用的client放在api)所以在启动的时候回扫描不到,所以我们需要在消费者启动类的
@EnableFeignClients(basePackages = "com.hmall.api.client")
中加上client中的包 就是像上面那样。