springcloud笔记简略

服务注册中心

Eureka(停更)、zookeeper、consul、nacos
步骤

  1. 修改POM
  2. application
  3. 启动类加注解

Eureka(服务注册与发现)

在项目中加入服务端依赖,就可以作为服务端使用

server
  1. 创建项目
  2. 修改pom
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--服务信息监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
		<!--新版-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>        
  1. 修改application.yml
server:
  port: 7001

eureka:
  instance:
    hostname: localhost #服务端实例名称
  client:
    register-with-eureka: false #false不想注册中心注册自己
    fetch-registry: false #false 表示自己就是注册中心
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  1. 启动类添加注解 @EnableEurekaServer
client
  1. 修改pom
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
  1. application.yml
eureka:
  client:
    register-with-eureka: true #true 将自己注册到eurekaServer,默认true
    fetch-registry: true #true 从eurkaServer抓取已有注册信息,默认true。集群模式必须设置为true配合Ribbon使用负载均衡
    service-url:
      defaultZone: http://localhost:7001/eureka
  1. 启动类添加注解@EnableEurekaClient

集群模式服务互相注册 service-url写其他服务的地址

RestTemplate

集群模式,配置文件中 fetch-registry要设置为true

eureka:
  client:
    fetch-registry: true #从eurkaServer抓取已有注册信息

restTemplate要实现负载均衡
访问路径要通过注册到eurkaServer的应用名称访问

Actuator(监控和管理应用)

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

启动8080项目 访问 http://localhost:8080/actuator/health:

{“status”:“UP”}

端口描述
auditevents显示应用暴露的审计事件 (比如认证进入、订单失败)
info显示应用的基本信息
health显示应用的健康状态
metrics显示应用多样的度量信息
loggers显示和修改配置的loggers
logfile返回log file中的内容(如果logging.file或者logging.path被设置)
httptrace显示HTTP足迹,最近100个HTTP request/repsponse
env显示当前的环境特性
flyway显示数据库迁移路径的详细信息
liquidbase显示Liquibase 数据库迁移的纤细信息
shutdown让你逐步关闭应用
mappings显示所有的@RequestMapping路径
scheduledtasks显示应用中的调度任务
threaddump执行一个线程dump
heapdump返回一个GZip压缩的JVM堆dump

@EnableDiscoveryClient(服务发现)

用法和@EnableEurekaClient基本一致,
如果选用的注册中心是eureka,那么就推荐@EnableEurekaClient,
如果是其他的注册中心,那么推荐使用@EnableDiscoveryClient

  1. 启动类上添加注解@EnableDiscoveryClient
  2. 注入DiscoveryClient
    @Resource
    private DiscoveryClient discouveryClient;
    
  3. 通过DiscoveryClient可以得到微服务的各种信息

zookeeper

zookeeper就是服务端,所以要安装zookeeper
客户端都注册到zookeeper,进行互相调用,客户端配置如下:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <!-- 如果包冲突,删除自带的zookeeper,然后在手动导入相同版本的
	            <exclusions>
	                <exclusion>
	                    <groupId>org.apache.zookeeper</groupId>
	                    <artifactId>zookeeper</artifactId>
	                </exclusion>
	            </exclusions>
			-->
        </dependency>

启动类添加 @EnableDiscoveryClient

server:
  port: 8004

spring:
  application:
    name: cloud-provider-payment
  cloud:
    zookeeper:
      connect-string: 192.168.206.128:2181

Consul(分布式服务发现和配置管理系统)

要独立安装Consul服务器

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
spring:
  application:
    name: consul-provider-payment
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}

server:
  port: 8006

通过服务调用去调用不同服务的端口。

服务调用

Ribbon

负载均衡+RestTemplate调用
在这里插入图片描述

负载均衡替换规则

Ribbon自定义配置类不可以@ComponentScan所扫描的包下,否则会被所有的客户端共享,不能达到定制效果。
@SpringBootApplication注解包含@ComponentScan注解,所以自定义的配置类不能放到启动类的同级目录及以下

策略描述
RoundRobinRule轮询,依次执行每个执行一次(默认)
RandomRule随机
BestAvailableRule先过滤掉故障实例,再选择并发较小的实例
WeightedResponseTimeRule响应时间越快服务权重越大,容易被选中的概率就越高
RetryRule先按照RoundRobinRule(轮询)的策略获取服务,如果获取的服务失败在指定的时间会进行重试,进行获取可用的服务
AvailabilityFilteringRule先过滤掉多次访问故障而处于断路器跳闸状态的服务,对剩余的服务列表安装轮询策略进行访问
ZoneAvoidanceRule默认规则,复合判断Server所在区域的性能和Server的可用性选择服务器

自定义规则

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

启动类添加@RibbonClient注解

@RibbonClient( name = "要调用的服务名称" , configuration = MySelfRule.class)

OpenFeign

声明式的web服务客户端。只需创建一个接口,并在接口上添加注解即可。
第一步

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

第二步
主启动类添加注解@EnableFeignClients

第三步
编写服务调用接口,添加@FeignClient

@Component
@FeignClient( value = "CLOUD-SERVICE")
public interface FeignService{
	@GetMapping(value = "/test/{id}")
	Result<T> getById(@PathVariable("id") Long id);
}

OpenFeign自带负载均衡

OpenFeign默认等待1秒钟

ribbon:
  # 建立连接所用时间 5s
  ReadTimeout: 5000
  # 读取资源所用时间 5s
  ConnectTimeout: 5000

OpenFeign日志增强
日志级别

NONE:默认的,不显示任何日志
BASIC:仅记录请求方法、url、响应状态码及执行时间
HEADERS:除BASIC内容外,还有请求和响应的头信息
FULL:除HEADERS内容外,还有请求和响应的正文及元数据

配置日志bean

@Configuration
public class FeignConfig{
	@Bean
	Logger.Level feignLoggerLevel(){
		// feign.Logger
		return Logger.Level.FULL;
	}
}

配置文件

logging:
  level:
    # feign日志要监控的接口和级别
    com.springcloud.service.PaymentFeignService: debug
    

服务降级

Hystrix

  • 服务降级(fallback):
    不让客户端等待,并立刻返回一个友好提示。
    例如:程序运行异常、超时、服务熔断触发服务降级、线程池/信号量打满等
  • 服务熔断(break):
  • 服务限流(flowlimit):
服务降级
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
    @HystrixCommand(fallbackMethod = "timeOut",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
            })
    public String payment(){
        return null;
    }

    public String timeOut(){
        return null;
    }

@fallbackMethod:服务降级调用的方法
@commandProperties:触发降级方法的条件
@HystrixProperty:具体属性,可以从HystrixCommandProperties类中查看源码

启动类添加:@EnableCircuitBreaker 启动

feign:
  hystrix:
    enabled: true
通配服务降级

在FeignClient标明回退的类

@FeignClient (value = "eureka-client-user-service", fallback = UserRemoteClientFallback.class)
public interface UserRemoteClient {
    @GetMapping("/user/hello")
    String hello();
}

回退的类要实现上FeignClient的类

@Component
public class UserRemoteClientFallback implements UserRemoteClient {
    @Override
    public String hello() {
        return "fail";
    }
}
断路器

在@HystrixProperty配置参数
重要的参数有三个

  • 快照式按键窗口:统计时间范围
  • 请求总数阀值:统计时间内的最大请求次数
  • 错误百分比阀值:错误百分比
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),//请求次数超过10,启动断路器
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//统计时间
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")//失败率超过60跳闸

熔断后正确请求也不会正常转发,在一定时间后才会正常。

hystrix图形化监控

创建新的项目

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

启用注解**@EnableHystrixDashboard**

凡是要显示图形化接面都要导入这两个包,包括被监控项目

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

http://localhost:8080/hystrix
在这里插入图片描述

被监控项目主启动类

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

    @Bean
    public ServletRegistrationBean getServlet(){
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
}

在这里插入图片描述
输入被监控的项目地址

http://localhost:8001/hystrix.stream

服务网关

GateWay

三大核心内容

  • Route(路由):构建网关的基本模块。由ID,目标URI,一系列的断言和过滤器组成,如果断言为true,则匹配该路由。
  • Predicate(断言):开发人员可以匹配HTTP请求中的所有内容,如果请求和断言想匹配则进行路由
  • Filter(过滤器):GatewayFilter的实例
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

网关作为微服务也要注册到服务注册中心

网关路由配置:

通过配置文件

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
      routes:
        - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
          #匹配后提供服务的路由地址
          #uri: http://localhost:8001
          # lb代表从注册中心获取服务,也就是动态路由配置
          uri: lb://cloud-payment-service
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由

        - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
          #匹配后提供服务的路由地址
          uri: http://news.baidu.com
          predicates:
          - Path=/mil # 断言,路径相匹配的进行路由

通过代码

@Configuration
public class GatewayConfig {
    /**
     * 配置了一个id为route-name的路由规则,原则上要不相同,但是相同没有报错,暂不知原因
     * 
     * 当访问地址 http://localhost:9527/guonei时会自动转发到地址: http://news.baidu.com/guonei
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("path_route_guonei",
                r -> r.path("/guonei")
                        .uri("http://news.baidu.com/guonei")).build();
        routes.route("path_route_guoji",
                r -> r.path("/guoji")
                        .uri("http://news.baidu.com/guoji")).build();
        return routes.build();
    }
}
Predicate断言

在这里插入图片描述
在配置中的predicates可以有多个

// 默认时区
ZonedDateTime now = ZonedDateTime.now();
filter过滤器

和predicates很相似
单一过滤器
全局过滤器

自定义全局过滤器

@Component
@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");
        if(uname == null) {
          log.info("*****用户名为null,非法用户,o(╥﹏╥)o");
          exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
          return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 加载过滤器的顺序,数字越小,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

配置中心

Config

服务端,从github读取所有配置

@EnableConfigServer

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
spring:
  application:
    name: cloud-config-center
  cloud:
    config:
      server:
        git:
          #Github上的git仓库名字
          uri: https://github.com/Aizen-Sousuke/my-config.git
          #搜索目录.
          search-paths:
            - my-config
      #选择要读取的分支
      label: master

启动项目后通过一下规则可以读取到配置信息

http://localhost:8080/分支/配置文件名

客户端,从服务段读取配置
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

application.yml 用户级资源配置项
bootstrap.yml 系统级,优先级更高

bootstrap.yml

spring:
  application:
    name: config-client
  cloud:
    #Config客户端配置
    config:
      label: master #分支名称
      name: config #配置文件名称
      profile: dev #读取后缀名称 上诉3个综合就是 master分支上 config-dev.yml
      uri: http://localhost:8080

读取config-dev.yml的内容
config-dev.yml

server:
  port: 4399
@RestController
public class ConfigClientController {
    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/configInfo")
    public String getConfigInfo() {
        return "serverPort: " + serverPort;
    }
}
config客户端手动动态刷新

客户端依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
#暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: "*"
@RestController
@RefreshScope
public class ConfigClientController {
    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/configInfo")
    public String getConfigInfo() {
        return "serverPort: " + serverPort;
    }
}

这些改完之后,仍然不能实现github上边修改内容,config客户端及时读取新内容
需要访问客户端刷新,每次修改github上内容后,在命令行输入:

curl -X POST “http://localhost:8081/actuator/refresh”
需要每个客户端发送一次

消息总线

Bus配合config实现消息动态刷新全局广播配置

Bus

只支持RabbitMQ和Kafka

config的服务端和客户端都要添加依赖

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

添加mq的配置

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

#rabbitmq相关设置 ,暴露 bus刷新配置的端点
management:
  endpoints:
    web:
      exposure:
        include: 'bus-refresh' #服务端要指定bus-refresh,客户端都还用*

github上配置文件修改后,只需要config服务端刷新

curl -X POST “http://localhost:8080/actuator/bus-refresh”

如果需要定点刷新,只需要指明要刷新的服务名称加端口号

curl -X POST “http://localhost:8080/actuator/bus-refresh/config-client:8081”

消息驱动

Stream

屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型

组成说明
Middleware中间件,目前只支持RabbitMQ和Kafka
Binder应用与消息中间件的连接的封装
@Input输入通道,接收消息进入应用程序
@Output输出通道,发布的消息离开应用程序
@StreamListener监听队列,接收消息
@EnableBinding绑定channel和exchange

使用rabbit

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

在这里插入图片描述

发送端,生产者
server:
  port: 8801

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders: #在此处配置要绑定的rabbitmq的服务信息
       defaultRabbit: #表示定义的名称,用于binding整合
         type: rabbit #消息组件类型
         environment: #设置rabbitmq的相关环境配置
           spring:
             rabbitmq:
               host: localhost
               port: 5672
               username: guest
               password: guest
      bindings: #服务的整合处理
        output: #发送**************
          destination: studyExchange #定义的Exchange名称
          content-type: application/json #设置消息类型,本次为json,本文要设置为“text/plain”
          binder: defaultRabbit #设置要绑定的消息服务的具体设置

eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 #设置心跳的时间间隔(默认是30S)
    lease-expiration-duration-in-seconds: 5 #如果超过5S间隔就注销节点 默认是90s
    instance-id: send-8801.com #在信息列表时显示主机名称
    prefer-ip-address: true #访问的路径变为IP地址

service

public interface IMessageProvider {

    public String send();
}

impl

/**
 *这个调用的是mq的,所以要添加mq的注解@EnableBinding而不是@service
 *这里的内容都是stream的
 */
@EnableBinding(Source.class) //定义消息的推送管道
public class MessageProviderImpl implements IMessageProvider {

    @Resource
    private MessageChannel output; // 消息发送管道

    @Override
    public String send() {
        String serial = UUID.randomUUID().toString();//发送的内容
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("*****serial: "  +serial);
        return null;
    }
}

controller

@RestController
public class SendMessageController {

    @Resource
    private IMessageProvider messageProvider;

    @GetMapping("/sendMessage")
    public String sendMessage() {
        return messageProvider.send();
    }
}
接收端,消费者
server:
  port: 8802

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders: #在此处配置要绑定的rabbitmq的服务信息
        defaultRabbit: #表示定义的名称,用于binding整合
          type: rabbit #消息组件类型
          environment: #设置rabbitmq的相关环境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: #服务的整合处理
        input: #接收**************
          destination: studyExchange #表示要使用的Exchange名称定义
          content-type: application/json #设置消息类型,本次为json,本文要设置为“text/plain”
          binder: defaultRabbit #设置要绑定的消息服务的具体设置

eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 #设置心跳的时间间隔(默认是30S)
    lease-expiration-duration-in-seconds: 5 #如果超过5S间隔就注销节点 默认是90s
    instance-id: receive-8802.com #在信息列表时显示主机名称
    prefer-ip-address: true #访问的路径变为IP地址

controller

/**
 *这个controller是消息处理的controller,并不是controller层
 */
 
@Component
@EnableBinding(Sink.class)
public class ReceiverMessageListenerController {

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

    @StreamListener(Sink.INPUT)
    public void input(Message<String> message) {
        System.out.println("消费者1号, -----> 接受到的消息: " + message.getPayload()
        + "\t port: " + serverPort);
    }
}
重复消费问题

如果两个消费者同时订阅一个相同的Exchange,会出现重复消费问题。两个消费者会都会接收到信息。
通过分组和持久化属性group解决

分组

stream中同一组内是竞争关系,只有一个可以消费。不同组是可以重复消费的。

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders: #在此处配置要绑定的rabbitmq的服务信息
        defaultRabbit: #表示定义的名称,用于binding整合
          type: rabbit #消息组件类型
          environment: #设置rabbitmq的相关环境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: #服务的整合处理
        input: #这个名字是一个通道的名称
          destination: studyExchange #表示要使用的Exchange名称定义
          content-type: application/json #设置消息类型,本次为json,本文要设置为“text/plain”
          binder: defaultRabbit #设置要绑定的消息服务的具体设置
          group: fenzu1 #这个配置就是分组,自动分组且支持持久化。
          						#如果设置了这个属性,另一个客户端不设置,那么另一个客户端不会受到消息。

分布式请求链路跟踪

Sleuth

Sleuth为服务之间调用提供链路追踪。通过Sleuth可以很清楚的了解到一个服务请求经过了哪些服务,每个服务处理花费了多长。
将信息发送到zipkin,利用zipkin的存储来存储信息,利用zipkin ui来展示数据
zipkin jar包下载 zipkin-server-2.12.9-exec.jar

使用 java -jar运行

访问

http://localhost:9411/zipkin/

在这里插入图片描述
每个服务都导入依赖

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

application

spring:
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      #采样取值介于 0到1之间,1则表示全部收集
      probability: 1

项目启动后,所有的调用就可以通过上边的地址,查看到图形化接面。

spring cloud alibaba

  • 服务限流降级
  • 服务注册与发现
  • 分布式配置管理
  • 消息驱动能力
  • 阿里云对象存储
  • 分布式任务调度

Nacos

注册中心和配置中心的组合
等价于 Eureka+Config+Bus

linux 安装
  1. 下载
  2. 解压
  3. 执行conf中的sql
  4. 运行bin下的startup.sh
服务注册
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
         <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>           
server:
  port: 9001

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.206.128:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

@EnableDiscoveryClient
调用其他服务

server:
  port: 83

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

#消费者将去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider

@RestController
@Slf4j
public class OrderNacosController {

    @Resource
    private RestTemplate restTemplate;

    @Value("${service-url.nacos-user-service}")
    private String serverURL;

    @GetMapping(value = "/consumer/payment/nacos/{id}")
    public String paymentInfo(@PathVariable("id") Integer id) {
        return restTemplate.getForObject(serverURL + "/payment/nacos/" + id,String.class);
    }

}
配置中心

在这里插入图片描述

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!-- SpringCloud ailibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

和spring-cloud-config一样,要先从配置中心拉取
也是两个配置:bootstrap(放配置中心的) 和 application(放自己的)

bootstrap

server:
  port: 3377

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
      config:
        server-addr: localhost:8848 #Nacos作为配置中心地址
        file-extension: yaml #指定yaml格式配置

application

spring:
  profiles:
    active: dev
  #active: dev 表示开发环境
  #active: test表示测试环境
  #active: prod 表示生产环境

在这里插入图片描述
新建配置的 Data ID 有固定的格式:

${spring.application.name}-${spring.profile.active}.${file-extension}
应用名-环境.文件格式
nacos-config-client-dev.yaml 

项目运行后,就可以读到nacos上的nacos-config-client-dev.yaml配置文件了

在这里插入图片描述

通过Data ID读取配置

上边的就是通过Data ID读取配置文件,没有设置group。读取的都是默认分组DEFAULT_GROUP的文件

通过 Group 读取配置

Data ID 设置成相同的。即spring.profile.active的值一致。
只需要设置分组就可以去不同分组读取配置

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
      config:
        server-addr: localhost:8848 #Nacos作为配置中心地址
        file-extension: yaml #指定yaml格式配置
        group: TEST_GROUP #这个属性和nacos创建配置时设置的group一致,就可以读到想要读取的配置文件了
通过命名空间读取

在这里插入图片描述
在这里插入图片描述
新建命名空间后,会生成对应的命名空间id

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
      config:
        server-addr: localhost:8848 #Nacos作为配置中心地址
        file-extension: yaml #指定yaml格式配置
        group: TEST_GROUP
        namespace: 18a80a02-398c-4bbf-bc58-2b46a9a85bb1 #会从对应的命名空间去找

@Value属性动态刷新

@SpringBootApplication
@RestController
@RefreshScope//配置中心更改配置后,初始化可以查到新内容,但是调用user()还是旧值,这个配置可以使@Value动态刷新。可以获得新值
public class NacosConfigSampleApplication {

    @Value("${user.name}")
    private String userName;

    @Value("${user.age}")
    private int userAge;

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }

    @RequestMapping("/user")
    public String user() {
        return String.format("[HTTP] user name : %s , age : %d", userName, userAge);
    }

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

}
数据持久化

在nacos/conf中的application.properties中添加

spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456

数据库脚本

集群化

Sentinel熔断与限流

java -jar sentinel-dashboard-1.7.1.jar

在这里插入图片描述

        <!-- SpringCloud ailibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!-- SpringCloud ailibaba sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinal-service
  cloud:
    nacos:
      discovery:
        #Nacos服务注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentin dashboard地址
        dashboard: localhost:8080
        # 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
        port: 8719

management:
  endpoints:
    web:
      exposure:
        include: '*'

feign:
  sentinel:
    enabled: true #激活Sentinel 对Feign的支持

@RestController
public class FlowLimitController {
    @GetMapping("/test")
    public String testA(){
        return "----------- testA";
    }
}
//sentinel是懒加载,访问一次http://localhost:8401/test

在这里插入图片描述

流量控制

在这里插入图片描述

  • QPS:达到每秒请求最大值,限流
  • 线程控制:超过最大线程数,限流
关联

在这里插入图片描述
当 /hahaha超过限制,/test就会被限流

WarmUp

在这里插入图片描述
预热。刚开始阀值是10/3,经过5秒后,阀值升到10。

熔断降级

在这里插入图片描述

热点规则(热点key限流)
	/**
	 *blockHandler:降级调用的方法
	 *value = "testHotKey":资源名称。可以根据地址或者资源名称设置限流
	 */
    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
    public String testHotKey(@RequestParam(value = "p1",required = false)String p1,
                             @RequestParam(value = "p2",required = false)String p2) {
        return "----testHotKey";
    }

    public String deal_testHotKey(String p1, String p2, BlockException exception) {
        return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting)
    }

在这里插入图片描述
针对参数限流,访问地址传入参数超过每秒访问请求数会被降级。
参数索引: 指的是testHotKey方法的参数位置,并不是发送参数的顺序
在这里插入图片描述
参数例外项: 针对上边限流的参数进行额外处理。当限流参数索引的参数的值是5,阀值是10。

自定义限流处理,SentinelResource配置
@RestController
public class RateLimitController {
	/**
	 *blockHandler:降级回调方法
	 *
	 */
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource() {
        return  new CommonResult(200,"按照资源名称限流测试",new Payment(2020L,"serial001"));
    }

    public CommonResult handleException(BlockException exception) {
        return  new CommonResult(444,exception.getClass().getCanonicalName() + "\t 服务不可用");
    }

    /**
	 *blockHandlerClass:指定降级回调方法所在的类
	 */
    @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class,blockHandler = "handlerException2")
    public CommonResult customerBlockHandler() {
        return  new CommonResult(200,"按照客户自定义限流测试",new Payment(2020L,"serial003"));
    }
}
public class CustomerBlockHandler {

    public static CommonResult handlerException(BlockException exception) {
        return  new CommonResult(444,"按照客户自定义的Glogal 全局异常处理 ---- 1",new Payment(2020L,"serial003"));
    }

    public static CommonResult handlerException2(BlockException exception) {
        return  new CommonResult(444,"按照客户自定义的Glogal 全局异常处理 ---- 2",new Payment(2020L,"serial003"));
    }
}
Sentinel持久化

每次项目重启,sentinel规则就没有了。所以要持久化

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
spring:
  application:
    name: cloudalibaba-sentinal-service
  cloud:
    nacos:
      discovery:
        #Nacos服务注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentin dashboard地址
        dashboard: localhost:8080
        # 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
        port: 8719
      # 持久化配置  
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848  #Nacos服务注册中心地址
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

在nacos配置管理的配置列表中新建配置
在这里插入图片描述

resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式:0直接,1关联,2链路
controlBehavior:流控效果;0快速失败,1warm up,2排队等待
clusterMode:是否集群

项目重启
在这里插入图片描述

Seata 分布式事务处理

是全局唯一ID+三组件模型

XID(Transaction ID)-全局唯一的事务ID

TC (Transaction Coordinator) - 事务协调者

  • 维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM (Transaction Manager) - 事务管理器

  • 定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM (Resource Manager) - 资源管理器

  • 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

流程:

  • TM想TC申请开启一个全局事务,全局事务创建成功生成一个全局唯一XID
  • XID在微服务调用链路上下文中传播
  • RM向TC注册分支事务,将其纳入XID对应全局事务的管辖
  • TM向TC发起针对XID的全局提交或回滚决议
  • TC调度XID下管辖的全部分支事务完成提交或回滚

下载

安装

在这里插入图片描述
修改registry.conf
在这里插入图片描述
修改file.conf
在这里插入图片描述
在这里插入图片描述
双击seata-server.bat运行

数据库准备

创建不同的数据库。然后再不同的数据库下创建对应的表。
例如 订单、库存、账户。
创建三个不同的数据库,并在这三个数据库下创建对应的表
然后每个数据库都要创建回滚日志表,seata目录下conf中的db_undo_log.sql就是回滚日志表
这时候一共有四个数据库:

  • seata库。(db_store.sql创建的 )。有三张表,branch_table,global_table,lock_table
  • 订单库,订单表和undo_log
  • 库存库,库存表和undo_log
  • 账号库,账号表和undo_log
通用项目配置

将file.conf和registry.conf两个配置复制到项目配置文件中

	<!--因为下载1.0.0没下载下来,下的是0.9.0,-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-all</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>0.9.0</version>
        </dependency>
server:
  port: 2001

spring:
  application:
    name: seata-order-service
  cloud:
    alibaba:
      seata:
        # 自定义事务组名称,刚才file.conf中设置过
        tx-service-group: tx_group
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  datasource:
    # 当前数据源操作类型,阿里的
    type: com.alibaba.druid.pool.DruidDataSource
    # mysql驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: root
feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info

mybatis:
  mapperLocations: classpath*:mapper/*.xml
//取消数据源依赖,改为seata的
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
@MapperScan({"com.springcloud.alibaba.dao"})//你的mapper接口路径
public class SeataOrderMain2001 {

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

seata数据源

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
/**
 *
 *seata数据源
 */
@Configuration
public class DataSourceProxyConfig {

    @Value("${mybatis.mapperLocations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSourceProxy);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        bean.setMapperLocations(resolver.getResources(mapperLocations));
        return bean.getObject();
    }
}

每个项目都进行以上配置。
在调用其他服务的方法上添加注解

业务模块中使用openfeign调用其他服务
然后再业务模块启用分布式事务

@GlobalTransactional(name = “create-order”,rollbackFor = Exception.class)
name是唯一的就可以。

例如创建订单

    @Override
    @GlobalTransactional(name = "create-order",rollbackFor = Exception.class)
    public void create(Order order)
    {
        log.info("----->开始新建订单");
        //1 新建订单

        //2 扣减库存
        log.info("----->订单微服务开始调用库存,做扣减Count");

        log.info("----->订单微服务开始调用库存,做扣减end");

        //3 扣减账户
        log.info("----->订单微服务开始调用账户,做扣减Money");

        log.info("----->订单微服务开始调用账户,做扣减end");

        //4 修改订单状态,从零到1,1代表已经完成
        log.info("----->修改订单状态开始");

        log.info("----->修改订单状态结束");

        log.info("----->下订单结束了,O(∩_∩)O哈哈~");

    }

seata服务器就是TC, order服务就是TM, 参与下订单的account服务和 storage服务就是RM

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值