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注册中心,服务消费者(订单服务) 从注册中心获取会员服务进行调用,具体调用关系如图所示:

 

 

四、环境搭建

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

4.1、搭建父工程springcloud-ribbon

 1. springcloud-ribbon依赖信息

<?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>

    <groupId>com.thinkingcao</groupId>
    <artifactId>springcloud-ribbon</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-ribbon</name>
    <description>Demo project for Spring Boot</description>

    <packaging>pom</packaging>

    <modules>
        <module>springcloud-eureka-server</module>
        <module>springcloud-app-member</module>
        <module>springcloud-app-order</module>
    </modules>

</project>

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

1. springcloud-app-member依赖信息

 <!--SpringBoot依赖版本-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/>
    </parent>

    <groupId>com.thinkingcao</groupId>
    <artifactId>springcloud-app-member</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-app-member</name>
    <description>SpringCloud整合Eureka组件之注册服务消费者</description>

    <!--项目编码、jdk版本、SpringCloud版本定义-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

    <!--声明管理SpringCloud版本依赖信息-->
    <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>

    <dependencies>
        <!-- SpringBootWeb组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- springcloud整合eureka客户端组件   -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

    <!--maven插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

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会员项目结构

 

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

 1. springcloud-app-order依赖信息

<!--SpringBoot依赖版本-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/>
    </parent>

    <groupId>com.thinkingcao</groupId>
    <artifactId>springcloud-app-order</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-app-order</name>
    <description>SpringCloud整合Eureka组件之注册服务提供者</description>

    <!--项目编码、jdk版本、SpringCloud版本定义-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

    <!--声明管理SpringCloud版本依赖信息-->
    <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>

    <dependencies>
        <!-- SpringBootWeb组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- springcloud整合eureka客户端组件,eureka默认整合了ribbon   -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

    <!--maven插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

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项目启动多个端口

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

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

2. 访问Eureka注册中心

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

 

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

APP-THINKINGCAO-ORDERn/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

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系列教程(汇总篇):专栏汇总篇(持续更新中)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thinkingcao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值