目录
简介
使用spring cloud的ribbon组件可以实现对下游微服务的负载均衡调度,但是官方ribbon是没有header转发功能的,这里我们在ribbon的restTemplate基础上,自定义实现header的转发功能。
1.自定义RestTemplate
package com.cloud.base.config.ribbon;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.client.*;
import java.io.IOException;
import java.net.URI;
import java.util.*;
import static feign.Util.checkNotNull;
/**
* @author langpf 2019/2/19
*/
public class MyRestTemplate extends RestTemplate {
private final Map<String, List<String>> headers = new LinkedHashMap<>();
@Nullable
@Override
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
// lpf: 添加headers
HttpHeaders myHeaders = request.getHeaders();
addAllHeaders(myHeaders);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name()
+ " request for \"" + resource + "\": " + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}
public void header(String name, String... values) {
checkNotNull(name, "header name");
if (values == null || (values.length == 1 && values[0] == null)) {
headers.remove(name);
} else {
List<String> headers = new ArrayList<>(Arrays.asList(values));
this.headers.put(name, headers);
}
}
private void addAllHeaders(HttpHeaders myHeaders) {
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String key = entry.getKey();
List<String> values = entry.getValue();
myHeaders.addAll(key, values);
}
}
}
2.将MyRestTemplate注册为Bean
下面代码一般放在SpringBoot工程的启动类中。
/**
* 让restTemplate具备Ribbon负载均衡的能力。
* 由于使用feign, 弃用该方式
*/
@Bean(name="myRestTemplate")
@LoadBalanced
MyRestTemplate restTemplate() {
return new MyRestTemplate();
}
3.构建spring拦截器
package com.cloud.base.interceptor.ribbon;
import com.alibaba.fastjson.JSONObject;
import com.cloud.base.config.ribbon.MyRestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// @Component("ribbonInterceptor1")
public class RibbonInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(RibbonInterceptor.class);
private MyRestTemplate myRestTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String lpfId = request.getHeader("lpf");
if (myRestTemplate == null) { //解决service为null无法注入问题
BeanFactory factory =
WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
myRestTemplate = (MyRestTemplate) factory.getBean("myRestTemplate");
}
myRestTemplate.header("lpf", lpfId);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub;
}
}
4.构建拦截器配置类
package com.cloud.base.config;
import com.cloud.base.interceptor.ribbon.RibbonInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 拦截器配置类
*/
@Configuration
public class WebMvcSessionConfigurer implements WebMvcConfigurer {
// @Autowired
// RibbonInterceptor ribbonInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 多个拦截器组成一个拦截器链
// addPathPatterns 用于添加拦截规则
// excludePathPatterns 用于排除拦截
// old: new RedisSessionInterceptor()
// registry.addInterceptor(ribbonInterceptor).addPathPatterns("/**");
registry.addInterceptor(new RibbonInterceptor()).addPathPatterns("/**");
}
}
5.controller调用
通过controller调用ribbon时会自动转发header
package com.cloud.controller;
import com.cloud.base.config.ribbon.MyRestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import static com.cloud.base.constant.Constants.SPRING_DATA_SRV;
import static com.cloud.base.constant.Constants.SPRING_SIDECAR_SRV_URL;
/**
* ribbon调用方式, 参考博客: https://www.jianshu.com/p/470a30f493cf
* @author langpf 2019/2/19
*/
@RestController
public class RibbonController {
@Autowired
MyRestTemplate myRestTemplate;
/**
* ribbon -- get请求
*/
@RequestMapping("ribbonDataSrvHi")
public String ribbonDataSrvHi() {
// myRestTemplate.headForHeaders()
return myRestTemplate.getForEntity(SPRING_DATA_SRV + "hi", String.class).getBody();
}
/**
* ribbon -- get请求
*/
@RequestMapping("ribbonGetHi")
public String ribbonGetHi() {
return myRestTemplate.getForEntity(SPRING_DATA_SRV + "hi", String.class).getBody();
}
/**
* ribbon -- get请求
* http://localhost:10232/python-user
*/
@RequestMapping("python-user")
public String pythonUser() {
// return myRestTemplate.getForEntity("http://spring-sidecar-python-server/getUser", String.class).getBody();
return myRestTemplate.getForEntity(SPRING_SIDECAR_SRV_URL + "getUser", String.class).getBody();
}
/**
* ribbon -- post请求
*/
@RequestMapping(value = "model/serving", method = RequestMethod.POST)
public String modelServing(@RequestBody HttpEntity entity) {
return myRestTemplate.postForEntity(SPRING_SIDECAR_SRV_URL + "model/serving", entity, String.class).getBody();
}
}