SpringCloud快速入门

442

Eureka注册中心

1、服务端

添加服务端pom依赖

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

设置application.xml

server:
  port: 7001
eureka:
  instance:
    hostname: eureka7001.com #服务端实例名称
  client:
    register-with-eureka:  false #不注册自己
    fetch-registry: false #自己是注册中心 维护服务实例 不检索服务
    service-url:
      defaultZone: http://localhost:7001/eureka/
#  server:
#    enable-self-preservation: false  #自我保护关闭

服务端开启注解

@EnableEurekaServer  //开启服务中心注解
@SpringBootApplication
public class EurekaServerMain {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerMain.class, args);
    }
}

2、客户端

添加客户端pom依赖

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

设置application.xml

server:
  port: 80
spring:
  application: #注册名
    name: order
eureka:
  client:
    register-with-eureka:  true #注册自己 默认为true
    fetch-registry: true #是否从eureka抓取自己注册信息,默认为true,集群必须为true才能配合Ribbon使用负载均衡
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
  instance: #实例名
    hostname: Order-Service

客户端开启注解

@SpringBootApplication
@EnableEurekaClient
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
}

3.集群注册中心

server:
  port: 7002
eureka:
  instance:
    hostname: eureka7002.com #服务端实例名称
  client:
    register-with-eureka:  false #不注册自己
    fetch-registry: false #自己是注册中心 维护服务实例 不检索服务
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/
server:
  port: 7001
eureka:
  instance:
    hostname: eureka7002.com #服务端实例名称
  client:
    register-with-eureka:  false #不注册自己
    fetch-registry: false #自己是注册中心 维护服务实例 不检索服务
    service-url:
      defaultZone: http://eureka7002.com:7002/eureka/

配置文件中设置相互守望7002注册到7001,7001注册到7002

客户端注册到集群上

server:
  port: 8001
spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource  # 当前数据源操作类型
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db2019?useSSL=false&serverTimezone=GMT&allowPublicKeyRetrieval=True
    username: root
    password: 123456
mybatis:
  mapper-locations: classpath:mapper/*.xml #配置Mybatis的Mapper文件夹路径
  type-aliases-package: com.zuoxiaozi.springcloud.entities  #所有entity别名类所在包
eureka:
  client:
    register-with-eureka:  true #注册自己 默认为true
    fetch-registry: true #是否从eureka抓取自己注册信息,默认为true,集群必须为true才能配合Ribbon使用负载均衡
    service-url:
      defaultZone: http://localhost:7001/eureka/ ,http://eureka7002.com:7002/eureka/
  instance:
    instance-id: Payment-8001
    prefer-ip-address: true #访问路径可以显示ip

Ribbon负载均衡

可以导入Ribbon依赖,Openfeign集成Ribbon,默认为轮询,application.yml配置负载均衡,在 “1” 调用 “多” 的 “1” 中配置针对 ”多“ 的服务名进行配置负载均衡,实现负载均衡效果;

默认是轮询,底层原理:访问次数%服务个数=调用服务id数

usersevice: #需要访问的服务名称 在服务的application.yml中对应
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #负载均衡规则 针对单独服务提供
#userservice: #服务的服务名称 在服务的application.yml中对应
#  ribbon:
#    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #针对单独服务提供
ribbon:
  eager-load:
    enabled: true #开启饥饿加载
    clients: userservice #指定饥饿加载的服务名称  多个换行: -userservice  -service2

Openfeign远程调用

1、客户端添加依赖

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

2、客户端建接口

使用注册@FeignClient(value = "CLOUD-PAYMENT-SERVICE")

@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")  /*指定调用的服务,注册在eureka中的名称/application.yml中配置的name*/
public interface PaymentFeignService {

    @GetMapping(value = "/payment/get/{id}")  /*远程调用的服务中的Controller*/
    public CommonResult getPaymentById(@PathVariable("id") Long id);
}

3、客户端编写自己的Controller类

@RestController
@Slf4j
public class OrderFeignController {
    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
        return paymentFeignService.getPaymentById(id); /*调用定义的服务接口*/
    }
}

4、客户端启动类开启注解

@SpringBootApplication
@EnableFeignClients   /*开启feign*/
public class FeignMainOrder80 {
    public static void main(String[] args) {
        SpringApplication.run(FeignMainOrder80.class, args);
    }
}

Gateway网关

1、添加pom依赖

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

2、设置application.xml

注册到eureka注册中心

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true   #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_routh    #路由的ID,没有固定规则但要求唯一,简易配合服务名
          #uri: http://localhost:8001         #匹配后提供服务的路由地址
          uri: lb://cloud-provider-hystrix-payment   #匹配后提供服务的路由地址 lb就是负载均衡
          predicates:
            - Path=/payment/hystrix/ok/**          #断言,路径相匹配的进行路由

        - id: payment_routh2 #payment_routh   #路由的ID,没有固定规则但要求唯一,简易配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-provider-hystrix-payment     #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/hystrix/timeout/**             #断言,路径相匹配的进行路由
            #- After=2021-11-08T12:35:07.412+08:00[GMT+08:00]
            #- Cookie=username,zzyy
            #- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性并且值为整数的正则表达式
            #- Host=**.atguigu.com
            #- Method=GET
            #- Query=username, \d+ #要有参数名username并且值还要啥整数才能路由
eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

3、自定义Filter过滤器

路由过滤器、defaultFilter、 全局过滤器的执行顺序?

  • order值越小, 优先级越高
  • 当order值一 样时,顺序是defaultFilter最先,然后是局部的路由过滤器,最后是全局过滤器
@Component /*注册到spring容器中*/
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("***********come in MyLogGateWayFilter: "+new Date());
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");//每次进来后判断带不带uname这个key
        if(uname == null){
            log.info("*********用户名为null ,非法用户,o(╥﹏╥)o");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);    //uname为null非法用户
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }
    
    @Override 
    public int getOrder() {
        return 0;  //设置过滤器的优先级
    }
}

Hystrix断路器

可在服务端,也可以在客户端

添加pom依赖

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

1、服务端

设置application.xml

server:
  port: 8001
spring:
  application:
    name: cloud-provider-hystrix-payment
eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:7001/eureka/

启动类开启注解

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker /*开启hystrix断路器配置*/
public class FeignHystrixOrder80Main {
    public static void main(String[] args) {
        SpringApplication.run(FeignHystrixOrder80Main.class, args);
    }
}

Service服务熔断Fallback(兜底)方法

@Service
public class PaymentService {
    /**
     * 正常访问
     * @param id
     * @return
     */
    public String paymentInfo_OK(Integer id) {
        return "线程池:" + Thread.currentThread().getName() + "paymentInfo_OK,id:"
            + id + "\t" + "√";
    }

    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
    })  /*配置fallback*/
    public String paymentInfo_TimeOut(Integer id) {
        int timeNumber = 3;
        //int age = 10 / 0;
        try {
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + "paymentInfo_TimeOut,id: " 
            + id + "\t" + "√" + " 耗时" + timeNumber + "s";
    }

    public String paymentInfo_TimeOutHandler(Integer id) {
        return "线程池:" + Thread.currentThread().getName() + "paymentInfo_TimeOut,id: " 
            + id + "\t" + " 系统繁忙";
    }

    /*==============服务熔断=====================*/
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),  //是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),   //请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),  //时间范围
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id){
        if (id < 0){
            throw new RuntimeException("*****id 不能负数 ******");
        }
        String serialNumber = IdUtil.simpleUUID();

        return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
    }
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
        return "id 不能负数,请稍候再试,(┬_┬)/~~     id: " +id;
    }
}

Controller中调用paymentCircuitBreaker()方法

@RestController
@Slf4j
public class PaymentController {

    @Autowired
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/hystrix/ok/{id}")
    private String paymentInfo_OK(@PathVariable("id") Integer id) {
        String result = paymentService.paymentInfo_OK(id);
        log.info("*********:" + result);
        return result;
    }

    @GetMapping("/payment/hystrix/timeout/{id}")
    private String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("*********:" + result);
        return result;
    }

    /*===============服务熔断==============*/
    @GetMapping("/payment/circuit/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id){
        String result = paymentService.paymentCircuitBreaker(id);  //调用断路器方法
        log.info("*******result:"+result);
        return result;
    }


}

2、客户端

设置application.xml

server:
  port: 80
spring:
  application:
    name: cloud-consumer-feign-hystrix-order80
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
    fetch-registry: true
    register-with-eureka: true
feign:
  hystrix:
    enabled: true #设置 对熔断降级的支持 如果处理自身的容错就开启。开启方式与生产端不一样。
  
  client:
    config:
      default:
        connectTimeout: 300000 #设置feign请求超时时间   解决Read timed out问题
        readTimeout: 300000

启动类开启注解

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix  /*开启hystrix*/
@EnableCircuitBreaker /*开启hystrix断路器配置*/
public class FeignHystrixOrder80Main {
    public static void main(String[] args) {
        SpringApplication.run(FeignHystrixOrder80Main.class, args);
    }
}

PaymentFallbackService实现接口PaymentHystrixService做为全局降级类

Feign接口注解上指定fallback类

@Component
@FeignClient(value = "cloud-provider-hystrix-payment", fallback = PaymentFallbackService.class) /*指定调用的服务名称 和 服务接口,指定fallback类*/
public interface PaymentHystrixService {

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}

降级类PaymentFallbackService

@Component
public class PaymentFallbackService implements PaymentHystrixService { //实现feign远程调用接口

    @Override
    public String paymentInfo_OK(Integer id) {
        return "PaymentFallbackService, fall back_paymentInfo_OK,505";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
        return "PaymentFallbackService, fall back_paymentInfo_TimeOut,505";
    }
}

Controller中正常方法

@RestController
@Slf4j
public class OrderHystrixController {
    @Autowired
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/consumer/feign/hystrix/order/ok/{id}")
    public String payment_InfoOK(@PathVariable("id") Integer id) {
        return paymentHystrixService.paymentInfo_OK(id);
    }

    @HystrixCommand /*启用fallback注解*/
    @GetMapping("/consumer/feign/hystrix/order/timeout/{id}")
    public String payment_InfoTime(@PathVariable("id") Integer id) {
        //int age = 10 / 0;
        return paymentHystrixService.paymentInfo_TimeOut(id);
    }
}