org.springframework.boot
spring-boot-starter-parent
2.7.10
8
8
3.1.6
2021.0.4.0
UTF-8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-web
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
${springcloudalibaba.version}
com.alibaba.nacos
nacos-client
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
${springcloudalibaba.version}
com.alibaba.nacos
nacos-client
org.springframework.cloud
spring-cloud-starter-bootstrap
${springcloud.version}
com.alibaba.nacos
nacos-client
2.1.1
org.springframework.cloud
spring-cloud-starter-openfeign
${springcloud.version}
org.springframework.cloud
spring-cloud-starter-loadbalancer
${springcloud.version}
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
2.2.9.RELEASE
org.springframework.cloud
spring-cloud-starter-gateway
3.1.5
org.springframework.boot
spring-boot-starter-web
org.springframework
spring-webmvc
spring-boot-starter-tomcat
org.springframework.boot
nacos搭建Nacos standalone单机搭建部署
application.yml
server:
port: 8082
spring:
profiles:
active: dev
main:
web-application-type: reactive
application:
name: api-gateway
cloud:
gateway:
routes:
- id: Mes
uri: lb://Mes
predicates:
- Path=/mes/**
- id: Test
uri: lb://Test
predicates:
- Path=/test/**
bootstrap.properties
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.group=dev
spring.cloud.nacos.discovery.namespace=1e6d33e2-5f43-45ec-8c1b-9c883c2c71d9
spring.cloud.nacos.config.group=dev
spring.cloud.nacos.config.prefix=gateway
spring.cloud.nacos.config.server-addr=localhost:8848
spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.config.namespace=1e6d33e2-5f43-45ec-8c1b-9c883c2c71d9
spring.profiles.active=dev
spring.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 seedPosition Round 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")
@Override
public 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;
@Configuration
public class RoundRobinLoadBalancerConfig {
@Bean
ReactorLoadBalancer<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)})
@Configuration
public class RestTemplateConfig {
@LoadBalanced
@Bean
public 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
@Component
public class RouteRecordGlobalFilter implements GlobalFilter, Ordered {
@Override
public 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));
}));
}
@Override
public int getOrder() {
// 优先级设为最低,先让RouteToRequestUrlFilter先调用
return Ordered.LOWEST_PRECEDENCE;
}
}
服务测试代码
负载效果
配置中心测试代码
本地配置项
配置中心配置
效果
配置中心注意:
这三个配置的拼接等于配置中心的配置 Data ID一定要正确
spring.cloud.nacos.config.prefix=gateway
spring.cloud.nacos.config.file-extension=properties
spring.profiles.active=dev