Feign 微服务调用 (nacos 作为注册中心)

Feign 微服务调用 (nacos 作为注册中心)

Feign 是 spring Cloud Netflix 组件中的一量级 Restful的HTTP 服务客户端,实现了负载均衡和 Rest 调用的开源框架,封装了RibbonRestTemplate,实现WebService的面向接口编程。

Feign 简化了RestTemplate代码,是声明式服务调用组件:核心就是像调用本地方法一样调用远程方法。让开发者无需关注,远程调用过程,和交互细节。

Feign 本身并不支持spring MVC注解,它有一套自己的注解,为了更方便使用Spring Cloud孵化OpenFeign。并且支持spring mvc的注解,例如:@RequestMapping、@PathVariable。

openFeign 的@FeignClient可以解析Spring MVC@RequestMapping注解下的接口,并通过动态代理方式产生实现类,实现类中做负载均衡调用服务。

soringboot 2.0 以后基本使用 OpenFeign

官网

特性

  • Hystrix 和它的 Fallback
  • HTTP 请求响应的压缩
  • Ribbon 负载均衡客户端
  • 可拔插的HTTP编码器和解码器
  • 拔插的注解支持,包括Feign 注解 和JAX-RS 注解

文章目录

  • Feign 微服务调用 (nacos 作为注册中心)
    • 快速开始
    • 项目
      • pom 文件
      • goods
      • order
      • 测试
    • @FeignClient ‼️
    • Feign 日志开启
    • HTTP Client 替换
      • 修改
      • 如果使用 okhttp 的可以自定义配置
    • 参数传递
      • 特殊需求,Get方法传递了多参数
    • 实现Token 传递
  • 底部

快速开始

项目预览

Feign 微服务调用 (nacos 作为注册中心)_第1张图片

项目

pom 文件

父项目

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.1.3.RELEASEversion>
        <relativePath/> 
    parent>
    
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-alibaba-nacos-discoveryartifactId>
            <version>2.0.1.RELEASEversion>
        dependency>
    dependencies>

    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>Finchley.RELEASEversion>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    
    <repositories>
        <repository>
            <id>spring-milestonesid>
            <name>Spring Milestonesname>
            <url>https://repo.spring.io/libs-milestoneurl>
            <snapshots>
                <enabled>falseenabled>
            snapshots>
        repository>
    repositories>

goods 消费者

    <parent>
        <artifactId>springcloud-openfeigartifactId>
        <groupId>org.examplegroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>openfeig-goodsartifactId>
    <description>商品服务description>

    <properties>
        <maven.compiler.source>8maven.compiler.source>
        <maven.compiler.target>8maven.compiler.target>
    properties>

order 生产者

    <parent>
        <artifactId>springcloud-openfeigartifactId>
        <groupId>org.examplegroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>openfeig-orderartifactId>

    <properties>
        <maven.compiler.source>8maven.compiler.source>
        <maven.compiler.target>8maven.compiler.target>
    properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        dependency>
    dependencies>

goods

server:
  port: 8081
spring:
  application:
    name: openfeig-goods
  cloud:
    nacos:
      server-addr: ...:8848
@RestController
public class GoodsController {

    @GetMapping("hello/{message}")
    public String hello(@PathVariable String message){
        return "来自goods的消息 =="+ message;
    }
}

order

server:
  port: 8080
spring:
  application:
    name: openfeig-order
  cloud:
    nacos:
      server-addr: ....:8848
@SpringBootApplication
//开启远程调用的注解 扫描,可以扫描 @FeignClient
@EnableFeignClients
public class OpenFeigOrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OpenFeigOrderApplication.class,args);
    }
}
// 准备远程调用的接口
@FeignClient(name = "openfeig-goods")
public interface  GoodsClienService {

    @GetMapping("hello/{message}")
    String hello(@PathVariable String message);
}
//提供对外暴露的 api
@RestController
public class OrderController  {

    //nacos 客户端信息
    @Autowired
    private LoadBalancerClient loadBalancerClient;

    //调用的 service
    @Autowired
    private GoodsClienService cartService;

    @GetMapping("add/{message}")
    public String add(@PathVariable String message){
        ServiceInstance choose = loadBalancerClient.choose("openfeig-goods");
        return cartService.hello(message) +"  "+ choose.getHost() + "--" + choose.getPort();
    }
}

测试

Feign 微服务调用 (nacos 作为注册中心)_第2张图片

@FeignClient ‼️

需要 @EnableFeignClients来开启扫描

当定义的Feign接口中的方法被调用时,通过JDK的代理方式,生成具体的 RequestTemplate。这个对象中,封装了http需要的全部信息。参数、方法名等等

属性 说明
name 指定FeignClient等名称,如果项目使用了Ribbon,那么name属性会作为微服务等名称,用于服务发现
url 一般用于调试,可以手动指定@FeignClient调用的服务地址
decode404 当发生404错误时,如果该字段值为true,会调用decoder进行编码,否则抛出FeignException
configuration Feign 配置列,可以自定义Feign 的 Encoder、Decoder、LogLevel、Contract
也可以在配置文件中配置
fallback 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现 @FeignClient标记的接口
fallbackFactory 工厂类,用于生成fallback类实例,通过这个属性可以实现每个接口通用的容错逻辑,减少重复的代码
path 定义当前FeignClient 的统一前缀

Feign 日志开启

需要在配置文件中 logback.xml 把日志级别改为debug

# open feign 日志设置
logging:
  level:
    com.springcloud.study.service.ProviderClientService: debug
//全局的配置 日志配置类
@Configuration
public class FeignServiceConfig {

    /*
    Logger.Level 设置级别
    NONE : 不记录任何信息
    BASIC: 仅记录请求方法,URL以及响应状态码
    HEADERS: 除了记录 BASIC级别信息外,还会记录请求和响应的头信息
    FULL: 记录所有的请求于响应的明细,包括头信息,请求体,元数据
     */
    @Bean
    Logger.Level feignLogger(){
        return Logger.Level.FULL;
    }
}
//指定单独的配置 MyFeignConfig 是一个 日志的配置类
@FeignClient(configurtion = MyFeignConfig.class)

HTTP Client 替换

Feign 默认使用的是JDK原生的 URLConnection发送HTTP请求,没有用链接池。对每个地址都会建立一个长链接。

feign 的HTTP客户端支持3中框架HttpURLConnection、HttpClient、OkHttp

默认是 HttpURLConnection

修改


<dependency>
    <groupId>io.github.openfeigngroupId>
    <artifactId>feign-httpclientartifactId>
dependency>



<dependency>
    <groupId>io.github.openfeigngroupId>
    <artifactId>feign-okhttpartifactId>
dependency>
feign:
  httpclient:
    # 开启httpclient
    enabled: true

如果使用 okhttp 的可以自定义配置

okHttp 优势

  1. 支持SPDY,合并多个请求到同一个主机
  2. 使用链接池
  3. 使用GZIP压缩减少传输数据体积
  4. 缓存响应结果,减少重复请求
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {

    @Bean
    public okhttp3.OkHttpClient okHttpClient() {
        return new OkHttpClient.Builder()
                .connectTimeout(60, TimeUnit.SECONDS) //链接超时时间
                .readTimeout(60,TimeUnit.SECONDS)     //读超时
                .writeTimeout(60,TimeUnit.SECONDS)     //写超时
                .retryOnConnectionFailure(true)                 //自动重试
                .connectionPool(new ConnectionPool())           //创建链接池 okhttp包的
                .build();
    }
}

参数传递

get 方式,@PathVariable、@RequestParm注解来接收

post 方式 ,@RequestBody接收请求参数

特殊需求,Get方法传递了多参数

需要时实现Feign 的RequestInterceptor中的pally进行统一处理

TODO 还是用 post 方法发送省事

实现Token 传递

认证鉴权的时候,使用JWT,或spring security 都需要拿到token

RequestInterceptor 拦截器,在feign 调用的时候,向请求头里添加需要传递的token

基于上面的代码改动 注意token 大小写的问题

//生产端
@Component
public class FeignTokenAddInterceptor  implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        if(null==getHttpServletRequest()){
            //此处省略日志记录
            return;
        }
        //将获取Token对应的值往下面传
        requestTemplate.header("oauthToken", getHeaders(getHttpServletRequest()).get("oauthtoken"));
    }

    private HttpServletRequest getHttpServletRequest() {
        try {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Feign拦截器拦截请求获取Token对应的值
     * @param request
     * @return
     */
    private Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> map = new LinkedHashMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }

        System.out.println("模拟token " +  map.get("oauthtoken"));

        return map;
    }
}

    @GetMapping("hello/{message}")
    public String hello(@PathVariable("message") String message, HttpServletRequest req){

        String oauthToken = req.getHeader("oauthToken");
        System.out.println( "token =" + oauthToken);

        return "来自goods的消息 =="+ message + oauthToken;
    }

Feign 微服务调用 (nacos 作为注册中心)_第3张图片

底部

你可能感兴趣的:(spring,Cloud,Alibaba,微服务,spring,java)