Spring Cloud OpenFeign
Spring Cloud OpenFeign声明式服务调用
一、体验demo
1.Eureka Server
新建一个Eureka Server工程,导入eureka-server依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
新建启动类:
@SpringBootApplication
// 开启eureka server
@EnableEurekaServer
public class Server02App {
public static void main(String[] args) {
SpringApplication.run(Server02App.class, args);
}
}
配置配置文件:
# 配置server sever端口
server:
port: 9000
eureka:
# eureka server 也是一个eureka client
client:
# 作为客户端不注册
registerWithEureka: false
# 不从注册中心获取注册信息
fetchRegistry: false
2.新建客户端
导入依赖:
<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-web</artifactId>
</dependency>
同时要导入Spring Boot插件,使jar打包能正常运行(避免执行时报错XXX.jar中没有主清单属性):
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
启动类:
@SpringBootApplication
@EnableDiscoveryClient
public class Client01App {
public static void main(String[] args) {
SpringApplication.run(Client01App.class, args);
}
}
服务类(用户提供服务):
@RestController
public class ServiceController {
@GetMapping("/hello/{name}")
public String getHello(@PathVariable("name") String name) {
return "hello " + name;
}
}
配置文件:
server:
port: 9010
eureka:
client:
serviceUrl:
# 设置注册中心的地址
defaultZone: http://localhost:9000/eureka
spring:
# 设置项目名等
application:
name: client-service
3.新建消费服务客户端
导入依赖:
<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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动类:
@SpringBootApplication
@EnableDiscoveryClient
// 开启OpenFeign支持
@EnableFeignClients
public class OpenFeignClientApp {
public static void main(String[] args) {
SpringApplication.run(OpenFeignClientApp.class, args);
}
}
新建一个接口,用于调用服务:
// 指定这是feign客户端 name属性指明要调用的服务名
@FeignClient(name = "client-service")
public interface IServiceFeign {
// 指定要调用的服务,要和调用的服务调用方式保持一致
@GetMapping("/hello/{name}")
public String getHello(@PathVariable("name") String name);
}
调用服务:
@RestController
public class ServiceController {
// 注入IServiceFeign(feign的客户端,来调用服务)
@Autowired
private IServiceFeign iServiceFeign;
@GetMapping("/consumer/{name}")
public String consumer(@PathVariable("name") String name) {
// 调用具体的服务
return iServiceFeign.getHello(name);
}
}
启动项目,访问接口@GetMapping("/consumer/{name}"),即可看到服务调用结果。
二、自定义配置
1.配置使用feign自带注解
上面的例子中,feign使用的完全是SpringMVC的注解,其实feign也提供了注解,如果需要使用feign的注解需要自己配置。
配置类不能被扫描,否则全局生效(不能作用于单个feign)
@Configuration
public class OpenFeignConfig {
// 使用feign默认的配置
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
}
修改接口配置:
// 指定这是feign客户端 name属性指明要调用的服务名,configuration指定要使用的配置类
@FeignClient(name = "client-service", configuration = OpenFeignConfig.class)
public interface IServiceFeign {
// RequestLine指定请求方式和地址
@RequestLine("GET /hello/{name}")
public String getHello(@Param("name") String name);
}
启动项目,再次请求即可。
2.配置认证
修改服务提供者配置,添加Spring Security依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
添加配置:
server:
port: 9010
eureka:
client:
serviceUrl:
# 设置注册中心的地址
defaultZone: http://localhost:9000/eureka
spring:
# 设置项目名等
application:
name: client-service
# 配置security登录名和密码
security:
user:
name: zhangsan
password: 12345
编写配置类:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 配置httpBasic认证,配置所有请求都要认证
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
由于服务提供者配置了认证,所以再去调用(消费)服务就会报错:
feign.FeignException: status 401 reading IServiceFeign#getHello(String); content:
{"timestamp":"2018-12-21T05:58:15.318+0000","status":401,"error":"Unauthorized","message":"Unauthorized","path":"/hello/hah"}
所以添加消费者配置:
@Import(FeignClientsConfiguration.class)
@RestController
public class ServiceController {
private IServiceFeign iServiceFeign;
@Autowired
public ServiceController(Decoder decoder, Encoder encoder, Client client, Contract contract) {
this.iServiceFeign = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
// 设置http-basic认证用户名及密码
.requestInterceptor(new BasicAuthRequestInterceptor("zhangsan", "12345"))
// 作用的feign和服务
.target(IServiceFeign.class, "http://client-service/");
}
@GetMapping("/consumer/{name}")
public String consumer(@PathVariable("name") String name) {
// 调用具体的服务
return iServiceFeign.getHello(name);
}
}
// 指定这是feign客户端 name属性指明要调用的服务名
@FeignClient(name = "client-service", configuration = OpenFeignConfig.class)
public interface IServiceFeign {
// 指定要调用的服务,要和调用的服务调用方式保持一致
@GetMapping("/hello/{name}")
public String getHello(@PathVariable("name") String name);
}
这里如果还是使用@RequestLine会出错。