SpringCloud中服务之间的两种调用RESTful接口通讯的方式:java
RestTemplate是一个Http客户端,相似于HTTPClient,org但比HTTPClient更简单。咱们经过RestTemplate来简单演示一下服务之间的调用,咱们使用两个服务来作演示。一个商品服务,一个订单服务。首先建立一个商品服务工程:web
选择相应的依赖:算法
项目建立完成后,编辑配置文件,须要配置服务的名称以及服务注册中心的地址:spring
spring: application: name: product eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true
注:若是对eureka还不太清楚的话,能够参考个人另外一篇关于eureka的文章:Spring Cloud Eureka-服务注册与发现bash
不要忘了在启动类中,加上@EnableEurekaClient
注解:架构
package org.zero.example.product; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class ProductApplication { public static void main(String[] args) { SpringApplication.run(ProductApplication.class, args); } }
接着建立一个controller类,用于模拟商品列表接口,提供给订单服务调用。代码以下:app
package org.zero.example.product.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; /** * @program: product * @description: product demo * @author: 01 * @create: 2018-09-06 22:09 **/ @RestController @RequestMapping("/product") public class ProductController { /** * 模拟商品列表接口 * * @return product list */ @GetMapping("/list") public List<String> list() { List<String> productList = new ArrayList<>(); productList.add("肥皂"); productList.add("可乐"); return productList; } }
而后启动项目,启动完成后,此时,在eureka的信息面板上应该能够看到product注册上去了,以下:负载均衡
商品服务准备好后,使用一样的步骤建立order项目,这里就再也不赘述了。配置文件中除了服务名称需为order,其余的配置项和product同样。由于8080已经被product服务占用了,因此还须要手动设置一下项目的端口号:dom
新建一个ClientController类,咱们来看看RestTemplate的第一种使用方式,代码以下:maven
package org.zero.example.order.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; /** * @program: order * @description: order demo * @author: 01 * @create: 2018-09-06 22:24 **/ @RestController @RequestMapping("/order") public class OrderController { @GetMapping("/info") public List info() { // 1.第一种方式,直接使用。缺点:须要指定url地址,不灵活,也没法适应多个地址 RestTemplate restTemplate = new RestTemplate(); return restTemplate.getForObject("http://localhost:8080/product/list", List.class); } }
写完后启动项目,能够看到order服务也注册到eureka上了:
接口测试结果以下,能够看到成功调用了商品服务的接口:
而后是RestTemplate的第二种使用方式,代码以下:
... import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; @RestController @RequestMapping("/order") public class OrderController { @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/info") public List info() { // 2.第二种方式,借助LoadBalancerClient获取服务实例,缺点:须要拼接url依旧不灵活 RestTemplate restTemplate = new RestTemplate(); // 参数传的是服务注册的id ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT"); String url = String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/product/list"); return restTemplate.getForObject(url, List.class); } }
接着是RestTemplate的第三种使用方式,这种方式下咱们须要先建立一个配置类,代码以下:
package org.zero.example.order.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * @program: order * @description: RestTemplate Config * @author: 01 * @create: 2018-09-06 22:47 **/ @Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
而后在controller中注入使用,这种方式虽然最简洁,其实本质上仍是第二种方式:
... @RestController @RequestMapping("/order") public class OrderController { @Autowired private RestTemplate restTemplate; @GetMapping("/info") public List info() { // 3.第三种方式,利用@LoadBalanced注解,可在restTemplate里使用应用名称进行调用 return restTemplate.getForObject("http://PRODUCT/product/list", List.class); } }
eureka是客户端发现机制的,因此使用的是客户端负载均衡器,所谓客户端负载均衡,也就是说负载的策略在客户端完成,俗称软负载。若是咱们的商品服务部署在多个节点上的话,当使用Feign进行服务调用的时候,默认会使用Ribbon来作负载均衡。固然使用RestTemplate的时候也是能够结合Ribbon作负载均衡的,例如上一小节中演示的第2、三种使用RestTemplate的方式就是结合了Ribbon。
Ribbon是Netflix发布的负载均衡器,是一种客户端负载均衡器,运行在客户端上,它有助于控制HTTP和TCP的客户端的行为。为Ribbon配置服务提供者地址后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为咱们提供了不少负载均衡算法,例如轮询、随机等。固然,咱们也可为Ribbon实现自定义的负载均衡算法。
咱们在配置文件中能够自定义负载均衡策略,以下:
PRODUCT: # 服务的名称 ribbon: # 负载均衡器 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 规则完整的类名,这里使用的是随机
注:如非必须,通常使用默认的轮询策略便可
Ribbon特性:
在Spring Cloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。下图展现了Ribbon与Eureka配合使用时的架构:
Feign是从Netflix中分离出来的轻量级项目,是一个声明式的REST客户端,它的出现使得咱们在服务中编写REST客户端变得更加容易。利用 Feign 能够建立一个接口并对它进行注解,该接口就会具备可插拔的注解支持包括Feign注解与JAX-RS注解,Feign还支持可插拔的编码器与×××。Feign 灵感来源于Retrofit、JAXRS-2.0和WebSocket,Feign 最初是为了下降统一绑定 Denominator 到 HTTP API 的复杂度,不区分是否支持 Restful。
Spring Cloud 增长了对 Spring MVC的注解,Spring Web 默认使用了HttpMessageConverters, Spring Cloud 集成 Ribbon 和 Eureka 提供的负载均衡的HTTP客户端 Feign。
Feign特性:
接下来咱们尝试一下使用Feign编写REST客户端,实现订单服务调用商品服务接口,看看Feign到底有多方便。在商品和订单服务的项目中,都加入Feign的依赖,pom.xml文件配置的依赖以下:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
注:若出现使用阿里云的仓库地址不能下载到该依赖的话,能够尝试使用maven中央仓库的地址进行下载
首先到商品服务工程中,新建一个client包。原本应该是新建一个Client模块的,可是为了方便演示,我就直接用包了。在client包下新建一个 ProductClinet 接口,编写代码以下:
package org.zero.example.product.client; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; @Component // 此注解用于声明一个Feign客户端,name属性指定服务的名称 @FeignClient(name = "PRODUCT") public interface ProductClinet { /** * 商品列表接口,注意这里的uri要写全 * * @return Product List */ @GetMapping("/product/list") List<String> list(); }
咱们在使用RestTemplate的时候,都是在订单服务上编写接口调用相关代码的,可是为何使用Feign就在商品服务上去写这个代码呢?这是由于使用Feign的时候,只须要经过注解就能在接口上声明客户端,当咱们在订单服务里面使用的时候,注入这个ProductClinet接口调用相应的方法便可实现商品服务接口的调用。而这些接口属于商品服务对外暴露的接口,因为职责的关系,因此都应该由商品服务去维护,不该该写在订单服务里。
编写好ProductClinet接口的代码后,使用以下命令将这个项目安装到本地的maven仓库中:
mvn clean -Dmaven.test.skip=true install
接着到订单服务的工程中,加入商品服务的依赖,以下:
<dependency> <groupId>org.zero.example</groupId> <artifactId>product</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
而后在启动类中,加上@EnableFeignClients
注解表示开启 Feign 客户端以及配置client包的路径,以下:
package org.zero.example.order; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; @EnableEurekaClient @SpringBootApplication @EnableFeignClients(basePackages = "org.zero.example.product.client") public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
修改 OrderController 代码以下:
... import org.zero.example.product.client.ProductClinet; @RestController @RequestMapping("/order") public class OrderController { @Autowired private ProductClinet productClinet; @GetMapping("/info") public List info() { return productClinet.list(); } }
重启这两个项目,测试接口以下: