k8s中使用Feign实现服务间调用

在spring cloud中服务之间的调用我们通常是通过Feign来完成的。Feign作为一个声明式WebService客户端,使用非常的简单,通过在我们的接口上添加@FeignClient
注解,我们很容易就实现一个服务调用的客户端。使用注解也可以减少开发的代码量,可以说非常的方便。另外Feign内部也集成了Ribbon从而自动帮我们实现客户端的负载均衡,可以说是spring cloud微服务的必用组件。

一、背景

通常我们使用spring cloud进行微服务开发的时候我们通常会配置相应的注册中心,比如:Eureka、Nacos、Consul
,这样Feign在进行服务调用的时候一般会到注册中心定位具体的服务地址,然后在通过Ribbon实现路由到具体的服务节点,从而实现服务的调用。这次我们项目小组在做项目改造的时候技术栈虽然也选择了spring cloud,但是我们并没有完全按照一般的模式进行开发。最开始的时候我们确定注册中心使用的是Nacos,但是通过和其他部门的商议,决定放弃使用Nacos,而使用k8s原生的服务发现功能(微服务部署在k8s集群)。所以随之而来的就是使用spring cloud kubernets。感兴趣的可以看官方的介绍:Spring Cloud Kubernetes,自己在家办公期间也看了一些文档,并做了一个demo进行了技术上的验证,使用上是没有问题的。感觉正常办公之后应该就可以进行正常开发了,然而到现场办公之后我们需要和其他部门的服务之间进行交互,这时候好像遇到了了问题,我们的服务是在自己的namespace下,这样服务之间怎么调用????(这里说明一点,应该是可以发现k8s所有namespace下的服务的)赶紧去找其他部门的大佬请教,毕竟在k8s上我真的是菜鸟,一看恍然大悟,自己咋就这么笨呢…….好吧,既然这样我也按照他们的方式,把spring cloud kubernets去掉吧(其实不需要去除),最终整个微服务保留的依赖只有Feign。
其实如果对Feign熟悉的话应该知道Feign除了可以通过服务名称调用之外,还可以通过URL,而同时使用name和url的话,以url为准(这个自己网上看到的资料,没有验证)。这样我们就可以直接通过在配置文件配置好相关服务的url就好了吗!!!完美解决.........

二、demo

下面我通过一个小demo来演示一下,我就创建两个服务,一个服务的提供方service-provider,一个服务的调用方service-consumer。
在pom文件中添加Feign的依赖。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 https://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.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ypc.cloud</groupId>
    <artifactId>service-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>service-provider</name>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR4</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-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </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>

1、 服务提供者:service-provider

在service-provider项目我们写一个很简单的被调用的接口,代码如下:

@Slf4j
@RestController
public class DemoController {

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

    @Value("${spring.application.name}")
    private String applicationName;

    @GetMapping("/provider/hello")
    public ResponseEntity<String> hello() {
        return ResponseEntity.ok("hello from " + applicationName + ", service port=" + port);
    }
}

服务配置文件如下:

spring.application.name=service-provider
server.port=18080

2、 服务调用者:service-consumer

service-consumer项目的pom和上文一样,代码部分我们编写一个调用service-provider的接口,代码如下:

@Slf4j
@RestController
@RequestMapping("/consumer")
public class HelloController {

    private ProviderClient providerClient;

    public HelloController(ProviderClient providerClient) {
        this.providerClient = providerClient;
    }

    @GetMapping("/hello")
    public ResponseEntity<String> hello() {
        return ResponseEntity.ok("hello from service-consumer");
    }

    @GetMapping("/feign")
    public ResponseEntity<String> call() {
        log.info(">>>> call service-provider <<<<");
        String result = providerClient.hello();
        return ResponseEntity.ok(result);
    }
}
![2020-05-24 11-52-24 的屏幕截图.png](https://upload-images.jianshu.io/upload_images/14339262-2af62d21371c491b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

接着我们需要定一个Feign的客户端,因为我们没有使用注册中心,因此通过服务名称来实现服务调用是行不通的,必须通过url。
ProviderClient代码如下:

@FeignClient(name = "${service.provider.name}",url = "${service.provider.url}",fallback = ProviderClientFallback.class)
public interface ProviderClient {

    @GetMapping("/provider/hello")
    String hello();
}

@FeignClient
注解中name或者value必须要有值,因此必须要配置,虽然没用。服务的name和url的值读取的是配置文件的值。配置文件如下:

spring.application.name=service-consumer
server.port=8080
service.provider.name=provider
service.provider.url=http://localhost:18080
feign.hystrix.enabled=true

这样两个服务的代码和配置就算完成了,接下来我们就测试一下,首先启动service-consumer,然后分别调用"hello"和"call"两个接口:
结果分别如下:

图-1.png

图-2.png


因为我们开启了hystrix
,所以在服务提供者不可用的时候,返回了Fallback的结果。
接着我们启动服务提供者service-provider,并再次调用"call"接口,结果如下:

图-3.png

返回的结果正确了,说明通过url成功实现了服务间的调用。

有人会说通过url实现服务间的调用没什么用的,你一个服务会有那个多实例,服务的负载均衡怎么办?确实如此,但是呢,在k8s中就好解决了啊,k8s本身提供了服务发现的功能。我们知道k8s中服务--Service是一个逻辑上的概念,服务本身并不会提供具体的服务,具体的服务是由服务的pod完成的。一个服务可以有一个或多个pod,也就是我们所说的实例,通过服务路由到某一个具体的pod,由k8s帮我们去完成,我们不需要关心,当然感兴趣的可以自己研究一下,其实原理应该都差不多,一个服务有个Endpoint的地址列表(虽然都是虚拟的,但是k8s内部可以访问)。所以Feign的url我们只需要配置成k8s中我们服务的地址即可,而在k8s中服务的地址是:<service_name>.<namespace>.svc.<domain>
,一般的值都是固定的,所以可以简写成<service_name>.<namespace>
,即我们只需要服务的名称和其所在的namespace就可以访问了。其实想想我们项目组还是不太应该去掉spring cloud kubernets依赖,去除掉之后服务间调用需要在配置文件添加服务的名称和url,不是很方便…..没有因为同一个namespace下直接根据服务名称就可以进行调用了。不得不说使用k8s之后真的方便了很多……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Feign是Spring Cloud用于实现服务相互调用的组件。通过Feign,我们可以像调用本地接口一样调用远程服务接口。在使用Feign实现服务相互调用时,需要进行以下步骤: 1. 在需要调用其他服务的微服务模块,创建一个Feign接口,用于定义调用其他服务的方法。在接口的方法上使用@RequestParam或@RequestBody注解来传递参数。注意,参数不能是HttpRequest类型,而应该是一个对象。 2. 在该微服务模块的启动类上添加@EnableFeignClients注解,开启Feign的功能。 3. 在需要调用其他服务的地方,通过注入Feign接口的方式来调用其他服务的方法。 需要注意的是,在服务刚启动的时候,调用另一个服务可能会出现找不到服务的错误,因为该服务可能还没有注册到Eureka注册心。如果遇到这种情况,可以考虑增加一些重试机制或者使用服务发现的方式来解决。\[2\] 总结起来,使用Feign实现服务相互调用的步骤包括:创建Feign接口、添加@EnableFeignClients注解、定义调用方法的参数和注入Feign接口。如果遇到服务找不到的问题,可以考虑增加重试机制或使用服务发现的方式来解决。\[2\]\[3\] #### 引用[.reference_title] - *1* *3* [SpringCloud,Feign实现服务相互调用](https://blog.csdn.net/weixin_42431933/article/details/122510287)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [【1.4 服务调用Feign)】使用Feign服务相互调用,其实OpenFeign也没有想象那么难嘛](https://blog.csdn.net/wstever/article/details/128698880)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值