微服务间接口调用常用openfeign或restemplate,两者底层都是通过ribbon+http发起远程调用并无大的区别,主要区别是openfeign较restTemplate功能比较齐全
restTemplate微服务调用
1 请求增强:增加超时设置,请求拦截增加请求头,响应增加报警
@Bean
@LoadBalanced
RestTemplate getRestTemplate() {
//配置超时必须同时设置连接池,否则调用方用不主动超时,只能一直等待服务方
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(connectionRequestTimeout)
.setConnectionRequestTimeout(connectTimeout)
.setSocketTimeout(readTimeout).build();
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setMaxConnTotal(maxConnTotal)
.setMaxConnPerRoute(maxConnPerRoute)
.setRetryHandler(new DefaultHttpRequestRetryHandler(retryCount,true))//一般配置为微服务部署实例数
.setDefaultRequestConfig(requestConfig)
.build();
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
restTemplate.setInterceptors(Lists.newArrayList(new CommonRestTemplateInterceptor()));//设置用户信息请求头
return restTemplate;
}
@Slf4j
public class CommonRestTemplateInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {//微信等回调该对象为null
HttpServletRequest req = attributes.getRequest();
HttpHeaders headers = request.getHeaders();
headers.add("zuul_user_id", req.getHeader("zuul_user_id")); //拦截restTemplate,请求前设置请求头zuul_user_id等用户信息
headers.add("x_forwarded_ip", req.getHeader("x_forwarded_ip"));
}
ClientHttpResponse response = execution.execute(request, body);
if (!response.getStatusCode().equals(HttpStatus.OK)) {
log.error("restTemplate服务调用异常",response.getStatusText());
MonitorUtils.sendDingdingMsg("restTemplate服务调用异常,"+response.getStatusText());
}
return response;
}
}
2.接口管理: 远程调用接口URL抽取到常量类或枚举统一管理,并放置到common通用依赖,方便重复使用,全局修改等
ResultEntity entity = restTemplate.getForObject("http://user/version/latest?apkType=1, ResultEntity.class);
public class XXApplicationServiceUrl {
public String getLatestVersionUrl="http://user/version/latest";
public String getLatestVersionUrl2="http://user/version/latest2";
public String getLatestVersionUrl3="http://user/version/latest3";
}
openfeign微服务调用
1.N多的feature
①请求超时,注意restTemplate不识别该配置项,是在配置类中设置
ribbon:
ReadTimeout: 2000 #无效,通过restTemplate.readTimeout自定义配置项控制
ConnectTimeout: 1000 #无效,通过restTemplate.connectTimeout自定义配置项控制
②请求拦截增加请求头,注意@FeignClient注解指定自定义拦截器
//实现feign的请求拦截器并在feign调用配置,@FeignClient(name = "capability-register", fallback = ApiServiceClientFallBack.class ,configuration = XXXXConfiguration.class)
@Configuration
public class XXXXConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
requestTemplate.header("Token", request.getHeader("Token"));
}
}
③请求与响应压缩,数据较大,压缩率高数据建议开启
#feign 请求与响应的压缩
feign.compression.request.enabled=true
feign.compression.response.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
④feign调用http日志,注意设置feignclient配置日志级别为debug
@Configuration
public class FeignLogConfig {
/**
* 日志level有4个级别
* 1.NONE,不记录任何日志
* 2.BASIC,仅记录请求方法、URL以及响应状态码和执行时间
* 3.HEADRES,除了BASIC以外的还会记录请求和响应的头信息
* 4.FULL,所有
* @return
*/
@Bean
Logger.Level feignLogger(){
return Logger.Level.FULL;
}
}
logging.level:
root: info
com.construn.vehicle.common.core.XXApplicationFeginService: debug
⑤降级-退而求其次,编写接口实现类,@FeignClient指定该降级实现 ,需要feign.hystrix.enabled=true开启降级
@Component
@FeignClient(value = "user",fallback = XXApplicationFallbackFeginService.class)
public interface XXApplicationFeginService {
@GetMapping("/version/latest")
ResultEntity getLatestVersion(@RequestParam(defaultValue = "1") String apkType);
@GetMapping("/version/latest2")
ResultEntity getLatestVersion2(@RequestParam(defaultValue = "1") String apkType);
@GetMapping("/version/latest3")
ResultEntity getLatestVersion3(@RequestParam(defaultValue = "1") String apkType);
}
@Service
public class XXApplicationFallbackFeginService implements XXApplicationFeginService{
@Override
public ResultEntity getLatestVersion(@RequestParam(defaultValue = "1") String apkType){
return ResultEntity.fail("降级返回");
}
@Override
public ResultEntity getLatestVersion2(String apkType) {
return null;
}
@Override
public ResultEntity getLatestVersion3(String apkType) {
return null;
}
;
}
⑥熔断测试,feign.hystrix.enabled=true开启熔断,降级,熔断都会进入fallback实现,100请求达到50%超时10ms或服务不可达即开启熔断,5s一次探测请求,测试工具:ab
#熔断器配置
hystrix:
command:
default: #default指路由的所有下级服务都使用相同默认的熔断配置,可指定具体服务具体配置如payment,defulat=>payment
execution:
isolation:
strategy: SEMAPHORE #默认THREAD,网关高扇出适合信号量
thread:
timeoutInMilliseconds: 10 #实现HystrixComand即的run(..业务代码..)方法的超时时间
semaphore:
maxConcurrentRequests: 200 #允许信号量1000并发-压测
circuitBreaker:
errorThresholdPercentage: 50 #错误或超时50%时开启熔断器
sleepWindowInMilliseconds: 5000 #//熔断器中断请求5秒后会进入半打开状态,放部分流量去正常请求,成功则关闭熔断器
requestVolumeThreshold: 100 #至少有100个请求并且达到错误阈值才会判断是否打开熔断器
ab -c 311 -n 511 -H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NUeXBlIjoiMSIsImlzQWRtaW4iOiIxIiwidXNlcklkIjoiN2RhMDUxMWUwZWI4NDZiZTI3Nzc1NzUyNGRiMzEwOGYiLCJlbnRwSWQiOiI3NTQ1Y2RkZjBiNGE3ZTU5M2M4ZWZlYmFlYTU0ZWM3ZSIsImlhdCI6MTYwNzM5OTAxOH0.HKUQbky8aNhSeXpPgQgpAD5WlE0VyfGW67nUexxx" http://xxx/service-order/page?pageNum=1&pageSize=10&status=1
⑦修改feingClient 默认httpClient,修改为okHttp进行并发测试
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>11.0</version>
</dependency>
feign.httpclient.enabled=false
feign.okhttp.enabled=true
/**
* 配置okhttp与连接池
* ConnectionPool默认创建5个线程,保持5分钟长连接
*/
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class OkHttpConfig {
@Bean
public okhttp3.OkHttpClient okHttpClient(){
return new okhttp3.OkHttpClient.Builder()
//设置连接超时
.connectTimeout(10 , TimeUnit.SECONDS)
//设置读超时
.readTimeout(10 , TimeUnit.SECONDS)
//设置写超时
.writeTimeout(10 , TimeUnit.SECONDS)
//是否自动重连
.retryOnConnectionFailure(true)
.connectionPool(new ConnectionPool(10 , 5L, TimeUnit.MINUTES))
.build();
}
}
测试结论:okhttp有连接池,高并发时,okHttp多次测试均优于httpClient,符合预期
httpClient
________________________________________
50% 965
66% 1316
75% 1444
80% 1598
90% 1994
95% 2425
98% 2805
99% 3214
100% 4711 (longest request)
okHttp
___________________________________________
50% 1002
66% 1290
75% 1403
80% 1525
90% 1847
95% 2223
98% 2726
99% 3163
100% 3703 (longest request)
2.接口管理:同样放置common通用依赖,但注意开启时指定扫描路径 @EnableFeignClients(basePackages = "com.XX.XX.common.core")
@Component
@FeignClient("user")
public interface XXApplicationFeginService {
@GetMapping("/version/latest")
ResultEntity getLatestVersion(@RequestParam(defaultValue = "1") String apkType);
@GetMapping("/version/latest2")
ResultEntity getLatestVersion2(@RequestParam(defaultValue = "1") String apkType);
@GetMapping("/version/latest3")
ResultEntity getLatestVersion3(@RequestParam(defaultValue = "1") String apkType);
}