springcloud有两种接口调用方式,一种是通过RestTemplate,像下面这样调用:
restTemplate.getForObject("http://app-member/getmember", String.class);
个人理解,这种方式将接口的调用与业务代码掺杂在一起,不太友好,基本不用。
另一种是Feign客户端调用,Fegin采用接口加注解的方式实现,易读性较强。
Feign客户端是一个web声明式http远程调用工具,提供了接口和注解方式进行调用。
创建一个父工程,然后再分别创建三个Module:Eureka Server ,memberService 和 orderService,并关闭eureka自我保护
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.lchtestgroupId>
<artifactId>springcloud2.0-eureka-selfprotectartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.3.RELEASEversion>
parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Finchley.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<modules>
<module>eureka-servermodule>
<module>springcloud2.0-eureka-selfprotect-eurekaservermodule>
<module>springcloud2.0-eureka-selfprotect-ordermodule>
<module>springcloud2.0-eureka-selfprotect-membermodule>
modules>
project>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>com.lchtestgroupId>
<artifactId>springcloud2.0-eureka-selfprotectartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>springcloud2.0-eureka-selfprotect-eurekaserverartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
dependencies>
project>
server:
port: 8100
eureka:
instance:
#注册中心ip地址
hostname: 127.0.0.1
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
# 不将自己注册到注册中心
register-with-eureka: false
fetch-registry: false
server:
# 关闭自我保护机制,保证服务不可用时及时剔出
enable-self-preservation: false
# 踢出失效服务的间隔
eviction-interval-timer-in-ms: 2000
package com.lchtest.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
//@EnableEureakServer表示开启注册中心
@EnableEurekaServer
public class AppEurekaServer {
public static void main(String[] args) {
SpringApplication.run(AppEurekaServer.class, args);
}
}
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>com.lchtestgroupId>
<artifactId>springcloud2.0-eureka-selfprotectartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>springcloud2.0-eureka-selfprotect-memberartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
project>
# 会员服务项目端口号
server:
port: 8070
# 服务别名--服务注册到注册中心的名称
spring:
application:
name: app-member
eureka:
client:
service-url:
#将当前服务注册到eureka注册中心
defaultZone: http://localhost:8100/eureka/
# 将自己注册到注册中心,设置为true
register-with-eureka: true
# 检索服务信息
fetch-registry: true
# eureka客户端与eureka服务端心跳检测及续约时间,本地开发时设置小些,保证服务不可用时就踢出
instance:
# eureka客户端向服务端发送心跳的时间间隔,单位为秒
lease-renewal-interval-in-seconds: 1
# eureka服务端在收到最后一次心跳检测之后等待的事件上限,单位为秒,超过此时间则剔除
lease-expiration-duration-in-seconds: 2
package com.lchtest.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class AppMember {
public static void main(String[] args) {
SpringApplication.run(AppMember.class, args);
}
}
package com.lchtest.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MemberController {
@Value("${server.port}")
private String port;
@RequestMapping("/member/getmember")
public String getMemberByFeign() {
return "member service port=" + port;
}
}
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>com.lchtestgroupId>
<artifactId>springcloud2.0-eureka-selfprotectartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>springcloud2.0-eureka-selfprotect-orderartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
dependencies>
project>
# 会员服务项目端口号
server:
port: 8001
# 服务别名--服务注册到注册中心的名称
spring:
application:
name: app-order
eureka:
client:
service-url:
#将当前服务注册到eureka注册中心
defaultZone: http://localhost:8100/eureka/
# 将自己注册到注册中心,设置为true
register-with-eureka: true
# 检索服务信息
fetch-registry: true
# eureka客户端与eureka服务端心跳检测及续约时间,本地开发时设置小些,保证服务不可用时就踢出
instance:
# eureka客户端向服务端发送心跳的时间间隔,单位为秒
lease-renewal-interval-in-seconds: 1
# eureka服务端在收到最后一次心跳检测之后等待的事件上限,单位为秒,超过此时间则剔除
lease-expiration-duration-in-seconds: 2
package com.lchtest.springcloud.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 声明式Feign客户端调用服务
* @author pc
*/
@FeignClient(value = "app-member")
public interface MemberFeign {
@RequestMapping("/member/getmember")
public String getmember();
}
@FeignClient(value = “app-member”) ,@FeignClient代表一个Feign客户端,value的值是该客户端要调用的服务在注册中心里的名称
@RequestMapping("/member/getmember") 就是rpc方式要调用的远程接口/member/getmember,是服务提供者对外暴露的接口
猜测FeignClient注解本质上应该是通过反射获取到注解的值,拿到要调用的服务的名称,然后去注册中心获取到服务对应的地址信息,然后再根据@RequestMapping注解
的值组装接口调用地址,最后以HttpClient方式去调用
package com.lchtest.springcloud.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.lchtest.springcloud.feignclient.MemberFeign;
@RestController
@RequestMapping("/orderfeign")
public class FeignClientTestController {
@Autowired
private MemberFeign memberFeign;
/**
* 通过feign客户端调用member服务:http://localhost:8001/orderfeign/getmemberByFeign
* @return
*/
@RequestMapping("/getmemberByFeign")
public String getmember() {
return "通过Feign客户端调用member服务,响应是:" + memberFeign.getmember();
}
}
package com.lchtest.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients // 启用feign客户端
public class AppOrder {
public static void main(String[] args) {
SpringApplication.run(AppOrder.class, args);
}
}
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
@FeignClient(value = "app-member")
public interface MemberFeign {
@RequestMapping("/member/getmember")
public String getmember();
}
// 注入
@Autowired
private MemberFeign memberFeign;
// 接口调用
memberFeign.getmember();
可以看到,order服务要通过Feign客户端调用member服务的/member/getmember接口,就必须在order服务中定义一个相同的Feign客户端接口,如果有很多个接口,这样的重复代码就很多了,因此这样的项目结构不是很合理。
点击下载代码
参见springcloud2.0-eureka-selfprotect 工程