使用openfeign微服务模块间远程调用
在前面我们在摸鱼模块上传图片,需要知道是哪个用户上传的。
用户携带了token,而解析token的业务在ucenter里,所以我们需要调用ucenter模块去解析用户。
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
如果是基于springCloud的话,不需要加版本号也行,在springCloud里已经有指定的版本号了。
定义client端接
@RestController
public class UserRemoteImpl {
@Autowired
private IUserService userService;
@GetMapping("/remote/uc/user")
public UserVo getUser() {
return userService.getUser();
}
}
这个是在用户中心模块的
里面的逻辑则是
定义调用端的接口
@Component
@FeignClient(name = "my-u-center", configuration = FeignConfiguration.class)
public interface UserRemote {
@GetMapping("/remote/uc/user")
UserVo getUser();
}
这里的的name,则是模块的应用名称,对应着配置文件里的名称
FeignConfiguration
则是一个转发配置
@Slf4j
public class FeignConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
if (attributes == null) {
return;
}
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
template.header(name, values);
}
}
}
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public Decoder feignDecoder() {
return new ResultStatusDecoder(new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))));
}
static class ResultStatusDecoder implements Decoder {
public static final String CONTENT_KEY = "content";
final Decoder delegate;
public ResultStatusDecoder(Decoder delegate) {
Objects.requireNonNull(delegate, "Decoder must not be null. ");
this.delegate = delegate;
}
@Override
public Object decode(Response response, Type type) throws IOException {
// 判断是否返回参数是否是异常
InputStream inputStream = response.body().asInputStream();
String resultStr = IOUtils.toString(inputStream, "UTF-8");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
if (attributes != null) {
HttpServletResponse targetResponse = attributes.getResponse();
//把cookies给到返回的response
Map<String, Collection<String>> headers = response.headers();
Set<Map.Entry<String, Collection<String>>> entries = headers.entrySet();
for (Map.Entry<String, Collection<String>> next : entries) {
if (next.getKey().equals("set-cookie")) {
Collection<String> value = next.getValue();
Iterator<String> iterator = value.iterator();
if (iterator.hasNext() && targetResponse != null) {
String tokenKey = iterator.next();
targetResponse.setHeader("Set-Cookie", tokenKey);
break;
}
}
}
}
return delegate.decode(response.toBuilder().body(resultStr, StandardCharsets.UTF_8).build(), type);
}
}
}
把header转到请求的模块上,这样子ucenter模块才可以知道来自其他模块请求里的Header,拿到token。
使用的地方调用
直接注入UserRemote
,这是一个组件,可以注入。
然后调用既可。
熔断
在远程调用过程中,有可能对方服务下线了,或者由于网络问题等不确定因素导致调用失败。
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
自己实现接口
@Component
public class UserRemoteFallBack implements UserRemote {
@Override
public UserVo getUser() {
return null;
}
}
也就是期望,如时远程出错了,超时之类的,就调用我们自己的实现。
也只是期望,如果要做到调用这里,还需要注册一下。
@Component
@FeignClient(name = "my-u-center", fallback = UserRemoteFallBack.class,
configuration = FeignConfiguration.class)
public interface UserRemote {
@GetMapping("/remote/uc/user")
UserVo getUser();
}
fallback 的值为自己的实现类
这样子,如果远程调用失败了,就会调用自己的实现了。
开启hytrix
feign:
hystrix:
enabled: true
但是开启了hytrix,就会拿不到request,解决方法也比较繁琐,所以同学们可以按视频里的try住异常,返回服务器繁忙既可。
okay啦,详情请看视频吧,以后果我还是发笔记到网站上吧,这样子可以增加网站的访问量,哈哈。