欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

程序员文章站 2022-04-01 18:14:54
一 前言 几大RPC框架介绍 1.支持多语言的RPC框架,google的gRPC,Apache(facebook)的Thrift 2.只支持特定语言的RPC框架,例如新浪的Motan 3.支持服务治理等服务化特性的分布式框架,例如阿里的dubbo 4.拥有完整生态的spring cloud spri ......

一 前言

几大rpc框架介绍

1.支持多语言的rpc框架,google的grpc,apache(facebook)的thrift
2.只支持特定语言的rpc框架,例如新浪的motan
3.支持服务治理等服务化特性的分布式框架,例如阿里的dubbo
4.拥有完整生态的spring cloud
 

spring cloud远程调用方式---feign

feign是一个声明似的web服务客户端,它使得编写web服务客户端变得更加容易。使用fegin创建一个接口并对它进行注解。它具有可插拔的注解支持包括feign注解与jax-rs注解,feign还支持可插拔的编码器与解码器,spring cloud 增加了对 spring mvc的注解,spring web 默认使用了httpmessageconverters, spring cloud 集成 ribbon 和 eureka 提供的负载均衡的http客户端 feign。

feign提供了http请求的模板,通过编写简单的接口和插入注解,就可以定义好http请求的参数、格式、地址等信息。而feign则会完全代理http请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。springcloud对feign进行了封装,使其支持springmvc标准注解和httpmessageconverters。feign可以与eureka和ribbon组合使用以支持负载均衡。

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

官方解释: feign is a java to http client binder inspired by retrofitjaxrs-2.0, and websocket. feign's first goal was reducing the complexity of binding denominator uniformly to http apis regardless of restfulness.

 

feign的两种调用方式

1 直接在调用者声明feign客户端

如下图所示, service-b 声明一个接口, 去调用路径为 /user/get 的服务 service-a 的服务

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

2 在被调用者接口api中声明feign客户端

如下图, 在被调用者中声明接口, 去调用自己, 这种方法遵循面向接口编程, 而且使用起来, 就类似dubbo一样, @autowire直接注入就可以使用了.

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

以上可能看得读者一头雾水, 以下具体的代码流程, 可以方便更加具体的了解

 

二 案例1直接在调用者声明feign客户端代码实现

以下步骤为手把手教学, 请明白我的良苦用心

步骤0 创建一个springcloud-eureka注册中心

首先创建一个父项目

把其他都删除, 剩下pom文件

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

以下为父项目的依赖

<?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.fegin</groupid>
    <artifactid>test</artifactid>
    <version>0.0.1-snapshot</version>

    <packaging>pom</packaging>

    <!--springboot version 2.1.4-->
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>2.1.4.release</version>
        <relativepath/> <!-- lookup parent from repository -->
    </parent>


    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>greenwich.sr1</spring-cloud.version>
    </properties>

    <!--springcloud version greenwish.sr1-->
    <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>

</project>

 

创建eureka项目

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

项目结构如图

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

添加eureka的依赖

<?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">
    <parent>
        <artifactid>test</artifactid>
        <groupid>com.fegin</groupid>
        <version>0.0.1-snapshot</version>
    </parent>
    <modelversion>4.0.0</modelversion>

    <artifactid>eureka</artifactid>

    <!--eureka服务端配置-->
    <dependencies>
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-eureka-server</artifactid>
        </dependency>
    </dependencies>
</project>

 

添加application.yml

server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    # 是否把自己作为服务注册到其他服务注册中心
    registerwitheureka: false
    # 是否从其他的服务中心同步服务列表
    fetchregistry: false
    serviceurl:
      defaultzone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    # 关闭保护机制,默认true
    enable-self-preservation: false
    # 剔除失效服务间隔,默认60000
    eviction-interval-timer-in-ms: 3000

 

添加启动类代码eurekaapplication

/**
 * @author c-can-z
 */
@enableeurekaserver
@springbootapplication
public class eurekaapplication {

    public static void main(string[] args) {
        springapplication.run(eurekaapplication.class,args);
    }

}

 

浏览器输入 http://localhost:8761/

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

步骤1 准备一个服务servicea --- 该服务为被调用者

创建一个普通springboot服务, 服务名称为 service-a
在该服务上, 创建一个端口, 该端口为:9992
访问该路径:
http://localhost:9991/user/get?id=5
返回为: 恭喜5号, 18岁的美美小姐
 
分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)
分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)
 

步骤2 准备一个服务serviceb --- 该服务为调用者

创建一个serviceb

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

添加一下serviceb必须的依赖

<?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">
    <parent>
        <artifactid>test</artifactid>
        <groupid>com.fegin</groupid>
        <version>0.0.1-snapshot</version>
    </parent>
    <modelversion>4.0.0</modelversion>

    <artifactid>serviceb</artifactid>

    <dependencies>
        <!--springboot web-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <!--springboot 测试-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
        </dependency>
        <!--eureka客户端配置-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid>
        </dependency>

        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-ribbon</artifactid>
        </dependency>
        <!--微服务调用-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-openfeign</artifactid>
        </dependency>

        <!--zipkin客户端配置, 已经包含sleuth-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-zipkin</artifactid>
        </dependency>
    </dependencies>

</project>

 

把 application.properties 修改为 application.yml, 本服务名称为 service-b 端口为 9992

server:
  port: 9992
spring:
  application:
    name: service-b
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      probability: 1
eureka:
  client:
    serviceurl:
      defaultzone: http://localhost:8761/eureka/
    registry-fetch-interval-seconds: 5 #eureka client刷新本地缓存时间,默认30
  instance:
    prefer-ip-address: true
    #eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则),默认30
    lease-renewal-interval-in-seconds: 5
    #eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除(客户端告诉服务端按照此规则等待自己),默认90
    lease-expiration-duration-in-seconds: 7
feign:
  client:
    config:
      default:
        connecttimeout: 7000
        readtimeout: 7000
service-b:
  ribbon:
    nfloadbalancerruleclassname: com.netflix.loadbalancer.randomrule
logging:
  level:
    root: info

 

导入启动类文件

@springbootapplication
@enablefeignclients
public class serivebapplication {

    public static void main(string[] args) {
        springapplication.run(serivebapplication.class,args);
    }

}

 

创建feign客户端

//在创建该步骤的时候, 需要关注一下步骤1的说明
//@feignclient(name = "service-a")注解来绑定该接口对应servic-a服务
@feignclient(name = "service-a")
public interface userfeginclient {
    //service-a服务对应资源路径.必须加上@requestparam, 否则会报错,返回参数也必须对应上
    @requestmapping("user/get")
    string get(@requestparam("id")long id);
}

 

在controller中直接进行调用

@restcontroller
@requestmapping("/product")
public class productcontroller {

    @autowired
    private userfeginclient userfeginclient;

    @requestmapping("/get")
    public string get(long id){
        return "产品服务抽奖: "+userfeginclient.get(id);
    }
}

 

步骤3 测试feign调用效果

http://localhost:9992/product/get?id=5
分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)
 
到此, 是fegin最简单的用法
 
相信如果按照我步骤一步一步做的同学, 应该可以理解什么是:
service-b 声明一个接口, 去调用路径为 /user/get 的服务 service-a 的服务
 

三 案例2 在被调用者接口api中声明feign客户端代码实现

步骤1 创建servicecapi, 该api用来创建feign客户端

可以在以上的项目上进行改造
项目结构
分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)
 
serviceapi依赖
<?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">
    <parent>
        <artifactid>test</artifactid>
        <groupid>com.fegin</groupid>
        <version>0.0.1-snapshot</version>
    </parent>
    <modelversion>4.0.0</modelversion>

    <artifactid>servicec-api</artifactid>

    <dependencies>
        <!--lombok-->
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
        </dependency>

        <!--微服务调用-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-openfeign</artifactid>
        </dependency>
    </dependencies>
</project>

 

实体类product

public class product implements serializable {
    private long id;
    private string name;

    public long getid() {
        return id;
    }

    public void setid(long id) {
        this.id = id;
    }

    public string getname() {
        return name;
    }

    public void setname(string name) {
        this.name = name;
    }
}

 

feign客户端

/**
 * 服务名称
 * @author c-can-z
 */
@feignclient(name="service-c")
public interface productfeignapi {

    //动态代理需要的地址, 但是我们实际操作不到
    @requestmapping("/servicec/get")
    product get(@requestparam("id") long id);
}

 

步骤2 创建servicec服务

项目结构

 

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

service项目依赖

<?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">
    <parent>
        <artifactid>test</artifactid>
        <groupid>com.fegin</groupid>
        <version>0.0.1-snapshot</version>
    </parent>
    <modelversion>4.0.0</modelversion>

    <artifactid>servicec</artifactid>
    <dependencies>
        <!--springboot web-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <!--eureka客户端配置-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid>
        </dependency>

        <!--zipkin客户端配置, 已经包含sleuth-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-zipkin</artifactid>
        </dependency>

        <!--springboot 测试-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <!--导入api-->
        <dependency>
            <groupid>com.fegin</groupid>
            <artifactid>servicec-api</artifactid>
            <version>0.0.1-snapshot</version>
        </dependency>
    </dependencies>

</project>

 

servicec的 application.yml

server:
  port: 9993
spring:
  application:
    name: service-c
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      probability: 1
eureka:
  client:
    serviceurl:
      defaultzone: http://localhost:8761/eureka/
    registry-fetch-interval-seconds: 5 #eureka client刷新本地缓存时间,默认30
  instance:
    prefer-ip-address: true
    #eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则),默认30
    lease-renewal-interval-in-seconds: 5
    #eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除(客户端告诉服务端按照此规则等待自己),默认90
    lease-expiration-duration-in-seconds: 7
feign:
  client:
    config:
      default:
        connecttimeout: 7000
        readtimeout: 7000
logging:
  level:
    root: info

 

实现feign客户端接口, 也是该文章的核心代码

采用实现的方式

/**
 * 远程调用接口的实现类
 * @author c-can-z
 */
@restcontroller
public class productfeignclient implements productfeignapi {

    @override
    public product get(long id) {
        product product = new product();
        product.setid(id);
        product.setname("我是服务c");
        return product;
    }
}

 

servicec的启动类

@springbootapplication
public class productserverapplication {
    public static void main(string[] args) {
        springapplication.run(productserverapplication.class, args);
    }
}

 

步骤3 创建serviced服务去调用servicec

项目结构

注意启动类的位置, servicecapi的路径必须被启动类扫描到

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

serviced的依赖, 

注意, 必须引入servicec的api, 有人会说有代码侵入的问题, 但是对比案例1, 如果多个项目调用, 要创建多个feign客户端, 孰是孰非, 还得看项目的具体需求

<?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">
    <parent>
        <artifactid>test</artifactid>
        <groupid>com.fegin</groupid>
        <version>0.0.1-snapshot</version>
    </parent>
    <modelversion>4.0.0</modelversion>

    <artifactid>serviced</artifactid>

    <dependencies>
        <!--springboot web-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <!--springboot 测试-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
        </dependency>
        <!--eureka客户端配置-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid>
        </dependency>
        <!--微服务调用-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-openfeign</artifactid>
        </dependency>
        <!--zipkin客户端配置, 已经包含sleuth-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-zipkin</artifactid>
        </dependency>

        <dependency>
            <groupid>com.fegin</groupid>
            <artifactid>servicec-api</artifactid>
            <version>0.0.1-snapshot</version>
        </dependency>
    </dependencies>
</project>

 

serviced的 application.yml

server:
  port: 9994
spring:
  application:
    name: service-d
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      probability: 1
eureka:
  client:
    serviceurl:
      defaultzone: http://localhost:8761/eureka/
    registry-fetch-interval-seconds: 5 #eureka client刷新本地缓存时间,默认30
  instance:
    prefer-ip-address: true
    #eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则),默认30
    lease-renewal-interval-in-seconds: 5
    #eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除(客户端告诉服务端按照此规则等待自己),默认90
    lease-expiration-duration-in-seconds: 7
feign:
  client:
    config:
      default:
        connecttimeout: 7000
        readtimeout: 7000
logging:
  level:
    root: info

 

serviced的控制类

/**
 * @author c-can-z
 */
@restcontroller
@requestmapping("/order")
public class ordercontroller {

    @autowired
    private productfeignapi productfeignapi;

    @requestmapping("/get")
    public string get(long id){
        product product = productfeignapi.get(id);
        return "订单为: 货品:" + product.getname() + ", 货品id:"+product.getid();
    }
}

 

serviced的启动类

/**
 * @author c-can-z
 */
@springbootapplication
@enablefeignclients
public class servicedapplication {
    public static void main(string[] args) {
        springapplication.run(servicedapplication.class,args);
    }
}

 

步骤4 测试feign调用效果

http://localhost:9994/order/get?id=5

分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

 

四 总结

到了这里, 不知道你是否理解 直接在调用者声明feign客户端 或者 在被调用者接口api中声明feign客户端

直接在调用者声明feign客户端:

每一个服务调用其他服务, 就需要创建一个客户端, 从代码方面来说, 相对比较麻烦

在被调用者接口api中声明feign客户端:

从调用者来看, 使用起来就跟使用淘宝的dubbo一样方便, 但是每一个服务调用其他服务, 就需要引入其他服务的api依赖, 从项目之间的互相依赖来看, 相对来说, 也会比较麻烦.