SpringCloud-服务间调用(REST)

简介

微服务中的系统都在独立的运行中,通过各个服务系统之间的写协作完成业务功能。服务系统间使用多种跨进程的方式通信协作,而Restful风格的网络请求是最常见的。

Spring Cloud提供的方式:

  1. RestTemplate
    • 方便调用别的第三方的http服务,适用于异构环境
  2. Feign
    • 一种负载均衡的HTTP客户端, 使用Feign调用API就像调用本地方法一样,从避免了调用目标微服务时。

RestTemplate

	@Bean
	// 开启负载均衡
	@LoadBalanced
	RestTemplate restTemplate() {
		return new RestTemplate();
	}

使用资源地址调用服务

String url ="http://provider/getHi";
String respStr = restTemplate.getForObject(url, String.class);

get 请求处理

getForEntity
ResponseEntity是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。

ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://provider/getHi", String.class);
responseEntity.getBody();

传参调用

  1. 使用占位符
    	String url ="http://provider/getObjParam?name={1}";
       ResponseEntity<Person> entity = restTemplate.getForEntity(url, Person.class,"hehehe...");
    
  2. 使用map
		String url ="http://provider/getObjParam?name={name}";
		   
		Map<String, String> map = Collections.singletonMap("name", " memeda");
		ResponseEntity<Person> entity = restTemplate.getForEntity(url, Person.class,map);

post请求处理

		String url ="http://provider/postParam";
		   
		Map<String, String> map = Collections.singletonMap("name", " memeda");
		 ResponseEntity<Person> entity = restTemplate.postForEntity(url, map, Person.class);

postForLocation

返回值为URI对象,因为Post请求最常用来添加数据,如果需要将刚刚添加成功的数据的URL返回来,此时就可以使用这个方法

		String url ="http://provider/postParam";
		   
		Map<String, String> map = Collections.singletonMap("name", " memeda");
		URI location = restTemplate.postForLocation(url, map, Person.class);
		
		System.out.println(location);

exchange

可以自定义http请求的头信息,同时保护get和post方法

拦截器

需要实现ClientHttpRequestInterceptor接口

public class LoggingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

	@Override
	public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
			throws IOException {

		System.out.println("拦截啦!!!");
		System.out.println(request.getURI());

		ClientHttpResponse response = execution.execute(request, body);

		System.out.println(response.getHeaders());
		return response;
	}

添加到resttemplate中

	@Bean
	@LoadBalanced
	RestTemplate restTemplate() {
		RestTemplate restTemplate = new RestTemplate();
		restTemplate.getInterceptors().add(new LoggingClientHttpRequestInterceptor());
		return restTemplate;
	}

feign

这样已经加入spring容器中,使用自动装配就可以使用了。

接口加注解,方法加注解
@FeignClient(name = "service-sms",
fallback = 降级的类.class,
fallbackFactory = FileServiceFallbackFactory.class,
configuration = OAuth2FeignConfig.class
)
public interface SmsClient {
	/**
	 * 按照短信模板发送验证码
	 * @param smsSendRequest
	 * @return
	 */
	@RequestMapping(value="/send/alisms-template", method = RequestMethod.POST)
	public ResponseResult sendSms(@RequestBody SmsSendRequest smsSendRequest);
}

开启feign的功能

@SpringBootApplication
@EnableFeignInterceptor
@EnableFeignClients
public class UserConsumerApplication {

	public static void main(String[] args) {
		SpringApplication.run(UserConsumerApplication.class, args);
	}

}

fallbackFactory就和标准的熔断降级差不多,可以把异常信息全部带过来,但是需要我们对异常做转换,常规的exception还是无法获取body部分的。

@Component
public class FileServiceFallbackFactory implements FallbackFactory<SmsClient> {
 @Override
  public SmsClient create(final Throwable throwable) {
    FeignException ex = (FeignException) throwable;
    JSONObject jsonObject = JSONObject.parseObject(ex.contentUTF8());
    return new TestApi() {
      @Override
      public String sendSms(SmsSendRequest smsSendRequest) {
        return jsonObject.getString("message");
      }
    };
  }
}

上下文中获取token,将token放在请求的header头部中,因为调用远程服务的api的时候,也要经过common模块的资源服务器的token拦截校验

@Configuration
public class OAuth2FeignConfig implements RequestInterceptor {

    private static final String client_credentials = "client_credentials";

    @Override
    public void apply(RequestTemplate requestTemplate) {
        //从上线文中获取token
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String header = requestAttributes.getRequest().getHeader(HttpHeaders.AUTHORIZATION);
        requestTemplate.header(HttpHeaders.AUTHORIZATION, header);
    }
}

客户端要熔断降级必须在yaml文件里进行配置,否则无法进入fallbackFactory

feign:
  hystrix:
    enabled: true

全局配置异常

@Configuration
public class FeignErrorDecoder implements ErrorDecoder {

  @Override
  public Exception decode(final String methodKey, final Response response) {
    try {
      String message = Util.toString(response.body().asReader());
      try {
        JSONObject jsonObject = JSONObject.parseObject(message);
        // 包装成自己自定义的异常,这里建议根据自己的代码改
        return new MyException(jsonObject.getString("message"), jsonObject.getInteger("code"));
      } catch (JSONException e) {
        e.printStackTrace();
      }

    } catch (IOException ignored) {
    }
    return decode(methodKey, response);
  }
}

区别

请求方式不一样

  • RestTemplate需要每个请求都拼接url+参数+类文件,灵活性高但是消息封装臃肿。

  • feign可以伪装成类似SpringMVC的controller一样,将rest的请求进行隐藏,不用再自己拼接url和参数,可以便捷优雅地调用HTTP API。

底层实现方式不一样

  • RestTemplate在拼接url的时候,可以直接指定ip地址+端口号,不需要经过服务注册中心就可以直接请求接口;也可以指定服务名,请求先到服务注册中心(如nacos)获取对应服务的ip地址+端口号,然后经过HTTP转发请求到对应的服务接口(注意:这时候的restTemplate需要添加@LoadBalanced注解,进行负载均衡)。

  • Feign的底层实现是动态代理,如果对某个接口进行了@FeignClient注解的声明,Feign就会针对这个接口创建一个动态代理的对象,在调用这个接口的时候,其实就是调用这个接口的代理对象,代理对象根据@FeignClient注解中name的值在服务注册中心找到对应的服务,然后再根据@RequestMapping等其他注解的映射路径构造出请求的地址,针对这个地址,再从本地实现HTTP的远程调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值