日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網營銷解決方案
SpringCloudGateway實現(xiàn)灰度發(fā)布實現(xiàn)原理

什么是灰度發(fā)布

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

成都創(chuàng)新互聯(lián)-專業(yè)網站定制、快速模板網站建設、高性價比赤壁網站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式赤壁網站制作公司更省心,省錢,快速模板網站建設找我們,業(yè)務覆蓋赤壁地區(qū)。費用合理售后完善,10多年實體公司更值得信賴。

灰度發(fā)布類型

  • 金絲雀發(fā)布

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

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

  1. 按比例將流量無差別地導向新版本,新版本故障影響范圍??;
  2. 發(fā)布期間逐步對新版本擴容,同時對老版本縮容,資源利用率高。

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

  1. 流量無差別地導向新版本,可能會影響重要用戶的體驗;
  2. 發(fā)布周期長。
  • A/B測試

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

  • 藍綠發(fā)布

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

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

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

  • 引入依賴?

org.springframework.cloud
spring-cloud-starter-gateway
3.1.4


org.springframework.cloud
spring-cloud-loadbalancer
3.1.4
  • 自定義負載均衡器

自定義負載均衡器作用是根據請求的header中的v進行服務實例的篩選。?

public class GrayRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {


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


final AtomicInteger position;


final String serviceId;


ObjectProvider serviceInstanceListSupplierProvider;


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


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


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


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


@SuppressWarnings("rawtypes")
private Response getInstanceResponse(List instances, Request request) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + serviceId);
}
return new EmptyResponse();
}
List result = instances.stream().filter(instance -> {
Map 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);
}


}

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

全局過濾器

該過濾器的作用是通過上面的負載均衡器從其中選擇一個服務實例進行服務的調用?

@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 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 supportedLifecycleProcessors = LoadBalancerLifecycleValidator
.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
RequestDataContext.class, ResponseData.class, ServiceInstance.class);
DefaultRequest 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:` mechanism was used, use `` 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(
CompletionContext.Status.FAILED, throwable, lbRequest,
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR)))))
.doOnSuccess(aVoid -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
.onComplete(new CompletionContext(
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> choose(Request lbRequest, String serviceId,
Set supportedLifecycleProcessors) {
ReactorLoadBalancer 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 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);
}

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

Map 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" ;
}
};
}

}

負載均衡客戶端設置默認的配置

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

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

測試,設置不同的v返回不同服務的結果數(shù)據

完畢!??!


網頁標題:SpringCloudGateway實現(xiàn)灰度發(fā)布實現(xiàn)原理
URL網址:http://m.5511xx.com/article/dpdjisi.html