首先还是需要去们的Spring Cloud服务管理框架Eureka简单示例(三)这篇博客底部拿到源码,这是一个最微型的集群。为了符合后面的测试,先把eureka-provider项目com.init.springCloud包下的ProviderApp类修改成按照端口启动:
package com.init.springCloud;
import java.util.Scanner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ProviderApp {
public static void main(String[] args) {
@SuppressWarnings("resource")
Scanner scan = new Scanner(System.in);
String port = scan.nextLine();
new SpringApplicationBuilder(ProviderApp.class).properties("server.port=" + port).run(args);
}
}
接着为ProviderController控制器添加一个方法,用于返回简单的JSON字符串:
@RequestMapping(value = "/hello/{name}", method = RequestMethod.GET)
public String sayHello(@PathVariable String name){
return "hello "+name+", I'm glad to see you.";
}
然后修改eureka-consumer项目的application.yml配置文件,将启动端口号修改为9090:
server:
port: 9090
之后依次运行三个项目的**App类里面的main方法,启动三个项目。注意,eureka-provider项目需要在控制台输入端口号之后回车。之后访问:http://localhost:8761,http://localhost:8080/search/1,http://localhost:9090/router,看看是否都能正常调用。
引入Spring Cloud中的Feign
因为feign是用于服务调用端,做服务调用的,我们在eureka-consumer项目中引入它的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
然后开启feign,让eureka-consumer能够使用到feign提供的能力。找到ConsumerApp启动类,增加@EnableFeignClients的注解:
package com.init.springCloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class, args);
}
}
然后,我们需要定义一个接口PersonClient,用做Feign处理请求调用:
package com.init.springCloud;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient("eureka-provider")
public interface PersonClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello/{name}")
String sayHello(@PathVariable("name") String name);
}
在接口上,我们使用了@FeignClient注解,表明需要请求的服务提供者的服务ID(ServiceId),然后在抽象方法上面用Spring提供的注解@RequestMapping,注明请求的方式和路径,参数也用Spring注解@PathVariable修饰(注意需要把参数名称填进去),Feign内部有专门的SpringMvcContract注解解释器(这个注解解释器可以查看OpenFeign之feign使用简介(十一))来理解Spring的@RequestMapping注解,所以在这里使用Spring的注解来替代feign提供的@RequestLine,让我们回归到了熟悉的配置上,简化了开发难度。
接下来就是重新编写ConsumerController控制器,注入我们刚刚编写的PersonClient接口:
package com.init.springCloud;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConsumerController {
@Autowired PersonClient PersonClient;
@RequestMapping(method = RequestMethod.GET, value = "/router/{name}",
produces = MediaType.APPLICATION_JSON_VALUE)
public String router(@PathVariable String name){
return PersonClient.sayHello(name);
}
}
重新启动eureka-consumer项目,访问:http://localhost:9090/router/spirit,浏览器能正常返回我们的字符串,就说明我们编写的Feign客户端运行正常
Feign的负载均衡效果
基于Ribbon,Feign也拥有负载均衡的效果(不用再引入Ribbon依赖)。
修改eureka-provider的Person实体类,新增一个属性,用端口来确认访问的哪一个服务:
package com.init.springCloud;
import lombok.Data;
@Data
public class Person {
private Integer id; //主键ID
private String name; //姓名
private String info; //url路径信息
}
然后修改ProviderController控制器,通过请求获取这个url信息:
@RequestMapping(value = "/search/{id}", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Person searchPerson(@PathVariable Integer id, HttpServletRequest request){
Person person = new Person();
person.setId(id);
person.setName("Spirit");
person.setInfo(request.getRequestURL().toString());
return person;
}
启动两个eureka-provider项目,端口分别是8080和8081。
然后拷贝eureka-provider项目的Person实体到eureka-consumer中(lombok需要引入相关依赖,也可以改写成自己生成getter、setter方法),在PersonClient接口中新增方法,用于返回这个实体:
@RequestMapping(method = RequestMethod.GET, value = "/search/{id}")
Person getPersonById(@PathVariable("id") Integer id);
然后在ConsumerController项目中新增方法,返回Person实体类的JSON字符串:
@RequestMapping(method = RequestMethod.GET, value = "/find/{id}",
produces = MediaType.APPLICATION_JSON_VALUE)
public Person getPersonById(@PathVariable Integer id){
return PersonClient.getPersonById(id);
}
重启eureka-consumer项目,浏览器多次访问:http://localhost:9090/find/1,我们可以看到Feign实现了负载均衡的效果:
Feign提供的Bean组件
Spring Cloud Netflix在默认情况下提供了以下bean(Bean类型 Bean名称:类名):
- Decoder feignDecoder: ResponseEntityDecoder (which wraps a SpringDecoder)
- Encoder feignEncoder: SpringEncoder
- Logger feignLogger: Slf4jLogger
- Contract feignContract: SpringMvcContract
- Feign.Builder feignBuilder: HystrixFeign.Builder
- Client feignClient: if Ribbon is enabled it is a LoadBalancerFeignClient, otherwise the default feign client is used.
我们可以通过设置feign.okhttp.enabled或者feign.httpclient.enabled属性为true来分别启用OkHttpClient和ApacheHttpClient的Feign客户端,然后分别在类路径中使用它们。
Spring Cloud Netflix在默认情况下不提供以下bean,但仍然从应用程序上下文中查找这些类型的bean,以创建feign客户端:
- Logger.Level
- Retryer
- ErrorDecoder
- Request.Options
- Collection<RequestInterceptor>
- SetterFactory
最后,大家有什么不懂的或者其他需要交流的内容,也可以进入我的QQ讨论群一起讨论:654331206
Spring Cloud系列:
Spring Cloud服务管理框架Eureka简单示例(三)