【Feign】远程调用微服务

首先,无论是调用/被调用的服务,都需要注册到注册中心,因此会用到eureka。

一、demo项目结构

二、pom文件

(1)父工程

一定要注意springboot和springcloud的版本问题,具体版本对应关系,可以看文章

【spring】springboot与springcloud版本对应关系_Kanseui ?的博客-CSDN博客

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
        <relativePath/>
    </parent>
<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- Spring Cloud 相关 START -->
        <!-- Spring Cloud 相关 END -->

        <!-- Spring Boot 相关 START -->
        <!-- Srping Boot Web 支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!-- 排除掉 内置 Tomcat -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 测试环境依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 内省 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- 内置服务器使用 Undertow -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <!-- Spring Boot 相关 END -->

        <!-- 第三方工具 START -->
        <!-- 自动生成 Getter/Setter/Log 等 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- 日志 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
        </dependency>
        <!-- Alibaba FastJson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.80</version>
        </dependency>
        <!-- Apache Commons Lang -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!-- 第三方工具 END -->
    </dependencies>

(2)注册中心 eureka-server

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

(3)服务提供者 product

导入eureka-client包

    <dependencies>
        <!-- Spring Cloud 相关 END -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 第三方工具 START -->
        <!-- Http Client 支持 -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!-- 第三方工具 END -->
    </dependencies>

(4)消费者 order

导入eureka-client包和openfeign

因为消费者order需要远程调用服务提供者product的接口

<dependencies>
        <!-- Spring Cloud 相关 START -->
        <!-- Spring Cloud Netflix Eureka Client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- Spring Cloud Feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- Spring Cloud 相关 END -->

        <!-- 第三方工具 START -->
        <!-- Http Client 支持 -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!-- 第三方工具 END -->
    </dependencies>

三、搭建feign

(1)注册中心

启动类:需要开启注册中心

@SpringBootApplication
//开启Eureka注册中心服务
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class,args);
    }
}

yml配置注册中心

  •   是否开启自我保护机制

 Server端服务会停止的原因:1、手动关闭 2、网络抖动导致    


 Eureka client端与Server端之间有个租约,Client要定时发送心跳来维持这个租约,表示自己还存活着。 Eureka通过当前注册的实例数,去计算每分钟应该从应用实例接收到的心跳数,如果最近一分钟接收到的续约的次数小于指定阈值的话,则关闭租约失效剔除,禁止定时任务剔除失效的实例,从而保护注册信息。
    # 此处关闭可以防止问题(测试环境可以设置为false):Eureka server由于开启并引入了SELF PRESERVATION模式,导致registry的信息不会因为过期而被剔除掉,直到退出SELF PRESERVATION模式才能剔除。

【关闭自我保护,即不剔除】

server:
  port: 8761

eureka:
  instance:
    # 主机名
    hostname: localhost
    # 使用 ip 注册到注册中心实例化
    prefer-ip-address: true
  client:
    # 此实例是否从注册中心获取注册信息
    fetch-registry: false
    # 是否将此实例注册到注册中心
    register-with-eureka: false
    service-url:
      # 默认注册分区地址
      defaultZone: https://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    # 同步为空时,等待时间
    wait-time-in-ms-when-sync-empty: 0
    # 是否开启自我保护机制
    enable-self-preservation: false
    # 指定 Eviction Task 定时任务的调度频率,用于剔除过期的实例,此处未使用默认频率,频率为:5/秒,默认为:60/秒
    # 有效防止的问题是:应用实例异常挂掉,没能在挂掉之前告知Eureka server要下线掉该服务实例信息。这个就需要依赖Eureka server的EvictionTask去剔除。
    eviction-interval-timer-in-ms: 5000
    # 设置read Write CacheMap的expire After Write参数,指定写入多长时间后过期
    # 有效防止的问题是:应用实例下线时有告知Eureka server下线,但是由于Eureka server的REST API有response cache,因此需要等待缓存过期才能更新
    response-cache-auto-expiration-in-seconds: 60
    # 此处不开启缓存,上方配置开启一个即可
    # use-read-only-response-cache: false
    # 指定每分钟需要收到的续约次数的阈值,默认值就是:0.85
    renewal-percent-threshold: 0.85
    # 续约频率提高,默认:30
    leaseRenewalIntervalInseconds: 10

eureka的缓存机制还有待学习。。。

(2)服务提供者product

yml文件:需要绑定到eureka

server:
  port: 8082
spring:
  application:
    name: demo-goods

eureka:
  instance:
    hostname: localhost
    # 使用ip注册到注册中心实例化
    prefer-ip-address: true

启动类需要开启服务可被发现

@SpringBootApplication
/** 开启服务发现 */
@EnableDiscoveryClient
public class DemoGoodsApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoGoodsApplication.class,args);
    }
}

controller:获取当前服务的端口号

@RestController
@RequestMapping(value = "/goods",
        produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class GoodsController {

    @GetMapping(value = "/getPort")
    String getDetail(HttpServletRequest request){
        return String.valueOf(request.getServerPort());
    }
}

(3)消费者 Order

yml文件:

server:
  port: 8081

spring:
  application:
    name: demo-order
eureka:
  instance:
    hostname: localhost
    # 使用ip注册到注册中心实例化
    prefer-ip-address: true    
feign:
  client:
    config:
      default: #这里用default就是全局配置
        #userservice: #如果是写服务名称,则是针对某个微服务的配置
        loggerLevel: FULL #日志级别

启动类:添加服务可被发现和开启feign注解

@SpringBootApplication
//服务发现
@EnableDiscoveryClient
//扫描支持
//全局配置
@EnableFeignClients
public class DemoOrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoOrderApplication.class,args);
    }
}

feign接口:@FeignClient注解需要表明引用的微服务名

@FeignClient(value = "demo-goods")
public interface GoodsFeign {
    @RequestMapping(
            value = "/goods/getPort",
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
            method = RequestMethod.GET
    )
    public String getDetail();
}

controller调用:用@Resource引入 GoodsFeign

@RestController
@RequestMapping(value = "/goods",
        produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class GoodsController {

    @Resource
    private GoodsFeign goodsFeign;

    @RequestMapping(
            value = "/getPort",
            method = RequestMethod.GET
    )
    public String getDetail(){
        return goodsFeign.getDetail();
    }
}

Feign中关键的几个注解:@FeignClient @EnableFeignClients @EnableDiscoveryClient @EnableEurekaServer

当调用的不是微服务,是外网API,可以不用注册中心

例如:调用github的搜索接口 http://api.github.com/search/repositories?q=xxxx

(1)生成feign接口

@FeignClient需要自己命名微服务的名字,并且把url写好

@FeignClient(name = "github-client",url = "https://api.github.com")
public interface GithubFeign {
    @RequestMapping(
            value = "/search/repositories",
            method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE
    )
    String searchRepo(@RequestParam("q") String q);
}

(2)controller调用:和普通调用一样,引入GithubFeign即可

@RequestMapping(
        value = "/github",
        produces = MediaType.APPLICATION_JSON_UTF8_VALUE
)
@RestController
public class GithubController {
    @Resource
    private GithubFeign githubFeign;

    @RequestMapping(
            value = "/search/repositories",
            method = RequestMethod.GET
    )
    String searchRepo(@RequestParam("q") String q){
        return githubFeign.searchRepo(q);
    }
    
}

猜你喜欢

转载自blog.csdn.net/kanseu/article/details/125650362