在SpringCloud集群中,各个角色的通信基于REST服务,因此在调用服务时,就不可避免的需要使用REST服务的请求客户端,以前我们是使用Spring自带的RestTemplate,RestTemplate使用HTTPClient发送请求。下面介绍另外一个REST客户端Feign。
首先新建项目,然后我们需要编写接口,然后启动项目 @RestController public class FirstController { @RequestMapping(value = "/person/{pid}",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Person findPerson(@PathVariable("pid") Integer pid, HttpServletRequest request){ Person person=new Person(); person.setPid(pid); person.setPname("朱海涛"); person.setMessage(request.getRequestURL().toString() +"服务端口:"+request.getServerPort() +"远程端口:"+request.getRemotePort()); return person; } }运行的得到请求信息
{"pid":1,"pname":"朱海涛","message":"http://localhost:8080/person/1服务端口:8080远程端口:58178"}
Feign是github上的一个开源项目,目的是简化WebService客户端的开发。在使用Feign时,可以使用注释来修饰接口,被修饰的接口具有访问Web Service的能力。
新建一个简单SpringBoot的web项目
1.编写控制器方法
@RequestMapping(value = "/hello",method = RequestMethod.GET) public String sayHello(){ return "Hello Feign"; }2.编写Feign的客户端访问web Service的接口
public interface HelloClient { @RequestLine("GET /hello") String sayHello(); }3.编写客户端创建Feign客户端实例,并调用接口方法。
public class HelloMain { public static void main(String[] args) { HelloClient helloClient= Feign.builder(). target(HelloClient.class,"http://localhost:8080/"); System.out.println(helloClient.sayHello()); } } 输出:Hello Feign /* 总结:编写控制器sayHello方法,Feign是利用接口来封装客户端请求, 我们创建接口来封装了 sayHello的方法,然后通过Feign创建实例,并对接口方法进行调用。 实际上Feign使用的是JDK的动态代理技术,生成的代理类会将请求的信息封装,交给Feign.client 接口进行请求处理,而改接口的默认实现类将会使用java.net.HttpURLConnection 来发送Http请求 */当我们返回对象时:
1.编写控制器方法
@RequestMapping(value = "/person/{pid}",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Person findPerson(@PathVariable("pid") Integer pid, HttpServletRequest request){ Person person=new Person(); person.setPid(pid); person.setPname("朱海涛"); person.setMessage(request.getRequestURL().toString() +"服务端口:"+request.getServerPort() +"远程端口:"+request.getRemotePort()); return person; } 2.编写Feign的客户端访问web Service的接口 @RequestLine("GET /person/{pid}") public Person getPersonById(@Param("pid") Integer pid);注意这里参数前面的注解 控制器REST风格的注解是@PathVariable
Feign接口封装参数的注解@Param
3.编写客户端创建Feign客户端实例,并调用接口方法。
public static void main(String[] args) { PersonClient personClient= Feign.builder().decoder(new GsonDecoder()) .target(PersonClient.class,"http://localhost:8080/"); Person person=personClient.getPersonById(2); System.out.println(person); }解码器:
和以上简单DEMO在客户端代码上的区别是 增加了decoder(new GsonDecoder())将返回的JSON字符串解析成对象 。
控制器方法:
@RestController public class PersonController { @RequestMapping(value = "/person/create", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public String createPerson(@RequestBody Person person) { System.out.println(person.getPname()); return "Success, Person Id: " + person.getPname(); } }Feign客户端接口:
public interface PersonClient { //编码器 @RequestLine("POST /person/create") @Headers("Content-Type: application/json") String createPerson(Person person); }调用函数:
public static void main(String[] args) { // 获取服务接口 PersonClient personClient = Feign.builder() .encoder(new GsonEncoder()) .target(PersonClient.class, "http://localhost:8080/"); // 创建参数的实例 Person person = new Person(); person.setPid(1); person.setPname("小猪"); person.setMessage("我快要疯掉了"); String response = personClient.createPerson(person); System.out.println(response); }encoder(new GsonEncoder()) 这样的feign客户端具有编码的功能了
新建 Eureka服务器3个项目项目 server provider invoker
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>server项目:
配置:
server: port: 8888 spring: application: name: cloud-feign-server eureka: client: fetch-registry: false register-with-eureka: false启动类:
@SpringBootApplication @EnableEurekaServerprovider项目:
配置:
eureka: instance: hostname: localhost client: service-url: defaultZone: http://localhost:8888/eureka/ spring: application: name: cloud-feign-provider控制器:
@RestController public class FirstController { @RequestMapping(value = "/person/{pid}",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Person findPerson(@PathVariable("pid") Integer pid, HttpServletRequest request){ Person person=new Person(); person.setPid(pid); person.setPname("朱海涛"); person.setMessage(request.getRequestURL().toString() +"服务端口:"+request.getServerPort() +"远程端口:"+request.getRemotePort()); return person; } @RequestMapping(value = "/hello",method = RequestMethod.GET) public String sayHello(){ return "Hello Feign"; } }启动类:
@SpringBootApplication @EnableEurekaClient public class CloudFeignProviderApplication { public static void main(String[] args) { //通过不同的端口号来启动服务 Scanner scanner=new Scanner(System.in); String port=scanner.nextLine(); new SpringApplicationBuilder(CloudFeignProviderApplication.class). properties("server.port="+port).run(args); } }客户端项目:
加入配置:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>配置:
server: port: 9000 eureka: instance: hostname: localhost client: service-url: defaultZone: http://localhost:8888/eureka/ spring: application: name: cloud-feign-invoker客户端控制器:
@RestController @Configuration public class InvokerController { /* @EnableFeignClients("com.example.cloudfeigninvoker.client") 该注解会帮我将@FeignClient修饰的接口实现类注入到SpringIOC容器当中 */ @Autowired private PersonClient personClient; @RequestMapping(value = "/invokerHello",method = RequestMethod.GET) public String InvokerHello(){ return personClient.hello(); } }Feign客户端接口 (封装客户端控制器,通过JDK动态代理技术帮助我们实现客户端调用)
@FeignClient("cloud-feign-provider")//声明调用的服务名称 public interface PersonClient { @RequestMapping(value ="/hello",method = RequestMethod.GET) //接口方式还满足@RequestParam @RequestHeader @PathVariable 三种参数 public String hello(); }启动类:
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients("com.example.cloudfeigninvoker.client") public class CloudFeignInvokerApplication { public static void main(String[] args) { SpringApplication.run(CloudFeignInvokerApplication.class, args); } }分别启动项目server 两次通过不同端口启动服务提供者项目,最后启动客户端项目,
然后通过客户端端口来访问自己控制器,feign 会帮助我们代理去在服务提供者的服务中调用其中的接口 。
SpringCloud实现的Feign客户端,类名为LoadBalanceFeignClient,在该类中,维护着与SpringClientFactory相关的实例,通过springClientFactory可以获取负载均衡器,负载均衡器会根据一定的规则来选取请求的服务器。
@RequestMapping(value = "/router",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE //必须要加,不然返回的为XML文档数据形的数据格式
) public Person ronter(){ Person person=personClient.getPersonById(2); return person; }Feign 客户端:
@RequestMapping(value = "person/{pid}",method = RequestMethod.GET) Person getPersonById(@PathVariable("pid") Integer pid);服务提供者:
@RequestMapping(value = "/person/{pid}",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Person findPerson(@PathVariable("pid") Integer pid, HttpServletRequest request){ Person person=new Person(); person.setPid(pid); person.setPname("朱海涛"); person.setMessage(request.getRequestURL().toString() +"服务端口:"+request.getServerPort() +"远程端口:"+request.getRemotePort()); return person; }通过客户端端口 访问 /router接口可以发现
返回的字符串 message信息中的端口在8001和 8002 中来回切换,拥有默认的负载均衡的功能了