声明式服务调用 Feign

所有服务调用都是手动写 RestTemplate 来实现的,大家可能已经发现这样写有点麻烦,每次都要写请求 Url 、
配置响应数据类型,最后还要组装参数,更重要的是这些都是一些重复的工作,代码高度相似,
每个请求只有 Url 不同,请求方法不同、参数不同,其它东西基本都是一样的,既然如此,那有没有办法简化请求呢?有!
OpenFeign
OpenFeign 则是 Spring Cloud 团队在 Netflix Feign 基础上开发出来的声明式服务调用组件
在这里插入图片描述

父项目

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <modules>
        <module>feign-commons</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ghgcn</groupId>
    <artifactId>open-feign</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>open-feign</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

建立服务

  • pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.ghgcn</groupId>
        <artifactId>open-feign</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <artifactId>eureka-server</artifactId>

    <name>eureka-server</name>
    <description>Demo project for Spring Boot</description>


    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>


</project>

application.properties

spring.application.name=eureka server
server.port=7777

eureka.instance.hostname=eureka-server
eureka.client.fetch-registry=true
eureka.client.register-with-eureka=true

package com.ghg.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }

}

启动
在这里插入图片描述

commons

  • pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ghgcn</groupId>
        <artifactId>open-feign</artifactId>

        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>feign-commons</artifactId>


</project>

在这里插入图片描述

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String username;
    private String address;
}

建立生产者

  • pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.ghgcn</groupId>
        <artifactId>open-feign</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <artifactId>feign-producer</artifactId>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ghgcn</groupId>
            <artifactId>feign-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

</project>

  • properties
spring.application.name=feigon-producer
server.port=7878
eureka.client.serviceUrl.defaultZone=http://localhost:7777/eureka


  • controller
package com.ghgcn.feignproducer.controller;

import com.ghgcn.feigncommons.entity.User;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * @author 刘楠
 * @since 2019/6/20
 */
@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello(String name) {
        return "hello " + name + " !";
    }


    @PostMapping("/user")
    public User hello(@RequestBody User user) {
        return user;
    }


    @RequestMapping("/register")
    public String register(User user) throws UnsupportedEncodingException {
        return "redirect:/loginPage?username=" + URLEncoder.encode(user.getUsername(),"UTF-8") + "&address=" + URLEncoder.encode(user.getAddress(),"UTF-8");
    }
    @GetMapping("/loginPage")
    @ResponseBody
    public String loginPage(User user) {
        return "loginPage:" + user.getUsername() + ":" + user.getAddress();
    }

    @PutMapping("/user/name")
    @ResponseBody
    public void updateUserByUsername(User User) {
        System.out.println(User);
    }
    @PutMapping("/user/address")
    @ResponseBody
    public void updateUserByAddress(@RequestBody User User) {
        System.out.println(User);
    }

    @DeleteMapping("/user/{id}")
    @ResponseBody
    public void deleteUserById(@PathVariable Integer id) {
        System.out.println(id);
    }
    @DeleteMapping("/user/")
    @ResponseBody
    public void deleteUserByUsername(String username) {
        System.out.println(username);
    }

    @GetMapping("/customheader")
    public String customHeader(HttpServletRequest req) {
        return req.getHeader("cookie");
    }


}

  • 启动类
package com.ghgcn.feignproducer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FeignProducerApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignProducerApplication.class, args);
    }

}


生产除了配置之外,和普通的Spring boot Web项目没什么区别

建立消费者

  • pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.ghgcn</groupId>
        <artifactId>open-feign</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <artifactId>feign-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>feign-consumer</name>
    <description>Demo project for Spring Boot</description>



    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
      
        <dependency>
            <groupId>com.ghgcn</groupId>
            <artifactId>feign-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--失败重试,加了依赖就自动开启-->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
    </dependencies>



</project>

  • properties
spring.application.name=feign-consumer
server.port=7979

eureka.client.serviceUrl.defaultZone=http://localhost:7777/eureka
#默认就是true
spring.cloud.loadbalancer.retry.enabled=true

  • controller
package com.ghgcn.feignconsumer.controller;

import com.ghgcn.feigncommons.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 刘楠
 * @since 2019/6/20
 */
@RestController
public class CallHelloController {

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    @Qualifier("loadBalancer")
    RestTemplate loadBalancer;

    @Autowired
    DiscoveryClient discoveryClient;


    @GetMapping("/call")
    public String callHello(String name){
        List<ServiceInstance> list = discoveryClient.getInstances("feigon-producer");
        ServiceInstance instance = list.get(0);
        String host = instance.getHost();
        int port = instance.getPort();
        String s = restTemplate.getForObject("http://" + host + ":" + port + "/hello?name={1}", String.class, name);
        return s;
    }

    @GetMapping("/hello7")
    public User hello7() {
        List<ServiceInstance> list = discoveryClient.getInstances("feigon-producer");
        ServiceInstance instance = list.get(0);
        String host = instance.getHost();
        int port = instance.getPort();
        String url = "http://" + host + ":" + port + "/user";
        User u1 = new User();
        u1.setUsername("刘楠");
        u1.setAddress("深圳坂田");
        ResponseEntity<User> responseEntity = restTemplate.postForEntity(url, u1, User.class);
        return responseEntity.getBody();
    }

    @GetMapping("/hello8")
    public String hello8() {
        List<ServiceInstance> list = discoveryClient.getInstances("feigon-producer");
        ServiceInstance instance = list.get(0);
        String host = instance.getHost();
        int port = instance.getPort();
        String url = "http://" + host + ":" + port + "/register";
        MultiValueMap map = new LinkedMultiValueMap();
        map.add("username", "刘楠");
        map.add("address", "深圳南山");
        URI uri = restTemplate.postForLocation(url, map);
        String s = restTemplate.getForObject(uri, String.class);
        return s;
    }

    @GetMapping("/hello9")
    public void hello9() {
        List<ServiceInstance> list = discoveryClient.getInstances("feigon-producer");
        ServiceInstance instance = list.get(0);
        String host = instance.getHost();
        int port = instance.getPort();
        String url1 = "http://" + host + ":" + port + "/user/name";
        String url2 = "http://" + host + ":" + port + "/user/address";
        MultiValueMap map = new LinkedMultiValueMap();
        map.add("username", "刘楠");
        map.add("address", "深圳龙岗");
        restTemplate.put(url1, map);
        User u1 = new User();
        u1.setAddress("刘楠1");
        u1.setUsername("深圳龙岗");
        restTemplate.put(url2, u1);
    }

    @GetMapping("/hello10")
    public void hello10() {
        List<ServiceInstance> list = discoveryClient.getInstances("feigon-producer");
        ServiceInstance instance = list.get(0);
        String host = instance.getHost();
        int port = instance.getPort();
        String url1 = "http://" + host + ":" + port + "/user/{1}";
        String url2 = "http://" + host + ":" + port + "/user/?username={username}";
        Map<String,String> map = new HashMap<>();
        map.put("username", "深圳龙岗444");
        restTemplate.delete(url1, 99);
        restTemplate.delete(url2, map);
    }

    @GetMapping("/hello11")
    public void hello11() {
        List<ServiceInstance> list = discoveryClient.getInstances("feigon-producer");
        ServiceInstance instance = list.get(0);
        String host = instance.getHost();
        int port = instance.getPort();
        String url = "http://" + host + ":" + port + "/customheader";
        restTemplate.setInterceptors(Collections.singletonList(new ClientHttpRequestInterceptor() {
            @Override
            public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
                HttpHeaders headers = request.getHeaders();
                headers.add("cookie","filecommons");
                return execution.execute(request,body);
            }
        }));
        String s = restTemplate.getForObject(url, String.class);
        System.out.println(s);
    }

    @GetMapping("/hello12")
    public void hello12() {
        List<ServiceInstance> list = discoveryClient.getInstances("feigon-producer");
        ServiceInstance instance = list.get(0);
        String host = instance.getHost();
        int port = instance.getPort();
        String url = "http://" + host + ":" + port + "/customheader";
        HttpHeaders headers = new HttpHeaders();
        headers.add("cookie","justdojava");
        HttpEntity<MultiValueMap<String,String>> request =  new HttpEntity<>(null,headers);
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, request, String.class);
        System.out.println(responseEntity.getBody());
    }


    @GetMapping("/hello13")
    public String hello13(String name){

        String object = loadBalancer.getForObject("http://feigon-producer/hello?name={1}", String.class, name);
        System.err.println("loadBalancer "+object);
        return object;
    }
}

  • 启动类
package com.ghgcn.feignconsumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class FeignConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignConsumerApplication.class, args);

    }



   @Bean
   RestTemplate restTemplate() {
       return new RestTemplate();
   }

    @Bean
    @LoadBalanced
    RestTemplate loadBalancer() {
        return new RestTemplate();
    }
}

添加feign

在消费者中添加依赖

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

  • 启动类
@SpringBootApplication
//启用feign 要使用 Feign ,首先在项目启动类上添加 @EnableFeignClients  注解表示开启 Feign 的支持,如下:
@EnableFeignClients
public class FeignConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignConsumerApplication.class, args);

    }



   @Bean
   RestTemplate restTemplate() {
       return new RestTemplate();
   }

    @Bean
    @LoadBalanced
    RestTemplate loadBalancer() {
        return new RestTemplate();
    }
}

  • 添加Service
package com.ghgcn.feignconsumer.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @author 刘楠
 * @since 2019/6/20
 */

/** 生产者项目spring.application.name 名称
 * 使用 @FeignClient(“feigon-producer”) 注解将当前接口和 provider 服务绑定, feigon-producer 是服务名,大小写不敏感;
 */
@FeignClient("feigon-producer")
public interface HelloService {

    /**
     * 然后使用 SpringMVC 的
     * @GetMapping("/hello") 注解将 hello 方法和 provider 中的 hello 接口绑定在一起。
     * 需要注意的是,在 SpringMVC 中,在需要给参数设置默认值或者要求参数必填的情况下才需要用到
     * @RequestParam 注解,而在这里,这个注解一定要加。
     * 这里有点类似dubbo的使用
     * @param name
     * @return
     */
    @GetMapping("/hello")
    String hello(@RequestParam("name") String name);
}

  • controller
package com.ghgcn.feignconsumer.controller;

import com.ghgcn.feignconsumer.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 刘楠
 * @since 2019/6/20
 */
@RestController
@RequestMapping("/my")
public class MyHelloController {


    @Autowired
    private HelloService helloService;

    @GetMapping("/hello")
     public  String hello(@RequestParam("name") String name){

        return  helloService.hello(name);
    }
}

在这里插入图片描述

启动后调用成功
通过 Feign ,我们只需要定义一下方法中最最关键的部分,就能实现调用

github:https://github.com/ln0491/openFeign
https://github.com/ln0491/open-feign-learning

猜你喜欢

转载自blog.csdn.net/ko0491/article/details/93056766