SpringCloudNetflix组件整合

SpringCloudNetflix组件整合

Eureka注册中心

Eureka是什么

Eureka是netflix的一个子模块,也是核心模块之一,Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务架构来说是非常重要的,有了服务发现和注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务,而不需要修改服务调用的配置文件了,功能类似于dubbo的注册中心,比如zookeeper。

原理

Eureka是一个服务注册与发现组件,简单说就是用来统一管理微服务的通信地址,它包含了EurekaServer服务端(也叫注册中心)和EurekaClient客户端两部分组成,EureakServer是独立的服务,而EurekaClient需要集成到每个微服务中。

服务注册:微服务(EurekaClient)在启动的时候会向EureakServer提交自己的通信地址清单如:服务名,ip,端口,在EurekaServer会形成一个微服务的通信地址列表。

服务发现:微服务(EurekaClient)会定期的从EureakServer拉取一份微服务通信地址列表缓存到本地。一个微服务在向另一个微服务发起调用的时候会根据目标服务的服务名找到其通信地址清单,然后基于HTTP协议向目标服务发起请求微服务(EurekaClient)采用“心跳”机制向EureakServer发请求进行服务续约,其实就是定时向。EureakServer发请求报告自己的健康状况,告诉EureakServer自己还活着,不要把自己从服务地址清单中掉,那么当微服务(EurekaClient)宕机未向EureakServer续约,或者续约请求超时,注册中心机会从地址清单中剔除该续约失败的服务。

在这里插入图片描述

Eureka注册中心搭建

创建项目

创建一个普通maven项目

​ eureka-server-1010

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

<!--Eureka服务端支持-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

yml配置

server:
  port: 1010
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false #是否要注册到eureka
    fetchRegistry: false #表示是否从Eureka Server获取注册信息
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机配置

主类

@SpringBootApplication
@EnableEurekaServer //标识是eureka服务端
public class EnrekaServerApplication_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EnrekaServerApplication_7001.class);
    }
}

服务提供者注册到Eureka

<!--eureka客户端支持 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
eureka:
  client:
    service-url:
     defaultZone: http://localhost:1010/eureka #告诉服务提供者要把服务注册到哪儿
 instance:
    prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
    ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找

启动类启用Eureka

@SpringBootApplication
//@EnableEurekaClient//这个表示开启eureka客户端,万一我的注册中心编程nacas
@EnableDiscoveryClient //通用性更好,以后可以切换支持主流注册中心

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

服务消费者从Eureka调用服务

<!-- Eureka客户端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
eureka:
  client:
    service-url:
     defaultZone: http://localhost:1010/eureka #告诉服务提供者要把服务注册到哪儿
 instance:
    prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
    ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找

入口类用Eureka客户端

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableEurekaClient
public class UserConsumerAppliction_9001 {

@Bean
    public RestTemplate restTemplate() {
        return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
    }
    public static void main(String[] args) {
        SpringApplication.run(UserConsumerAppliction_9001.class);
    }
}

整改服务调用

@Autowired
private DiscoveryClient discoveryClient;// Eureka客户端,可以获取到服务实例信息

// String baseUrl = "http://localhost:8081/user/";
        // 根据服务名称,获取服务实例
        List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
        // 因为只有一个UserService,因此我们直接get(0)获取
        ServiceInstance instance = instances.get(0);
        // 获取ip和端口信息
        String baseUrl = "http://"+instance.getHost() + ":" + instance.getPort()+"/user/";
       this.restTemplate.getForObject(baseUrl + id, User.class)

还使用没有负载均衡

服务消费者常见负载均衡实现技术

Ribbon:通过RestTemplate,以url完成服务的调用
Feign/OpenFeign:Feign/OpenFeign底层还是ribbon,只是进行了封装,让我们以接口的方式进行调用

Ribbon负载均衡调用

是什么?
Ribbon是Netflix发布的云中间层服务开源项目,主要功能是提供客户端负载均衡算法。Ribbon客户端组件提供一系列完善的配置项,如,连接超时,重试等。简单的说,Ribbon是一个客户端负载均衡器,我们可以在配置文件中列出load Balancer后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。

Ribbon是一个客户端负载均衡器,它可以按照一定规则来完成一种服务多个服务实例负载均衡调用,这些规则默认提供了很多而且还支持自定义。

集成原理
在这里插入图片描述

   <!--客户端负载均衡实现 ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
@Configuration
public class CfgBean {

    @Bean
    @LoadBalanced //开启负载均衡
    public RestTemplate getRestTemplate(){
        return  new RestTemplate();
    }
}
@RestController
@RequestMapping("/consumer")
public class UserController {

    //多个方法调用只需改一处就ok
    //public static  final String URL_PREFIX = "http://localhost:8001";
    public static  final String URL_PREFIX = "http://USER-PROVIDER"; //通过服务名从注册中心获取服务列表,通过负载均衡调用

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/user/{id}")
    public User getUser(@PathVariable("id")Long id){
        //调用远程服务 http请求
        String url = URL_PREFIX+"/provider/user/"+id;
        return restTemplate.getForObject(url,User.class );
    }
}

为了看到测试效果,改造下服务提供者

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.*;

import java.applet.AppletContext;

@RestController
@RequestMapping("/user/provider")
public class UserController {

    @Autowired
    private ApplicationContext context;

    @GetMapping("/{id}")
    public User getById(@PathVariable(value = "id")Long id){

        System.out.println(context.getEnvironment().getActiveProfiles());
     //正常来说需要调用service,进而调用mapper获取数据,现在模拟一些直接构造返回。
String[] activeProfiles = applicationContext.getEnvironment().getActiveProfiles();
if (activeProfiles!=null || activeProfiles.length>0){
    String profile = activeProfiles[0];
         return new User(id,"zs"+profile);}

        return new User(id,"zs");

    }
}

在这里插入图片描述

不断刷新会出现轮询效果

负载均衡策略

ribbon通过服务名获取到多个服务实例后,要根据一定规则来选择一个服务实例来完成调用.这个规则就叫负载均衡策略. 默认负载均衡策略是轮询。

*内置负载均衡规则类**规则描述*
RoundRobinRule(默认)简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。
AvailabilityFilteringRule对以下两种服务进行忽略:(1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。注意:可以通过修改配置loadbalancer..connectionFailureCountThreshold来修改连接失败多少次之后被设置为短路状态。默认是3次。(2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上线,可以由客户端的..ActiveConnectionsLimit属性进行配置。
WeightedResponseTimeRule为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。
ZoneAvoidanceRule以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。
BestAvailableRule忽略哪些短路的服务器,并选择并发数较低的服务器。
RandomRule随机选择一个可用的服务器。
Retry重试机制的选择逻辑
@Configuration
public class ConfigBean {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @Bean
    public IRule myRule(){
        return new RandomRule();
    }
}

优化

超时配置

使用Ribbon进行服务通信时为了防止网络波动造成服务调用超时,针对Ribbon配置超时时间以及重试机制

ribbon:
  http:
    client:
      enabled: true   # 开启
  ConnectTimeout: 1000          #连接超时时间
  ReadTimeout: 1000                #读取(响应)超时时间
  MaxAutoRetries: 1               #重试机制:同一台实例最大重试次数 1除了首次调用之外,还要重试一次
  MaxAutoRetriesNextServer: 1     #切换服务器次数,如果第一台服务器失败,最多只切换1次到其他服务器
  OkToRetryOnAllOperations: false  #是否所有操作都重试,如果设置了false,除了get请求其他的都不做重试

饥饿加载

启动Ribbon发起服务调用的时候往往会出现找不到目标服务的情况,这是因为Ribbon在进行客户端负载均衡的时候并不是启动时就创建好的,而是在实际请求的时候才会去创建,所以在发起第一次调用的时候会出现超时导致服务调用失败,我们可以通过设置Ribbon的饥饿加载来改善此情况,即在服务启动时就把Ribbon相关内容创建好。

ribbon:
  eager-load:
    enabled: true #开启饥饿加载
    clients: user-server,yyy #针对于哪些服务需要饥饿加载

OpenFeign负载均衡

是什么?

我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻。

OpenFeign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的接口模板(上面标的有访问地址),通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。而OpenFeign则会完全代理(动态代理)HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。Feign整合了Ribbon和Hystrix(关于Hystrix我们后面再讲),可以让我们不再需要显式地使用这两个组件。

总起来说,Feign具有如下特性:

可插拔的注解支持,包括Feign注解和JAX-RS注解;
支持可插拔的HTTP编码器和解码器;
支持Hystrix和它的Fallback;
支持Ribbon的负载均衡;
支持HTTP请求和响应的压缩。

这看起来有点像我们springmvc模式的Controller层的RequestMapping映射。这种模式是我们非常喜欢的。Feign是用@FeignClient来映射服务的。

Open Feign是以接口方式进行服务调用,而不是通过RestTemplate来调用地址,feign底层是对ribbon进行了封装,让我们调用起来更加happy.

替换ribbon jar为feigin
<!--feign的支持-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = "cn.ronghuanet.client") //如果是主类所在子包,可以不用加basePackages,但是最好加上
public class UserConsumerFeignApp5011 {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumerFeignApp5011.class,args);
    }
}

创建一个接口

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

//通过给出线索,完全可以产生一个动态代理类并且交给Spring管理,完对特定服务,特定地址的调用,封装是ribbon
@FeignClient("USER-PROVIDER")
public interface UserClient {
    //正常要调用service,进而调用数据库,但是现在模拟一下,直接返回
    @RequestMapping("/user/provider//{id}")
    User getById(@PathVariable("id") Long id);
}

调用

import com.netflix.appinfo.InstanceInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@RequestMapping("/user/consumer")
public class UserController {

    @Autowired
    private UserClient userClient;

    //正常要调用service,进而调用数据库,但是现在模拟一下,直接返回
    @RequestMapping("/{id}")
    public User getById(@PathVariable("id") Long id)
    {
        System.out.println(userClient.getClass());
        return userClient.getById(id);
    }
}

Feign的超时配置

如果在服务调用时出现了 “feign.RetryableException : Read timed out…”错误日志,说明Ribbon处理超时 ,我们可以配置Ribbon的超时时间:

ribbon:
    ConnectTimeout: 3000
    ReadTimeout: 6000

如果服务调用出现“com.netflix.hystrix.exception.HystrixRuntimeException:… timed - out and no fallback available” 错误日志,是因为Hystrix超时,默认Feign集成了Hystrix,但是高版本是关闭了Hystrix,我们可以配置Hystrix超时时间:

feign:
   hystrix:
       enabled: true #开启熔断支持hystrix:
  command:
      default:
        execution:
          isolation:
            thread:
              timeoutInMilliseconds: 6000   #hystrix超时时间

Hystrix断路器

Hystrix简介

Hystrix是国外知名的视频网站Netflix所开源的非常流行的高可用架构框架。Hystrix能够完美的解决分布式系统架构中打造高可用服务面临的一系列技术难题。

Hystrix “豪猪”,具有自我保护的能力。hystrix 通过如下机制来解决雪崩效应问题。

资源隔离(限流):包括线程池隔离和信号量隔离,限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。

熔断:当失败率达到阀值自动触发降级(如因网络故障/超时造成的失败率高),熔断器触发的快速失败会进行快速恢复

降级机制:超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。

缓存:提供了请求缓存、请求合并实现。

原理分析-命令模式

Hystrix使用命令模式(继承HystrixCommand(隔离)类)来包裹具体的服务调用逻辑(run方法), 并在命令模式中添加了服务调用失败后的降级逻辑(getFallback).

同时我们在Command的构造方法中可以定义当前服务线程池和熔断器的相关参数

在这里插入图片描述

在使用了Command模式构建了服务对象之后, 服务便拥有了熔断器和线程池的功能.

资源隔离&限流

(1)线程池隔离模式(默认):使用一个线程池来存储当前请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求先入线程池队列。这种方式要为每个依赖服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)
(2)信号量隔离模式:使用一个原子计数器(或信号量)记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)

在这里插入图片描述

在这里插入图片描述

服务熔断

如果调用持续出错或者超时,电路被打开进入熔断状态(Open),后续一段时间内的所有调用都会被拒绝
一段时间以后,保护器会尝试进入半熔断状态(Half-Open),允许少量请求进来尝试,
如果调用仍然失败,则回到熔断状态;如果调用成功,则回到电路闭合状态;

在这里插入图片描述

Hystrix提供了如下的几个关键参数,来对一个熔断器进行配置:

circuitBreaker.requestVolumeThreshold //滑动窗口的大小,默认为20

circuitBreaker.sleepWindowInMilliseconds //过多长时间,熔断器再次检测是否开启,默认为5000,即5s钟

circuitBreaker.errorThresholdPercentage //错误率,默认50%
3个参数放在一起,所表达的意思就是:

每当20个请求中,有50%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到5s钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开

服务降级

有了熔断和限流,就得有降级。所谓降级,就是当某个服务出错,超时、熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。

这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。

Hystrix实现-服务提供者

<!--断路器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
server:
  port: 4020
spring:
  application:
    name: user-provider
eureka:
  instance:
    prefer-ip-address: true #显示eureka客户端真实ip
  client:
    service-url:
     #defaultZone: http://localhost:1010/eureka #告诉服务提供者要把服务注册到哪
     #eureka集群环境
      defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka

主类

@SpringBootApplication
@EnableDiscoveryClient //效果一样 @EnableEurekaClient
@EnableHystrix //效果一样 @EnableCircuitBreaker
public class OrderApp
{
    public static void main( String[] args ) {
        SpringApplication.run(OrderApp.class, args);
    }
}

服务代码:

@RestController
@RequestMapping("/order")
public class OrderController {

    @GetMapping("/user/{userId}")
    @HystrixCommand(fallbackMethod = "getOrderByUserIdFallback")
    public List<Order> getOrderByUserId(@PathVariable("userId") Long userId) {
        System.out.println(1/0);
        List<Order> orders = Arrays.asList(
                new Order(1L, "订单1", userId),
                new Order(2L, "订单2", userId),
                new Order(3L, "订单3", userId)
        );
        return orders;
    }

    public List<Order> getOrderByUserIdFallback(Long userId){
        return Arrays.asList(new Order(-1L, "服务异常", userId));
    }
}

注意:

1 熔断我们是在服务提供方做
2 调用时不能使用openfeign调用(有自己熔断机制),要用ribbon调用,应该用open自己的机制

我们往往会把Ribbon和Hystrix的超时配合起来配置:

ribbon:
  MaxAutoRetries: 1 #最大重试次数
  MaxAutoRetriesNextServer: 1 #切换实例的重试次数
  OkToRetryOnAllOperations: false # 对所有的操作请求都进行重试,
  ConnectTimeout: 1000 #请求连接的超时时间
  ReadTimeout: 1800 #请求处理的超时时间hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000 #hystrix超时,置thread和semaphore两种隔离策略的超时时间

Feign实现

为什么使用?
问题: 每个方法都要加回调并且耦合业务代码类中

解决方案:
使用spring面向切面编程,为feign的接口创建一个代理对象,完成对服务调用,当发现熔断后就调用同名托底方法.

实现(feign服务消费者实现)

  1. 创建项目-拷贝feign
  2. 导入jar-没有额外的
  3. 配置
server:
  port: 9003
eureka:
  client:
    registerWithEureka: false #不注册到Eureka,不在注册中心显示
    service-url:
      defaultZone: http://localhost:7001/eureka
feign:
   hystrix:
       enabled: true #开启熔断支持
   client:
    config:
      remote-service:           #服务名,填写default为所有服务
        connectTimeout: 3000
        readTimeout: 3000
hystrix:
  command:
      default:
        execution:
          isolation:
            thread:
              timeoutInMilliseconds: 3000
  1. 入口类

​ 如果接口包结构不按照规范来,额外加入扫描包.@ComponentScan

@SpringBootApplication
@EnableDiscoveryClient  // 开启服务发现客户端
@EnableFeignClients //当client接口类在启动类平级的包下或子孙包下,就可以简写,否则需要添加包路径
public class UserApp {

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

}
  1. 代码-公共里面实现
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
//UserClient 表示对那个接口进行托底处理
@Component
public class UserClientHystrixFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
        return new UserClient() {
            @Override
            public User getUser(Long id) {
                return new User(id,"出异常,请联系管理员!");
            }
        };
    }
}
//调用服务名字
@FeignClient(value = "USER-PROVIDER",fallbackFactory = UserClientHystrixFallbackFactory.class)
@RequestMapping("/provider")
public interface UserClient {
    @RequestMapping("/user/{id}")
    User getUser(@PathVariable("id") Long id);

}

通过feign.hystrix.enabled=true开启Hystrix

feign:
  hystrix:
    enabled: true #开启熔断支持ribbon:
  ReadTimeout: 3000
  SocketTimeout: 3000
  ConnectTimeout: 3000hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10300

总结

如果服务消费者实现的技术为ribbon,服务提供者方使用Hystrix的断路器,也可以在服务消费者来做,但是不能两边做。建议在消费端(能监控服务端是否启动),如果我们服务消费者实现的技术为feign,必须在服务消费者通过feign的断路器,feign断路器底层还是Hystrix的断路器. 默认是关闭的,要启用。

Zuul路由网关

是什么?
Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet(filter)应用。
Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门入口,也要注册入Eureka。

基本配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

	<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
		</dependency>

yml

server:
  port: 8848
spring:
  application:
    name: zuul-gateway
eureka:
  client:
    service-url:
      defaultZone: defaultZone: http://eureka1.com:1010/eureka,http://eureka2.com:1020/eureka,http://eureka3.com:1030/eureka
  instance:
    instance-id: zuul:2010
    prefer-ip-address: false

修改入口类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulGatewayApp2010 {

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

启动测试

Eureka 服务提供者 zuul

​ 直接访问地址:http://localhost:{user服务端口}/user/provider/1
​ 路由访问地址:http://localhost:{网关服务端口}/user-provider(小写)/user/provider/1

路由访问映射规则

  1. 安全加固:不用服务名,映射路径
  2. 忽略原来服务名访问:原来模式不可以访问
  3. 加上统一前缀
zuul:
  routes:
    myUser.serviceId: user-provider # 服务名
    myUser.path: /user/** # 把user打头的所有请求都转发给user-provider
  ignored-services: "*" #所有服务都不允许以服务名来访问
  prefix: "/services" #加一个统一前缀

请求路径变为: http://localhost:{网关服务端口}/services/user/user/provider/1

过滤器

Zuul作为网关的其中一个重要功能,就是实现请求的鉴权(认证授权)。而这个动作我们往往是通过Zuul提供的过滤器来实现的。

ZuulFilter

ZuulFilter是过滤器的顶级父类。

在这里插入图片描述

- shouldFilter:返回一个Boolean值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
- run:过滤器的具体业务逻辑。
- filterType:返回字符串,代表过滤器的类型。包含以下4种:
- pre:请求在被路由之前执行
- routing:在路由请求时调用
- post:在routing和errror过滤器之后调用
- error:处理请求时发生错误调用
- filterOrder:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

过滤器执行周期

在这里插入图片描述

- 正常流程:

- 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

- 异常流程:

- 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。

- 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。

- 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。

使用场景

- 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了

- 异常处理:一般会在error类型和post类型过滤器中结合来处理。

- 服务调用时长统计:pre和post结合使用。

自定义过滤器

@Component
public class LoginFilter extends ZuulFilter{
    @Override
    public String filterType() {
        // 登录校验,肯定是在前置拦截
        return "pre";
    }

    @Override
    public int filterOrder() {
        // 顺序设置为1
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        // 返回true,代表过滤器生效。
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        // 登录校验逻辑。
        // 1)获取Zuul提供的请求上下文对象
        RequestContext ctx = RequestContext.getCurrentContext();
        // 2) 从上下文中获取request对象
        HttpServletRequest req = ctx.getRequest();
        // 3) 从请求中获取token
        String token = req.getHeader("token");
        // 4) 判断
        if(token == null || "".equals(token.trim())){
            // 没有token,登录校验失败,拦截
            ctx.setSendZuulResponse(false);
            // 返回401状态码。也可以考虑重定向到登录页。
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        // 校验通过,可以考虑把用户信息放入上下文,继续向后执行
        return null;
    }
}

负载均衡与熔断

Zuul中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。因此建议我们手动进行配置。

zuul:
  retryable: true
ribbon:
  ConnectTimeout: 250 # 连接超时时间(ms)
  ReadTimeout: 2000 # 通信超时时间(ms)
  OkToRetryOnAllOperations: false# 是否对所有操作重试
  MaxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数
  MaxAutoRetries: 1 # 同一实例的重试次数
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMillisecond: 3000 # 熔断超时时长:3000ms

SpringCloud Config配置中心

微服务架构中,每个微服务都有一个或多个yml配置,里面内容有可能很多,管理起来麻烦。要使用统一配置中心来统一管理。常见配置中心有springcloud config,nacos等

是什么?

在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client。

在这里插入图片描述

能干什么

统一管理配置

配置实时刷新

和gitee集成

可以和svn,git集成,但是推荐使用gitee集成使用

1)方案1:configserver不集群有单点故障。(所有的配置都要放到里面)

2)方案2:Eureka集群,Eureka配置不能放到里面(不建议使用)

3)方案3:外置负载均衡器(Nginx),所有的配置都放到仓库中。(采纳)

步骤分析:zuul配置为例

1)创建一个gitee仓库

准备配置文件

2)创建config server

通过config server获取配置文件

3)在zuul实现config client

最终达到效果,zuul配置是从配置中心获取的

1)gitee创建配置文件,将application-zuul-dev.yml放在gitee仓库

server:
  port: 8848
spring:
  application:
    name: zuul-gateway-dev
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:1010/eureka,http://eureka2:1020/eureka,http://eureka3:1030/eureka
  instance:
    instance-id: zuul:2010
    prefer-ip-address: false
zuul:
  retryable: true
  routes:
    myUser.serviceId: user-provider #服务名
    myUser.path: /user/** #哪个路径
    xxx.serviceId: order-provider #服务名
    xxx.path: /order/** #哪个路径
  ignored-services: "*" #哪些服务不能通过服务名访问,*表示所有的服务
  prefix: "/services" #所有访问地址都加一个前缀
ribbon: #负载均衡配置
  connectTimeout: 250 # 连接超时时间(ms)
  readTimeout: 2000 # 通信超时时间(ms)
  okToRetryOnAllOperations: true # 是否对所有操作重试
  maxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数
  maxAutoRetries: 1 # 同一实例的重试次数
hystrix: #熔断配置
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMillisecond: 3000 # 熔断超时时长:3000ms

config服务端的导包

<dependencies>
    <!--springboot支持-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

   
    <!--配置中心支持-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
</dependencies>

config服务端的配置文件

server:
  port: 3010
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: git仓库地址
          username: 账号
          password: 密码
@SpringBootApplication
@EnableConfigServer //启用配置服务端
public class ConfigServerApplication_1299 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication_1299.class);
    }
}

客户端配置

zuul模块导包

<dependencies>
    <!--springboot支持-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
<!--eureka客户端-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

    <!--configclient端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
</dependencies>
	<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

准备bootstrap.yml/properties

spring:
  cloud:
    config:
      name: application-user #gitee上面名称,文件名,放在仓库根目录
      profile: dev #环境,配置文件名后缀 如:application-zuul-dev.yml
      label: master #分支
      uri: http://127.0.0.1:3010 #配置服务器,config服务端的地址
@SpringBootApplication
@EnableEurekaClient
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class,args);
    }
}

SpringCloud bus配置文件自动刷新

原理分析

配置中心实现了配置的统一管理,但是如果有很多个微服务实例,当更改配置时,需要重启多个微服务实例,会非常麻烦。SpringCloud Bus 就能让这个过程变得非常简单,当我们Git仓库中的配置更改后,只需要某个微服务实例发送一个POST请求,通过消息组件通知其他微服务实例重新获取配置文件,以达到配置的自动刷新效果。

Config-server实现

Pom.xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.60</version>
</dependency>
server:
  port: 3010
spring:
  rabbitmq: #集成RabbitMQ如果配置是默认,可以省略
    host: localhost #mq连接地址
    port: 5672 #mq端口
    username: guest
    password: guest
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: git仓库
          username: 账号
          password: 密码
eureka:
  client:
    service-url:
      defaultZone: http://eureka1.com:1010/eureka
  instance:
    instance-id: config-server:3010
    prefer-ip-address: true
#actuator配置
management:
  endpoint:
    health:
      show-details: always #打印日志
  endpoints:
    web:
      exposure:
        include: "*" #向外暴露的接口,这里*则表示所有的actuator接口都可以被访问
    enabled-by-default: true  #开启actuator监控

Config-client实现-zuul为例

Pom.xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置
spring:
  rabbitmq: #集成RabbitMQ如果配置是默认,可以省略
    host: localhost #mq连接地址
    port: 5672 #mq端口
    username: guest
    password: guest
  cloud:
    config:
       name: application-user #gitee上面名称
       profile: dev #环境
       label: master #分支
       uri: http://127.0.0.1:1299 #配置服务器
      bus: #这行代码很重要,根据官方定义的标准来的 ,就是为了产生一个bus.id
        id: ${spring.application.name}:${spring.cloud.config.profile}:${random.value}

PostMan测试

配置文件中修改配置

使用Post触发测试 http://localhost:3010/actuator/bus-refresh

Gitee webhook触发

内网穿透技术:http://localhost:9010/actuator/bus-refresh-----》:http://xxx.yyy.com/actuator/bus-refresh

软件支持:场景的软件,nat123(30元),花生壳(6元),Natapp(域名经常变不要钱)–花钱买固定域名

Natapp配置

在这里插入图片描述

gitee, Webhook配置:管理-WebHooks

在这里插入图片描述

错误解决:config server格式错误

import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;

        @Component
        public class WebHooksFilter implements Filter {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException {
            }

            @Override
            public void destroy() {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String url = new String(httpServletRequest.getRequestURI());
        if(!url.endsWith("/bus-refresh")){
            filterChain.doFilter(servletRequest,servletResponse);
            return;
        }
        RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
        filterChain.doFilter(requestWrapper, servletResponse);
    }
    private class RequestWrapper extends HttpServletRequestWrapper {
        public RequestWrapper(HttpServletRequest request) {
            super(request);
        }
        @Override
        public ServletInputStream getInputStream() throws IOException {
            byte[] bytes = new byte[0];
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            ServletInputStream servletInputStream = new ServletInputStream() {
                @Override
                public int read() throws IOException {
                    return byteArrayInputStream.read();
                }
                @Override
                public boolean isFinished() {
                    return byteArrayInputStream.read() == -1 ? true : false;
                }
                @Override
                public boolean isReady() {
                    return false;
                }
                @Override
                public void setReadListener(ReadListener listener) {
                }
            };
            return servletInputStream;
        }
    }
}
@SpringBootApplication
@EnableConfigServer //启动configserver
@ServletComponentScan("cn.ronghuanet.config")
public class ConfigServerApp1030 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApp1030.class,args);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/702167.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

复制网页文字和图片到Word中-Word插件-大珩助手

问题整理&#xff1a; 为什么从浏览器的网页上复制文字和图片后&#xff0c;在Word中粘贴时图片无法显示&#xff1f;有没有插件可以将网页中的文字和图片复制到Office Word 中&#xff1f; Word大珩助手是一款功能丰富的Office Word插件&#xff0c;旨在提高用户在处理文档时…

5分钟安装Kubernetes:+带你轻松安装istio服务网格指南

上次我跟大家简单介绍了一下Kubernetes的各个组件及其含义&#xff0c;本期本来计划带领大家一起学习一些常用命令&#xff0c;但我认为这种方式可能无法达到学习的效果。有可能你们会直接忘记&#xff0c;甚至可能没有兴趣去学。我也理解&#xff0c;心想这跟我有什么关系&…

进口电动对夹式硬密封蝶阀的特点-美国品牌

进口电动对夹式硬密封蝶阀的特点可以归纳如下&#xff1a; 一、结构特点 对夹式设计&#xff1a;采用对夹式连接&#xff0c;无需法兰和螺栓&#xff0c;安装简便快捷&#xff0c;降低了安装成本和空间占用。三偏心结构&#xff1a;阀座与蝶板之间采用三偏心设计&#xff0c;…

YOLOv8常见错误汇总

1.训练过程中loss出现Nan值. 可以尝试关闭AMP混合精度训练&#xff0c;如何关闭amp呢&#xff1f;找到如下文件ultralytics/cfg/default.yaml&#xff0c;其中有一个参数是 amp: False # (bool) Automatic Mixed Precision (AMP) training, choices[True, False], True runs…

互联网金融新潮流下的拆分盘投资解析

随着互联网金融的浪潮席卷全球&#xff0c;投资者们对于各种新型投资模式的探索也愈发深入。其中&#xff0c;拆分盘作为一种独特且备受瞩目的投资方式&#xff0c;引发了市场的广泛关注。本文将对拆分盘的投资逻辑进行深入剖析&#xff0c;并结合实际案例&#xff0c;探讨其潜…

计算机网络(4) 最长前缀匹配(路由转发表)

一.路由转发 网络数据包IP段只包含源地址与目的地址&#xff0c;经过数据链路层包装与物理层信号形式转换&#xff0c;最终经由不同的链路节点到达目的地址。这个过程是一步一步&#xff08;hop by hop&#xff09;进行的&#xff0c;路过一个路由节点则称为一跳。每个路由节点…

借助ServiceDesk Plus,更接近ISO 27001变更管理标准

如果实施不当&#xff0c;变更支持可能会中断业务流程并导致停机。许多组织尚未建立不同的阶段来记录整个变更过程。这通常会导致 IT 环境&#xff0c;在这种环境中&#xff0c;实施变更的成功依赖于单个主题专家。这并不高效&#xff0c;并且对 IT 团队来说可能难以管理和压力…

怎么提高音频的播放速度?提高音频播放速度的四种方法

怎么提高音频的播放速度&#xff1f;提高音频的播放速度是在处理音频文件时经常需要面对的问题。音频播放速度的调整可以带来多种应用场景和效果&#xff0c;例如加快语音记录的回放速度以节省时间、提高听力理解和语速训练的效果等。然而&#xff0c;对于不同的音频播放工具和…

SAP 生产订单工序创建BAPI外协加工字段增强CO_SE_PRODORD_OPR_CREATE

需求&#xff1a; 使用BAPI对工单进行新增工序时&#xff0c;需要同时维护外协加工页签上的部分字段&#xff0c;但是该BAPI不包含其中的一些字段&#xff0c;故对此BAPI进行增强以实现该效果。 实现方式&#xff1a; 1.老规矩&#xff0c;COPY标准BAPI出来&#xff0c;再对其…

文化融合,市场共赢:品牌海外推广中的符号与象征策略

在全球化的今天&#xff0c;品牌海外推广不再仅仅是产品的输出&#xff0c;更是一种文化的交流和融合。品牌如何在保持自身特色的同时&#xff0c;又能融入当地文化&#xff0c;成为品牌海外拓展成功与否的关键。本文Nox聚星将和大家分析品牌如何运用具有当地文化特色的符号和象…

SAP 中的Incoterms国际贸易条款术语解释

之前写代码建交货单的时候总是会遇到这个字段&#xff0c;通常我们可能会填FOB或者CIF或者其他&#xff0c;但并不清楚这些都是什么意思&#xff0c;偶然间看到一篇帖子对此作了解释&#xff0c;也记录分享一下。 原文地址&#xff1a; Incoterms&#xff5c;FOB、CFR和CIF&a…

查分易如何上传成绩?

在过去&#xff0c;公布成绩的过程对老师们来说是一项极具挑战的任务。他们手里握着厚重的成绩册&#xff0c;需要逐页翻查学生名单&#xff0c;然后逐一通知他们领取成绩。如果涉及到分班&#xff0c;情况就更加复杂&#xff0c;需要手动整理学生名单&#xff0c;打印出分班表…

学习笔记——路由网络基础——环回接口(loopback)

6、环回接口(loopback) (1)定义 环回接口(loopback) &#xff1a;是一种虚拟的接口&#xff0c;是一种纯软件性质的虚拟接口&#xff0c;模拟一个单独的网段。 Loopback等于在设备中模拟另外不同的网络&#xff0c;实现不需要物理接口连接设备&#xff0c;依然可以模拟的功能…

【吊打面试官系列-Mysql面试题】什么是通用 SQL 函数?

大家好&#xff0c;我是锋哥。今天分享关于 【什么是通用 SQL 函数&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 什么是通用 SQL 函数&#xff1f; 1、CONCAT(A, B) – 连接两个字符串值以创建单个字符串输出。通常用于将两个或多个字段合并为一个字段。 10…

最新全开源版招聘小程序源码 人力资源服务小程序源码 类似58同城和智联招聘平台运营版 让招聘更智能更高效

在数字化快速发展的今天&#xff0c;企业对于招聘效率的需求越来越高。分享一款最新全开源版招聘小程序源码&#xff0c;为企业提供一套类似58同城和智联招聘平台运营版的高效、智能的招聘解决方案。通过搭建这样一款小程序&#xff0c;企业可以更加便捷地发布招聘信息&#xf…

uniapp使用vue3语法构建自定义导航栏,适配小程序胶囊

具体代码 <view v-if"isCustom" class"nav-content-container" :style"height:navContentHeight px;"><slot name"left"></slot><slot name"middle"> </slot><view :style"width:…

Mysql中使用where 1=1有什么问题吗

昨天偶然看见一篇文章&#xff0c;提到说如果在mysql查询语句中&#xff0c;使用where 11会有性能问题&#xff1f;&#xff1f; 这着实把我吸引了&#xff0c;因为我项目中就有不少同事&#xff0c;包括我自己也有这样写的。为了不给其他人挖坑&#xff0c;赶紧学习一下&…

【Echarts系列】带图片的饼图

【Echarts系列】带图片的饼图 序前提说明示例数据格式代码动态旋转图片 序 为了节省后续开发学习成本&#xff0c;这个系列将记录我工作所用到的一些echarts图表。 前提说明 因为饼图中间需要添加图片&#xff0c;所以比较特殊&#xff0c;对于饼图中间数据的对齐很容易出现…

debain安装不上redis,采用docker安装

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 前言 新服务器&#xff0c;出现各种各样的问题&#xff0c;是完全&#xff01;可以&#xff01;理解的&#xff01;md…比如我的这个&#xff0c;就死活装不上redis。横装竖装用面板等等…

WMS仓储管理系统第三方冷库温湿度管理解决方案

随着现代物流行业的迅猛发展&#xff0c;第三方冷库作为冷链物流体系中的关键环节&#xff0c;其温湿度管理对于保障货物质量、提升物流效率具有至关重要的意义。近年来&#xff0c;WMS仓储管理系统技术的不断革新&#xff0c;为第三方冷库的温湿度管理带来了革命性的变革。本文…