自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring Cloud Gateway實(shí)現(xiàn)灰度發(fā)布實(shí)現(xiàn)原理

開(kāi)發(fā) 前端
灰度發(fā)布(又名金絲雀發(fā)布)是指在黑與白之間,能夠平滑過(guò)渡的一種發(fā)布方式。在其上可以進(jìn)行A/B testing,即讓一部分用戶(hù)繼續(xù)用產(chǎn)品特性A,一部分用戶(hù)開(kāi)始用產(chǎn)品特性B,如果用戶(hù)對(duì)B沒(méi)有什么反對(duì)意見(jiàn),那么逐步擴(kuò)大范圍,把所有用戶(hù)都遷移到B上面來(lái)。

什么是灰度發(fā)布

灰度發(fā)布(又名金絲雀發(fā)布)是指在黑與白之間,能夠平滑過(guò)渡的一種發(fā)布方式。在其上可以進(jìn)行A/B testing,即讓一部分用戶(hù)繼續(xù)用產(chǎn)品特性A,一部分用戶(hù)開(kāi)始用產(chǎn)品特性B,如果用戶(hù)對(duì)B沒(méi)有什么反對(duì)意見(jiàn),那么逐步擴(kuò)大范圍,把所有用戶(hù)都遷移到B上面來(lái)?;叶劝l(fā)布可以保證整體系統(tǒng)的穩(wěn)定,在初始灰度的時(shí)候就可以發(fā)現(xiàn)、調(diào)整問(wèn)題,以保證其影響度。

灰度發(fā)布類(lèi)型

  • 金絲雀發(fā)布

將少量的請(qǐng)求引流到新版本上,因此部署新版本服務(wù)只需極小數(shù)的機(jī)器。驗(yàn)證新版本符合預(yù)期后,逐步調(diào)整流量權(quán)重比例,使得流量慢慢從老版本遷移至新版本,期間可以根據(jù)設(shè)置的流量比例,對(duì)新版本服務(wù)進(jìn)行擴(kuò)容,同時(shí)對(duì)老版本服務(wù)進(jìn)行縮容,使得底層資源得到最大化利用。

圖片


金絲雀發(fā)布的優(yōu)點(diǎn):

  1. 按比例將流量無(wú)差別地導(dǎo)向新版本,新版本故障影響范圍?。?/li>
  2. 發(fā)布期間逐步對(duì)新版本擴(kuò)容,同時(shí)對(duì)老版本縮容,資源利用率高。

金絲雀發(fā)布的缺點(diǎn):

  1. 流量無(wú)差別地導(dǎo)向新版本,可能會(huì)影響重要用戶(hù)的體驗(yàn);
  2. 發(fā)布周期長(zhǎng)。

  • A/B測(cè)試

A/B 測(cè)試基于用戶(hù)請(qǐng)求的元信息將流量路由到新版本,這是一種基于請(qǐng)求內(nèi)容匹配的灰度發(fā)布策略。只有匹配特定規(guī)則的請(qǐng)求才會(huì)被引流到新版本,常見(jiàn)的做法包括基于 Header 和 Cookie?;?Header 方式例子,例如 User-Agent 的值為 Android 的請(qǐng)求 (來(lái)自安卓系統(tǒng)的請(qǐng)求)可以訪(fǎng)問(wèn)新版本,其他系統(tǒng)仍然訪(fǎng)問(wèn)舊版本。基于 Cookie 方式的例子,Cookie 中通常包含具有業(yè)務(wù)語(yǔ)義的用戶(hù)信息,例如普通用戶(hù)可以訪(fǎng)問(wèn)新版本,VIP 用戶(hù)仍然訪(fǎng)問(wèn)舊版本。

圖片


  • 藍(lán)綠發(fā)布

藍(lán)綠發(fā)布需要對(duì)服務(wù)的新版本進(jìn)行冗余部署,一般新版本的機(jī)器規(guī)格和數(shù)量與舊版本保持一致,相當(dāng)于該服務(wù)有兩套完全相同的部署環(huán)境,只不過(guò)此時(shí)只有舊版本在對(duì)外提供服務(wù),新版本作為熱備。當(dāng)服務(wù)進(jìn)行版本升級(jí)時(shí),我們只需將流量全部切換到新版本即可,舊版本作為熱備。由于冗余部署的緣故,所以不必?fù)?dān)心新版本的資源不夠。如果新版本上線(xiàn)后出現(xiàn)嚴(yán)重的程序 BUG,那么我們只需將流量全部切回至舊版本,大大縮短故障恢復(fù)的時(shí)間。

圖片

Gateway實(shí)現(xiàn)灰度發(fā)布

本篇將文章將通過(guò)A/B測(cè)試方式實(shí)現(xiàn)灰度發(fā)布。接下來(lái)將展示在Spring Cloud Gateway中實(shí)現(xiàn)A/B測(cè)試核心組件。

  • 引入依賴(lài)?
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<version>3.1.4</version>
</dependency>
  • 自定義負(fù)載均衡器

自定義負(fù)載均衡器作用是根據(jù)請(qǐng)求的header中的v進(jìn)行服務(wù)實(shí)例的篩選。?

public class GrayRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {


private static final Log log = LogFactory.getLog(RoundRobinLoadBalancer.class);


final AtomicInteger position;


final String serviceId;


ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;


public GrayRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId) {
this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));
}


public GrayRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId, int seedPosition) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.position = new AtomicInteger(seedPosition);
}


@SuppressWarnings("rawtypes")
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances, request));
}


@SuppressWarnings("rawtypes")
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
List<ServiceInstance> serviceInstances, Request request) {
Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances, request);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}


@SuppressWarnings("rawtypes")
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + serviceId);
}
return new EmptyResponse();
}
List<ServiceInstance> result = instances.stream().filter(instance -> {
Map<String, String> metadata = instance.getMetadata();
Object orgId = metadata.get("v");
RequestDataContext context = (RequestDataContext) request.getContext() ;
RequestData requestData = context.getClientRequest() ;
String v = null ;
if (requestData instanceof GrayRequestData) {
GrayRequestData grayRequestData = (GrayRequestData) requestData ;
queryV = grayRequestData.getQueryParams().getFirst("v") ;
}
String value = requestData.getHeaders().getFirst("v") ;
return v != null && (v.equals(value) || v.equals(queryV)) ;
}).collect(Collectors.toList());
if (result.isEmpty()) {
result = instances;
}
int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;


ServiceInstance instance = result.get(pos % result.size());


return new DefaultResponse(instance);
}


}

以上負(fù)載均衡器將從header或者請(qǐng)求參數(shù)中獲取v參數(shù),然后根據(jù)v參數(shù)的值從服務(wù)實(shí)例列表中獲取metadata信息進(jìn)行比對(duì)。

全局過(guò)濾器

該過(guò)濾器的作用是通過(guò)上面的負(fù)載均衡器從其中選擇一個(gè)服務(wù)實(shí)例進(jìn)行服務(wù)的調(diào)用?

@SuppressWarnings({ "rawtypes", "unchecked" })
@Component
public class GrayReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {


private static final Log log = LogFactory.getLog(GrayReactiveLoadBalancerClientFilter.class);

/**
* Order of filter.
*/
public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;


private final LoadBalancerClientFactory clientFactory;


private final GatewayLoadBalancerProperties properties;




public GrayReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory,
GatewayLoadBalancerProperties properties) {
this.clientFactory = clientFactory;
this.properties = properties;
}


@Override
public int getOrder() {
return LOAD_BALANCER_CLIENT_FILTER_ORDER;
}


@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
if (url == null || (!"packlb".equals(url.getScheme()) && !"packlb".equals(schemePrefix))) {
return chain.filter(exchange);
}
// preserve the original url
addOriginalRequestUrl(exchange, url);


URI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
String serviceId = requestUri.getHost();
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
RequestDataContext.class, ResponseData.class, ServiceInstance.class);
DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(
new RequestDataContext(new GrayRequestData(exchange.getRequest()), getHint(serviceId)));
LoadBalancerProperties loadBalancerProperties = clientFactory.getProperties(serviceId);
return choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext(response -> {


if (!response.hasServer()) {
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
.onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, response)));
throw NotFoundException.create(properties.isUse404(), "Unable to find instance for " + url.getHost());
}


ServiceInstance retrievedInstance = response.getServer();


URI uri = exchange.getRequest().getURI();


// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
// if the loadbalancer doesn't provide one.
String overrideScheme = retrievedInstance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}


DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(retrievedInstance,
overrideScheme);


URI requestUrl = reconstructURI(serviceInstance, uri);


if (log.isTraceEnabled()) {
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
}
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
exchange.getAttributes().put(GATEWAY_LOADBALANCER_RESPONSE_ATTR, response);
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, response));
}).then(chain.filter(exchange))
.doOnError(throwable -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
CompletionContext.Status.FAILED, throwable, lbRequest,
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR)))))
.doOnSuccess(aVoid -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
CompletionContext.Status.SUCCESS, lbRequest,
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR), buildResponseData(exchange,
loadBalancerProperties.isUseRawStatusCodeInResponseData())))));
}


@SuppressWarnings("deprecation")
private ResponseData buildResponseData(ServerWebExchange exchange, boolean useRawStatusCodes) {
if (useRawStatusCodes) {
return new ResponseData(new GrayRequestData(exchange.getRequest()), exchange.getResponse());
}
return new ResponseData(exchange.getResponse(), new RequestData(exchange.getRequest()));
}


protected URI reconstructURI(ServiceInstance serviceInstance, URI original) {
return LoadBalancerUriTools.reconstructURI(serviceInstance, original);
}


private Mono<Response<ServiceInstance>> choose(Request<RequestDataContext> lbRequest, String serviceId,
Set<LoadBalancerLifecycle> supportedLifecycleProcessors) {
ReactorLoadBalancer<ServiceInstance> loadBalancer = this.clientFactory.getInstance(serviceId,
ReactorServiceInstanceLoadBalancer.class);
if (loadBalancer == null) {
throw new NotFoundException("No loadbalancer available for " + serviceId);
}
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
return loadBalancer.choose(lbRequest);
}


private String getHint(String serviceId) {
LoadBalancerProperties loadBalancerProperties = clientFactory.getProperties(serviceId);
Map<String, String> hints = loadBalancerProperties.getHint();
String defaultHint = hints.getOrDefault("default", "default");
String hintPropertyValue = hints.get(serviceId);
return hintPropertyValue != null ? hintPropertyValue : defaultHint;
}


}

配置

@Configuration
public class GrayDefaultConfiguration {


@Bean
public GrayRoundRobinLoadBalancer grayRandomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new GrayRoundRobinLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}

// 由于沒(méi)有使用服務(wù)注冊(cè)及發(fā)現(xiàn),這里通過(guò)編碼的方式定義服務(wù)實(shí)例
@Bean
public ServiceInstanceListSupplier sscServiceInstanceListSupplier() {
return new ServiceInstanceListSupplier() {
@Override
public Flux<List<ServiceInstance>> get() {
List<ServiceInstance> instances = new ArrayList<>() ;
Map<String, String> metadata1 = new HashMap<>() ;
metadata1.put("v", "1") ;
ServiceInstance s1 = new DefaultServiceInstance("s1", "ssc", "localhost", 8088 , false, metadata1) ;
instances.add(s1) ;

Map<String, String> metadata2 = new HashMap<>() ;
metadata2.put("v", "2") ;
ServiceInstance s2 = new DefaultServiceInstance("s2", "ssc", "localhost", 8099 , false, metadata2) ;
instances.add(s2) ;

return Flux.just(instances) ;
}
@Override
public String getServiceId() {
return "ssc" ;
}
};
}

}

負(fù)載均衡客戶(hù)端設(shè)置默認(rèn)的配置

@LoadBalancerClients(defaultConfiguration = GrayDefaultConfiguration.class)
public class SpringCloudGatewayApplication {
}

以上就是實(shí)現(xiàn)灰度發(fā)布的核心組件。

圖片

測(cè)試,設(shè)置不同的v返回不同服務(wù)的結(jié)果數(shù)據(jù)

完畢?。?!

責(zé)任編輯:武曉燕 來(lái)源: 實(shí)戰(zhàn)案例錦集
相關(guān)推薦

2024-01-29 08:00:00

架構(gòu)微服務(wù)開(kāi)發(fā)

2019-08-22 09:55:17

RedisAPI數(shù)據(jù)

2024-05-13 18:35:06

負(fù)載均衡主機(jī)端口

2024-12-16 13:34:35

2023-02-28 08:57:06

Spring上下線(xiàn)緩存

2025-03-04 08:53:10

2022-08-15 09:22:12

JWT認(rèn)證系統(tǒng)

2021-12-27 15:01:21

KubernetesLinux命令

2023-07-24 08:00:56

客戶(hù)端訪(fǎng)問(wèn)指定

2022-08-02 08:32:21

Spring項(xiàng)目網(wǎng)關(guān)

2022-01-07 07:29:08

Rbac權(quán)限模型

2023-09-15 08:18:49

cookie網(wǎng)關(guān)代理

2023-02-13 08:10:40

Gateway網(wǎng)關(guān)Spring

2023-07-13 09:01:39

Bean接口容器

2023-03-08 09:03:55

2023-01-26 01:41:27

核心全局過(guò)濾器

2022-02-15 14:22:46

灰度發(fā)布互聯(lián)網(wǎng)業(yè)務(wù)

2021-01-14 07:54:19

Spring Clou應(yīng)用路由

2021-06-04 08:48:46

Spring ClouMaven Centr版本

2025-02-10 00:23:11

Spring微服務(wù)架構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)