public interface ServiceInstanceChooser {
/**
* Chooses a ServiceInstance from the LoadBalancer for the specified service.
* @param serviceId The service ID to look up the LoadBalancer.
* @return A ServiceInstance that matches the serviceId.
*/
ServiceInstance choose(String serviceId);
}
public interface LoadBalancerClient extends ServiceInstanceChooser {
/**
* Executes request using a ServiceInstance from the LoadBalancer for the specified
* service.
* @param serviceId The service ID to look up the LoadBalancer.
* @param request Allows implementations to execute pre and post actions, such as
* incrementing metrics.
* @param <T> type of the response
* @throws IOException in case of IO issues.
* @return The result of the LoadBalancerRequest callback on the selected
* ServiceInstance.
*/
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
/**
* Executes request using a ServiceInstance from the LoadBalancer for the specified
* service.
* @param serviceId The service ID to look up the LoadBalancer.
* @param serviceInstance The service to execute the request to.
* @param request Allows implementations to execute pre and post actions, such as
* incrementing metrics.
* @param <T> type of the response
* @throws IOException in case of IO issues.
* @return The result of the LoadBalancerRequest callback on the selected
* ServiceInstance.
*/
<T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException;
/**
* Creates a proper URI with a real host and port for systems to utilize. Some systems
* use a URI with the logical service name as the host, such as
* http://myservice/path/to/service. This will replace the service name with the
* host:port from the ServiceInstance.
* @param instance service instance to reconstruct the URI
* @param original A URI with the host as a logical service name.
* @return A reconstructed URI.
*/
URI reconstructURI(ServiceInstance instance, URI original);
}
public interface LoadBalancerRequestTransformer {
/**
* Order for the load balancer request tranformer.
*/
int DEFAULT_ORDER = 0;
HttpRequest transformRequest(HttpRequest request, ServiceInstance instance);
}
因为现在的微服务理论好多都是来自 Martin Fowler 的那篇微服务的文章,下面是其中一句话:
In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API.
里面写到:“使用轻量级的机制通信,通常是HTTP资源API”,即restful形式的。所以 Spring Cloud 里面通常使用RestTemplate调用其它微服务。
public interface ClientHttpRequest extends HttpRequest, HttpOutputMessage {
/**
* Execute this request, resulting in a {@link ClientHttpResponse} that can be read.
* @return the response result of the execution
* @throws IOException in case of I/O errors
*/
ClientHttpResponse execute() throws IOException;
}
public interface ClientHttpResponse extends HttpInputMessage, Closeable {
/**
* Return the HTTP status code of the response.
* @return the HTTP status as an HttpStatus enum value
* @throws IOException in case of I/O errors
* @throws IllegalArgumentException in case of an unknown HTTP status code
* @see HttpStatus#valueOf(int)
*/
HttpStatus getStatusCode() throws IOException;
/**
* Return the HTTP status code (potentially non-standard and not
* resolvable through the {@link HttpStatus} enum) as an integer.
* @return the HTTP status as an integer
* @throws IOException in case of I/O errors
* @since 3.1.1
* @see #getStatusCode()
* @see HttpStatus#resolve(int)
*/
int getRawStatusCode() throws IOException;
/**
* Return the HTTP status text of the response.
* @return the HTTP status text
* @throws IOException in case of I/O errors
*/
String getStatusText() throws IOException;
/**
* Close this response, freeing any resources created.
*/
@Override
void close();
}
public interface ClientHttpRequestFactory {
/**
* Create a new {@link ClientHttpRequest} for the specified URI and HTTP method.
* <p>The returned request can be written to, and then executed by calling
* {@link ClientHttpRequest#execute()}.
* @param uri the URI to create a request for
* @param httpMethod the HTTP method to execute
* @return the created request
* @throws IOException in case of I/O errors
*/
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}
public interface ClientHttpRequestInterceptor {
/**
* Intercept the given request, and return a response. The given
* {@link ClientHttpRequestExecution} allows the interceptor to pass on the
* request and response to the next entity in the chain.
* <p>A typical implementation of this method would follow the following pattern:
* <ol>
* <li>Examine the {@linkplain HttpRequest request} and body</li>
* <li>Optionally {@linkplain org.springframework.http.client.support.HttpRequestWrapper
* wrap} the request to filter HTTP attributes.</li>
* <li>Optionally modify the body of the request.</li>
* <li><strong>Either</strong>
* <ul>
* <li>execute the request using
* {@link ClientHttpRequestExecution#execute(org.springframework.http.HttpRequest, byte[])},</li>
* <strong>or</strong>
* <li>do not execute the request to block the execution altogether.</li>
* </ul>
* <li>Optionally wrap the response to filter HTTP attributes.</li>
* </ol>
* @param request the request, containing method, URI, and headers
* @param body the body of the request
* @param execution the request execution
* @return the response
* @throws IOException in case of I/O errors
*/
ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException;
}
public interface ClientHttpRequestExecution {
/**
* Execute the request with the given request attributes and body,
* and return the response.
* @param request the request, containing method, URI, and headers
* @param body the body of the request to execute
* @return the response
* @throws IOException in case of I/O errors
*/
ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException;
}
说到这里顺便给大家推荐一个Java架构方面的交流学习群:536976563,点击立即加入里面会免费分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源和前辈的面试经验和面试题,相信对于已经工作和遇到技术瓶颈的码友,在这个群里会有你需要的内容。
public class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {
private final List<ClientHttpRequestInterceptor> interceptors;
/**
* Create a new instance of the {@code InterceptingClientHttpRequestFactory} with the given parameters.
* @param requestFactory the request factory to wrap
* @param interceptors the interceptors that are to be applied (can be {@code null})
*/
public InterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory,
@Nullable List<ClientHttpRequestInterceptor> interceptors) {
super(requestFactory);
this.interceptors = (interceptors != null ? interceptors : Collections.emptyList());
}
@Override
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
}
}
public class RibbonLoadBalancerClient implements LoadBalancerClient {
private SpringClientFactory clientFactory;
public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
this.clientFactory = clientFactory;
}
@Override
public URI reconstructURI(ServiceInstance instance, URI original) {
Assert.notNull(instance, "instance can not be null");
String serviceId = instance.getServiceId();
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
URI uri;
Server server;
if (instance instanceof RibbonServer) {
RibbonServer ribbonServer = (RibbonServer) instance;
server = ribbonServer.getServer();
uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
}
else {
server = new Server(instance.getScheme(), instance.getHost(),
instance.getPort());
IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
uri = updateToSecureConnectionIfNeeded(original, clientConfig,
serverIntrospector, server);
}
return context.reconstructURIWithServer(server, uri);
}
@Override
public ServiceInstance choose(String serviceId) {
return choose(serviceId, null);
}
/**
* New: Select a server using a 'key'.
* @param serviceId of the service to choose an instance for
* @param hint to specify the service instance
* @return the selected {@link ServiceInstance}
*/
public ServiceInstance choose(String serviceId, Object hint) {
Server server = getServer(getLoadBalancer(serviceId), hint);
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
}
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
throws IOException {
return execute(serviceId, request, null);
}
/**
* New: Execute a request by selecting server using a 'key'. The hint will have to be
* the last parameter to not mess with the `execute(serviceId, ServiceInstance,
* request)` method. This somewhat breaks the fluent coding style when using a lambda
* to define the LoadBalancerRequest.
* @param <T> returned request execution result type
* @param serviceId id of the service to execute the request to
* @param request to be executed
* @param hint used to choose appropriate {@link Server} instance
* @return request execution result
* @throws IOException executing the request may result in an {@link IOException}
*/
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException {
Server server = null;
if (serviceInstance instanceof RibbonServer) {
server = ((RibbonServer) serviceInstance).getServer();
}
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
try {
T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
return returnVal;
}
// catch IOException and rethrow so RestTemplate behaves correctly
catch (IOException ex) {
statsRecorder.recordStats(ex);
throw ex;
}
catch (Exception ex) {
statsRecorder.recordStats(ex);
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
}