SpringCloud(part 4)REST客户端Feign

    xiaoxiao2022-07-07  196

    在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; } }

    1.使用CXF调用REST服务

    public class CxfClient { public static void main(String[] args) throws IOException { //创建WebClient WebClient webClient=WebClient.create("http://localhost:8080/person/1"); //获取响应 Response response=webClient.get(); //获取响应的内容 InputStream ent= (InputStream) response.getEntity(); String content= IOUtils.readStringFromStream(ent); //输入字符串 System.out.println(content); } }

    运行的得到请求信息

    {"pid":1,"pname":"朱海涛","message":"http://localhost:8080/person/1服务端口:8080远程端口:58178"}

    2.Feign框架介绍

    Feign是github上的一个开源项目,目的是简化WebService客户端的开发。在使用Feign时,可以使用注释来修饰接口,被修饰的接口具有访问Web Service的能力。

    新建一个简单SpringBoot的web项目

    简单DEMO:

    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请求 */

    当我们返回对象时:

    对象DEMO:

    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字符串解析成对象 。

    编码器:是将传入的对象编码成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客户端具有编码的功能了

    3.请求拦截器

    public class MyInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { requestTemplate.header("Content-Type","application/json"); System.out.println("Feign客户端拦截器。。。0.0"); } } public class InterceptorTest { public static void main(String[] args) { PersonClient personClient= Feign.builder().requestInterceptor(new MyInterceptor()). target(PersonClient.class,"http://localhost:8080/"); } }

    4.SpringCloud中使用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 @EnableEurekaServer

     provider项目:

    配置:

    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 会帮助我们代理去在服务提供者的服务中调用其中的接口

     

    5.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 中来回切换,拥有默认的负载均衡的功能了

     
    最新回复(0)