
定义一个简单的restful接口 @RestController public class TestController { @RequestMapping(value = "testPost", method = RequestMethod.POST) public ResponseBean testPost(@RequestBody RequestBean requestBean) { ResponseBean responseBean = new ResponseBean()responseBean.setRetCode("0000")responseBean.setRetMsg("succ")return responseBean} } 使用RestTemplate访问该服务 //
请求地址 String url = ""//入参 RequestBean requestBean = new RequestBean()requestBean.setTest1("1")requestBean.setTest2("2")requestBean.setTest3("3")RestTemplate restTemplate = new RestTemplate()ResponseBean responseBean = restTemplate.postForObject(url, requestBean, ResponseBean.class)从这个例子可以看出,使用restTemplate访问restful接口非常的简单粗暴无脑。(url, requestMap, ResponseBean.class)这三个参数分别代表 请求地址、请求参数、HTTP响应转换被转换成的对象类型。 RestTemplate
方法的名称遵循命名约定,第一部分指出正在
调用什么HTTP方法,第二部分指示返回的内容。本例中调用了restTemplate.postForObject方法,post指调用了HTTP的post方法,Object指将HTTP响应转换为您选择的对象类型。还有其他很多类似的方法,有兴趣的同学可以参考官方api。 三.手动指定转换器(HttpMessageConverter) 我们知道,调用reseful接口传递的数据内容是json格式的字符串,返回的响应也是json格式的字符串。然而restTemplate.postForObject方法的请求参数RequestBean和返回参数ResponseBean却都是java类。是RestTemplate通过HttpMessageConverter自动帮我们做了转换的 *** 作。 默认情况下RestTemplate自动帮我们注册了一组HttpMessageConverter用来处理一些不同的contentType的请求。 如StringHttpMessageConverter来处理text/plainMappingJackson2HttpMessageConverter来处理application/jsonMappingJackson2XmlHttpMessageConverter来处理application/xml。 你可以在org.springframework.http.converter包下找到所有spring帮我们实现好的转换器。 如果现有的转换器不能满足你的需求,你还可以实现org.springframework.http.converter.HttpMessageConverter接口自己写一个。详情参考官方api。 选好了HttpMessageConverter后怎么把它注册到我们的RestTemplate中呢。 RestTemplate restTemplate = new RestTemplate()//获取RestTemplate默认配置好的所有转换器 List<HttpMessageConverter<?>>messageConverters = restTemplate.getMessageConverters()//默认的MappingJackson2HttpMessageConverter在第7个 先把它移除掉 messageConverters.remove(6)//添加上GSON的转换器 messageConverters.add(6, new GsonHttpMessageConverter())这个简单的例子展示了如何使用GsonHttpMessageConverter替换掉默认用来处理application/json的MappingJackson2HttpMessageConverter。 四.设置底层连接方式 要创建一个RestTemplate的实例,您可以像上述例子中简单地调用默认的无参数构造函数。这将使用java.NET包中的标准Java类作为底层实现来创建HTTP请求。 但很多时候我们需要像传统的HttpClient那样设置HTTP请求的一些属性。RestTemplate使用了一种很偷懒的方式实现了这个需求,那就是直接使用一个HttpClient作为底层实现...... //生成一个设置了连接超时时间、请求超时时间、异常最大重试次数的httpClient RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(10000).setConnectTimeout(10000).setSocketTimeout(30000).build()HttpClientBuilder builder = HttpClientBuilder.create().setDefaultRequestConfig(config).setRetryHandler(new DefaultHttpRequestRetryHandler(5, false))HttpClient httpClient = builder.build()//使用httpClient创建一个ClientHttpRequestFactory的实现 ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient)//ClientHttpRequestFactory作为参数构造一个使用作为底层的RestTemplate RestTemplate restTemplate = new RestTemplate(requestFactory)五.设置拦截器(ClientHttpRequestInterceptor) 有时候我们需要对请求做一些通用的拦截设置,这就可以使用拦截器进行处理。拦截器需要我们实现org.springframework.http.client.ClientHttpRequestInterceptor接口自己写。 举个简单的例子,写一个在header中根据请求内容和地址添加令牌的拦截器。 public class TokenInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { //请求地址 String checkTokenUrl = request.getURI().getPath()//token有效时间 int ttTime = (int) (System.currentTimeMillis() / 1000 + 1800)//请求方法名 POST、GET等 String methodName = request.getMethod().name()//请求内容 String requestBody = new String(body)//生成令牌 此处调用一个自己写的方法,有兴趣的朋友可以自行google如何使用ak/sk生成token,此方法跟本教程无关,就不贴出来了 String token = TokenHelper.generateToken(checkTokenUrl, ttTime, methodName, requestBody)//将令牌放入请求header中 request.getHeaders().add("X-Auth-Token",token)return execution.execute(request, body)} } 创建RestTemplate实例的时候可以这样向其中添加拦截器 RestTemplate restTemplate = new RestTemplate()//向restTemplate中添加自定义的拦截器 restTemplate.getInterceptors().add(new TokenInterceptor())在Spring cloud 框架中,我们都知道,起一个MVC的服务非常简单。 通过controller 注释就可以写一个API的接口了。
但是如果我们的service 想要发起一个新的调用如何做呢?在Spring cloud 下有不少这样的需求。
主要有这么几种方式:
简单介绍下背景, RestTemplate 是一个轻量级的 HTTP的Client ,相对于 Feign 而言。 RestTemplate 有丰富的接口,支持用户对HTTP的各种请求方法和参数的封装 。
OpenFeign 是有 Feign 开发而来。是 Spring Cloud 的子项目之一,在微服务调用中, OpenFeign 非常的得心应手,而且 OpenFeign 还支持各种 HTTP 调用封装,同时还可以整合服务注册( Eureka )、负载均衡、服务治理(熔断、限流、重试)的组件,灵活性提升很多而且实现起来比较简单。
RestTemplate 支持HTTP的 HEAD, GET, POST, PUT, DELETE, OPTIONS、PATCH等大部分方法,还有两个TRACE、CONECT 用的比较少,不支持(查询官方接口确认)。
我们就来总结一下常用的方法的实现。
支持的方法有:
client 的配置,发起一个get请求,把获取的数据 用String 格式化。
server 端的API的是这样式的
Client 如下配置
client的封装如下
服务器端例子,构造一个返回的请求类型
客户端解析例子
DELETE有三个方法的重载。
这三个重载方法,传入的变量不一样,支持 MAP 类型 参数与URL ,支持 Object对象多值传入 , 支持 URL 。都无返回值。
用法就像controller 的API 一样,可以对HTTPserver发起请求
首先,需要在启动类里加上注释
可以通过这么几个HTTP client 组件来封装。例如:
用aop 拦截所有controller 的请求 生成全局traceId
@Pointcut("execution(* *.*.controller..*.*(..))")
public void cutOffPoint() {
}
@Before("cutOffPoint()")
public void doBefore(JoinPoint joinPoint)throws Throwable {
MDC.put("traceId", "生成全局traceid")
}
在logback.xml中配置
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %X{TRACEID}- %msg%n
</appender>
到此可以满足大部分的需求
需要注意的部分就是如果用到线程池需要特殊处理
可以参考
https://blog.csdn.net/yangcheng33/article/details/80796129
下面说下我的实现
项目中主要使用ThreadPoolTaskExecutor因此对他进行改造
复制public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
//内容为ThreadPoolTaskExecutor 类中的内容 我这里从新写了一下几个方法
initializeExecutor 方法中 super.execute(new MDCRunnable(decorated))
submitListenable方法中
ListenableFutureTask future =new ListenableFutureTask<>(new MDCCallable<>(task))
//住所有执行的方法都要改为MDCRunnable MDCCallable
}
MDCRunnable类 为
public class MDCRunnable implements Runnable {
private final Runnable runnable
private transient final Map_cm = MDC.getCopyOfContextMap()
public MDCRunnable(Runnable runnable) {
this.runnable = runnable
}
@Override
public void run() {
if (_cm !=null) {
MDC.setContextMap(_cm)
}
try {
runnable.run()
}finally {
MDC.clear()
}}}
public class MDCCallable implements Callable {
private final Callablecallable
private transient final Map_cm = MDC.getCopyOfContextMap()
public MDCCallable(Callable callable) {
this.callable = callable
}
@Override
public V call()throws Exception {
if (_cm !=null) {
MDC.setContextMap(_cm)
}
try {
return callable.call()
}finally {
MDC.clear()
}}}
至此我们项目中就完美解决了traceId在本项目中传递
如果是在微服务中传递则 我们微服务之间的调用使用的restTemplate
所有增加一个拦截器
@Configuration
public class RestTemplateConfiguration {
@Bean
@LoadBalanced
public RestTemplateloadbalancedRestTemplate() {
SimpleClientHttpRequestFactory requestFactory =new SimpleClientHttpRequestFactory()
// 超时时间,单位毫秒
requestFactory.setConnectTimeout(3 *1000)
requestFactory.setReadTimeout(3 *60 *1000)
RestTemplate restTemplate =new RestTemplate(requestFactory)
restTemplate.getInterceptors().add(new TraceIdInterceptor())
return restTemplate
}
public class TraceIdInterceptorimplements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponseintercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)throws IOException {
HttpHeaders headers = request.getHeaders()
traceId= MDC.get("traceId")
if(StringUtils.isNotBlank(traceId)){
headers.add("traceId",traceId)
}
return execution.execute(request,body)
}}}
然后在下游服务器写一个拦截器就可在request head中可以获取traceidl
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()
String traceId = request.getHeader("traceId")
至此就完成了所有的traceid 本项目 线程 项目之间 都可以用了
评论列表(0条)