在微服务盛行的当下,会有很多 JAVA 开发者去选择 RPC 框架来做服务之间的调用,如 Dubbo、GRPC 等等,当然也许会有开发者使用 Spring Cloud 来构建微服务。
在微服务的初级阶段,开发者需要在自己的工程代码里边处理一系列由于微服务化带来的问题,如服务发现、负载均衡、熔断、重试、限流等等,其直接结果就是导致在业务代码以外增加了许多额外的工作,增加了一堆非业务的代码。
如 JEE 一样,随着微服务的持续发展,出现了一些类库和框架来针对这些问题的处理做封装,如我们所了解的 Spring Cloud 。只需要几行配置,几个注解,就能帮助开发者完成很多工作。
所以下一代微服务应运而生,Service Mesh 正汹涌而来。
A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.
服务网格是一个基础设施层,用于处理服务间通信。云原生应用有着复杂的服务拓扑,服务网格负责在这些拓扑中实现请求的可靠传递。 在实践中,服务网格通常实现为一组轻量级网络代理,它们与应用程序部署在一起,而对应用程序透明。
Service Mesh 在以一种网络代理的形式,对服务的流量做控制,实现了服务限流、熔断等。
关于 Service Mesh 的更多,可以参考文末的 PPT 。
各种框架的出现,大大提升了开发效率。在框架用的很爽的同时,其实自己也有思考这样到底是好是坏。一方面,框架带来的生产力的提升是毋庸置疑的,可以让开发者更加专注于业务。而另一方面,框架表面看起来实在是太简单了,导致许多开发者只会写业务,而很难了解其工作原理。很多时候都会想,自己除了 CRUD 还会些什么?
完成一件事的路有长有短,一大部分人都会选择短的路。就比如开发这件事来说,很多人会选择用现有的框架。谁会为了构建一个微服务去开发一个框架呢?
条件不允许,时间也不允许,当然,你老板也不会允许,最重要的是,,,实力不允许,哈哈哈 😝。
这样就导致了业务在写了一段时间后,到达一个技术瓶颈。所以今后希望自己在陷入无穷无尽的业务的同时,能够多阅读一些源码叭。
Service Mesh 为我们解决了一堆业务以外乱七八糟的问题,现在开发者只需要专注业务的开发。通常 Web 服务以 HTTP 形式对外暴露服务,通过使用 Feign client ,可以简化我们通过 HTTP 对服务的调用。
现在我们要做的就是类似于使用 Dubbo 一样,抽象出一个公共的基础服务包,定义统一的响应格式,对服务认证等进一步封装,后面调用这些服务只需要引入这个 Jar 包,做一些简单的配置就可以实现各微服务之间的调用。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
nested exception is java.lang.NoClassDefFoundError: feign/Request$HttpMethod
错误,下面是可以使用的版本,比如:<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
HttpClient
替换 Feign 原始的 HTTP Client,通过设置连接池、超时时间等对服务之间的调用调优。所以在这个包中,使用了 HttpClient ,除此之外还可以使用 OkHttpClient 。feign.httpclient.enabled=true
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
配置项
feign.httpclient.enabled=false
feign.okhttp.enabled: true
依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
配置类
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignClientOkHttpConfiguration {
@Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()
// 连接超时
.connectTimeout(20, TimeUnit.SECONDS)
// 响应超时
.readTimeout(20, TimeUnit.SECONDS)
// 写超时
.writeTimeout(20, TimeUnit.SECONDS)
// 是否自动重连
.retryOnConnectionFailure(true)
// 连接池
.connectionPool(new ConnectionPool())
.build();
}
}
除此之外,还可以对最大连接数,最大路由等参数进行合理配置。
@GetMapping
、@PostMapping
等注解,注意每个参数必须指定@RequestParam(value="xxx")
,当参数不是必须时,必须指定@RequestParam(value="xxx", require=false)
。@Host
和 @Signature
RequestContextHolder
来保存本次请求的额外参数,会遇到获得参数时为 null 的问题,这是由于 Feign 在使用Hystrix时,采取不同的隔离策略,传播 ThreadLocal 对象的问题。我们知道 RequestContextHolder
是使用ThreadLocal
来保存参数的,如下:THREAD
时,是没办法拿到 ThreadLocal
中的值的。package com.aysaml.common.feign.interceptor;
import com.aysaml.common.feign.constant.Constants;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.apache.http.protocol.HTTP;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
/**
* Host interceptor , to add host into header.
*
* @author wangning
* @date 2019-10-29
*/
public class HostRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
if (null != attributes) {
String host = (String) attributes.getAttribute(Constants.FEIGN_HOST, 0);
if (null != host) {
requestTemplate.header(HTTP.TARGET_HOST, host);
}
}
}
}
当开启feign.hystrix.enabled=true
时,RequestContextHolder.getRequestAttributes();
获得的是 null 。而Hystrix的默认隔离策略是 THREAD
,所以在不同的线程中,获得 ThreadLocal 的值是 null。
有两种解决方案:
❀更改隔离策略:
hystrix.command.default.execution.isolation.strategy: SEMAPHORE
❀自定义并发策略:
参考Spring Cloud Sleuth以及Spring Security是如何传递 ThreadLocal
对象的,我们可以知道只需编写一个类,让其继承 HystrixConcurrencyStrategy
,并重写 wrapCallable
方法即可。
关于这块的详细内容:参见这篇博客 。
使用 Hystrix 进行服务的降级,只需要在 @FeignClient
中指定fallback
相应的类即可。 详细的代码就不再这里做展示了,可以参见 github:https://github.com/wangning1018/feign ,如果有任何问题欢迎留言讨论。
服务网格 PPT service mesh next generationmicro service