Spring Cloud系列教程(二) - 服务消费者Rest+Ribbon(Finchley版本)

一、前言

在上一篇文章:SpringCloud系列教程(一):服务的注册与发现(Eureka) 中,讲解了如何搭建一个Eureka服务注册与发现的注册中心,有注册那么就有服务提供者,有发现那么就有服务消费者,这篇文章我们会服务注册到Eureka和消费者从Eureka注册中心消费服务接口;

二、SpringCloud如何实现RPC远程调用

在一个大型的微服务项目中,很多个服务都会被拆分成一个个独立的服务,服务与服务之间通过RPC进行服务与服务之间的调用,并且采用HTTP+JSON协议进行通讯(Restful)SpringCloud中进行服务消费有两种方式调用:一种是rest+ribbon,另一种是fegin,feign默认集成并开启了Ribbon负载均衡,

1. Rest方式调用原理

其中rest方式由Spring web组件提供RestTemplate API方式来进行调用,其实rest方式底层原理是采用HttpClient技术进行调用,通过引入eureka组件的依赖后,分析依赖信息我们可以看出,其实在eureka其底层默认集成了Ribbon负载均衡器的,在使用rest方式进行服务消费调用时,ribbon负载均衡器默认会在本地通过负载均衡算法去实现负载均衡效果(默认算法是轮询机制);

2. Feign方式调用原理(推荐)

Feign是一个声明式的伪Http客户端,Feign伪装成类似SpringMVC的Controller一样。无需自己拼接url,拼接参数,服务消费者无需知道服务提供者的ip和端口,只需要指定服务名,即可通过注册中心调用到目标服务,这一切工作都交给Feign去做;同时Feign提供了接口和注解方式进行调用。

三、准备示例

以一个在电商项目中 "订单服务调取会员服务" 接口为例,将服务提供者(会员服务)注册到Eureka注册中心,服务消费者(订单服务) 从注册中心获取会员服务进行调用,具体调用关系如图所示:

 

Spring Cloud系列教程(二) - 服务消费者Rest+Ribbon(Finchley版本)_第1张图片

 

四、环境搭建

这篇文章基于上一篇,需要用到eureka注册中心,在搭建服务提供者和服务消费者之前,需要先启动我们的eureka服务,然后新建三个工程:springcloud-ribbon(父工程,pom类型)、springcloud-app-member(服务提供)、springcloud-app-order(服务消费),具体创建项目过程这里不再累赘,我只贴出pom依赖信息

4.1、搭建父工程springcloud-ribbon

 1. springcloud-ribbon依赖信息



    4.0.0

    com.thinkingcao
    springcloud-ribbon
    0.0.1-SNAPSHOT
    springcloud-ribbon
    Demo project for Spring Boot

    pom

    
        springcloud-eureka-server
        springcloud-app-member
        springcloud-app-order
    


4.2、搭建服务提供者springcloud-app-member

1. springcloud-app-member依赖信息

 
    
        org.springframework.boot
        spring-boot-starter-parent
        2.0.3.RELEASE
        
    

    com.thinkingcao
    springcloud-app-member
    0.0.1-SNAPSHOT
    springcloud-app-member
    SpringCloud整合Eureka组件之注册服务消费者

    
    
        UTF-8
        UTF-8
        1.8
        Finchley.RELEASE
    

    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

    
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        
    

    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

2. 配置application.yml

##=========服务提供者-会员服务配置========
#服务端口号
server:
  port: 8762
#定义服务名称(服务注册到eureka名称)
spring:
  application:
    name: app-thinkingcao-member
#在此指定服务注册中心地址,将当前会员服务注册到eureka注册中心上
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8000/eureka
    #启动注册操作,该值默认为true。若设置为fasle将不会启动注册操作。是否需要去检索寻找服务,默认是true
    register-with-eureka: true
    #是否需要从eureka上获取注册信息
    fetch-registry: true

  ##心跳检测与续约时间(测试环境和本地开发环境将值设置小一点,保证服务关闭后,注册中心能够及时踢出)
  instance:
    #客户端向Eureka注册中心发送心跳的时间间隔,单位为秒(默认为30s),(客户端会按照此规则向Eureka服务端发送心跳检测包)
    lease-renewal-interval-in-seconds: 2
    #Eureka注册中心在收到客户端最后一次心跳之后等待的时间上限,单位为秒(默认为90s),超过时间则剔除(客户端会按照此规则向Eureka服务端发送心跳检测包)
    lease-expiration-duration-in-seconds: 2

3. MemberController 中定义一个获取会员信息的接口

会员服务接口这里我们返回端口号是为了后面演示订单以rest+ribbon方式调用会员服务时,当会员服务为2台集群环境,ribbon会在本地使用负载均衡算法,为我们自动做了负载均衡,默认是使用轮询机制;

package com.thinkingcao.api.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @desc:   会员服务接口
 * @author: cao_wencao
 * @date: 2020-02-19 18:12
 */
@RestController
public class MemberController {

    @Value("${server.port}")
    private String serverPort;

    //会员服务接口
    @RequestMapping("/getMember")
    private String getMember() {
        return "我是会员服务,订单服务调用会员服务成功啦, 端口号为: " + serverPort;
    }
}

4. 开启Eureka客户端

springcloud-app-member工程的启动类中,通过@EnableEurekaClient 注解或者@EnableDiscoveryClient 注解,作用是表明启用向注册中心注册服务功能,

@EnableDiscoveryClient和@EnableEurekaClient共同点就是:都是能够让注册中心能够发现,扫描到改服务。

不同点:@EnableEurekaClient只适用于Eureka作为注册中心,@EnableDiscoveryClient 可以是其他注册中心,spring cloud中discovery service有许多种实现(eureka、consul、zookeeper等等),如果采用consul或者zookeeper作为注册中心,那么就是用@EnableDiscoveryClient注解,@EnableEurekaClient只是eureka的专属注解;

注意: 从SpringCloud Edgware开始,@EnableDiscoveryClient 或@EnableEurekaClient可以省略。只需加上相关注册中心依赖,并在application.yml进行相应配置,即可将微服务注册到服务发现组件上,上面我们yml中配置了,所以这里也可以省略@EnableEurekaClient注解。

 AppMemberProvider.java启动类

package com.thinkingcao.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class AppMemberProvider {

    public static void main(String[] args) {
        SpringApplication.run(AppMemberProvider.class, args);
    }

}

5.  springcloud-app-member会员项目结构

Spring Cloud系列教程(二) - 服务消费者Rest+Ribbon(Finchley版本)_第2张图片

 

4.3、搭建服务消费者springcloud-app-order

 1. springcloud-app-order依赖信息


    
        org.springframework.boot
        spring-boot-starter-parent
        2.0.3.RELEASE
        
    

    com.thinkingcao
    springcloud-app-order
    0.0.1-SNAPSHOT
    springcloud-app-order
    SpringCloud整合Eureka组件之注册服务提供者

    
    
        UTF-8
        UTF-8
        1.8
        Finchley.RELEASE
    

    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

    
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        
    

    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

2. 配置application.yml

##=========服务消费者-订单服务配置========
#服务端口号
server:
  port: 8761
#定义服务名称(服务注册到eureka名称)
spring:
  application:
    name: app-thinkingcao-order
#在此指定服务注册中心地址,将当前订单服务注册到eureka注册中心上
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8000/eureka
    #启动注册操作,该值默认为true。若设置为fasle将不会启动注册操作。是否需要去检索寻找服务,默认是true
    register-with-eureka: true
    #是否需要从eureka上获取注册信息
    fetch-registry: true

  ##心跳检测与续约时间(测试环境和本地开发环境将值设置小一点,保证服务关闭后,注册中心能够及时踢出)
  instance:
    #客户端向Eureka注册中心发送心跳的时间间隔,单位为秒(默认为30s),(客户端会按照此规则向Eureka服务端发送心跳检测包)
    lease-renewal-interval-in-seconds: 2
    #Eureka注册中心在收到客户端最后一次心跳之后等待的时间上限,单位为秒(默认为90s),超过时间则剔除(客户端会按照此规则向Eureka服务端发送心跳检测包)
    lease-expiration-duration-in-seconds: 2

3. 使用rest方式消费服务

 1. 订单在调用会员服务的时候,采用RestTemplate调用,RestTemplate有3种调用方式,分别如下:

  • 一:直接使用IP地址调用(但是不会走注册中心,这种方式不推荐)。
  • 二:采用服务别名方式调用,需要依赖ribbon负载均衡器,因为在使用别名方式时,基于ribbon会实现负载均衡效果,使用别名方式会去注册中心上获取对应的服务调用地址,然后ribbon在本地使用负载均衡算法,做负载均衡,同样也是使用HttpClient;
  • 三:利用LoadBalancerClient通过应用的服务名称获取调用地址URL+端口号,然后再使用restTemplate进行调用

 第一种:以IP地址直接调用(不推荐)

1. 需要向ioc注入一个bean: restTemplate

package com.thinkingcao.api.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @desc:  订单调用会员服务接口
 * @author: cao_wencao
 * @date: 2020-02-19 17:47
 */
@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    //订单服务(消费者)调用会员服务(生产者)接口
    @RequestMapping("/getOrderToMember")
    public String getOrderToMember() {
        String url = "http://127.0.0.1:8762/getMember";
        String result = restTemplate.getForObject(url, String.class);
        return result;
    }


    // 有两种方式调用,一种是采用服务别名方式调用,使用别名去注册中心上获取对应的服务调用地址,另一种是直接调用 
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

第二种:采用服务别名方式调用

 1. 向程序的ioc注入一个bean: restTemplate;并通过@LoadBalanced注解, 该注解表明这个restRemplate在底层使用HTTPClient调用时,本地会开启负载均衡的功能

package com.thinkingcao.api.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @desc:  订单调用会员服务接口
 * @author: cao_wencao
 * @date: 2020-02-19 17:47
 */
@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    //订单服务(消费者)调用会员服务(生产者)接口
    @RequestMapping("/getOrderToMember")
    public String getOrderToMember() {
        String url = "http://app-thinkingcao-member/getMember";
        String result = restTemplate.getForObject(url, String.class);
        return result;
    }


    // 有两种方式调用,一种是采用服务别名方式调用,另一种是直接调用 使用别名去注册中心上获取对应的服务调用地址
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

第三种:  使用LoadBalancerClient获取服务调用地址

package com.thinkingcao.api;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @desc:  订单调用会员服务接口
 * @author: cao_wencao
 * @date: 2020-02-19 17:47
 */
@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    //订单服务(消费者)调用会员服务(生产者)接口
    @RequestMapping("/getOrderToMember")
    public String getOrderToMember() {
        //String url = "http://app-thinkingcao-member/getMember";
        ServiceInstance serviceInstance = loadBalancerClient.choose("app-thinkingcao-member");
        String response = String.format("http://%s:%s",serviceInstance.getHost(), serviceInstance.getPort()+"/getMember");
        System.out.println("response= " + response);
        return response;
    }


    // 有两种方式调用,一种是采用服务别名方式调用,另一种是直接调用 使用别名去注册中心上获取对应的服务调用地址
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

 

4. 启动类

package com.thinkingcao.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class AppMemberProvider {

    public static void main(String[] args) {
        SpringApplication.run(AppMemberProvider.class, args);
    }

}

五、开始测试

1. 开启会员服务多个实例

上述springcloud-eureka-server的端口号为8000、springcloud-app-order为8761、springcloud-app-member为8762,首先我们启动eureka注册中心,然后在启动会员服务、然后再启动订单服务,顺序是:注册中心、服务提供者、服务消费者 , 上面说了我们要开启会员服务集群功能,测试ribbon本地是否做了负载均衡,所以我们这里为了简单就不新建会员项目了,直接启动会员服务多个实例: SpringBoot 系列教程(八十二):Intellij IDEA实现SpringBoot项目启动多个端口

第一步:首先设置允许多个实例运行

Spring Cloud系列教程(二) - 服务消费者Rest+Ribbon(Finchley版本)_第3张图片

第二步:修改springcloud-app-membe端口号为8765,然后再次运行会员服务

Spring Cloud系列教程(二) - 服务消费者Rest+Ribbon(Finchley版本)_第4张图片

2. 访问Eureka注册中心

访问Eureka注册中心,URL地址 : http://127.0.0.1:8000/, 两台会员服务和一台订单服务已经注册到Eureka注册中心了

Spring Cloud系列教程(二) - 服务消费者Rest+Ribbon(Finchley版本)_第5张图片

 

观察Eureka注册中心服务列表,左侧代表服务的别名,右侧代表服务调用地址:

APP-THINKINGCAO-ORDER n/a (1) (1) UP (1) - DESKTOP-JBFTEEM:app-thinkingcao-order:8761

APP-THINKINGCAO-ORDER: 表示会员服务别名

DESKTOP-JBFTEEM:app-thinkingcao-order:8761 : 表示会员服务别名所对应的服务真实调用地址

3. 访问订单接口

URL: http://127.0.0.1:8761/getOrderToMember   ,在浏览器上多次访问该地址,会交替显示结果

我是会员服务,订单服务调用会员服务成功啦, 端口号为: 8765

我是会员服务,订单服务调用会员服务成功啦, 端口号为: 8762

Spring Cloud系列教程(二) - 服务消费者Rest+Ribbon(Finchley版本)_第6张图片

Spring Cloud系列教程(二) - 服务消费者Rest+Ribbon(Finchley版本)_第7张图片

4. 结论

从上一步说明当我们通过调用restTemplate.getForObject(“http://app-thinkingcao-member/getMember,String.class)方法时,ribbon在本地使用负载均衡算法已经做了负载均衡,访问了不同的端口的服务实例。

五、源码

1. 项目源码: https://github.com/Thinkingcao/SpringCloudLearning/tree/master/springcloud-ribbon

六、SpringCloud系列教程

1. SpringCloud系列教程(三): Spring Cloud系列教程(三) - 实现高可用集群环境搭建Eureka(Finchley版本)

 

SpringCloud教程汇总: Spring Cloud系列教程(汇总篇):专栏汇总篇(持续更新中)

你可能感兴趣的:(#,Spring,Cloud2.x系列教程)