Hi,大家好。

目标:3个SpringBoot的项目,一个作为EurekaServer,另外2个作为客户端,客户端A用四种方式调用客户端B来实现通信。

知识点:

  • Eureka的基本配置
  • 界面查看服务注册情况及判断是否真正注册成功
  • 三种方式实现服务间调用

服务名

作用

简称

EurekaServer

Eureka服务器

ParamsValidation

客户端

客户端B

EurekaClient2

客户端

客户端A

注册效果:

eureka 微服务高可用 eureka服务间调用_eureka 微服务高可用

客户端分别使用:
@DiscoveryClient、Ribbon的RestTemplate来调用、Feign三种方式调用,postman数据连接:

https://www.getpostman.com/collections/d221c48fd9097d869db4

详细代码见git仓库,由于模仿独立的项目,所以每个项目都是一个独立的仓库(也没法混合提交到一个仓库里)。

git@github.com:cmhhcm/SecurityTest.git 客户端B
git@github.com:cmhhcm/EurekaClient2.git 客户端A
git@github.com:cmhhcm/EurekaServer.git Eureka服务端

完整代码克隆下来,切换到develop分支,即可运行调试。
特别声明,处于安全考虑贴图的地方内网IP一律简化为:localhost了。

一、三种方式实现服务间调用

这里展示下核心代码和配置:详细代码见:EurekaClient2

1.1 如果没有Eureka,我们如何调用?

没有eureka的时候,A服务调用B服务,一般把B服务的URL配置到A服务的配置文件里(便于修改),然后直接这么调用。

package com.cmh.eurekaclient2.service.impl;

import com.cmh.eurekaclient2.value.BookQueryCondition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
@Slf4j
public class ParamsValidationOriginalClient {

    public String invokeParamsValidationServiceByOriginal() {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity httpEntity = new HttpEntity(new BookQueryCondition(), headers);
        ResponseEntity<String> exchange = restTemplate.exchange("http://localhost:8080/ParamsValidation/params_validation/book/list", HttpMethod.POST, httpEntity, String.class, "");
        String result = "";
        if (exchange != null) {
            result = exchange.getBody();
            log.info("invoked body is:{}", result);
        }
        return result;
    }
}

反正这么调用很不爽,说不出来的不爽。之前用Dubbo的时候,是不存在这种问题的。

1.2 方式1-@DiscoveryClient

核心:
1)启动类配置@EnableDiscoveryClient
2)调用类,A调用B,那么A调用的地方注入discoverClient

package com.cmh.eurekaclient2.service.impl;

import com.cmh.eurekaclient2.value.BookQueryCondition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@Component
@Slf4j
public class ParamsValidationDiscoveryClient {
    @Autowired
    private DiscoveryClient discoveryClient;

    public String invokeParamsValidationService() {
        RestTemplate restTemplate = new RestTemplate();
        List<ServiceInstance> instances = discoveryClient.getInstances("PARAMSVALIDATION");
        if (instances == null || instances.size() == 0) {
            log.info("instances is empty..");
            return "";
        }
        String uri = instances.get(0).getUri().toString();
        String serviceUrl = String.format("%s/ParamsValidation/params_validation/book/list", uri);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        BookQueryCondition condition = new BookQueryCondition();
        HttpEntity entity = new HttpEntity(condition, headers);
        log.info("uri is :{},formatted url is:{}", uri, serviceUrl);
        ResponseEntity<String> resultExchange = restTemplate.exchange(serviceUrl, HttpMethod.POST, entity, String.class, "");
        if (resultExchange == null) {
            log.info("invoke result is null");
            return "";
        }
        String body = resultExchange.getBody();
        log.info("body is :{}", body);
        return body;
    }
}

1.3 方式2-Ribbon的RestTemplate

核心:
1、restTemplate注入的时候加了@LoadBalanced,作用:可以直接用服务实例名(忽略大小写的)访问,对调用方隐藏了IP+端口
2、

package com.cmh.eurekaclient2.service.impl;

import com.cmh.eurekaclient2.value.BookQueryCondition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Slf4j
@Component
public class ParamsValidationRestTemplateClient {

    @Autowired
    RestTemplate restTemplate;

    public String invokeParamsValidationService() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        BookQueryCondition queryCondition = new BookQueryCondition();
        HttpEntity httpEntity = new HttpEntity(queryCondition, headers);
        ResponseEntity<String> exchange = restTemplate.exchange("http://ParamsValidation/ParamsValidation/params_validation/book/list", HttpMethod.POST, httpEntity, String.class, "");
        String result = "";
        if (exchange != null) {
            result = exchange.getBody();
            log.info("rest template invoke result: {}", result);
        }
        return result;
    }
}

感觉本质和DiscoverClient类似,都是拿到服务实例。

1.4 方式3- feign

记住使用的2个核心注解都是来自这个依赖,而不是spring-cloud-netflix-core,因为这个在Maven仓库已经标记为废弃了,调试时候发现总是提示@FeignClient校验有问题,找不到url。

依赖:

<!--Feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>3.0.1</version>
        </dependency>

核心:
1)启动类加@EnableFeignClients注解
2)调用客户端加@FeignClient注解

package com.cmh.eurekaclient2.service.impl;

import com.cmh.eurekaclient2.value.BookQueryCondition;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

@FeignClient("ParamsValidation")
public interface ParamsValidationFeignClient {

    @PostMapping("ParamsValidation/params_validation/book/list")
    String invokeParamsValidationService(@RequestBody BookQueryCondition condition);
}

eureka 微服务高可用 eureka服务间调用_spring_02

二、对比

调用方式

优点

缺点

推荐与否

Feign

屏蔽了很多细节

暂未发现


Ribbon的RestTemplate

屏蔽了IP和端口

加了注解的restTemplate无法调用其他未注册到Eureka的接口了,需要新new

比较推荐

DiscoverClient

过程,否

原始HTTP调用

摒弃

三、遇到的问题

1、Feign方式开始未调通,报如下错,后来更换了依赖,调通了。

java.lang.NoSuchMethodError: ‘java.lang.String org.springframework.core.annotation.AnnotationAttributes.getAliasedString(java.lang.String, java.lang.Class, java.lang.Object)’
FeignClientsRegistrar.validate
FeignClientsRegistrar.java:202)
重点查看报错,源码提示找不到url,错误很明显,但是明明已经配置了url的还提示找不到…

解决办法:依赖使用OpenFeign即可。

2、netflix-core 在Maven仓已被标记废弃,推荐使用 spring-cloud-netflix-hystrix

四、总结

1、这里仅仅介绍的是运用
2、需要了解原理,知道思想性的东西
3、研究下Dubbo,做对比,没有对比就没有伤害。

好了,晚安。