RestTemplate 里的一些问题
RestTemplate 是1个经典的java http 客户端
黎睇睇常见的http call 代码
@Autowired
private RestTemplate restTemplate;
private String user_service = "demo-cloud-user-service";
public Order queryOrderById(Long orderId) {
// 1.get order object
Order order = orderMapper.findById(orderId);
//2 use user service to get the user details
String url = "http://" + user_service.toUpperCase(Locale.ROOT) + "/user/" + order.getUserId();
System.out.println("url:" + url);
User user = restTemplate.getForObject(url, User.class); // User.class means return format
order.setUser(user);
return order;
}
可以见到, 即使我们使用了Eureka注册中心, 不用hardcode hostname到url中, 而是使用service name代替,但是还是存在如下的问题
1. 代码可读性不好
对于下面这一行, 没接触过RestTemplate 的开发人员不容易理解
User user = restTemplate.getForObject(url, User.class); // User.class means return format
2. 感觉在写前端,不够优雅
出现了url 代码的风格乱了, 总想封装它
3. 复杂的url 难以维护
上面的url例子只有1个参数, 已经是拼接到一定程度了
Spring Cloud Fegin的介绍
OpenFeign是1个声明式客户端
https://spring.io/projects/spring-cloud-openfeign
官方介绍:
Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign’s first goal was reducing the complexity of binding Denominator uniformly to HTTP APIs regardless of ReSTfulness.
Feign的基本客户端代码:
定义1个User service 的Client 接口
@FeignClient("demo-cloud-user-service")
public interface UserServiceClient {
@GetMapping("/user/{id}")
User findbyId(@PathVariable("id") long id);
}
其中包含了:
service name: demo-cloud-user-service
Request type: Get
Request url: /usr/{id}
Request parameter: Long id
Return format: User
使用Feign的客户端代码:
@Autowired
private UserServiceClient userServiceClient;
public Order queryOrderById(Long orderId) {
// 1.get order object
Order order = orderMapper.findById(orderId);
//2 use user service client to get the user details
User user = userServiceClient.findByid(order.getUserId());
order.setUser(user);
return order;
}
是不是很简洁优雅, 定义interface 的写法就像是写controller
使用的客户端的代码就像是简单调用另1个service 函数
OpenFeign 内部已经继承Ribbon, 默认就已经开启LoadBalancer
添加Pom.xml 依赖
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
可以知道openfeign 也是spring cloud的组件
开启feign 自动装配
在启动类上加上 注解@EnableFeignClients
@MapperScan("com.home.order.mapper")
@SpringBootApplication
@EnableFeignClients
@Import(SpringConfiguration.class)
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
编写Feign Client 接口
@FeignClient("demo-cloud-user-service")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") long id);
}
在Order Service 里使用 上面的都接口实现 http调用
@Autowired
private UserClient userClient;
//new method to use OpenFeign
public Order queryOrderById(Long orderId) {
// 1.get order object
Order order = orderMapper.findById(orderId);
//2 use user service to get the user details
User user = userClient.findById(order.getUserId());
order.setUser(user);
return order;
}
OpenFeign 的一些自定义配置
可以编写一些自定义配置来覆盖默认配置, 包括且不限于:
Item | desc | remark |
---|---|---|
feing.logger.level | update logs level | includs: NONE/BASIC/HEADERS/FULL |
feing.codec.Decoder | parse of return result | includs: e.g translate json -> java objs |
feing.codec.Encoder | Encoder of request parameters | Encode request parameters for transition |
fegn.Contract | format of annotation | For default, use Spring MVC’s annotation |
feign.Retryer | Failure retry mechanism | For Default: None, but we could use the built-in Ribbon |
一般我们需要配置的是日志级别
如何修改日志级别
https://www.baeldung.com/java-feign-logging
方法一,修改项目配置文件
feign.client.config.default.loggerLevel= basic # default means global setting, for all the services
default 代表全局配置
feign.client.config.demo-cloud-user-service.loggerLevel= full # for specified service
单独为某1个微服务服务配置日志级别
方法二:在Java里配置
首先创建1个配置类, 里面必须有个bean for 日志level
import feign.Logger.Level;
import org.springframework.context.annotation.Bean;
public class FeignClientConfiguration {
@Bean
public Level feignLogLevel(){
return Level.BASIC;
}
}
如果想全局生效
放入启动类的注解里
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
如果想在单独service里生效 放在对应的FeignClient的注解里
@FeignClient(value = "demo-cloud-user-service", configuration = FeignClientConfiguration.class)
亲测 方法一会覆盖方法二
注意, feign的日志要开启 debug level 才有考, 如果全局开debug level会令到可读性下降, 所以一般用下面配置只令到 项目的类开启debug level
如:
logging:
level:
com.home.api: DEBUG
Feign 的性能优化
3种实现模式
item | desc |
---|---|
URLConnection | 默认,jdk 自带, 不支持connection Pool |
Apache HttpClient | 默认, 支持connection Pool |
OKHttp | 默认, 支持connection Pool |
因此,我们通常会使用连接池替代默认的URLConnection
第一步, 引入 feign-httpclient 依赖
<!-- http client for Feign -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
注意 版本已被spring 管理, 不需要指定版本号
第二步, 在springboot配置文件配置连接池
feign:
httpclient:
enabled: true
max-connections: 200 # for all
max-connections-per-route: 50 # for each url
如何验证,我从日志中真的看不出来连接池有没有开启。。。
但系中debug中的确可以见到, 但是要进入到 feign-core 这个包
上面两个测试通常要做压力测试才能确定最佳值
如果真的追求性能, 建议日志基本也降为Null or basic