wordpress网站需要多大空间,重庆森林电影简介,网站项目建设流程和项目方案,微网站界面设计基础1. 前言
随着微服务架构的流行#xff0c;服务之间的通信变得越来越重要。Spring Cloud 提供了一系列工具来帮助开发者构建分布式系统#xff0c;其中 OpenFeign 是一个轻量级的 HTTP 客户端#xff0c;它简化了 Web 服务客户端的开发。本文将介绍如何在 Spring Cloud 应用…1. 前言
随着微服务架构的流行服务之间的通信变得越来越重要。Spring Cloud 提供了一系列工具来帮助开发者构建分布式系统其中 OpenFeign 是一个轻量级的 HTTP 客户端它简化了 Web 服务客户端的开发。本文将介绍如何在 Spring Cloud 应用中使用 OpenFeign 来进行服务间的调用并解决一些常见的配置问题。
2. 什么是 OpenFeign
OpenFeign 是 Spring Cloud 的一部分它是一个声明式的 Web 服务客户端。通过使用 OpenFeign开发者可以以声明的方式编写 Web 服务客户端接口而不需要创建模板化的 HTTP 请求。Feign 内置了 Ribbon用于实现客户端负载均衡使得调用服务注册中心中的服务变得更加容易。
官方地址https://github.com/OpenFeign/feign 本篇文章示例代码https://github.com/kerrsixy/cloud-demo 3. 快速入门
以调用user-service模块的 /user/{id} 接口为例。 3.1 调用端引入依赖
在order-service服务的pom文件中引入feign的依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId
/dependency
3.2 调用端添加注解
在order-service的启动类添加注解开启Feign的功能
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;EnableFeignClients
SpringBootApplication
public class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}
}
3.3 编写Feign的客户端
import com.zjp.orderservice.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;FeignClient(name user-service)
public interface UserClient {GetMapping(/user/{id})User getUser(PathVariable(id) Long id);
}
如果该类里都是以/user开头的可以改为
import com.zjp.orderservice.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;FeignClient(name user-service, path /user)
public interface UserClient {GetMapping(/{id})User getUser(PathVariable(id) Long id);
}
如果没有配置中心或配置中心有多个同名服务需要指定url则需要额外配置url
import com.zjp.orderservice.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;FeignClient(name user-service, url http://localhost:8080, path /user)
public interface UserClient {GetMapping(/{id})User getUser(PathVariable(id) Long id);
}
这个客户端主要是基于SpringMVC的注解来声明远程调用的信息比如
服务名称user-service请求方式GET请求路径/user/{id}请求参数Long id返回值类型User
这样Feign就可以帮助我们发送http请求。 注意 如果没有指定url则name要写为服务名。如果指定了url则name无论起什么名字都行但是不能为null。 3.4. 测试
import com.zjp.orderservice.entity.User;
import com.zjp.feginservice.client.UserClient;
import com.zjp.orderservice.entity.Order;
import com.zjp.orderservice.mapper.OrderMapper;
import com.zjp.orderservice.service.OrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;Service
public class OrderServiceImpl implements OrderService {Autowiredprivate UserClient userClient;Overridepublic User getOrderById(Long id) {User user userClient.getUser(id);return user;}
}
3.5. 总结
使用Feign的步骤
引入依赖添加EnableFeignClients注解编写FeignClient接口使用FeignClient中定义的方法代替RestTemplate
4. 自定义配置
Feign可以支持很多的自定义配置如下表所示 类型 作用 说明 feign.Logger.Level 修改日志级别 包含四种不同的级别NONE、BASIC、HEADERS、FULL feign.codec.Decoder 响应结果的解析器 http远程调用的结果做解析例如解析json字符串为java对象 feign.codec.Encoder 请求参数编码 将请求参数编码便于通过http请求发送 feign. Contract 支持的注解格式 默认是SpringMVC的注解原生注解GitHub - OpenFeign/feign: Feign makes writing java http clients easier feign. Retryer 失败重试机制 请求失败的重试机制默认是没有不过会使用Ribbon的重试 一般情况下默认值就能满足我们使用如果要自定义时只需要创建自定义的Bean覆盖默认Bean即可。
下面以日志为例来演示如何自定义配置。
4.1 配置文件方式
1. 针对单个服务
feign: client:config: user-service: # 针对某个微服务的配置logger-level: FULL # 日志级别
2. 针对所有服务
feign: client:config: default: # 这里用default就是全局配置如果是写服务名称则是针对某个微服务的配置logger-level: FULL # 日志级别 日志的级别分为四种 NONE不记录任何日志信息这是默认值。BASIC仅记录请求的方法URL以及响应状态码和执行时间HEADERS在BASIC的基础上额外记录了请求和响应的头信息FULL记录所有请求和响应的明细包括头信息、请求体、元数据。 4.2. Java代码方式
也可以基于Java代码来修改日志级别先声明一个类然后声明一个Logger.Level的对象
import feign.Logger;
import org.springframework.context.annotation.Bean;public class DefaultFeignConfiguration {Beanpublic Logger.Level feignLogLevel() {return Logger.Level.BASIC; // 日志级别为BASIC}
}
如果要全局生效将其放到启动类的EnableFeignClients这个注解中例如
import com.zjp.orderservice.config.DefaultFeignConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;EnableFeignClients(defaultConfiguration DefaultFeignConfiguration.class)
SpringBootApplication
public class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}
}
如果是局部生效则把它放到对应的FeignClient这个注解中例如
import com.zjp.orderservice.entity.User;
import com.zjp.orderservice.config.DefaultFeignConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;FeignClient(name user-service,configuration DefaultFeignConfiguration.class)
public interface UserClient {GetMapping(/user/{id})User getUser(PathVariable(id) Long id);
} 注意 需要将feign包下的日志级别设置为DEBUGfeign的日志才能在控制台输出例如 logging:level:com.zjp.orderservice.client: DEBUG 5. Feign的原理 6. Feign使用优化
6.1 http连接池
Feign底层发起http请求依赖于其它的框架。其底层客户端实现包括
URLConnection默认实现不支持连接池Apache HttpClient 支持连接池OKHttp支持连接池
因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。
这里我们用Apache的HttpClient来演示。
1. 引入依赖
在order-service的pom文件中引入Apache的HttpClient依赖
dependencygroupIdio.github.openfeign/groupIdartifactIdfeign-httpclient/artifactId
/dependency
2. 配置连接池
在order-service的application.yml中添加配置
feign:httpclient:enabled: true # 开启feign对HttpClient的支持max-connections: 200 # 最大的连接数max-connections-per-route: 50 # 每个路径的最大连接数
3. 校验是否配置成功
在FeignClientFactoryBean中的loadBalance方法中打断点 Debug方式启动order-service服务可以看到这里的client底层就是Apache HttpClient 6.2 feign超时 Feign的底层用的是Ribbon或者LoadBalancer我们可以手动设置超时时间。 可以针对单个服务
feign:client:config:user-service:connect-timeout: 5000 # 请求连接的超时时间read-timeout: 5000 # 请求响应的超时时间 可以针对所有服务
feign:client:config:default:connect-timeout: 5000 # 请求连接的超时时间read-timeout: 5000 # 请求响应的超时时间 6.3 总结
Feign的优化
1. 日志级别尽量用basic
2. 使用HttpClient或OKHttp代替URLConnection 1引入feign-httpClient依赖 2配置文件开启httpClient功能设置连接池参数
3. 修改 OpenFeign 的超时时间让 OpenFeign 能够正确的处理业务。
7. 最佳实践
在项目开发过程中可以发现Feign的客户端与服务提供者的controller代码非常相似
feign客户端
FeignClient(name user-service)
public interface UserClient {GetMapping(/user/{id})User getUser(PathVariable(id) Long id);
}
服务提供者
GetMapping(/user/{id})
public User getUser(PathVariable(id) Long id) {return userService.getUserById(id);
}
有没有一种办法简化这种重复的代码编写呢
7.1 继承方式不推荐 定义一个API接口利用定义方法并基于SpringMVC注解做声明。 Feign客户端和Controller都集成改接口 优点
代码简单实现了代码的共享
缺点
服务提供方、服务消费方紧耦合 参数列表中的注解映射并不会继承因此Controller中必须再次声明方法、参数列表、注解
7.2 抽取方式
将Feign的Client抽取为独立模块并且把接口有关的实体类、默认的Feign配置都放到这个模块中提供给所有消费者使用。
例如将UserClient、User、Feign的默认配置都抽取到一个feign-api包中所有微服务引用该依赖包即可直接使用。 7.2.1. 抽取
1. 创建新moudle 2. 在fegin-service中然后引入feign的starter依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId
/dependency
3. 在order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到fegin-service项目中。 4. 删除order-service中编写的UserClient、User、DefaultFeignConfiguration。
7.2.2 在order-service中使用feign-service
1. 在order-service的pom文件中中引入feign-service的依赖
dependencygroupIdcom.zjp/groupIdartifactIdfegin-service/artifactIdversion0.0.1-SNAPSHOT/version
/dependency 2. 确保order-service服务能扫描到fegin-service服务下的包
方式一指定Feign应该扫描的包
在order-service的启动类加EnableFeignClients(basePackages com.zjp.feginservice.client)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;EnableFeignClients(basePackages com.zjp.feginservice.client)
SpringBootApplication
public class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}
}
方式二指定需要加载的Client接口
在order-service的启动类加EnableFeignClients(clients {UserClient.class})
import com.zjp.feginservice.client.UserClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;EnableFeignClients(clients {UserClient.class})
SpringBootApplication
public class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}
}注意 order-service项目启动时启动类会扫描其所在的包及其子包如果不EnableFeignClients注解在order-service项目启动过程中无法扫描fegin-service项目下的包会出现以下错误 ***************************
APPLICATION FAILED TO START
***************************Description:Parameter 1 of constructor in com.zjp.orderservice.service.impl.OrderServiceImpl required a bean of type com.zjp.feginservice.client.UserClient that could not be found.Action:Consider defining a bean of type com.zjp.feginservice.client.UserClient in your configuration.8. Feign的拦截器 在order-service服务中创建自定义的RequestInterceptor。
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;Slf4j
Component
public class FeignInterceptor implements RequestInterceptor {Overridepublic void apply(RequestTemplate requestTemplate) {ServletRequestAttributes servletRequestAttributes (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request servletRequestAttributes.getRequest();log.info(request: {}, requestTemplate.url());log.info(Request URL: {}, request.getRequestURL());log.info(Request Method: {}, request.getMethod());log.info(Request Headers: {}, request.getHeaderNames());// 设置请求头requestTemplate.header(Authorization, Bearer token);// 将上游请求头全部设置到下游请求中EnumerationString headerNames request.getHeaderNames();while (headerNames.hasMoreElements()) {String name headerNames.nextElement();String value request.getHeader(name);requestTemplate.header(name, value);}}
}
重启测试 注意 Feign 默认情况下不会自动传递所有请求头到被调用的服务端。这是因为 Feign 有一个默认的配置它只会传递一部分特定的请求头。如果你需要传递特定的请求头需要手动配置。 1. 默认传递的请求头 1Feign 默认会传递一些基本的请求头如 Accept-Encoding、Connection 和 User-Agent 等。 2其他请求头需要手动配置才能传递。 2. 手动配置请求头 1在feign接口的注解上添加headers参数例如 import com.zjp.common.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;FeignClient(name user-service)
public interface UserClient {GetMapping(value /user/{id}, headers {Authorization: Hello World!})User getUser(PathVariable(id) Long id);
} 2通过参数传递例如 import com.zjp.common.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;FeignClient(name user-service)
public interface UserClient {GetMapping(value /user/{id})User getUser(RequestHeader(Authorization) String token, PathVariable(id) Long id);
}调用时需要手动传请求头 import com.zjp.common.entity.User;
import com.zjp.feginservice.client.UserClient;
import com.zjp.orderservice.entity.Order;
import com.zjp.orderservice.mapper.OrderMapper;
import com.zjp.orderservice.service.OrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;Slf4j
Service
RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {private final OrderMapper orderMapper;private final UserClient userClient;Overridepublic Order getOrderById(Long id) {Order order orderMapper.selectOrderById(id);User user userClient.getUser(token,order.getUserId());order.setUser(user);return order;}
}3通过拦截器传递请求头 9. Feign的异常捕获
9.1 自定义 ErrorDecoder
在fegin-service模块中自定义ErrorDecoder例如
import feign.Response;
import feign.codec.ErrorDecoder;
import lombok.extern.slf4j.Slf4j;Slf4j
public class CustomErrorDecoder implements ErrorDecoder {private final ErrorDecoder defaultErrorDecoder new Default();Overridepublic Exception decode(String s, Response response) {if (response.status() 200) {log.error(请求失败);return new RuntimeException(请求失败);} else {return defaultErrorDecoder.decode(s, response);}}
}
将 CustomErrorDecoder配置到feign接口
import com.zjp.common.entity.User;
import com.zjp.feginservice.client.decoder.CustomErrorDecoder;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;FeignClient(name user-service, configuration CustomErrorDecoder.class)
public interface UserClient {GetMapping(value /user/{id})User getUser(PathVariable(id) Long id);
} 注意 ErrorDecoder与fallback的执行顺序先走ErrorDecoder拦截器再走熔断的fallback。FeignClient加上decode404 true这一个参数Feign对于2XX和404 都不会走Fallback了。 10. 常见报错
10.1 feign接口注入失败
包扫描路径问题没有添加注册中心的依赖无法从注册中心拉取ip地址和端口如果是单元测试注意junit的版本如果是4.X的版本需要在测试类上添加Runwith没有做到依赖最小化加入一些无关依赖 启动类的包名层级只有一层如只在com包下。
10.2 feign接口调用失败 检查feign接口的url地址需要跟controller的完整的url保持一致 检查feign接口的参数参数上的RequestParam注解不能省略 检查接口的返回值是否漏掉了泛型信息如果没加分型默认元素类型是LinkedHashMap EnableFeignClients 注解没有指定需要扫描的包/类 更新代码却未重启服务 其他情况需要开启feign日志逐步排查。