首先还是需要去们的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