欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringCloud遠(yuǎn)程服務(wù)調(diào)用三種方式及原理

 更新時(shí)間:2022年12月19日 14:37:42   作者:nimo10050  
本文給大家介紹SpringCloud遠(yuǎn)程服務(wù)調(diào)用實(shí)戰(zhàn)筆記,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

一個(gè)簡(jiǎn)單的微服務(wù)架構(gòu)圖

本文設(shè)計(jì)的 Spring Cloud 版本以及用到的 Spring Cloud 組件

  • Spring Cloud Hoxton.SR5
  • eureka
  • feign
  • ribbon

后面的內(nèi)容都將圍繞上面的圖來(lái)分析.

調(diào)用遠(yuǎn)程服務(wù)的三種方式

在 Spring Cloud 服務(wù)架構(gòu)中, 一個(gè)服務(wù)可能部署多個(gè)實(shí)例, 通常情況下, 這個(gè)時(shí)候請(qǐng)求一個(gè)服務(wù)接口, 是需要通過(guò) 服務(wù)名 去調(diào)用的, 比如: http://user-service/getUser.

然后在 外力 的幫助下, 通過(guò)服務(wù)名拿到多個(gè)實(shí)例的地址列表, 再借助負(fù)載均衡算法, 從地址列表中選擇一個(gè)具體的地址, 發(fā)送 HTTP 請(qǐng)求.

具體的做法分為如下三種:

1、基于 RestTemplate 和 @LoadBalanced 注解

RestTemplate 是 spring-web 包提供的, 用來(lái)調(diào)用 HTTP 接口的工具類, 它提供了 GET、POST 等常用的請(qǐng)求方法.使用方式如下:

添加到 spring 容器

@Bean
public RestTemplate restTemplate() {
  return new RestTemplate();
}

使用前注入依賴

@Autowired
private RestTemplate restTemplate;

常用 API

// 發(fā)送 GET 請(qǐng)求
restTemplate.getForObject(...)
// 發(fā)送 POST 請(qǐng)求
restTemplate.postForObject(...)
// 自定義
restTemplate.execute(...)

按照上面那種簡(jiǎn)單的寫法, 我們只能調(diào)用有明確 IP 和 端口 的接口, 要想實(shí)現(xiàn)我們的需求, 至少要做兩件事情:

  • 根據(jù)服務(wù)名拿到服務(wù)實(shí)例的信息
  • 負(fù)載均衡算法

RestTemplate 提供了攔截器的功能 ClientHttpRequestInterceptor, 開發(fā)者可以 手動(dòng)編碼 實(shí)現(xiàn)上面兩個(gè)功能. Spring Cloud 已經(jīng)幫我們實(shí)現(xiàn)了這個(gè)功能.使用方式如下:

在原有基礎(chǔ)上加上 @LoadBalanced 注解

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
  return new RestTemplate();
}

調(diào)用接口時(shí),傳入服務(wù)名稱

User user = restTemplate.getForObject("http://user-service/getUser", User.class);

一個(gè)注解就幫我們完成了負(fù)載均衡.

2、基于DiscoveryClient

org.springframework.cloud.client.discovery.DiscoveryClient 可以幫我們實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)的功能, 只要我們拿到服務(wù)對(duì)應(yīng)的實(shí)例信息, 后面 負(fù)載均衡 可以手動(dòng)編碼實(shí)現(xiàn).

注入依賴

@Autowired
private DiscoveryClient discoveryClient;

獲取注冊(cè)中心服務(wù)實(shí)例列表

List<ServiceInstance> instances = discoveryClient.getInstances("user-service");

選取一個(gè)實(shí)例的地址信息, 發(fā)送請(qǐng)求

3、基于 Feign 的聲明式調(diào)用

在啟動(dòng)類上加對(duì)應(yīng)的注解.

@EnableFeignClients

聲明接口

@FeignClient("user-service")
public interface UserFeignClient {
    @GetMapping("/getUser")
    User getUser();
}

原理分析

關(guān)于源碼分析部分, 本文并不會(huì)逐行分析, 只會(huì)把 關(guān)鍵方法 注釋說(shuō)明(如果讀者自行 debug, 是不會(huì)迷路的.), 中間很多無(wú)聊的方法跳轉(zhuǎn)的過(guò)程都省略了.

RestTemplate 與 @LoadBalanced 注解的帶來(lái)的 “化學(xué)反應(yīng)”

先看一下大致的實(shí)現(xiàn)思路.

1、以 @LoadBalanced 為入口開啟源碼之旅

源碼注釋的大概意思是, 在 RestTemplate 上加上這個(gè)注解, 就能使用 LoadBalancerClient 接口 做一些事情, 通過(guò)查看這個(gè)接口的注釋, 它能提供的能力跟負(fù)載均衡相關(guān).

所以,到這里我們已經(jīng)清楚的了解到 @LoadBalanced 注解能為我們提供 負(fù)載均衡 的能力, 下面就需要弄清楚底層是如何實(shí)現(xiàn)負(fù)載均衡的.

Annotation to mark a RestTemplate or WebClient bean to be configured to use a LoadBalancerClient

通過(guò)查看源代碼, 我們?cè)谌缦聝蓚€(gè)地方看到了 @LoadBalanced 的使用, 通過(guò)調(diào)試發(fā)現(xiàn), 斷點(diǎn)根本沒(méi)有走到第二個(gè)地方.

public class LoadBalancerAutoConfiguration {
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();
}
public class LoadBalancerWebClientBuilderBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if (bean instanceof WebClient.Builder) {
			if (context.findAnnotationOnBean(beanName, LoadBalanced.class) == null) {
				return bean;
			}
			((WebClient.Builder) bean).filter(exchangeFilterFunction);
		}
		return bean;
	}
}

所以我們還是要把目光聚焦到下面的源代碼:

public class LoadBalancerAutoConfiguration {
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();
}

這里我通過(guò)描述這塊代碼的邏輯, 來(lái)引出一個(gè)有趣的 Spring 相關(guān)的知識(shí)點(diǎn)(關(guān)于這個(gè)知識(shí)點(diǎn)原理, 可以先直接跳到文末 Spring @Qualifier 注解的妙用):

首先, 我們應(yīng)該知道, 通過(guò)如下方式, 我們可以把 Spring 容器中的所有 RestTemplate 類型的 Bean 對(duì)象添加到下面的集合中.

@Autowired
private List<RestTemplate> restTemplates = Collections.emptyList();

而我們?cè)谏厦娴幕A(chǔ)上再加上 @LoadBalanced 注解, 那么這個(gè)集合收集的元素就加了一層限制條件, 集合中的 Bean 不僅要是 RestTemplate 類型, 而且 Bean 在聲明時(shí), 必須加上 @LoadBalanced 注解, 比如下面的聲明方式:

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
  return new RestTemplate();
}

然后我們接著看 Spring Cloud 如何對(duì) RestTemplate 進(jìn)行加工的

public class LoadBalancerAutoConfiguration {
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();
	@Autowired(required = false)
	private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
  // 第一步: 遍歷 restTemplates 集合
	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
			for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
        // RestTemplateCustomizer#customize
				for (RestTemplateCustomizer customizer : customizers) {
					customizer.customize(restTemplate);
				}
			}
		});
	}
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {
    // 第二步: 進(jìn)行自定義操作, 也就是把 LoadBalancerInterceptor 這個(gè)我們文章開頭提到的攔截器設(shè)置進(jìn)去.
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}
	}

到此為止, 程序啟動(dòng)前的一些關(guān)鍵步驟已經(jīng)搞清楚了, 下面繼續(xù)分析調(diào)用流程.

2、請(qǐng)求調(diào)用流程

源碼入口:

User user = restTemplate.getForObject("http://user-service/getUser", User.class);

順著 getForObject 進(jìn)到關(guān)鍵方法

public class RestTemplate  {
  // doExecute
  protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
			@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
    	// 創(chuàng)建請(qǐng)求對(duì)象
      // 這里最終其實(shí)通過(guò) InterceptingClientHttpRequestFactory#createRequest 方法
      // 創(chuàng)建了 InterceptingClientHttpRequest 
			ClientHttpRequest request = createRequest(url, method);
			response = request.execute();
	}
}

緊接著 看 InterceptingClientHttpRequestexecute 方法

class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {
  // 第一步: 執(zhí)行完父類的 execute 方法后, 會(huì)來(lái)到這里.
	@Override
	protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
		InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
		return requestExecution.execute(this, bufferedOutput);
	}
	private class InterceptingRequestExecution implements ClientHttpRequestExecution {
		private final Iterator<ClientHttpRequestInterceptor> iterator;
		public InterceptingRequestExecution() {
			this.iterator = interceptors.iterator();
		}
    // 第二步: 先 執(zhí)行前面設(shè)置的攔截器 LoadBalancerInterceptor 通過(guò) 服務(wù)名, + 負(fù)載均衡 , 拿到其中一個(gè)實(shí)例的請(qǐng)求地址.
    // 然后根據(jù)真實(shí)的地址, 發(fā)送 http 請(qǐng)求.
		@Override
		public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
        // 先 執(zhí)行前面設(shè)置的攔截器 LoadBalancerInterceptor 通過(guò) 服務(wù)名, + 負(fù)載均衡 , 拿到其中一個(gè)實(shí)例的請(qǐng)求地址.
				nextInterceptor.intercept(request, body, this);  
        // 然后根據(jù)真實(shí)的地址, 發(fā)送 http 請(qǐng)求.  AbstractClientHttpRequest#execute      
				return delegate.execute();
			}
		}
	}
}

LoadBalancerInterceptor 的負(fù)載均衡處理

到這里, 我們就可以回答開頭提到的問(wèn)題: @LoadBalanced 是如何給 RestTemplate 提供負(fù)載均衡能力的, 眾所周知 Ribbon 的能力就 負(fù)載均衡.

源碼再往后看就是 Ribbon 的領(lǐng)域了, 我們不再繼續(xù)深究. 后面可以單獨(dú)寫一篇文章對(duì) Ribbon 的原理和源碼進(jìn)行分析.

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
  // 看到這里, 我們應(yīng)該回想到一開始說(shuō)的, @LoadBalanced 注解的相關(guān)注釋說(shuō)明.
  // 加上 @LoadBalanced 注解, 我們就能給 RestTemplate 賦予負(fù)載均衡的能力.
	private LoadBalancerClient loadBalancer;
	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
    // 因?yàn)槲覀兗闪?Ribbon、 所以這里 loadBalancer 就是 RibbonLoadBalancerClient
		return this.loadBalancer.execute(serviceName,
				this.requestFactory.createRequest(request, body, execution));
	}
}

Spring @Qualifier 注解的妙用

/**
 * This annotation may be used on a field or parameter as a qualifier for
 * candidate beans when autowiring. It may also be used to annotate other
 * custom annotations that can then in turn be used as qualifiers.
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
	String value() default "";
}

不管是根據(jù)上面的注釋, 還是我們的使用經(jīng)驗(yàn)來(lái)講, 我們都應(yīng)該知道 @Qualifier這個(gè)注解:它起到的是限定, “精確匹配”Bean 的作用,比如: 當(dāng)同一類型的 Bean 有多個(gè)不同實(shí)例時(shí),可通過(guò)此注解來(lái)做 篩選或匹配。

然后再來(lái)看下這個(gè)注解的一段注釋:

It may also be used to annotate other custom annotations that can then in turn be used as qualifiers.

簡(jiǎn)單翻一下就是: @Qualifier 可以注解其他 自定義的注解, 然后這些 自定義注解 就可以反過(guò)來(lái)為我們注入 Bean 時(shí), 起到限定的作用(上面已經(jīng)講過(guò)它限定了什么).

于是我們?cè)倩剡^(guò)頭看下 @LoadBalanced 注解源碼:

@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

從上可以看出, 這個(gè)自定義注解上是包含 @Qualifier, 所以 @LoadBalanced 注解是可以在我們注入 bean 時(shí), 起到限定作用的.

關(guān)于 @Qualifier詳細(xì)的源碼和原理分析 可以圍繞 QualifierAnnotationAutowireCandidateResolver 這個(gè)類做檢索, 這里不再詳細(xì)闡述.

到此這篇關(guān)于SpringCloud遠(yuǎn)程服務(wù)調(diào)用三種方式及原理的文章就介紹到這了,更多相關(guān)SpringCloud服務(wù)調(diào)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 帶你快速搞定java多線程(4)

    帶你快速搞定java多線程(4)

    這篇文章主要介紹了java多線程編程實(shí)例,分享了幾則多線程的實(shí)例代碼,具有一定參考價(jià)值,加深多線程編程的理解還是很有幫助的,需要的朋友可以參考下
    2021-07-07
  • Java后臺(tái)線程操作示例【守護(hù)線程】

    Java后臺(tái)線程操作示例【守護(hù)線程】

    這篇文章主要介紹了Java后臺(tái)線程操作,結(jié)合實(shí)例形式分析了java守護(hù)線程相關(guān)原理、操作技巧與使用注意事項(xiàng),需要的朋友可以參考下
    2019-09-09
  • Java創(chuàng)建型設(shè)計(jì)模式之抽象工廠模式(Abstract?Factory)

    Java創(chuàng)建型設(shè)計(jì)模式之抽象工廠模式(Abstract?Factory)

    當(dāng)系統(tǒng)所提供的工廠所需生產(chǎn)的具體產(chǎn)品并不是一個(gè)簡(jiǎn)單的對(duì)象,而是多個(gè)位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中屬于不同類型的具體產(chǎn)品時(shí)需要使用抽象工廠模式,抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態(tài)
    2022-09-09
  • spring profile 多環(huán)境配置管理詳解

    spring profile 多環(huán)境配置管理詳解

    這篇文章主要介紹了 spring profile 多環(huán)境配置管理詳解的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • Java與Unix時(shí)間戳的相互轉(zhuǎn)換詳解

    Java與Unix時(shí)間戳的相互轉(zhuǎn)換詳解

    這篇文章主要為大家詳細(xì)介紹了Java與Unix時(shí)間戳的相互轉(zhuǎn)換,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • java中的快捷鍵小結(jié)

    java中的快捷鍵小結(jié)

    以下是myeclipse中的所有快捷鍵列表
    2013-03-03
  • 使用Jackson反序列化遇到的問(wèn)題及解決

    使用Jackson反序列化遇到的問(wèn)題及解決

    這篇文章主要介紹了使用Jackson反序列化遇到的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • JAVA中static方法的用法實(shí)例詳解

    JAVA中static方法的用法實(shí)例詳解

    這篇文章主要介紹了JAVA中static方法的用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Java中static方法的功能、使用技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2015-12-12
  • ChatGPT介紹及Java?API調(diào)用

    ChatGPT介紹及Java?API調(diào)用

    本文主要介紹了ChatGPT介紹及Java?API調(diào)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • 四個(gè)Java必須知道的負(fù)載均衡算法分享

    四個(gè)Java必須知道的負(fù)載均衡算法分享

    我們?cè)谠O(shè)計(jì)系統(tǒng)的時(shí)候,為了系統(tǒng)的高擴(kuò)展性,會(huì)創(chuàng)建無(wú)狀態(tài)的系統(tǒng)。但是,要使系統(tǒng)具有更好的可擴(kuò)展性,除了無(wú)狀態(tài)設(shè)計(jì)之外,還要考慮采用什么負(fù)載均衡算法,本文就帶領(lǐng)大家認(rèn)識(shí)以下常見(jiàn)的4種負(fù)載均衡算法
    2023-01-01

最新評(píng)論