敖胤

绳锯木断,水滴石穿;聚沙成塔,集腋成裘。

导航

Spring Cloud Netflix 学习笔记(三)—服务映射(Feign)

Feign是一个声明式的REST客户端,它能让REST调用更加简单。

Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。

Spring Cloud对Feign进行了封装,使其支持SpringMVC标准注解和HttpMessageConverters。

Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

1、简单使用

1.1、导入依赖包

在调用方(emp模块)导入openfeign的依赖包

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

1.2、添加注解

在启动类上添加@EnableFeignClients注解

@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients(basePackages = "org.silence.emp.remote")
public class EmpApplication {
    public static void main(String[] args) {
        SpringApplication.run(EmpApplication.class, args);
    }
}

1.3、添加映射接口

在调用方添加被调用服务的接口——服务提供方controller的接口拷贝。

@FeignClient注解标识当前是一个Feign的客户端,value属性是对应的服务名称。

package org.silence.emp.remote;

@FeignClient("dept")//映射服务名
public interface DeptClient {
    //服务提供方方法的copy
    @RequestMapping(method = RequestMethod.GET,value = "/dept/all")
    String getAll();
}

1.4、调用

通过方法调用的方式,调用映射接口访问远程服务。

@RestController
@RequestMapping("/emp")
public class EmpController {
    @Autowired
    private DeptClient dept;

    @GetMapping("/all")
    public String getAll() {
        String result = dept.getAll();

        System.out.println(result);
        return "emp" + "\t" + result;
    }
}

2、服务调用方式变化

可以发现,我们的调用方式变得越来越简单了,从最开始的指定地址,到后面通过Eureka中的服务名称来调用,再到现在直接通过定义接口来调用。

譬如,emp模块调用dept中的如下接口:

@RestController
@RequestMapping("/dept")
public class DeptController {
    @Value("${server.port}")
    private String port;

    @GetMapping("/all")
    public String getAll() {
        return "dept=====>" + port;
    }
}

2.1、原始RestTemplate方式

@RestController
@RequestMapping("/emp")
public class EmpController {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private EurekaClient eurekaClient;

    @GetMapping("/emp/all")
    public String getAll() {
        //使用eurekaClient对象通过服务名获取目标服务信息
        InstanceInfo instanceInfo = eurekaClient.getNextServerFromEureka("DEPT", false);
        //获取访问地址
        String url = instanceInfo.getHomePageUrl();
        System.out.println(url);
        //通过restTemplate对象访问目标服务
        String result = restTemplate.getForObject(url + "/dept/all", String.class);
        //获取目标结果并返回
        System.out.println(result);
        return "emp: " + "\t" + result;
    }
}

2.2、RestTemplate + Ribbon方式

@RestController
@RequestMapping("/emp")
public class EmpController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/emp/all")
    public String getAll() {
        //通过restTemplate对象访问目标服务
        String result = restTemplate.getForObject("http://dept/dept/all", String.class);
        //获取目标结果并返回
        System.out.println(result);
        return "emp: " + "\t" + result;
    }
}

2.3、Feign方式

  • 映射接口
@FeignClient("dept")//服务名
public interface DeptClient {
    //服务提供方方法的copy
    @RequestMapping(method = RequestMethod.GET,value = "/dept/all")
    String getAll();

    @RequestMapping(value = "/dept/getDept", method = RequestMethod.GET)
    Dept getDept(@RequestParam Integer deptNo);
}
  • 调用
@RestController
@RequestMapping("/emp")
public class EmpController {

    @Autowired
    private DeptClient deptClient;

    @GetMapping("/all")
    public String getAll() {
        String result = deptClient.getAll();
        return "emp: " + "\t" + result;
    }
}

3、参数传递

使用feign实现服务调用时,需要注意如下几点:

  • 当传递的参数比较复杂时,服务提供方会采用POST请求的方式接收数据,即使显示设置了请求方法为GET

  • 复杂对象作为参数传递时,统一使用POST方法,数据使用json格式传输,注意添加@RequestBody注解(可不写)

  • 映射接口的方法必须使用@RequestMapping注解来映射路径


3.1、定义方法

修改dept的controller,添加不同的带参方法:

@RestController
@RequestMapping("/dept")
public class DeptController {

    @Value("${server.port}")
    private String port;

    @GetMapping("/all")
    public String getAll() {
        return "dept====>" + port;
    }

    @GetMapping("/getDept")
    public Dept getDept(Integer deptNo) {
        System.out.println("deptNo: " + deptNo);
        return new Dept(10, "ACCOUNTING", "NEW YORK");
    }

    @GetMapping("/getDept1/{deptNo}")
    public Dept getDept1(@PathVariable Integer deptNo) {
        System.out.println("deptNo: " + deptNo);
        return new Dept(deptNo, "ACCOUNTING", "NEW YORK");
    }

    @GetMapping("/getDept2")
    public Dept getDept2(@RequestParam Integer deptNo) {
        System.out.println("deptNo: " + deptNo);
        return new Dept(deptNo, "ACCOUNTING", "NEW YORK");
    }

    @GetMapping("/byDeptNo/{deptNo}/{dName}")
    public Dept getByDeptNo(@PathVariable Integer deptNo, @PathVariable String dName) {
        System.out.println(deptNo + "\t" + dName);
        return new Dept(deptNo, dName, "CHICAGO");
    }

    @GetMapping("/byDeptNo1")
    public Dept getByDeptNo1(@RequestParam Integer deptNo, @RequestParam String dName) {
        System.out.println(deptNo + "\t" + dName);
        return new Dept(deptNo, dName, "CHICAGO");
    }

    @GetMapping("/byDeptNo2")
    public Dept getByDeptNo2(Integer deptNo, String dName) {
        System.out.println(deptNo + "\t" + dName);
        return new Dept(deptNo, dName, "CHICAGO");
    }

    @PostMapping("/save")
    public Dept save(@RequestBody Dept dept) {
        System.out.println(dept);
        return dept;
    }

}

3.2、修改映射

修改emp中的映射接口:

@FeignClient("dept")//服务名
public interface DeptClient {
    //服务提供方方法的copy
    @RequestMapping(method = RequestMethod.GET,value = "/dept/all")
    String getAll();

    @RequestMapping(value = "/dept/getDept", method = RequestMethod.GET)
    Dept getDept(@RequestParam Integer deptNo);

    @RequestMapping(value = "/dept/getDept1/{deptNo}", method = RequestMethod.GET)
    Dept getDept1(@PathVariable Integer deptNo);

    @RequestMapping(value = "/dept/getDept2", method = RequestMethod.GET)
    Dept getDept2(@RequestParam Integer deptNo);

    @RequestMapping(value = "/dept/byDeptNo/{deptNo}/{dName}", method = RequestMethod.GET)
    Dept getByDeptNo(@PathVariable Integer deptNo, @PathVariable String dName);

    @RequestMapping(value = "/dept/byDeptNo1", method = RequestMethod.GET)
    Dept getByDeptNo1(@RequestParam Integer deptNo, @RequestParam String dName);

    @RequestMapping(value = "/dept/byDeptNo2", method = RequestMethod.GET)
    Dept getByDeptNo2(@RequestParam Integer deptNo, @RequestParam String dName);

    @RequestMapping(value = "/dept/save")
    Dept save(Dept dept);
}

3.3、调用

@RestController
@RequestMapping("/emp")
public class EmpController {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DeptClient deptClient;

    @GetMapping("/all")
    public String getAll() {
        String result = deptClient.getAll();
        System.out.println(result);
        return "emp" + "\t" + result;
    }

    @GetMapping("/getDept")
    public Dept getDept(Integer deptNo) {
        System.out.println("deptNo: " + deptNo);
        return deptClient.getDept(deptNo);
    }

    @GetMapping("/getDept1/{deptNo}")
    public Dept getDept1(@PathVariable Integer deptNo) {
        System.out.println("deptNo: " + deptNo);
        return deptClient.getDept1(deptNo);
    }

    @GetMapping("/getDept2")
    public Dept getDept2(@RequestParam Integer deptNo) {
        System.out.println("deptNo: " + deptNo);
        return deptClient.getDept2(deptNo);
    }

    @GetMapping("/byDeptNo/{deptNo}/{dName}")
    public Dept getByDeptNo(@PathVariable Integer deptNo, @PathVariable String dName) {
        System.out.println(deptNo + "\t" + dName);
        return deptClient.getByDeptNo(deptNo, dName);
    }

    @GetMapping("/byDeptNo1")
    public Dept getByDeptNo1(@RequestParam Integer deptNo, @RequestParam String dName) {
        System.out.println(deptNo + "\t" + dName);
        return deptClient.getByDeptNo1(deptNo, dName);
    }

    @GetMapping("/byDeptNo2")
    public Dept getByDeptNo2(Integer deptNo, String dName) {
        System.out.println(deptNo + "\t" + dName);
        return deptClient.getByDeptNo2(deptNo, dName);
    }

    @GetMapping("/save")
    public Dept save(Dept dept, HttpServletRequest request) {
        System.out.println(request.getMethod());
        System.out.println(dept);
        return deptClient.save(dept);
    }

}

4、Feign配置

4.1、日志输出

正常情况下,控制台不会输出Feign调用的详细信息,而是要自定义Feign日志的配置。

Feign日志等级有4种,分别是:

  • NONE:不输出日志。

  • BASIC:只输出请求方法的URL和响应的状态码以及接口执行的时间。

  • HEADERS:将BASIC信息和请求头信息输出。

  • FULL:输出完整的请求信息。

4.1.1、自定义配置类

在emp中,定义一个Feign的日志配置类,输出Feign的全部日志。

@Configuration
public class FeignConfig {
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}
4.1.2、修改日志配置

在emp的application.yml中添加如下配置:

#自定义springboot的日志输出
logging:
  level:
  	#Feign映射接口全路径名
    org.silence.emp.remote.DeptClient: debug

运行后,可在控制台看到如下日志:

posted on 2021-03-15 20:36  敖胤  阅读(237)  评论(0编辑  收藏  举报