springcloud基于netfilx的eureka进行二次封装(eureka server 服务注册(用来注册服务实例) eureka client 服务发现(用来简化java客户端调用服务实例相当于java客户端))
先把服务实例使用server方式把服务注册到注册中心,再通过client来发现拿取服务中心的服务(clent需要与server通讯,即配置指定server的ip),再通过ribbon的restTemplate方式调用服务。
Eureka注册服务与发现服务
eureka服务注册中心 添加对应的boot cloud版本
server:
pom.xml
<spring-cloud.version>2.0.2.RELEASE</spring-cloud.version>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>${spring-cloud.version}</version>
</dependency>
application.yml
server:
port: 8080
#指定服务名启动
spring:
application:
name: eureka
#ureka服务实例默认注册的lnstanceld是它的主机名
eureka:
instance:
hostname: server
instance-id: ${spring.application.name}-${random.value}
#server服务
#表明该服务不会向 Eureka Server 注册自己的信息
client:
register-with-eureka: false
#表明该服务不会向 Eureka Server 获取注册信息
fetch-registry: false
#Eureka Server注册中心的地址,用于cl ent server进行交流
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
eureka实现高可用 搭建集群进行俩俩之间注册到达高可用
application.yml添加多一个注册地址
eureka:
instance:
hostname: server
instance-id: ${spring.application.name}-${random.value}
#server服务
#表明该服务不会向 Eureka Server 注册自己的信息
client:
register-with-eureka: false
#表明该服务不会向 Eureka Server 获取注册信息
fetch-registry: false
#Eureka Server注册中心的地址,用于cl ent server进行交流
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/,http://localhost:8762/eureka/
入口类APP.java中加入注解 @EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args){
SpringApplication.run(EurekaServerApplication.class,args);
}
}
测试即可
client:eureka客户端的服务发现
pom.xml
<!--springboot父工程依赖(把项目变成springboot项目) -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath></relativePath>
</parent>
<!--解决post乱码 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>2.0.2.RELEASE</spring-cloud.version>
</properties>
<!--添加springboot依赖包-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>${spring-cloud.version}</version>
</dependency>
</dependencies>
application.yml添加注册中心拿取地址填写多个目的是为了当一个注册服务实例挂断,可以继续使用别的服务实例
server:
port: 8084
#指定服务名启动
spring:
application:
name: payOrder
#client服务
eureka:
instance:
hostname: client
instance-id: ${spring.application.name}-${random.value}
client:
serviceUrl:
defaultZone: http://localhost:8080/eureka/,......
测试即可
eureka client源码分析
client的工作过程:
EurekaDiscoveryClientConfiguration 中属性读取和配置类:
EurekaClientConfig:封装 Eureka Client与 Eureka Server交互所需要的配置信息。 Spring Cloud为其提 供了一个默认配置类的 EurekaClientConfigBean,可 以在配置文件中通过前缀 eureka. clien什 属性名进行属性覆盖。
ApplicationlnfoManager:作为应用信息管理器,管理服务实例的信息类 InstanceInfo 和服务实例的配置信息类Eurekalnstanc巳Config。
Instancelnfo:封装将被发送到 Eureka Server进行服务注册的服务实例元数据。 它在 Eureka Server 的注册表中代表一个服务实例, 其他服务实例可以通过 Instancelnfo 了解该服 务实例的相关信息从而发起服务请求。
EurekalnstanceConfig : 封装 Eureka Client自身服务实例的配置信息,主要用于构建 lnstancelnfo通常这些信息在配置文件中的 eureka.instance前 缀下进行设置, Spring Cloud EurekalnstanceConfigBean 配置类提供了默认配置。
DiscoveryClient:Spring Cloud 中定义用来服务发现的客户端接口。
DiscoveryClient服务发现顶级接口 (包:org.springframework.cloud.client.discovery)
public interface DiscoveryClient {
String description();//获取实现类的描述
//通过服务工d获取服务实例的信息
List<ServiceInstance> getInstances(String serviceId);
//获取所有的服务实例 Id
List<String> getServices();
}
EurekaDiscoveryClient实现DiscoveryClient接口
关系图:
DiscoveryClient类(包:com.netflix.discovery):
1.注册服务实例到Eureka Server中;
2.发送心跳更新与Eureka Server的租约;
3.在服务关闭时从 EurekaServer 中取消租约,服务下线 ;
4.查询在 Eureka Server 中注册的服务实例列表
LookupService:发现活跃的服务实例
public interface LookupService<T> {
//根据服务实例注册的 appNarne来获取封装有相同 appNarne的服务实例信息容器
Application getApplication(String var1);
//返回当前注册表中所有的服务实例信息
Applications getApplications();
//根据服务实例的 id获取服务实例信息
List<InstanceInfo> getInstancesById(String var1);
InstanceInfo getNextServerFromEureka(String var1, boolean var2);
}
Application和Applications里面很多是有synchronized同步锁操作。
EurekaCient:在 LookupService 的基础上扩充 了更多的接口
@ImplementedBy(DiscoveryClient.class)
public interface EurekaClient extends LookupService {
Applications getApplicationsForARegion(@Nullable String var1);
Applications getApplications(String var1);
List<InstanceInfo> getInstancesByVipAddress(String var1, boolean var2);
List<InstanceInfo> getInstancesByVipAddress(String var1, boolean var2, @Nullable String var3);
List<InstanceInfo> getInstancesByVipAddressAndAppName(String var1, String var2, boolean var3);
Set<String> getAllKnownRegions();
InstanceStatus getInstanceRemoteStatus();
/** @deprecated */
@Deprecated
List<String> getDiscoveryServiceUrls(String var1);
/** @deprecated */
@Deprecated
List<String> getServiceUrlsFromConfig(String var1, boolean var2);
/** @deprecated */
@Deprecated
List<String> getServiceUrlsFromDNS(String var1, boolean var2);
/** @deprecated */
@Deprecated
void registerHealthCheckCallback(HealthCheckCallback var1);
//为 Eureka Client注册健康检查处理器
void registerHealthCheck(HealthCheckHandler var1);
//为Eureka Client注册一个EurekaEventListener (事件监听器)
//监听Clie口t服务实例信息的更新
void registerEventListener(EurekaEventListener var1);
boolean unregisterEventListener(EurekaEventListener var1);
//获取HealthCheckHandler
HealthCheckHandler getHealthCheckHandler();
void shutdown();
EurekaClientConfig getEurekaClientConfig();
ApplicationInfoManager getApplicationInfoManager();
}
HealthCheckHandler接口:定时发送心跳包给eureka server来保持实例状态
public interface HealthCheckHandler {
InstanceStatus getStatus(InstanceStatus var1);
}
DiscoveryClient:构造函数主要完成启动时大部分操作,如拉取注册表信息、服务注册、 初始化发送心跳、缓存刷新(重新拉取注册表信息、)和按需注册定时任务等操作。
//ApplicationInfoManager:应用信息管理器
//EurekaClientConfig:封装了Client与Server交互配置信息的类
//AbstractDiscoveryClientOptionalArgs:注入一些可选参数如过滤器
//BackupRegistry充当了备份注册中心的职责(当 Eureka Client无 法从任何 一个 Eureka Server 中获取注册表信息时,BackupRegistry将被调用以获取注册表信息。默认的实现是 NotlmplementdRegistrylmpl,即没有实现)
public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args) {
this(applicationInfoManager, config, args, new Provider<BackupRegistry>() {
private volatile BackupRegistry backupRegistryInstance;
.........
}
部分代码:两个配置均为false, 那么Discovery的初始化将直接结束,表示该客户端既不进行服务注册也不进行服务发现
if (config.shouldFetchRegistry()) {
this.registryStalenessMonitor = new ThresholdLevelsMetric(this, "eurekaClient.registry.lastUpdateSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
} else {
this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
}
if (config.shouldRegisterWithEureka()) {
this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, "eurekaClient.registration.lastHeartbeatSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
} else {
this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
}
部分代码:定义一个基于线程池的定时器线程池 ScheduledExecutorService,线程池大小为 2, 一个线程用于发送心跳, 另一个线程用于缓存刷新,同时定义了发送心跳和缓存刷新线程池。
EurekaTransport:封装了DiscoveryClient与Eureka Server进行 HTTP调用的Jersey客户端
try {
this.scheduler = Executors.newScheduledThreadPool(2, (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-%d").setDaemon(true).build());
this.heartbeatExecutor = new ThreadPoolExecutor(1, this.clientConfig.getHeartbeatExecutorThreadPoolSize(), 0L, TimeUnit.SECONDS, new SynchronousQueue(), (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-HeartbeatExecutor-%d").setDaemon(true).build());
this.cacheRefreshExecutor = new ThreadPoolExecutor(1, this.clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0L, TimeUnit.SECONDS, new SynchronousQueue(), (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d").setDaemon(true).build());
this.eurekaTransport = new DiscoveryClient.EurekaTransport();
this.scheduleServerEndpointTask(this.eurekaTransport, args);
Object azToRegionMapper;
if (this.clientConfig.shouldUseDnsForFetchingServiceUrls()) {
azToRegionMapper = new DNSBasedAzToRegionMapper(this.clientConfig);
} else {
azToRegionMapper = new PropertyBasedAzToRegionMapper(this.clientConfig);
}
if (null != this.remoteRegionsToFetch.get()) {
((AzToRegionMapper)azToRegionMapper).setRegionsToFetch(((String)this.remoteRegionsToFetch.get()).split(","));
}
this.instanceRegionChecker = new InstanceRegionChecker((AzToRegionMapper)azToRegionMapper, this.clientConfig.getRegion());
}
Eureka Server源码解析
开箱即用服务注册中心:服务注册,接收心跳包服务,服务集群同步,服务剔除,服务下线,获取注册表服务实例信息。
lnstanceRegistry: Eureka Server注册表的最核心接口。
关系图:
lnstanceRegistry:(包:com.netflix.eureka.registry)
LeaseManager 接口:服务注册、 服务下线、服务租约更新以及服务剔除等操作。
public interface LeaseManager<T> {
void register(T var1, int var2, boolean var3);
boolean cancel(String var1, String var2, boolean var3);
boolean renew(String var1, String var2, boolean var3);
void evict();
}
调用服务
第一种 ribbon的restTemplate方式默认采用轮询客户端负载均衡形式,基于rest方式
入口类创建bean对象
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
调用
@Autowired
private RestTemplate restTemplate
String res= restTemplate.getForObject("http://PRODUCT/test",String.class);
第一个参数:URL中PRODUCT相当于ip:端口号不需要指定,目的集群调动
第二个参数:调用返回值类型
第二种使用feign以接口加注解方式调用服务实例 伪rpc 底层还是和ribbon一样
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
入口类中加入注解 @EnableFeignClients
@EnableFeignClients
@SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class);
}
}
定义调用的接口:
@FeignClient(name="product")
public Interface ProductClient{
@GetMapping("/msg")
String productMsg();
}
@FeignClient注解标识服务(服务名) @GetMapping("/msg"):与被调用服务接口一致
使用:
@Autowired
private ProductClient productClient;
String res=productClient.productMsg();
openfeign源码解析
feign关系图:
@EnableFeignClients :
1.引入FeignClientsRegistrar ;
2.指定扫描i FeignClient 的包信息,就是指定 FeignClient接口类 所在的包名;
3.指定FeignClient接口类的自定义配置类
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
//ImportBeanDef工nitionRegistrar的子类 ,用于处理由FeignCl工ent注解
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
下面三个函数都是为了指定需要扫描的包
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
//指定自定义 feign client的自定义配置,可以配置Decoder、 Encoder和Contract等组件,FeignClientsConfiguration是默认的配置类
Class<?>[] defaultConfiguration() default {};
// 指定被自FeignClient修饰的类,如果不为空, 那么路径自动检测机制会被关闭
Class<?>[] clients() default {};
}
FeignClientsRegistrar:注册@EnableFeignClients 提供的自定义配置类中的相关Bean实 例;根据@EnableFeignClients提供的包信息扫描 @FeignClient 注解修饰的FeignClient接口类,然后进行Bean实例注册 。
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//从EnableFeignClients的属性值来构建 Feign的自定义Configuration进行注册
this.registerDefaultConfiguration(metadata, registry);
//扫描package , 注册被日 FeignCl工ent修饰的接口类的 Bean信息
this.registerFeignClients(metadata, registry);
}
registerDefaultConfiguration方法:判断@EnableFeignClients注解是否设置了defaultConfiguration属性。如果有,则将调用 registerClientConfiguration方法,进行BeanDefinitionRegistry的注册。
.....................
断路器: Hystrix
1.在通过第三方客户端访问(通常是通过网络)依赖服务出现高延迟或者失败时,为系统提供保护和控制 。
2.在复杂的分布式系统中防止级联失败(服务雪崩效应) 。
3.快速失败 (Fail fast) 同时能快速恢复。
4.提供失败回滚 (Fallback) 和优雅的服务降级机制。
5.提供近实时的监控、 报警和运维控制手段。
原因:丛多微服务之间调用必不可少,有可能因为网络延迟,发生异常,负载过大无法及时响应。
解决:使用hystrix为延迟,失败提供强大的容错能力,为服务之间提供控制和保护。
pom.xml (注意:版本要对应,否则没有hystrixcommand注解)
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>RELEASE</version>
</dependency>
入口类添加@EnableCircuitBreaker注解开启hystrix
@EnableCircuitBreaker
@EnableFeignClients
@SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class);
}
}
方法上添加@HystrixCommand(fallbackMethod = "instanceinfoGetFail ")注解将该方法纳入到 Hystrix 的监控中 (异步时返回值为futrue,异步回调执行命令observableExecutionMode = ObservableExecutionMode .LAZY 返回值Observable)
@FeignClient(name = "payOrder")
public interface PayOrderClient {
@GetMapping("/test")
String test();
}
请求合并:将多个请求合并成一个请求进行批请求发送,减少网络通信时间和线程池资源浪费,对高并发和高延迟效果很好。通过注解@HystrixCollapser或继承HystrixCollapser类实现。
编写测试类(正常返回预期接口,当关闭服务时返回回滚结果)。需要再service层设置注解@HystrixCommand和回滚方法
@RestController
public class TestController {
@Autowired
private PayOrderClient payOrderClient;
@HystrixCommand(fallbackMethod = "hstrixTest")
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String test(){
return payOrderClient.test();
}
public String hstrixTest(){
System.out.println("hystirx 哈哈");
return "error";
}
}
服务雪崩:可能由于某一个服务出现服务器奔溃,网络延迟,程序异常,缓存击穿,太慢导致用户疯狂发送请求和程序重试机制,从而加大服务提供者流量,耗尽线程池中线程后服务奔溃无法响应。如果服务之前有依赖调用关系会继续扩大影响。
断路器:当调用服务多次失败时,达到一定阀值会自动关闭断开服务,然后不再调用服务直接返回回滚的信息,设置了一个重置时间,在重置时间结束后,断路器来到半开状态(即可以再次调用真正服务) 。失败回到打开状态,成功回到关闭状态。
好处:服务不可用时,防止大量同步等待,减少服务资源消耗,一段时间后又再次执行恢复服务可用状态。(它也是一种服务降级:为了在整体资源不够的时候,适当放弃部分服务,将主要的资源投放到 核心服务中,待渡过难关之后,再重启已关闭的服务,保证了系统核心服务的稳定);资源隔离(服务线程与访问服务的线程分隔开来,调用线程能够空出来去做其他的工作而不至于因为服务调用的执行阻塞过长时间,保证当一个线程池的线程阻塞不会影响其他线程池的线程执行任务);信号量(来控制系统的负载)。
Hystrix的实现思路 :
1.它将所有的远程调用逻辑封装到 HystrixCommand 或者 HystrixObservableCommand对象中,这些远程调用将会在独立的线程中执行(资源隔离),这里使用了设计模式中的命令模式 。
2.Hystrix 对访问耗时超过设置阀值的请求采用自动超时的策略 。该策略对所有的命令都有效(如果资源隔离的方式为信号量,该特性将失效),超时的阀值可以通过命令配置进行自定义 。
3.为每一个服务提供者维护一个线程池(或者信号量),当线程池被占满时,对于该服务提供者的请求将会被直接拒绝(快速失败)而不是排队等待,减少系统的资源等待。
4.针对请求服务提供者划分出成功、失效、超时和线程池被占满等 四种可能出现的情况 。
5.断路器机制将在请求服务提供者失败次数超过一定阀值后手动或者自动切断服务一段时间 。
6.当请求服务提供者出现服务拒绝、超时和短路( 多个服务提供者依次顺序请求,前面的服务提供者请求失败,后面的请求将不会发出) 等情况时,执行其 Fallback方法,服务降级。
7.提供接近实时的监控和配置变更服务 。
hystrix源码解析:
关系图:
简单的流程如下 :
1.构建 HystrixCommand 或者 HystrixObservableCommand 对象 。
2.执行命令 。
3.检查是否有相同命令执行的缓存 。
4.检查断路器是否打开 。
5.检查线程池或者信号量是否被消耗完 。
6.调用 HystrixObservableCommand#construct或 HystrixCommand#run执行被封装的远程调用逻辑 。
7.计算链路的健康情况 。
8.在命令执行失败时获取 Fallback 逻辑。
9.返回成功的 Observable。
@HystrixCommand:主要使用fallbackMethod方法
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {
String groupKey() default "";//默认为被注解方法的运行时类名
//hystriX的命令键,用于区分不同的注解方法 //默认为注解方法的名称
String commandKey() default "";
String threadPoolKey() default "";//线程池键用来指定命令执行的HystrixThreadPool
//指定 Fallback方法名, Fallback方法也可以被 HystrixCommand注解
String fallbackMethod() default "";
HystrixProperty[] commandProperties() default {};//自定义命令的相关配置
HystrixProperty[] threadPoolProperties() default {};//自定义线程池的相关配置
Class<? extends Throwable>[] ignoreExceptions() default {};//定义忽略哪些异常
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
HystrixException[] raiseHystrixExceptions() default {};
String defaultFallback() default "";//默认的 fallback
}
注解@HystrixCollapser:主要是batchMethod方法,HystrixCommand用于请求合并操作的注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HystrixCollapser {
String collapserKey() default "";
String batchMethod();
Scope scope() default Scope.REQUEST;
HystrixProperty[] collapserProperties() default {};
}
HystrixCommandAspect切面类:使用切面把hystrix注解修饰的方法进行封装调用
部分代码:通过 MetaHolderFactory 构建出被注解修饰方法中用于构建 HystrixCommand 必要信息集合类 MetaHolder;根据MetaHolder 通过 HystrixCommandFactory 构建出合适的 HystrixCommand;委托 CommandExecutor 执行 HystrixCommand,得到结果 。
@Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()")
public Object methodsAnnotatedWithHystrixCommand(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = AopUtils.getMethodFromTarget(joinPoint);
Validate.notNull(method, "failed to get method from joinPoint: %s", new Object[]{joinPoint});
if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) {
throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser annotations at the same time");
} else {
//通过工厂的方式构建 metaHolder
HystrixCommandAspect.MetaHolderFactory metaHolderFactory = (HystrixCommandAspect.MetaHolderFactory)META_HOLDER_FACTORY_MAP.get(HystrixCommandAspect.HystrixPointcutType.of(method));
MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);
ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ? metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType();
try {
Object result;
if (!metaHolder.isObservable()) {
result = CommandExecutor.execute(invokable, executionType, metaHolder);
} else {
result = this.executeObservable(invokable, executionType, metaHolder);
}
return result;
} catch (HystrixBadRequestException var9) {
throw var9.getCause();
} catch (HystrixRuntimeException var10) {
throw this.hystrixRuntimeExceptionToThrowable(metaHolder, var10);
}
}
}
MetaHolder类:用于构建 HystrixCommand和与被包装方法相关的必要信息,如被注解的方法、失败回滚执行的方法和默认的命令键等属性。
HystrixCommandFactory类:构建请求合并的命令。比如HystrixCommand或者HystrixObservableCommand,前者将同步或者异步执行命令,后者异步回调执行命令 。
.........................
客户端负载均衡:ribbon
服务端负载均衡与客户端负载均衡区别:客户端负载均衡中客户端节点会获取全部服务端地址列表,服务器负载均衡中客户端节点只获得单一服务代理地址,服务代理获取全部服务端地址列表。
ribbon负载均衡:使用注解@LoadBalanced,客户端就会根据负载均衡策略选中一个服务端发起请求,从而实现负载均衡。
使用:用@FeignClient时, OpenFeign默认使用 Ribbon来进行网络请求的负载均衡或用restTemplate方式也默认使用ribbon。
源码...........................
服务API网关:Gateway
报错:org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of
type 'org.springframework.http.codec.ServerCodecConfigurer' that could not be found. https://blog.csdn.net/jamesleel/article/details/84838057
多个服务暴露在外不太安心,网关服务提供路由配置、 路由断言和过滤器等功能。
pom.xml
<!--依赖于WebFlux 必须引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-core</artifactId>
<version>${spring-cloud.version}</version>
</dependency>
使用网关API:
路由断:根据请求的时间、 Host 地址、路径和请求方法等。如:(当请求的路径为 test1时,直接返回状态码 200且响应体为hello字符串)
@Bean
public RouterFunction<ServerResponse> testFunRouterFunction(){
RouterFunction<ServerResponse> route = RouterFunctions.route(RequestPredicates.path("test1"),
request -> ServerResponse.ok().body(BodyInserters.fromObject("hello ")));
return route;
}
过滤器:网关经常需要对路由请求进行过滤,对符合条件的请求进行一 些操作,如增加请求头、增加请求参数、增加响应头和断路器功能。如(当请求路径为 /test2时,网关将请求转发到http://localhost:8084/test,并对响应进行过滤处理,增加响应的头部token:11111111)
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder){
return builder.routes()
.route(r -> r.path("/test2")
.filters(f -> f.addResponseHeader("token","11111111"))
.uri("http://localhost:8084/test")
).build();
}
使用配置进行定义:
如(定义了全局过滤器与一条路由,全局过滤器为所有的响应加上头部token:111111111另外还定义了id为key的路由,优先级比较低。符合路由断言条件的请求将会转发到http://localhost:8084/test)
WebSocket:可以把uri配置websocker地址:ws://localhost:9000
spring:
cloud:
gateway:
discovery:
locator:
# 是否可以通过其他服务的serviceId来转发到具体的服务实例。默认为false
# 为true,自动创建路由,路由访问方式:http://Gateway_HOST:Gateway_PORT/大写的serviceId/**,其中微服务应用名默认大写访问
enabled: true
routes:
- id: key
uri: http://localhost:8084/test
predicates:
- Path=/user/**
filters: #添加了一个age=18的请求参数,且请求头也多了X-Request-Foo=Bar
- AddRequestHeader=X-Request-Foo, Bar
- AddRequestParameter=age, 18
网关处理流程:
1.请求发送到网关, DispatcherHandler是 HTTP 请求的中央分发器,将请求匹配到相应的 HandlerMapping。
2.请求与处理器之间有一个映射关系, 网关将会对请求进行路由 , handler此处会匹配到 RoutePredicateHandlerMapping, 以匹配请求所对应的 Route。
3.随后到达网关的 Web处理器,该 WebHandler代理了一系列网关过滤器和全局过滤器的实例,如对请求或者响应的头部进行处理(增加或者移除某个头部) 。最后转发到具体的代理服务。
源码.........................
限流机制:为了保证系统的稳定运行, 一旦达到了设定限制的值,就会限制系统输入、输出流量。
限流算法:
漏桶算法:定义桶大小容量burst,再定义桶漏洞大小rate(固定速率),当请求超过桶能装的容量时,就会阻塞或拒绝请求。(因为对桶漏洞速率无法动态控制,对突发情况处理不太友好)。
漏桶算法基于内置的限流过滤器配置实现:
pom.xml
<dependency>
<groupId>org springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
application.yml
spring:
cloud:
gateway:
routes:
- id: key
uri: http://localhost:8084/test
predicates:
- Path=/rate/**
filters: #使用spEL表达式#{@beanName}从Spring容器中获取Bean对象,默认情况下,使用 PrincipalNameKeyResolver, 即请求认证 的 java.security.Principal 作为限流键
- name: RequestRateLimiter
args:
key-resolver: "#{@testKeyResolver}"
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
容器对象
//该限流键的定义很简单,获取请求中的user参数。burstCapacity对应令牌桶容量,replenishRate 对应令牌桶每秒填充平均速率 。 配置执行的过滤器效果是:每个用户的请求每秒限制为10次,令牌桶的上限为 20。
@Bean
public KeyResolver testKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst(”user")) ;
令牌桶算法:当请求发送过来,通过拿取令牌再去执行操作;如果桶满,就会阻塞或拒绝请求。一旦突发大量请求需要提交桶漏洞速率就通过动态实时添加令牌方式解决。
令牌桶算法基于过滤器接口的自定义实现: (实现GatewayFilterFactory接口)
pom.xml
<dependency>
<groupid>org.isomorphism</groupid>
<artifactid>token-bucket</artifactid>
<version>l.6</vers工on>
</dependency>
............
熔断降级:与hystrix熔断器一样
filters:
- Hystrix=myCommandName
过滤器则会应用 Hystrix熔断与降级,将请求包装成名为 myCommandName 的路由指令RouteHystrixCommand, RouteHystrixCommand继承于HystrixObservableCommand, 其内包含了 Hystrix 中断路、资源隔离等诸多断路器核心功能,当转发服务出现问题时,网关能对此进行快速失败,执行特定的失败逻辑,保护网关巾的线程池安全,减少同步等待 。
@RequestMapping ("/fallbackcontroller”)
public String fallbackcontroller(){
return "fallback";
}
#Spring Cloud Gateway 的 Hystrix过滤器只支持 forward:格式的 URI。 如果 fallback
被调用,请求将会被转发到匹配的接口(当服务关闭时,即返回)
filters :
- name: Hystrix
args:
name: fallbackcmd
fallbackUrl: forward:/fallbackcontroller
网关重试:当转发到代理服务时,遇到指定的服务端错误,如 HTTP 状态码为 500 时,我们可以设定请求重试一定数量。
retry当请求失败时,重新发起请求次数为2。
配置中心:Spring Cloud Config
原因:每一个微服务都有自己的配置文件,当在生成环境条件下,运维修改配置会十分麻烦。
实现:需要有配置中心服务端server、配置中心客户端(多个)、git(作为管理空间存储)。
服务端server:
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
application.yml
server:
port: 8005
spring:
application:
name: eternal-config-server
cloud:
config:
server:
git:
#配置的Git地址
uri: https://github.com/xuwujing/springcloud-study/
#git仓库地址下的相对地址 多个用逗号","分割。
search-paths: /springcloud-config/config-repo
#git仓库的账号
username: admin
#git仓库的密码
password: 123456
#client服务
eureka:
instance:
instance-id: ${spring.application.name}-${random.value}
client:
#设置与Eureka Server交互的地址,客户端的查询服务和注册服务都需要依赖这个地址。
serviceUrl:
defaultZone: http://localhost:8080/eureka/
入口类添加@EnableConfigServer注解
@SpringBootApplication
@EnableConfigServer
public class ConfigEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigEurekaApplication.class, args);
}
}
客户端:获取配置中心参数的
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-clien</artifactId>
</dependency>
bootstrap.yml(相关配置会先于application.yml,而bootstrap.yml的加载也是先于application.yml)不配置会启动失败
spring:
cloud:
config:
#获取配置文件的名称
name: configtest
#获取配置的策略
profile: pro
#获取配置文件的分支,默认是master。如果是是本地获取的话,则无用。
label: master
discovery:
#开启配置信息发现
enabled: true
#指定配置中心的service-id,便于扩展为高可用配置集群。
serviceId: eternal-config-server
#client服务
eureka:
instance:
instance-id: ${spring.application.name}-${random.value}
client:
#设置与Eureka Server交互的地址,客户端的查询服务和注册服务都需要依赖这个地址。
serviceUrl:
defaultZone: http://localhost:8080/eureka/
applicatiom.yml
server:
port: 8005
spring:
application:
name: eternal-config-client
认证和授权:spring cloud security
OAuth2流程:
1.客户端请求资源所有者的授权。
2.资源所有者同意授权,返回授权许可(Authorization Grant),这代表了资源所有者的授权凭证。
3.客户端携带授权许可要求授权服务器进行认证,请求访问令牌。
4.授权服务器对客户端进行身份验证,并认证授权许可,如果有效,返回访问令牌 。
5.客户端携带访问令牌向资源服务器请求受保护资源的访问。
6.资源服务器验证访问令牌,如果有效,接受访问请求,返回受保护资源 。