服务的调用,主要是服务调用方请求注册中心,注册中心返回服务提供方实例。 服务调用方通过服务提供方实例获取服务的URL地址,再发送HTTP调用服务。调用方发现服务有很多方式。比如:

  • 使用 DiscoveryClient。(服务发现)
  • 使用 EurekaClient。(服务发现)
  • 使用 LoadBalancerClient 。(服务发现) ,这个是Ribbon的底层API。Ribbon包含了LoadBalancerClient
  • 使用RibbonRibbon是client(调用方)端的负载均衡。官方文档,中文文档
  • 使用openfeignFeign包含了Ribbon(或者说依赖Ribbon)。(推荐使用这个)官方文档

下面通过示例说明:

1.1. 一、服务提供方代码。

1.1.1. 1、依赖。

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

1.1.2. 2、服务提供方application.yml配置如下:

server:
  port: 8000
spring:
  application:
    name: user-provider
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/user-provider?serverTimezone=GMT%2b8
    username: root
    password: 123456
eureka:
  client:
    serviceUrl:
      # 注册中心的地址
      defaultZone: http://localhost:9999/eureka/
  instance:
    preferIpAddress: true
    instanceId: ${spring.cloud.client.ip-address}:${server.port}

简单配置了像注册中心注册服务。

1.1.3. 3、服务提供方就只提供一个/test/add接口。

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/add")
    public int add(int a,int b) {
        return a + b;
    }

}

1.2. 二、服务调用方

1.2.1. 2.1、使用 DiscoveryClient。

排除掉jersey.

官方文档
默认情况下,EurekaClient 使用 Jersey 进行 HTTP 通信。如果您希望避免来自 Jersey 的依赖项,则可以将其从依赖项中排除。 Spring Cloud 默认会使用 Spring RestTemplate请求工具。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-core</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.jersey.contribs</groupId>
            <artifactId>jersey-apache-client4</artifactId>
        </exclusion>
    </exclusions>
</dependency>

配置RestTemplate

//之前的版本需要 @EnableDiscoveryClient
@SpringBootApplication
public class UserConsumerApplication {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(UserConsumerApplication.class, args);
    }

}

调用服务。

@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private DiscoveryClient discoveryClient;
    @Autowired
    private RestTemplate restTemplate;

    public String serviceUrl() {
        //user-provider 是注册到eureka的服务提供端的名字。
        //获取服务的列表。
        List<ServiceInstance> list = discoveryClient.getInstances("user-provider");
        if (list != null && list.size() > 0 ) {
            //获取服务。返回 http://192.168.32.236:8000
            return String.valueOf(list.get(0).getUri());
        }
        return null;
    }

    @GetMapping("/callAdd")
    public int callAdd() {
        String url = serviceUrl() +"/test/add?a=3&b=6";
        return restTemplate.getForObject(url, Integer.class);
    }
}

访问 http://localhost:9000/test/callAdd就可以看到结果了。
调用方使用DiscoveryClient获取到所有的user-provider服务的实例,然后选择第1个调用。

1.2.2. 2.2、使用 EurekaClient。

使用EurekaClient和前面的配置一样。

调用服务。

调用方可以使用EurekaClient作为发现服务的工具。 详细可以查看官方文档

@RestController
@RequestMapping("/test")
public class TestEurekaClientController {


    @Qualifier("eurekaClient")
    @Autowired
    private EurekaClient discoveryClient;
    @Autowired
    private RestTemplate restTemplate;


    public String serviceUrl() {
        InstanceInfo instance = discoveryClient.getNextServerFromEureka("user-provider", false);
        return instance.getHomePageUrl();
    }

    @GetMapping("/callAdd2")
    public int callAdd() {
        String url = serviceUrl() +"/test/add?a=3&b=6";
        return restTemplate.getForObject(url, Integer.class);
    }

}

访问http://localhost:9000/test/callAdd2就可以看到结果了。

1.2.3. 2.3、使用 LoadBalancerClient。

这是一个负载均衡客户端的抽象定义。官方文档

调用服务。

@RestController
@RequestMapping("/test")
public class TestLoadBalancerClientController {


    @Autowired
    private LoadBalancerClient loadBalancerClient;
    @Autowired
    private RestTemplate restTemplate;


    public String serviceUrl() {
        ServiceInstance instance = loadBalancerClient.choose("user-provider");
        return String.valueOf(instance.getUri());
    }

    @GetMapping("/callAdd3")
    public int callAdd() {
        String url = serviceUrl() +"/test/add?a=3&b=6";
        return restTemplate.getForObject(url, Integer.class);
    }

}

通过LoadBalancerClient的choose函数来负载均衡的选出一个user-provider服务实例, 再拼接请求地址,最后使用RestTemplate发送请求。

1.2.4. 2.4、使用 Ribbon。

官方文档
中文文档
Ribbon是一个client端的负载均衡器,可让您对HTTP和TCP客户端的行为进行大量控制。Feign已经使用了Ribbon,因此,如果使用@FeignClient,也就包含了Ribbon。
Ribbon是一个client端的负载均衡。client端维护并且缓存了一个服务列表,并定期从Eureka注册中心同步最新的服务列表。

添加依赖。

要使用Ribbon,需要添加依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

RestTemplate@LoadBalanced

客户端的负载均衡。底层就是把LoadBalancerClient配置给RestTemplateLoadBalancerClient做负载均衡选出调用哪个服务,RestTemplate发送请求。

//之前的版本需要 @EnableDiscoveryClient
@SpringBootApplication
public class UserConsumerApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(UserConsumerApplication.class, args);
    }

}

注意:加上@LoadBalanced之后,前面请求路径/test/callAdd/test/callAdd2/test/callAdd3不会被初始化了。请求会报错(路径不存在)。

调用。

@RestController
@RequestMapping("/test")
public class TestRibbonController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/callAdd4")
    public int callAdd() {
        String url = "http://user-provider/test/add?a=3&b=6";
        return restTemplate.getForObject(url, Integer.class);
    }

}

调用的时候直接使用user-provider服务名。不需要组装ip和端口了。 因为Spring Cloud Ribbon有一个拦截器,它能够在进行实际调用的时候,自动的去选取服务实例,并将实际要请求的IP地址和端口替换这里的服务名,从而完成服务接口的调用。

Ribbon自定义负载均衡策略。(一般不配置)

默认情况下, Ribbon使用轮询的方式选择服务器,给client端调用。但是这个负载均衡策略是可以自定义调整的。 官方文档16.4 Customizing the Ribbon Client by Setting Properties 就说明了这个情况。配置如下:

user-provider:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule

上面的配置表示,在选择user-provider服务器的时候,以响应时间作为权重,选择服务器。
NFLoadBalancerRuleClassName配置的是选择服务器的规则,要设置为com.netflix.loadbalancer.IRule 的实现类。 spring-cloud内置了很多实现类。对应不同的策略。一般不去配置,使用默认的策略就行了。

1.2.5. 2.5、使用 OpenFeign。

OpenFeign底层用Ribbon(或者说依赖Ribbon)。Ribbon的配置对OpenFeign都生效。
OpenFeign是在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口, 并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。官方文档

添加依赖。

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

配置application.yml

server:
  port: 9001
spring:
  application:
    name: user-consumer-feign
#  datasource:
#    driverClassName: com.mysql.jdbc.Driver
#    url: jdbc:mysql://localhost:3306/user-consumer?serverTimezone=GMT%2b8
#    username: root
#    password: 123456
eureka:
  client:
    serviceUrl:
      # 注册中心的地址
      defaultZone: http://localhost:9999/eureka/
  instance:
    preferIpAddress: true
    instanceId: ${spring.cloud.client.ip-address}:${server.port}
#user-provider:
#  ribbon:
#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
feign:
  client:
    config:
      default:
        # 远程调用超时时间。默认是1秒。我改为15秒
        readTimeout: 15000

需要注意配置readTimeout,这个远程调用超时时间默认是1秒,如果服务提供方响应时间超过1秒,client方就会报错。所以这里设置为15秒给服务提供方更多时间响应。

启用OpenFeign

使用@EnableFeignClients启用OpenFeign

@EnableFeignClients
@SpringBootApplication
public class UserConsumerFeignApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserConsumerFeignApplication.class, args);
    }

}

声明服务端的接口。

/**
 *调用user-provider 服务。
 */
@FeignClient("user-provider")
public interface UserProviderClient {

    // 请求/test/add 接口
    @GetMapping("/test/add")
    int add(@RequestParam(name = "a") int a, @RequestParam(name = "b") int b);
}

使用@FeignClient声明接口类是要调用外部服务user-provider的。使用@GetMapping("/test/add")来表示要调用的接口路径。

调用。

@RestController
@RequestMapping("/test")
public class TestOpenFeignController {

    @Autowired
    private UserProviderClient userProviderClient;

    @GetMapping("/openfeign/callAdd")
    public int callAdd() {
        return userProviderClient.add(3,8);
    }

}

直接注入连接端UserProviderClient调用服务即可。访问http://localhost:9001/test/openfeign/callAdd可以看到结果。
OpenFeign 有点像RPC调用,实际上底层是HTTP调用。

1.3. 参考文档。

官方文档的翻译
官方文档

results matching ""

    No results matching ""