文章目录

  • POM依赖
  • 环境准备
  • 配置
    • 配置文件
    • 配置类
  • 案例展示

POM依赖

 org.springframework.bootspring-boot-starter-parent2.7.10883.1.62021.0.4.0UTF-8  org.springframework.bootspring-boot-starterorg.springframework.bootspring-boot-starter-webcom.alibaba.cloudspring-cloud-starter-alibaba-nacos-discovery${springcloudalibaba.version}com.alibaba.nacosnacos-clientcom.alibaba.cloudspring-cloud-starter-alibaba-nacos-config${springcloudalibaba.version}com.alibaba.nacosnacos-clientorg.springframework.cloudspring-cloud-starter-bootstrap${springcloud.version}com.alibaba.nacosnacos-client2.1.1org.springframework.cloudspring-cloud-starter-openfeign${springcloud.version}org.springframework.cloudspring-cloud-starter-loadbalancer${springcloud.version} org.springframework.cloudspring-cloud-starter-netflix-hystrix2.2.9.RELEASE org.springframework.cloudspring-cloud-starter-gateway3.1.5 org.springframework.bootspring-boot-starter-weborg.springframeworkspring-webmvcspring-boot-starter-tomcatorg.springframework.boot 

环境准备

nacos搭建Nacos standalone单机搭建部署

配置

配置文件

application.yml

server:port: 8082spring:profiles:active: devmain:web-application-type: reactiveapplication:name: api-gatewaycloud:gateway:routes:- id: Mesuri: lb://Mespredicates:- Path=/mes/**- id: Testuri: lb://Testpredicates:- Path=/test/**

bootstrap.properties

spring.cloud.nacos.discovery.server-addr=localhost:8848spring.cloud.nacos.discovery.group=devspring.cloud.nacos.discovery.namespace=1e6d33e2-5f43-45ec-8c1b-9c883c2c71d9spring.cloud.nacos.config.group=devspring.cloud.nacos.config.prefix=gatewayspring.cloud.nacos.config.server-addr=localhost:8848spring.cloud.nacos.config.file-extension=propertiesspring.cloud.nacos.config.namespace=1e6d33e2-5f43-45ec-8c1b-9c883c2c71d9spring.profiles.active=devspring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedOriginPatterns=*spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedHeaders=*spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedMethods=*spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowCredentials=true

bootstrap-dev.properties

# 用于配置中心测试message.name=lisi

配置类

自定义Gateway负载均衡器,采用nacos所配置的权重进行负载均衡调用,随机权重算法

import com.alibaba.cloud.nacos.balancer.NacosBalancer;import com.alibaba.nacos.api.naming.pojo.Instance;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.*;import reactor.core.publisher.Mono;import java.math.BigDecimal;import java.util.List;import java.util.Map;import java.util.Random;import java.util.concurrent.atomic.AtomicInteger;import java.util.stream.Collectors;public class CustomRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {private static final Log log = LogFactory.getLog(RoundRobinLoadBalancer.class);final AtomicInteger position;final String serviceId;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 CustomRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));}/** * @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 * @param seedPositionRound Robin element position marker */public CustomRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, int seedPosition) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;this.position = new AtomicInteger(seedPosition);}@SuppressWarnings("rawtypes")@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));}private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());}return serviceInstanceResponse;}/** * 按nacos权重 * * @return */private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> serviceInstances) {Map<String, List<ServiceInstance>> collect = serviceInstances.stream().collect(Collectors.groupingBy(g -> {//nacos在2.0版本之后移除了对实例id查询//return g.getMetadata().get("nacos.instanceId");return g.getHost() +":"+ g.getPort();}));if (serviceInstances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + serviceId);}return new EmptyResponse();}List<Instance> instances = serviceInstances.stream().map(i -> {Instance instance = new Instance();instance.setInstanceId(i.getInstanceId());Map<String, String> metadata = i.getMetadata();instance.setInstanceId(metadata.get("nacos.instanceId"));instance.setWeight(new BigDecimal(metadata.get("nacos.weight")).doubleValue());instance.setClusterName(metadata.get("nacos.cluster"));instance.setEphemeral(Boolean.parseBoolean(metadata.get("nacos.ephemeral")));instance.setHealthy(Boolean.parseBoolean(metadata.get("nacos.healthy")));instance.setPort(i.getPort());instance.setIp(i.getHost());instance.setServiceName(i.getServiceId());instance.setMetadata(metadata);return instance;}).collect(Collectors.toList());//采用nacos所配置的权重进行负载均衡调用,随机权重算法Instance instance = NacosBalancer.getHostByRandomWeight2(instances);//// TODO: enforce order?//int pos = Math.abs(this.position.incrementAndGet());//ServiceInstance instance = instances.get(pos % instances.size());return new DefaultResponse(collect.get(instance.getIp()+":"+ instance.getPort()).stream().findFirst().get());}}

负载均衡配置类,指定使用哪一个负载均衡器

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;@Configurationpublic class RoundRobinLoadBalancerConfig {@BeanReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new CustomRoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}}

LoadBalancerClient,负载均衡调用客户端,指定负载均衡器配置类,LoadBalancerClient注解中value要对用配置文件中路由的id

import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.client.RestTemplate;@LoadBalancerClients({@LoadBalancerClient(value = "Mes", configuration = RoundRobinLoadBalancerConfig.class), @LoadBalancerClient(value = "Test", configuration = RoundRobinLoadBalancerConfig.class)})@Configurationpublic class RestTemplateConfig {@LoadBalanced@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}}

全局过滤器

import lombok.extern.slf4j.Slf4j;import org.springframework.cloud.gateway.filter.GatewayFilterChain;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;import org.springframework.core.Ordered;import org.springframework.stereotype.Component;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;import java.net.URI;@Slf4j@Componentpublic class RouteRecordGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {URI proxyRequestUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);long start = System.currentTimeMillis();return chain.filter(exchange).then(Mono.fromRunnable(() -> {long end = System.currentTimeMillis();log.info("实际调用地址为:{},调用耗时为:{}ms", proxyRequestUri, (end - start));}));}@Overridepublic int getOrder() {// 优先级设为最低,先让RouteToRequestUrlFilter先调用return Ordered.LOWEST_PRECEDENCE;}}

案例展示

Nacos服务列表

服务权重配置

服务测试代码

负载效果

配置中心测试代码

本地配置项

配置中心配置

效果

配置中心注意
这三个配置的拼接等于配置中心的配置 Data ID一定要正确

spring.cloud.nacos.config.prefix=gatewayspring.cloud.nacos.config.file-extension=propertiesspring.profiles.active=dev