在上一节中,我们说了Consul注册中心实现了服务的注册和发现功能,这一节说一下服务的调用
在单体应用中,代码可以直接依赖,依赖后可直接调用,在微服务的架构中,服务都运行在各自的进程中,还有可能在不同的服务器上,所以需要相关的远程调用技术
Spring Cloud有两种比较广泛的微服务调用
1、使用RestTemplate进行调用,可以通过Ribbon注解RestTemplate模板,使用其拥有负载均衡的功能。
2、使用Feign进行声明式服务调用,声明之后就像调用本地方法一样,Feign默认使用Ribbon实现负载均衡。
方法一、RestTemplate方法
一、服务提供者
新建mango-producer项目
在pom中添加
Swagger:API文档
Consul:注册中心
Spring boot Admin :服务监控
依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.louis</groupId>
<artifactId>mango-producer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mango-producer</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.version>1.0.0</project.version>
<java.version>1.8</java.version>
<swagger.version>2.9.2</swagger.version>
<mybatis.spring.version>1.3.2</mybatis.spring.version>
<druid.version>1.1.10</druid.version>
<spring.boot.admin.version>2.0.4</spring.boot.admin.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--spring-boot-admin-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring.boot.admin.version}</version>
</dependency>
<!--consul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改配置文件
将application.properties改为application.yml
在yml配置文件中添加以下内容,将服务注册到注册中心并添加服务监控相关配置
server:
port: 8003
spring:
application:
name: mango-producer
cloud:
consul:
host: localhost
port: 8500
discovery:
serviceName: ${spring.application.name} # 注册到consul的服务名称
boot:
admin:
client:
url: "http://localhost:8000"
# 开放健康检查接口
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
修改启动类
在启动类添加 @EnableDiscoveryClient开启服务发现支持
@EnableDiscoveryClient
@SpringBootApplication
public class MangoProducerApplication {
public static void main(String[] args) {
SpringApplication.run(MangoProducerApplication.class, args);
}
}
添加控制器
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "hello Mango !";
}
}
为了模拟负载均衡,我们将上面的项目复制一份,修改端口为8004
然后启动注册中心、服务监控、和两个服务提供者
启动项目后访问http://localhost:8500/
可以看到我们的服务已经注册到了注册中心
可以看到两个提供的服务已经在监控的列表中
上面我们完成了服务提供者的搭建
下面进行服务消费者
二、服务消费者
新建项目mango-consumer
在pom文件中添加
Swagger:API文档
Consul:注册中心
Spring boot Admin :服务监控
依赖
配置yml
server:
port: 8005
spring:
application:
name: mango-consumer
cloud:
consul:
host: localhost
port: 8500
discovery:
serviceName: ${spring.application.name} # 注册到consul的服务名称
boot:
admin:
client:
url: "http://localhost:8000"
# 开放健康检查接口
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
启动类
在启动类添加
@EnableDiscoveryClient
开启服务发现支持
@EnableDiscoveryClient
@SpringBootApplication
public class MangoConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(MangoConsumerApplication.class, args);
}
}
服务消费
添加消费服务测试类
ServiceController.java
添加两个接口,一个查询所有我们注册的服务
另一个从我们注册的服务中选一个服务,采取轮询的方式
@RestController
public class ServiceController {
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private DiscoveryClient discoveryClient;
/**
* 获取所有服务
*/
@RequestMapping("/services")
public Object services() {
return discoveryClient.getInstances("mango-producer");
}
/**
* 从所有服务中选择一个服务(轮询)
*/
@RequestMapping("/discover")
public Object discover() {
return loadBalancerClient.choose("mango-producer").getUri().toString();
}
}
添加完后,启动项目访问http://localhost:8500/
可以看到我们的消费服务已经注册到了注册中心
可以看到消费服务已在监控列表中
访问http://localhost:8005/services
可以看到返回的两个服务分别是8003和8004
然后反复访问http://localhost:8005/discover
交替返回8003和8004,因为默认负载均衡采用的是轮询方式
大多数情况下,我们采用负载均衡的形式去获取服务端提供的服务,因此我们新建一个CallHelloController.java
模拟调用服务的hello方法
@RestController
public class CallHelloController {
@Autowired
private LoadBalancerClient loadBalancer;
@RequestMapping("/call")
public String call() {
ServiceInstance serviceInstance = loadBalancer.choose("mango-producer");
System.out.println("服务地址:" + serviceInstance.getUri());
System.out.println("服务名称:" + serviceInstance.getServiceId());
String callServiceResult = new RestTemplate().getForObject(serviceInstance.getUri().toString() + "/hello", String.class);
System.out.println(callServiceResult);
return callServiceResult;
}
}
在上面内容中,我们是先通过LoadBalancerClient选取出对应的服务,然后使用RestTemplate进行远程调用
LoadBalancerClient就是负载均衡器,RibbonLoadBalancerClient是Ribbon默认使用的负载均衡器,采用的负载均衡策略是轮询
1、查找服务,通过loadBalancer
ServiceInstance serviceInstance = loadBalancer.choose("mango-producer")
2、调用服务,通过RestTemplate
String callServiceResult = new RestTemplate().getForObject(serviceInstance.getUri().toString() + "/hello", String.class);
这样就完成了一个简单的服务调用和负载均衡。下面我们说一下Ribbon
Ribbon负载均衡器
Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP的客户端的行为。
Ribbon默认为我们提供了很多负载均衡算法,例如轮询、随机等。我们也可以实现自定义负载均衡算法。
下面我们进行Ribbon配置
1、修改启动类
在启动类注入RestTemplate,并添加@LoadBalanced用于拦截请求,以使用ribbon来进行负载均衡
@EnableDiscoveryClient
@SpringBootApplication
public class MangoConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(MangoConsumerApplication.class, args);
}
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2、添加服务
新建一个控制类RibbonHelloController .java,注入RestTemplate,并调用服务提供者的hello服务
@RestController
public class RibbonHelloController {
@Autowired
private RestTemplate restTemplate;
@Bean
@RequestMapping("/ribbon/call")
public String call() {
// 调用服务, service-producer为注册的服务名称,LoadBalancerInterceptor会拦截调用并根据服务名找到对应的服务
String callServiceResult = restTemplate.getForObject("http://mango-producer/hello", String.class);
return callServiceResult;
}
}
配置完重启服务,访问http://localhost:8005//ribbon/call
访问返回不同的结果,说明负载均衡已生效。
修改均衡策略只需要在yml中配置对应策略即可
service-producer:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
方法二、Feign方法
Feign是一套基于Netflix Feign实现的声明式服务调用客户端,使用Feign可使得Web服务客户端的写入更加方便。
它具有可插拔注释支持,包括Feign注解和JAX-RS注解、Feign还支持可插拔编码器和解码器、Spring Cloud增加了对Spring MVC注释的支持,并HttpMessageConverters在Spring Web中使用了默认使用的相同方式。
第一步、添加pom依赖
添加feign依赖
<!--feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
第二步、启动类
在启动类添加@EnableFeignClients注解,开启扫描Spring cloud Feign客户端的功能。
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class MangoConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(MangoConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
第三步、添加Fegin接口
在类头上添加@FeignClient(name = "mango-producer")注入要调的服务名,
添加一个和调用目标一样的接口方法
@FeignClient(name = "mango-producer")
public interface MangoProducerService {
@RequestMapping("/hello")
public String hello();
}
第四步、添加控制器
添加FeignHelloController.java
注入MangoProducerService.java
@RestController
public class FeignHelloController {
@Autowired
private MangoProducerService mangoProducerService;
@RequestMapping("/feign/call")
public String call() {
// 像调用本地服务一样
return mangoProducerService.hello();
}
}
配置完后,重启项目,访问http://localhost:8005/feign/call
可以看到,我们也实现了负载均衡,第二种方法比较简单