乐趣区

跟我学SpringCloud-第三篇服务的提供与Feign调用

Springboot: 2.1.6.RELEASE

SpringCloud: Greenwich.SR1

如无特殊说明,本系列教程全采用以上版本

上一篇,我们介绍了注册中心的搭建,包括集群环境吓注册中心的搭建,这篇文章介绍一下如何使用注册中心,创建一个服务的提供者,使用一个简单的客户端去调用服务端提供的服务。

本篇文章中需要三个角色,分别是服务的提供者,服务的消费者,还有一个是上一篇文章的主角——注册中心 Eureka(使用单机版本即可,本篇的示例也会使用单机版本的 Eureka)。

整体流程为:

  1. 先启动注册中心 Eureka
  2. 启动服务的提供者将提供服务,并将服务注册到注册中心 Eureka 上
  3. 启动服务的消费者,在注册中心中找到服务并完成消费

1. 服务提供者

1. pom.xml

<?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 http://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.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.springcloud</groupId>
    <artifactId>producer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>producer</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

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

2. 配置文件 application.yml

server:
  port: 8080
spring:
  application:
    name: spring-cloud-producer
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

3. 启动类 ProducerApplication.java

增加 @EnableEurekaClient,如果是其他注册中心可以使用注解 @EnableDiscoveryClient 来进行服务的注册

package com.springcloud.producer;

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

@SpringBootApplication
@EnableEurekaClient
public class ProducerApplication {public static void main(String[] args) {SpringApplication.run(ProducerApplication.class, args);
    }

}

4. Controller

package com.springcloud.producer.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created with IntelliJ IDEA.
 *
 * @Date: 2019/7/2
 * @Time: 0:02
 * @email: inwsy@hotmail.com
 * Description:
 */
@RestController
public class HelloController {@RequestMapping("/hello")
    public String hello(@RequestParam String name) {return "hello"+name+",producer is ready";}
}

先在可以先启动上一篇当中单机版的 Eureka,再启动我们刚写好的 producer 服务提供者,启动成功后,访问链接 http://localhost:8761/,可以看到我们的的服务提供者 producer 已经成功注册在注册中心上了。

至此,服务的提供者已经配置完成。

2. 服务消费者

1. pom.xml

<?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 http://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.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.springcloud</groupId>
    <artifactId>consumers</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>consumers</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

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

spring-boot-starter-web: 这个包是通用的 web 开发包,里面包含了 spring-web、spring-webmvc 等包

spring-cloud-starter-openfeign: 这个包是 springcloud 对于 Feign 的封装,Feign 是一个声明式的 Web 服务客户端。它支持 Feign 本身的注解、JAX-RS 注解以及 SpringMVC 的注解。Spring Cloud 集成 Ribbon 和 Eureka 以在使用 Feign 时提供负载均衡的 http 客户端。

2. 配置文件 application.yml

server:
  port: 8081
spring:
  application:
    name: spring-cloud-consumers
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

3. 启动类 ConsumersApplication.java

同上,增加 @EnableEurekaClient,如果是其他注册中心可以使用注解 @EnableDiscoveryClient 来进行服务的注册

package com.springcloud.consumers;

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

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumersApplication {public static void main(String[] args) {SpringApplication.run(ConsumersApplication.class, args);
    }

}

@EnableFeignClients: 这个注解是通知 SpringBoot 在启动的时候,扫描被 @FeignClient 修饰的类,@FeignClient 这个注解在进行远程调用的时候会用到。

4. Feign 远程调用

Feign 是一个声明式 Web Service 客户端。使用 Feign 能让编写 Web Service 客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持 JAX-RS 标准的注解。Feign 也支持可拔插式的编码器和解码器。Spring Cloud 对 Feign 进行了封装,使其支持了 Spring MVC 标准注解和 HttpMessageConverters。Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。

创建一个 remote 接口

package com.springcloud.consumers.remote;

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

/**
 * @Author: shiyao.wei
 * @Date: 2019/7/2 11:14
 * @Version: 1.0
 * @Desc:
 */
@FeignClient(name= "spring-cloud-producer")
public interface HelloRemote {@RequestMapping(value = "/hello")
    String hello(@RequestParam(value = "name") String name);
}
  • name:远程服务名,及 spring.application.name 配置的名称
  • 此类中的方法和远程服务中 contoller 中的方法名和参数需保持一致

5. web 层调用远程接口 Controller

package com.springcloud.consumers.controller;

import com.springcloud.consumers.remote.HelloRemote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: shiyao.wei
 * @Date: 2019/7/2 11:25
 * @Version: 1.0
 * @Desc:
 */
@RestController
public class HelloController {
    @Autowired
    HelloRemote helloRemote;

    @RequestMapping("/hello/{name}")
    public String index(@PathVariable("name") String name) {return helloRemote.hello(name);
    }
}

现在,一个最简单的服务注册和调用的例子就完成了。

3. 测试

简单调用

顺次启动 eureka、producer、consumer 三个项目

启动成功后,先在浏览器输入 http://localhost:8080/hello?name=springcloud

可以看到页面显示:hello springcloud,producer is ready

证明我们的 producer 已经正常启动,提供的服务也正常

接下来,我们测试服务消费者,在浏览器中输入:http://localhost:8081/hello/spring

可以看到页面显示:hello spring,producer is ready

说明客户端已经成功的通过 feign 调用了远程服务 hello,并且将结果返回到了浏览器。

负载均衡

将上面的 producer 复制一份,修改名称为 producer2,修改 pom.xml 中的 <name></name> 为 producer2,修改其中的 Controller:

package com.springcloud.producer.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created with IntelliJ IDEA.
 *
 * @Date: 2019/7/2
 * @Time: 0:02
 * @email: inwsy@hotmail.com
 * Description:
 */
@RestController
public class HelloController {@RequestMapping("/hello")
    public String hello(@RequestParam String name) {return "hello"+name+",producer2 is ready";}
}

修改 application.yml 配置文件启动端口为 8082

启动我们刚复制好的 producer2,这时可以看一下注册中心 Eureka,我们现在已经有两个 producer 服务了。

这时我们再去访问:http://localhost:8081/hello/spring

第一次返回结果:hello spring,producer is ready

第二次返回结果:hello spring,producer2 is ready

连续刷新页面,两个结果会交替出现,说明注册中心提供了服务负载均衡功能。将服务数提高到 N 个,会发现测试结果一样,请求会自动轮询到每个服务端来处理。

好了,现在可以将代码打包扔到 Github 上去了,欢迎大家前往 Github 骚扰:)

示例代码 -Github

退出移动版