多服务间的调用

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
EMR Serverless StarRocks,5000CU*H 48000GB*H
应用型负载均衡 ALB,每月750个小时 15LCU
简介: 上文我们把我们项目注册到服务器上了,但是在微服务中,我们会有多个服务,同时也会使用A服务调用B服务的接口。springcloud netflix这里有两种方式ribbon和feign,我们分别介绍。


image.png

上文我们把我们项目注册到服务器上了,但是在微服务中,我们会有多个服务,同时也会使用A服务调用B服务的接口。springcloud netflix这里有两种方式ribbon和feign,我们分别介绍。

1.ribbon

ribbon说白了就是使用restTemplate。上文编写了被调用方的代码,下文将编写调用方的代码。

1.修改pom文件

<?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.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.baocl</groupId>
    <artifactId>eureka-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-consumer</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.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-ribbon</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>
            <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>

2.修改Application

然后修改EurekaConsumerApplication类,需要在新建RestTemplate的bean。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaConsumerApplication {
  @Bean
  @LoadBalanced // ribbon注解
  public RestTemplate restTemplate() {
    return new RestTemplate();
  }
  public static void main(String[] args) {
    SpringApplication.run(EurekaConsumerApplication.class, args);
  }
}

3.新建调用方接口DcController

新建普通接口,调用上文被调用方的服务。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/eureka1")
public class DcController {
  @Autowired
  LoadBalancerClient loadBalancerClient;
  @Autowired
  RestTemplate restTemplate;
  @Autowired
  DiscoveryClient discoveryClient;
  @GetMapping("/consumer")
  public ResponseEntity<String> dc() {
    //String url = "http://eureka-client/aaa/dc?dk=3001";
    String url = "http://EUREKA-CLIENT/aaa/dc?dk=3001";
    System.out.println(url);
    // ServiceInstance serviceInstance =
    // loadBalancerClient.choose("eureka-client");// 获取服务提供者信息
    // // String url = "http://localhost:" + serviceInstance.getPort() + "/aaa/dc";
    // System.out.println(serviceInstance.getPort() );
    // System.out.println(url);
    // ResponseEntity r = restTemplate.getForEntity("http://eureka-client/aaa/dc",
    // String.class);
    // System.out.println("r.getHeaders().getHost():"+r.getHeaders().getHost());
    // System.out.println("r.getHeaders().getLocation():"+r.getHeaders().getLocation());
    // System.out.println("r.getHeaders().getOrigin():"+r.getHeaders().getOrigin());
    return restTemplate.getForEntity(url, String.class); // ribbon应用
  } 
}

有一个坑 在调用时候需要String url =  “http://eureka-client/aaa/dc?dk=3001”,而不是String url  =“http://localhost:2002/aaa/dc?dk=3001”,否则会报错java.lang.IllegalStateException:  No instances available for localhost.

4.修改application

最后是配置文件application.yml,这里我们使用.yml文件,都是基础配置。

eureka:
  client:
    registery-fetch-interval-seconds: 5000 
    serviceUrl:
      defaultZone: http://localhost:1001/eureka/      
eureka-client:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule 
server:
  port: 3001
spring:
  application:
    name: eureka-consumer

输入http://localhost:3001/eureka1/consumer就可以调用到上文的client服务了,同时还可以配置负载均衡策略。在NFLoadBalancerRuleClassName:  com.netflix.loadbalancer.RandomRule 处配置。

2.feign

1.修改pom文件

<?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.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.baocl</groupId>
    <artifactId>eureka-consumer-feign</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-consumer-feign</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.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.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.7.RELEASE</version>
        </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>

版本问题一定要控制!!!!这里有一个坑 就是版本问题,有时会报错

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to read candidate component class: file [F:\workspace\springcloud\tse9\eureka-consumer-feign\target\classes\com\cloud\DcClient.class]; nested exception is org.springframework.core.annotation.AnnotationConfigurationException: Attribute 'value' in annotation [org.springframework.cloud.netflix.feign.FeignClient] must be declared as an @AliasFor 'serviceId', not 'name'.

解决办法http://www.pianshen.com/article/2239869942/

2.修改EurekaConsumerFeignApplication

首先在EurekaConsumerFeignApplication使用@EnableFeignClients中开启feign

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaConsumerFeignApplication {
  public static void main(String[] args) {
    SpringApplication.run(EurekaConsumerFeignApplication.class, args);
  }
}

3.新建接口DcClient

新建一个接口DcClient,配置调用其他服务的名称和接口名称。(被调用方客户端的服务与接口信息)

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "eureka-client")
public interface DcClient {
  @GetMapping("/aaa/dc?dk=3002")
  String consumer();
  @GetMapping("/aaa/dc?dk=3002")
  String consumer2();
}

4.新建调用接口DcController

业务接口的书写,同时注入dcClient调用其他服务。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cloud.DcClient;
@RestController
@RequestMapping("/eureka2")
public class DcController {
  @Autowired
  DcClient dcClient;
  @GetMapping("/consumer")
  public String dc() {
    return dcClient.consumer();
  }
}

5.修改application.properties

下文是最基础的配置。

spring.application.name=eureka-consumer-feign
server.port=3002
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/

然后调用http://localhost:3002/eureka2/consumer 可以发现接口掉通了 ,在被调用方也有相应的日志。

3.ribbon和Eureka整合原理

基本流程就是 客户端从eureka中获取到缓存的服务列表,获取到目的服务的信息,然后通过自身的负载均衡策略,去调用对应的服务。

image.png

1.负载均衡策略

  1. BestAvailableRule 选择一个最小的并发请求的server。
  2. AvailabilityFilteringRule 过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)。
  3. WeightedResponseTimeRule 根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。
  4. RetryRule 对选定的负载均衡策略机上重试机制。
  5. RoundRobinRule roundRobin方式轮询选择server.。
  6. RandomRule 随机选择一个server。
  7. ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择server。


相关文章
|
2月前
|
JavaScript 前端开发
调用带参数的函数
调用带参数的函数
36 17
|
6月前
|
存储 搜索推荐 Python
函数的调用和返回值
函数的调用和返回值
|
6月前
|
C语言 C++ 容器
C调用C++代码
C调用C++代码
36 1
调用DescribeRouteTables接口
调用DescribeRouteTables接口
74 1
|
存储 C语言 容器
调用一个函数时发生了什么?
调用一个函数时发生了什么?
148 0
调用一个函数时发生了什么?
|
Java
CheerpJ调用的两种方式
CheerpJ调用的两种方式
248 0
|
小程序
小程序调用接口不生效?
小程序调用接口不生效?
357 0
|
C++
c调用c++函数
c调用c++普通函数     cpp_test/cpp.h #ifndef CPP_H #define CPP_H #include "extern_cpp.h" int add(int a, int b); char add(char a, char b); #endif // CPP_H     cpp_test/extern_cpp.
2021 0
|
Java 开发工具 应用服务中间件