0
点赞
收藏
分享

微信扫一扫

Spring Cloud LoadBalancer--自定义负载均衡策略--方法/实例


简介

说明

        本文用示例介绍Spring Cloud LoadBalancer如何自定义负载均衡策略。

        Spring Cloud LoadBalancer的默认负载均衡策略为轮询策略。我们可以自定义负载均衡策略。自定义负载均衡策略可以用于:灰度发布、蓝绿发布等。

相关网址

​​Spring Cloud LoadBalancer--默认的负载均衡策略_IT利刃出鞘的博客-CSDN博客​​

​​Spring Cloud LoadBalancer--指定负载均衡策略--方法/实例_IT利刃出鞘的博客-CSDN博客​​

本文目标

        本处的场景是:order(订单微服务)调用storage(库存微服务)扣减库存。

        启动两个storage(库存微服务),将order的feign请求全部路由到IP为192.168.5.1,端口号为9211的实例上。

基础代码

注意(本处为了简单,直接用@RequestParam传参)

order的controller

package com.knife.order.controller;

import com.knife.common.entity.Result;
import com.knife.order.entity.Order;
import com.knife.storage.api.StorageFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* http://localhost:9011/order/create/?userId=1&productId=1&count=10&money=100
* http://localhost:9011/order/createFault/?userId=1&productId=1&count=10&money=100
*/

@Slf4j
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private StorageFeignClient storageFeignClient;

// 正常流程
@PostMapping("create")
public Result create(Order order) {
log.info("订单服务:创建订单:{}", order);

storageFeignClient.decreaseStorage(order.getProductId(), order.getCount());

return new Result().message("创建订单成功");
}
}

storage的feign定义

package com.knife.storage.api;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient("storage")
public interface StorageFeignClient {
@PostMapping("/feign/storage/decreaseStorage")
void decreaseStorage(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}

storage的controller

package com.knife.storage.feign;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class FeignController {
@PostMapping("/feign/storage/decreaseStorage")
public void decreaseStorage(@RequestParam("productId")Long productId,
@RequestParam("count")Integer count) {
log.info("库存服务:减少库存。productId: {}, count: {}", productId, count);
}
}

自定义负载均衡策略

自定义策略类

/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.knife.order.config;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import java.util.List;

/**
* 强制路由到某个实例
*/
public class ForceLoadBalancer implements ReactorServiceInstanceLoadBalancer {

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

private final String serviceId;

private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

/**
* @param serviceInstanceListSupplierProvider a provider of
* {@link ServiceInstanceListSupplier} that will be used to get available instances
* @param serviceId id of the service for which to choose an instance
*/
public ForceLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
}

@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);

return supplier.get(request).next().map(this::getInstanceResponse);
}

private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + serviceId);
}
return new EmptyResponse();
}

// 将特定应用的请求路由到指定实例
String serviceId = "storage";
String host = "192.168.5.1";
int port = 9021;

ServiceInstance instanceResult = null;
for (ServiceInstance instance : instances) {
if (serviceId.equals(instance.getInstanceId())
&& host.equals(instance.getHost())
&& port == instance.getPort()) {
instanceResult = instance;
break;
}
}

// 如果指定的实例不可用,则随机取一个可用的服务
if (instanceResult == null) {
instanceResult = instances.get(0);
}

return new DefaultResponse(instanceResult);
}

}

配置类(不要加@Configuration)

package com.knife.order.config;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

@Configuration
public class LoadBalancerConfiguration {
@Bean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new ForceLoadBalancer(
loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}

注册策略配置

package com.knife.order;

import com.knife.common.annotation.CommonApplication;
import com.knife.order.config.LoadBalancerClientConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients("com.knife.**.api")
@LoadBalancerClients(defaultConfiguration = {LoadBalancerConfiguration.class})
public class OrderApplication {

public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}

}

测试

        启动两个storage实例,端口号分别为90211和9022。Idea可以一个应用启动多个实例,方法见​​这里​​。

Post方式访问:​​http://localhost:9011/order/create/?userId=1&productId=1&count=10&money=100​​

1.默认的负载均衡策略

默认情况下,是轮询策略,调用10次接口的结果如下(每个实例都是5次请求):

Spring Cloud LoadBalancer--自定义负载均衡策略--方法/实例_java

Spring Cloud LoadBalancer--自定义负载均衡策略--方法/实例_java_02

2.自定义的负载均衡策略

可以发现,所有请求全部路由到了我们指定的9021实例。

Spring Cloud LoadBalancer--自定义负载均衡策略--方法/实例_spring cloud_03

Spring Cloud LoadBalancer--自定义负载均衡策略--方法/实例_ide_04

举报

相关推荐

0 条评论