07-01_SpringCloud_微服务间的通信_RestTemplate+Ribbon

7 微服务间通信

7.1 RestTemplate+Ribbon

7.1.1 微服务间通信的目的

在微服务架构下,服务和服务之间必不可少的会产生一些依赖,导致彼此之间会产生一些调用。
目前Spring Cloud提供了两种解决方案,其中一种就是“RestTemplate+Ribbon”。

微服务项目,会员服务就依赖于图书管理服务:

在这里插入图片描述

7.1.2 创建被调用服务接口

在book应用服务中:

在包com.marshal.springcloudsctsjybook.service.exception中创建BookNotFoundException类

package com.marshal.springcloudsctsjybook.service.exception;

public class BookNotFoundException extends RuntimeException{

    public BookNotFoundException(String msg){
        super();
    }
}  

在包com.marshal.springcloudsctsjybook.service中创建BookService的服务方法

public Book getInfo(Long bid){

    Optional<Book> optional = bookRepository.findById(bid);

    Book book = null;
    if(optional.isPresent()){
        book = optional.get();
    }else{
        throw new BookNotFoundException("BOOKID:" + bid + " not found");
    }

    return book;
}

在包com.marshal.springcloudsctsjybook.controller中编写BookController的方法

@GetMapping("/info")
private Map info(Long bid){

    Map result = new HashMap();

    try{
        Book book = bookService.getInfo(bid);
        result.put("code" , "0");
        result.put("message" , "success");
        result.put("data" , book);
    }catch (Exception e){
        e.printStackTrace();
        result.put("code" , e.getClass().getSimpleName());
        result.put("message" , e.getMessage());
    }

    return result;
}  

7.1.3 创建调用服务接口服务

7.1.3.1 第一种方式:使用Spring Cloud内置的RestTemplate对象

在包com.marshal.springcloudsctsjymember.controller的控制类MemberController中编写调用接口测试方法。
这是一种最简单的方式,其实RestTemplate底层进行的http传输就是使用的Apache的HttpClient组件。

@GetMapping
@ResponseBody
public String test(Long bid){

    RestTemplate restTemplate = new RestTemplate();
    String json = restTemplate.getForObject("http://localhost:8100/info?bid="+bid, String.class);

    System.out.println(json);
    return json;
}  

该种方式由于访问地址及端口号被写在代码中,维护不便。因此该种方式不推荐使用。

7.1.3.2 第二种方式:采用Ribbon进行客户端负载均衡

在包com.marshal.springcloudsctsjymember.controller的控制类MemberController中添加负载均衡客户端

@Resource
private LoadBalancerClient loadBalancerClient;

LoadBalancerClient是Ribbon的核心组件。

@GetMapping
@ResponseBody
public String test2(Long bid){

    RestTemplate restTemplate = new RestTemplate();
    // 获取服务列表
    ServiceInstance serviceInstance = loadBalancerClient.choose("book");
    // 获取主机名
    String host = serviceInstance.getHost();
    // 获取端口号
    Integer port = serviceInstance.getPort();
    String json = restTemplate.getForObject("http://"+ host + ":" + port + "/info?bid="+bid, String.class);

    System.out.println(json);
    return json;
}  

在这里插入图片描述

对于member这个应用服务来说,在程序运行之初通过运行程序

loadBalancerClient.choose("book")  

向注册中心Eureka发送与“book”相关的请求,获取服务相关的服务器地址。对于LoadBalance来说就会向获取的两个应用服务发送请求,进行两个被调用接口服务的应用的负载均衡服务。

但是这种方式并不优雅,主要目的是讲解原理,在实际开发中并不常用。所以可以采用注解的方式进行,则有第三种方式。

7.1.3.3 第三种方式:利用注解简化URL通信

在入口类com.marshal.springcloudsctsjymember.SpringCloudSctsjyMemberApplication中增加类RestTemplate的注册方法。

@Bean // 将返回的RestTemplate对象注入到IOC容器中
@LoadBalanced // 对RestTemplate进行负载均衡
public RestTemplate restTemplate(){
    return new RestTemplate();
}  

入口类全部代码:

package com.marshal.springcloudsctsjymember;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudSctsjyMemberApplication {

    @Bean // 将返回的RestTemplate对象注入到IOC容器中
    @LoadBalanced // 对RestTemplate进行负载均衡
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    public static void main(String[] args) {

        SpringApplication.run(SpringCloudSctsjyMemberApplication.class, args);
    }
}

在类com.marshal.springcloudsctsjymember.controller.MemberController中将

@Resource
private LoadBalancerClient loadBalancerClient;

进行注解掉,同时新增在入口类中注入的restTemplate对象

@Resource
private RestTemplate restTemplate;

并在该类中进行应用接口的注解方式调用

@GetMapping
@ResponseBody
public String test3(Long bid){

    String json = restTemplate.getForObject("http://book/info?bid=" + bid, String.class);
    return json;
}  

利用注解开发的时候,URL主机名和端口号都要换成serviceid。

7.1.4 微服务间的通信组件Feign

7.1.4.1 Feign的概念解释
  • Feign是一个声明web服务客户端,它出现唯一的目的就是为了简化微服务的通信过程
  • 使用Feign开发创建一个接口并使用Spring MVC注解
  • Feign集成Ribbon内置负载均衡
7.1.4.2 进行Feign的实现

1:首先需要在依赖包中增加对Feign的依赖支持

在pom.xml配置文件中增加依赖

<!-- 增加OpenFeign的dep -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2:在入口类com.marshal.springcloudsctsjymember.SpringCloudSctsjyMemberApplication中增加注解来启用Feign客户端。

@EnableFeignClients  

入口类代码被修改为

package com.marshal.springcloudsctsjymember;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients // 启用Feign客户端
public class SpringCloudSctsjyMemberApplication {

    @Bean // 将返回的RestTemplate对象注入到IOC容器中
    @LoadBalanced // 对RestTemplate进行负载均衡
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    public static void main(String[] args) {

        SpringApplication.run(SpringCloudSctsjyMemberApplication.class, args);
    }
}

3:在member应用中新建包bookclient及接口BookClient

在包com.marshal.springcloudsctsjymember.bookclient中创建接口BookClient

package com.marshal.springcloudsctsjymember.bookclient;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

// book是service-id
// @FeignClient(name="book") 指明这是Book微服务的调用客户端
@FeignClient(name="book")
public interface BookClient {

    // 当调用getInfo方法的时候,自动向book微服务的/info发起请求
    @GetMapping("/info")
    // 调用时自动会将?bid=XXX附加到url中
    public String getInfo(@RequestParam("bid") Long bid);
}

4:在微服务member中增加使用Feign的调用方法

在包com.marshal.springcloudsctsjymember.controller中的类MemberController中增加测试方法

首先在MemberController中将接口BookClient进行注入

@Resource
private BookClient bookClient;  

编写测试方法

@GetMapping
@ResponseBody
public String test(Long bid){
    String json = bookClient.getInfo(bid);
    return json;
}

举例来说,如果使用post请求体访问则在BookClient接口中进行如下代码编写

@PostMapping("/create")
public String create(@RequestBody Map rcc);  

注意:

  • Get请求对应GetMapping
    Get请求使用“@RequestParam”注解发送参数
  • Post请求对应PostMapping
    Post请求发送请求体使用“@RequestBody”注解发送

7.1.5 对实际返回数据结果的加工处理

在进行远程微服务的接口方法调用时,往往的实际结果为

{"code":"0",
 "data":
    {"bid":3,
     "sn":"9787121331084",
     "name":"疯狂Java讲义(第4版)",
     "author":"李刚",
     "publishing":"电子工业出版社",
     "bprice":36.0,
     "sprice":109.0,
     "btype":"计算机与互联网",
     "stock":99},
 "message":"success"}

可是真正需要的是中间的那个部分Book实体类的相关属性。

解决方法是只需要按照以上结构创建对象就可以了。

在包com.marshal.springcloudsctsjymember.bookclient中新建实体类。
该类与微服务book中的实体类是一样的。

package com.marshal.springcloudsctsjymember.bookclient;

public class BookDTO {

    private Long bid;
    private String sn;
    private String name;
    private String author;
    private String publishing;
    private Float bprice;
    private Float sprice;
    private String btype;
    private Integer stock;

    public Long getBid() {
        return bid;
    }

    public void setBid(Long bid) {
        this.bid = bid;
    }

    public String getSn() {
        return sn;
    }

    public void setSn(String sn) {
        this.sn = sn;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getPublishing() {
        return publishing;
    }

    public void setPublishing(String publishing) {
        this.publishing = publishing;
    }

    public Float getBprice() {
        return bprice;
    }

    public void setBprice(Float bprice) {
        this.bprice = bprice;
    }

    public Float getSprice() {
        return sprice;
    }

    public void setSprice(Float sprice) {
        this.sprice = sprice;
    }

    public String getBtype() {
        return btype;
    }

    public void setBtype(String btype) {
        this.btype = btype;
    }

    public Integer getStock() {
        return stock;
    }

    public void setStock(Integer stock) {
        this.stock = stock;
    }
}

然后在相同的包中再创建一个RestResult类。

package com.marshal.springcloudsctsjymember.bookclient;

public class RestResult {

    private String code;
    private String message;
    private BookDTO data;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public BookDTO getData() {
        return data;
    }

    public void setData(BookDTO data) {
        this.data = data;
    }

}

接下来只需要将BookClient接口返回的结果修改为RestResult就可以了

@GetMapping("/info")
public RestResult getInfo(@RequestParam("bid") Long bid);

而对于调用服务的MemberController中的方法则可以修改为

@GetMapping
@ResponseBody
public String test(Long bid){
    RestResult restResult = bookClient.getInfo(bid);
    return restResult.getData().getName();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值