• SpringCloud简记_part2


    Zookeeper服务注册与发现

    1)Eureka停止更新了,你怎么办?

    https://github.com/Netflix/eureka/wiki

    2)SpringCloud整合Zookeeper替代Eureka

    1. 注册中心Zookeeper

    Zookeeper是一个分布式协调工具,可以实现注册中心功能
    关闭Linux服务器防火墙后启动Zookeeper服务器

    1. linux
    systemctl stop firewalld
    systemctl status firewalld
     
    2.windows
    
    
    

    Zookeeper服务器取代Eureka服务器,zk作为服务注册中心

    2. 服务提供者

    新建cloud-provider-payment8004
    POM
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-provider-payment8004</artifactId>
        <description>Zookeeper服务提供者</description>
    
        <dependencies>
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-common</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--SpringBoot整合Zookeeper客户端-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    </project>
     
    
    
    
    YML
    server:
      # 8004表示注册到zookeeper服务器的支付服务提供者端口号
      port: 8004
    spring:
      application:
        # 服务别名---注册zookeeper到注册中心的名称
        name: cloud-provider-payment
      cloud:
        zookeeper:
          # 默认localhost:2181
          connect-string: localhost:2181
     
    
    
    
    主启动类
    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class PaymentMain8004 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8004.class, args);
        }
    }
    
    
    

    @EnableDiscoveryClient

    Controller
    package com.atguigu.springcloud.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.UUID;
    
    @RestController
    @RequestMapping("/payment")
    public class PaymentController {
    
        @Value("${server.port}")
        private String SERVER_PORT;
    
        @RequestMapping("/zk")
        public String paymentZK() {
            return "com.com.springcloud with zookeeper :" + SERVER_PORT + "	" + UUID.randomUUID().toString();
        }
    }
    
    
    
    启动8004注册进zookeeper
    1. 启动zk
      zkServer.sh start

    2. 启动后问题
      image-20200408214540366

    3. why
      解决zookeeper版本jar包冲突问题

      image-20200408214600978

      排除zk冲突后的新POM

       <!--SpringBoot整合Zookeeper客户端-->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
                  <exclusions>
                      <!--先排除自带的zookeeper3.5.3-->
                      <exclusion>
                          <groupId>org.apache.zookeeper</groupId>
                          <artifactId>zookeeper</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
              <!--添加zookeeper3.4.9版本-->
              <dependency>
                  <groupId>org.apache.zookeeper</groupId>
                  <artifactId>zookeeper</artifactId>
                  <version>3.4.9</version>
              </dependency>
      
      
      
    验证测试

    image-20200408214647041

    http://localhost:8004/payment/zk
    image-20200408214731671

    验证测试2

    获得json串后用在线工具查看试试

    [zk: localhost:2181(CONNECTED) 13] get /services/cloud-provider-payment/ba3eface-c269-41b9-b186-5f05b6eda6fc 
    {"name":"cloud-provider-payment","id":"ba3eface-c269-41b9-b186-5f05b6eda6fc","address":"localhost","port":8004,"sslPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"application-1","name":"cloud-provider-payment","metadata":{}},"registrationTimeUTC":1586352884172,"serviceType":"DYNAMIC","uriSpec":{"parts":[{"value":"scheme","variable":true},{"value":"://","variable":false},{"value":"address","variable":true},{"value":":","variable":false},{"value":"port","variable":true}]}}
    cZxid = 0x7600000007
    ctime = Wed Apr 08 21:34:52 CST 2020
    mZxid = 0x7600000007
    mtime = Wed Apr 08 21:34:52 CST 2020
    pZxid = 0x7600000007
    cversion = 0
    dataVersion = 0
    aclVersion = 0
    ephemeralOwner = 0x17159cb25ed0001
    dataLength = 530
    numChildren = 0
    [zk: localhost:2181(CONNECTED) 14] 
    
    
    {
    	"name": "cloud-provider-payment",
    	"id": "ba3eface-c269-41b9-b186-5f05b6eda6fc",
    	"address": "localhost",
    	"port": 8004,
    	"sslPort": null,
    	"payload": {
    		"@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance",
    		"id": "application-1",
    		"name": "cloud-provider-payment",
    		"metadata": {}
    	},
    	"registrationTimeUTC": 1586352884172,
    	"serviceType": "DYNAMIC",
    	"uriSpec": {
    		"parts": [{
    			"value": "scheme",
    			"variable": true
    		}, {
    			"value": "://",
    			"variable": false
    		}, {
    			"value": "address",
    			"variable": true
    		}, {
    			"value": ":",
    			"variable": false
    		}, {
    			"value": "port",
    			"variable": true
    		}]
    	}
    }
    
    
    思考

    服务节点是临时节点还是持久节点?

    是临时节点,查看可以发现在服务重启后,结点信息不断发生着变化:

    image-20200408215306745

    3. 服务消费者

    新建cloud-consumerzk-order80
    POM
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloude2020_lecture</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-consumerzk-order80</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--SpringBoot整合Zookeeper客户端-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
                <exclusions>
                    <!--先排除自带的zookeeper3.5.3-->
                    <exclusion>
                        <groupId>org.apache.zookeeper</groupId>
                        <artifactId>zookeeper</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!--添加zookeeper3.4.6版本-->
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.4.6</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </project>
    
    
    YML
    server:
      port: 80
    spring:
      application:
        name: cloud-provider-order
      cloud:
        zookeeper:
          connect-string: 192.168.137.11:2181
    
    
    
    主启动
    package com.bigdata.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class OrderZKMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderZKMain80.class, args);
        }
    }
    
    
    
    业务类

    ​ 配置bean

    package com.bigdata.springcloud.config;
    
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.RestTemplate;
    
    
    @Configuration
    public class ApplicationContextConfig {
    
        @Bean
        @LoadBalanced//开启负载均衡
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    
    
    

    ​ controller

    package com.bigdata.springcloud.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    @Slf4j
    @RequestMapping("/consumer")
    public class OrderZKController {
    
        private static final String INVOKE_URL = "http://cloud-provider-payment";
    
        @Autowired
        private RestTemplate restTemplate;
    
        @RequestMapping("/payment/zk")
        public String get() {
            String result = restTemplate.getForObject(INVOKE_URL + "/payment/zk", String.class);
            return result;
        }
    
    
    }
    
    
    
    验证测试

    image-20200408223142373

    访问测试地址

    http://localhost/consumer/payment/zk

    image-20200408223214295

    Consul服务注册与发现

    1)Consul简介

    是什么

    https://www.consul.io/intro/index.html

    能干嘛

    • 服务发现:提供HTTP/DNS两种发现方式

    • 健康检测:支持多种方式,HTTP、TCP、Docker、shell脚本定制化

    • KV存储:Key、Value的存储方式

    • 多数据中心: Consul支持多数据中心

    • 可视化界面

    下哪下

    https://www.consul.io/downloads.html

    怎么玩

    https://www.springcloud.cc/spring-cloud-consul.html

    2)安装并运行Consul

    官网说明

    https://learn.hashicorp.com/consul/getting-started/install.html

    下载完成后只有一个consul.exe文件 硬盘路径下双击运行,查看版本信息

    下面是在Linux上安装(废弃)

    [root@Linux5 software]# wget https://releases.hashicorp.com/consul/1.7.2/consul_1.7.2_linux_amd64.zip
    --2020-04-08 21:19:43--  https://releases.hashicorp.com/consul/1.7.2/consul_1.7.2_linux_amd64.zip
    Resolving releases.hashicorp.com (releases.hashicorp.com)... 151.101.1.183, 151.101.65.183, 151.101.129.183, ...
    Connecting to releases.hashicorp.com (releases.hashicorp.com)|151.101.1.183|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 39650427 (38M) [application/zip]
    Saving to: ‘consul_1.7.2_linux_amd64.zip’
    
    100%[==========================================================================================>] 39,650,427   169KB/s   in 4m 32s 
    
    2020-04-08 21:24:16 (142 KB/s) - ‘consul_1.7.2_linux_amd64.zip’ saved [39650427/39650427]
    
    [root@Linux5 software]# ls
    consul_1.7.2_linux_amd64.zip 
    [root@Linux5 software]# unzip -l consul_1.7.2_linux_amd64.zip 
    Archive:  consul_1.7.2_linux_amd64.zip
      Length      Date    Time    Name
    ---------  ---------- -----   ----
    107811060  03-16-2020 16:06   consul
    ---------                     -------
    107811060                     1 file
    
    [root@Linux5 software]# unzip consul_1.7.2_linux_amd64.zip 
    Archive:  consul_1.7.2_linux_amd64.zip
      inflating: consul                  
    [root@Linux5 software]# ll
    total 145956
    -rwxr-xr-x. 1 root root 107811060 Mar 16 16:06 consul
    -rw-r--r--. 1 root root  39650427 Mar 16 16:44 consul_1.7.2_linux_amd64.zip
    
    [root@Linux5 software]# mv consul /opt/module/
    [root@Linux5 software]# cd /opt/module/
    [root@Linux5 module]# ll
    total 105288
    -rwxr-xr-x. 1 root    root    107811060 Mar 16 16:06 consul
    [root@Linux5 module]# ./consul --version
    Consul v1.7.2
    Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)
    [root@Linux5 module]# 
    
    
    

    使用开发模式启动

    1. ​ consul agent -dev
      下面是在windows上启动时:

      D:Program_Filesconsul>consul agent -dev
      ==> Starting Consul agent...
                 Version: 'v1.7.2'
                 Node ID: '3eb33ddb-f5da-aca7-4d22-304a088f9399'
               Node name: 'git'
              Datacenter: 'dc1' (Segment: '<all>')
                  Server: true (Bootstrap: false)
             Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
            Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
                 Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false
      
      ==> Log data will now stream in as it occurs:
      
          2020-04-09T11:25:07.697+0800 [DEBUG] agent: Using random ID as node ID: id=3eb33ddb-f5da-aca7-4d22-304a088f9399
          2020-04-09T11:25:07.865+0800 [DEBUG] agent.tlsutil: Update: version=1
          2020-04-09T11:25:07.868+0800 [DEBUG] agent.tlsutil: OutgoingRPCWrapper: version=1
          2020-04-09T11:25:07.870+0800 [INFO]  agent.server.raft: initial configuration: index=1 servers="[{Suffrage:Voter ID:3eb33ddb-f5da-aca7-4d22-304a088f9399 Address:127.0.0.1:8300}]"
          2020-04-09T11:25:07.872+0800 [INFO]  agent.server.raft: entering follower state: follower="Node at 127.0.0.1:8300 [Follower]" leader=
          2020-04-09T11:25:07.873+0800 [INFO]  agent.server.serf.wan: serf: EventMemberJoin: git.dc1 127.0.0.1
          2020-04-09T11:25:07.874+0800 [INFO]  agent.server.serf.lan: serf: EventMemberJoin: git 127.0.0.1
          2020-04-09T11:25:07.875+0800 [INFO]  agent.server: Adding LAN server: server="git (Addr: tcp/127.0.0.1:8300) (DC: dc1)"
          2020-04-09T11:25:07.875+0800 [INFO]  agent.server: Handled event for server in area: event=member-join server=git.dc1 area=wan
          2020-04-09T11:25:07.875+0800 [INFO]  agent: Started DNS server: address=127.0.0.1:8600 network=udp
          2020-04-09T11:25:07.876+0800 [INFO]  agent: Started DNS server: address=127.0.0.1:8600 network=tcp
          2020-04-09T11:25:07.878+0800 [INFO]  agent: Started HTTP server: address=127.0.0.1:8500 network=tcp
          2020-04-09T11:25:07.880+0800 [INFO]  agent: Started gRPC server: address=127.0.0.1:8502 network=tcp
          2020-04-09T11:25:07.880+0800 [INFO]  agent: started state syncer
      ==> Consul agent running!
          2020-04-09T11:25:07.915+0800 [WARN]  agent.server.raft: heartbeat timeout reached, starting election: last-leader=
          2020-04-09T11:25:07.915+0800 [INFO]  agent.server.raft: entering candidate state: node="Node at 127.0.0.1:8300 [Candidate]" term=2
          2020-04-09T11:25:07.916+0800 [DEBUG] agent.server.raft: votes: needed=1
          2020-04-09T11:25:07.916+0800 [DEBUG] agent.server.raft: vote granted: from=3eb33ddb-f5da-aca7-4d22-304a088f9399 term=2 tally=1
          2020-04-09T11:25:07.917+0800 [INFO]  agent.server.raft: election won: tally=1
          2020-04-09T11:25:07.917+0800 [INFO]  agent.server.raft: entering leader state: leader="Node at 127.0.0.1:8300 [Leader]"
          2020-04-09T11:25:07.918+0800 [INFO]  agent.server: cluster leadership acquired
          2020-04-09T11:25:07.919+0800 [INFO]  agent.server: New leader elected: payload=git
      Processing server acl mode for: git - 0
          2020-04-09T11:25:07.920+0800 [INFO]  agent.server: Cannot upgrade to new ACLs: leaderMode=0 mode=0 found=true leader=127.0.0.1:8300
          2020-04-09T11:25:07.921+0800 [DEBUG] connect.ca.consul: consul CA provider configured: id=07:80:c8:de:f6:41:86:29:8f:9c:b8:17:d6:48:c2:d5:c5:5c:7f:0c:03:f7:cf:97:5a:a7:c1:68:aa:23:ae:81 is_primary=true
      
      

      下面是在Linux上启动时,绑定启动的IP地址(废弃)

      [root@Linux5 module]# ./consul agent -dev   -client 192.168.137.15 -ui        
      ==> Starting Consul agent...
                 Version: 'v1.7.2'
                 Node ID: '1f756f7d-d476-4fae-1f22-1cd56c714fe1'
               Node name: 'Linux5'
              Datacenter: 'dc1' (Segment: '<all>')
                  Server: true (Bootstrap: false)
             Client Addr: [192.168.137.15] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
            Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
                 Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false
      
      ==> Log data will now stream in as it occurs:
      
      
      
    2. ​ 通过以下地址可以访问Consul的首页: http://192.168.137.15:8500(废弃)

      image-20200409095533707

      http://localhost:8500/ui/dc1/services

      image-20200409112621036

    3. 结果页面

    3)服务提供者

    新建Module支付服务provider8006

    cloud-providerconsul-payment8006

    POM

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloude2020_lecture</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-providerconsul-payment8006</artifactId>
    
        <dependencies>
            <!--SpringCloud consul-server-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <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.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </project>
    
    

    YML

    linux上的配置(废弃)

    server:
      port: 8006
    spring:
      application:
        name: consul-provider-payment
      cloud:
        consul:
          host: 192.168.137.15
          port: 8500
          discovery:
            prefer-ip-address: true
            tags: version=1.0
            instance-id: ${spring.application.name}:${server.port}
            healthCheckInterval: 15s
            health-check-url: http://${spring.cloud.client.ip-address}:${server.port}/actuator/health
        inetutils:
          preferred-networks:
            - 192.168.137.1
    
    
    

    window上的配置:

    server:
      port: 8006
    spring:
      application:
        name: consul-provider-payment
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            service-name: ${spring.application.name}
    
    
    

    主启动类

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class PaymentMain8006 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8006.class, args);
        }
    }
    
    
    

    业务类Controller

    package com.atguigu.springcloud.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.UUID;
    
    @RestController
    @RequestMapping("/payment")
    public class PaymentController {
    
        @Value("${server.port}")
        private String SERVER_PORT;
    
        @RequestMapping("/consul")
        public String paymentZK() {
            return "com.com.springcloud with consul :" + SERVER_PORT + "	" + UUID.randomUUID().toString();
        }
    }
    
    
    

    验证测试

    http://localhost:8006/payment/consul

    image-20200409103328338

    http://192.168.137.15:8500/ui/dc1/services(废弃)

    image-20200409103406426

    http://localhost:8500/ui/dc1/services

    image-20200409113014516

    4)服务消费者

    新建Module消费服务order80

    ​ cloud-consumerconsul-order80

    POM

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloude2020_lecture</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-consumerconsul-order80</artifactId>
        <dependencies>
            <!--SpringCloud consul-server-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.xzq.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <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.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    </project>
    
    

    YML

    Linux配置(废弃)

    server:
      port: 80
    spring:
      application:
        name: consul-consumer-payment
      cloud:
        consul:
          host: 192.168.137.15
          port: 8500
          discovery:
            service-name: ${spring.application.name}
    
    
    

    windows配置

    server:
      port: 80
    spring:
      application:
        name: consul-consumer-payment
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            service-name: ${spring.application.name}
    
    
    

    主启动类

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class OrderConsulMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderConsulMain80.class, args);
        }
    }
    
    
    

    配置bean

    package com.atguigu.springcloud.config;
    
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.RestTemplate;
    
    
    @Configuration
    public class ApplicationContextConfig {
    
        @Bean
        @LoadBalanced//开启负载均衡
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    
    
    

    Controller

    package com.atguigu.springcloud.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    
    @RestController
    @RequestMapping("/consumer")
    public class OrderConsulController {
    
        private static final String INVOKE_URL = "http://consul-provider-payment";
    
        @Autowired
        private RestTemplate restTemplate;
    
        @RequestMapping("/payment/consul")
        public String get() {
            String result = restTemplate.getForObject(INVOKE_URL + "/payment/consul", String.class);
            return result;
        }
    
    }
    
    

    验证测试

    访问测试地址

    http://localhost/consumer/payment/consul

    image-20200409113326137

    http://localhost:8500/ui/dc1/services

    image-20200409113340580

    5)三个注册中心异同点

    CAP

    分区容错性要保证,所以要么是CP,要么是AP

    • C: Consistency(强一致性)
    • A: Availability(可用性)
    • P: Parttition tolerance(分区容错性)
    • CAP理论关注粒度是否是数据,而不是整体系统设计的策略

    经典CAP图

    image-20200409113709646

    ​ AP(eureka)

    image-20200409113725519

    ​ CP(Zookeeper/Consul)

    CP架构 当网络分区出现后,为了保证一致性,就必须拒绝请求,否则无法保证一致性
    结论:违背了可用性A的要求,只满足一致性和分区容错,即CP

    image-20200409113817719

    Ribbon负载均衡调用

    1)概述

    是什么

    官网资料

    https://github.com/Netflix/ribbon/wiki/Getting-Started
    Ribbon目前也进入维护模式

    image-20200413084547229

    ​ 未来替换方案

    image-20200413084556673

    能干嘛

    LB(负载均衡)
    image-20200413084619403

    • 集中式LB
      image-20200413084627526

    • 进程内LB
      image-20200413084637320

      前面我们讲解过了80通过轮询负载访问8001/8002
      一句话: 负载均衡+RestTemplate调用

    2)Ribbon负载均衡演示

    架构说明

    image-20200413084729184

    总结: Ribbon其实就是一个软负载均衡的客户端组件, 他可以和其他所需请求的客户端结合使用,和eureka结合只是其中一个实例.
    POM

    image-20200413084747636

    image-20200413084752592

    RestTemplate的使用

    • 官网
      image-20200413084906776

    • getForObject方法/getForEntity方法
      image-20200413084920233

      image-20200413084942906

    • postForObject/postEntity

    • GET请求方法

    • POST请求方法

    3)Ribbon核心组件IRule

    IRule:根据特定算法从服务列表中选取一个要访问的服务

    • com.netflix.loadbalancer.RoundRobinRule:轮询

    • com.netflix.loadbalancer.RandomRule: 随机

    • com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务

    • WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越多大,越容易被选择

    • BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

    • AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例

    • ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器

    如何替换

    修改cloud-consumer-order80
    注意配置细节

    image-20200413085224227

    image-20200413085255788

    image-20200413085318868

    新建package
    com.atguigu.myrule
    上面包下新建MySelfRule规则类

    package com.atguigu.myrule;
    
    import com.netflix.loadbalancer.IRule;
    import com.netflix.loadbalancer.RoundRobinRule;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * 自定义负载均衡路由规则类
     *
     * @author zzyy
     * @date 2020/3/6 15:15
     **/
    @Configuration
    public class MySelfRule {
    
        @Bean
        public IRule myRule() {
            // 定义为随机
            return new RoundRobinRule();
        }
    }
     
     
    
    
    

    主启动类添加@RibbonClient

    package com.atguigu.springcloud;
    
    import com.atguigu.myrule.MySelfRule;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.ribbon.RibbonClient;
    
    /**
     * @author zzyy
     * @date 2020/02/18 17:20
     **/
    @SpringBootApplication
    @EnableEurekaClient
    @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
    public class OrderMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderMain80.class, args);
        }
    }
     
     
    
    
    

    测试
    http://localhost/consumer/payment/get/31

    4)Ribbon负载均衡算法

    原理

    image-20200413085536162

    源码

    public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                log.warn("no load balancer");
                return null;
            }
    
            Server server = null;
            int count = 0;
            while (server == null && count++ < 10) {
                List<Server> reachableServers = lb.getReachableServers();
                //获取服务清单
                List<Server> allServers = lb.getAllServers();
                int upCount = reachableServers.size();
                int serverCount = allServers.size();
                 //如果服务清单没有任何的服务,则返回null
                if ((upCount == 0) || (serverCount == 0)) {
                    log.warn("No up servers available from load balancer: " + lb);
                    return null;
                }
                //选取服务
                int nextServerIndex = incrementAndGetModulo(serverCount);
                //根据选取的服务,找到对应的Server
                server = allServers.get(nextServerIndex);
                //如果Server不可用,则跳出循环再次获取
                if (server == null) {
                    /* Transient. */
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive() && (server.isReadyToServe())) {
                    return (server);
                }
    
                // Next.
                server = null;
            }
    
            if (count >= 10) {
                log.warn("No available alive servers after 10 tries from load balancer: "
                        + lb);
            }
            return server;
    }
    
    

    在“incrementAndGetModulo”中会根据服务清单中的服务数量,先获取“nextServerCyclicCounter”值,然后将current+1后和服务总数modulo取模,然后将它设置到“nextServerCyclicCounter”中。每次请求都是这样,能够看到nextServerCyclicCounter中的值,总是在“0 ~ modulo-1”之间变动。前面在原理描述部分,使用rest第几次请求模上集群中服务总数量的表述并不准确,实际上和第几次请求并没有关系,例如:现在服务总数量为5,则第1次请求中“nextServerCyclicCounter”保存的值为1,第2次为2,第3次为3,第4次请求为4,第5次请求为0,第6次请求为1,第7次请求为2,似乎是请求数模上modulo的结果,然而并非如此。不过这样简单理解也是可以行得通的。

     private int incrementAndGetModulo(int modulo) {
            for (;;) {
                int current = nextServerCyclicCounter.get();
                int next = (current + 1) % modulo;
                if (nextServerCyclicCounter.compareAndSet(current, next))
                    return next;
            }
     }
    
    

    手写

    自己试着写一个本地负载均衡器试试
    7001/7002集群启动
    8001/8002集群启动
    在8001和8002的controller中,添加如下语句

    @GetMapping(value = "/payment/lb")
    public String getPaymentLB() {
        return serverPort;
    }
    
    
    

    ​ 80订单微服务改造
    ​ 1.ApplicationContextBean去掉注解@LoadBalanced

        @Bean
    //    @LoadBalanced//开启负载均衡
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    
    

    ​ 2.LoadBalancer接口

    public interface LoadBalancer {
        ServiceInstance instances(List<ServiceInstance> serviceInstances);
    }
    
    

    ​ 3.MyLB

    @Component
    public class MyLB implements LoadBalancer {
    
        private AtomicInteger atomicInteger = new AtomicInteger(0);
    
        private final int getAndIncrement() {
            int current;
            int next;
    
            do {
                current = this.atomicInteger.get();
                next = current >= Integer.MAX_VALUE ? 0 : current + 1;
            } while (!atomicInteger.compareAndSet(current, next));
            System.out.println("第几次访问,次数next:" + next);
            return next;
        }
    
        @Override
        public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
            int index = getAndIncrement() % serviceInstances.size();
            return serviceInstances.get(index);
        }
    }
    
    

    ​ 4.OrderController

        @GetMapping("/consumer/payment/lb")
        public String getPaymentLB() {
            List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
            if (instances == null || instances.size() <= 0) {
                return null;
            }
    
            ServiceInstance serviceInstance = loadBalancer.instances(instances);
            URI uri = serviceInstance.getUri();
    
            return restTemplate.getForObject(uri + "/payment/lb", String.class);
        }
    
    

    ​ 5.测试

    image-20200413085519622
    http://localhost/consumer/payment/lb

    OpenFeign服务接口调用

    1)概述

    OpenFeign是什么

    image-20200413152845428

    https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-openfeignimage-20200413152914066

    Feign是一个声明式的Web服务客户端,让编写Web服务客户端变得非常容易,只需 创建一个接口并在接口上添加注解即可
    GitHub
    https://github.com/spring-cloud/spring-cloud-openfeign
    能干嘛

    image-20200413152923261Feign和OpenFeign两者区别

    image-20200413153001419

    2)OpenFeign使用步骤

    接口+注解

    微服务调用接口+@FeignClient

    新建cloud-consumer-feign-order80

    Feign在消费端使用

    image-20200413153019573

    POM

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-consumer-feign-order80</artifactId>
        <description>订单消费者之feign</description>
    
        <dependencies>
            <!--openfeign-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <!--eureka client-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-common</artifactId>
                <version>${project.version}</version>
            </dependency>
            <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.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    </project>
     
    
    
    

    YML

    server:
      port: 80
    eureka:
      client:
        register-with-eureka: false
        fetch-registry: true
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
     
    
    
    

    主启动

    @EnableFeignClients

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableFeignClients
    public class OrderFeignMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderFeignMain80.class, args);
        }
    }
    
    
    

    业务类

    业务逻辑接口+@FeignClient配置调用provider服务
    新建PaymentFeignService接口并新增注解@FeignClient
    @FeignClient
    控制层Controller

    测试

    先启动2个eureka集群7001/7002
    再启动2个微服务8001/8002
    启动OpenFeign
    http://localhost/consumer/payment/get/31
    Feign自带负载均衡配置项
    小总结

    image-20200413153133349

    3)OpenFeign超时控制

    超时设置,故意设置超时演示出错情况

    服务提供方8001故意写暂停程序
    服务消费方80添加超时方法PaymentFeignService
    服务消费方80添加超时方法OrderFeignController
    测试
    http://localhost/consumer/payment/feign/timeout
    错误页面

    image-20200413153211078

    OpenFeign默认等待1秒钟,超过后报错

    是什么

    image-20200413153222440

    OpenFeign默认支持Ribbon

    YML文件里需要开启OpenFeign客户端超时控制

    server:
      port: 80
    eureka:
      client:
        register-with-eureka: false
        fetch-registry: true
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
    # 设置feign客户端超时时间(OpenFeign默认支持ribbon)
    ribbon:
      # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
      ReadTimeout: 5000
      # 指的是建立连接后从服务器读取到可用资源所用的时间
      ConnectTimeout: 5000
     
    
    
    

    4)OpenFeign日志打印功能

    日志打印功能

    是什么

    image-20200413153315595

    日志级别

    image-20200413153324533

    配置日志bean

    package com.atguigu.springcloud.config;
    
    import feign.Logger;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class FeignConfig {
    
        /**
         * feignClient配置日志级别
         *
         * @return
         */
        @Bean
        public Logger.Level feignLoggerLevel() {
            // 请求和响应的头信息,请求和响应的正文及元数据
            return Logger.Level.FULL;
        }
    }
     
     
    
    
    

    YML文件里需要开启日志的Feign客户端

    server:
      port: 80
    eureka:
      client:
        register-with-eureka: false
        fetch-registry: true
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
    # 设置feign客户端超时时间(OpenFeign默认支持ribbon)
    ribbon:
      # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
      ReadTimeout: 5000
      # 指的是建立连接后从服务器读取到可用资源所用的时间
      ConnectTimeout: 5000
    logging:
      level:
        # feign日志以什么级别监控哪个接口
        com.atguigu.springcloud.service.PaymentFeignService: debug
     
    
    
    

    后台日志查看

    image-20200413153359202

    Hystrix熔断器

    1)概述

    分布式系统面临的问题

    分布式系统面临的问题
    复杂分布式体系结构中的应用程序 有数10个依赖关系,每个依赖关系在某些时候将不可避免地失败
    image-20200413153648544

    image-20200413153657956

    是什么

    image-20200413153711457

    能干嘛

    服务降级
    服务熔断
    接近实时的监控

    官网资料

    https://github.com/Netflix/hystrix/wiki

    Hystrix官宣,停更进维

    image-20200413153737011

    https://github.com/Netflix/hystrix
    被动修复bugs
    不再接受合并请求
    不再发布新版本

    2)HyStrix重要概念

    服务降级

    服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback
    哪些情况会发出降级
    程序运行异常
    超时
    服务熔断触发服务降级
    线程池/信号量也会导致服务降级

    服务熔断

    类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
    就是保险丝
    服务的降级->进而熔断->恢复调用链路

    服务限流

    秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行

    3)hystrix案例

    构建

    新建cloud-provider-hystrix-payment8001
    POM

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-provider-hystrix-payment8001</artifactId>
    
        <dependencies>
            <!--hystrix-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>
            <!--eureka client-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-common</artifactId>
                <version>${project.version}</version>
            </dependency>
            <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.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    </project>
     
    
    
    

    YML

    server:
      port: 8001
    spring:
      application:
        name: cloud-provider-hystrix-payment
    eureka:
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
     
     
    
    
    

    主启动

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class PaymentHystrixMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentHystrixMain8001.class, args);
        }
    }
     
    
    

    业务类
    service

    package com.atguigu.springcloud.service;
    
    import org.springframework.stereotype.Service;
    
    import java.util.concurrent.TimeUnit;
    
    
    @Service
    public class PaymentService {
        /**
         * 正常访问
         *
         * @param id
         * @return
         */
        public String paymentInfo_OK(Integer id) {
            return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" + id + "	" + "O(∩_∩)O哈哈~";
        }
    
        /**
         * 超时访问
         *
         * @param id
         * @return
         */
        public String paymentInfo_TimeOut(Integer id) {
            int timeNumber = 3;
            try {
                // 暂停3秒钟
                TimeUnit.SECONDS.sleep(timeNumber);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "	" +
                    "O(∩_∩)O哈哈~  耗时(秒)" + timeNumber;
        }
    }
     
     
    
    
    

    ​ controller

    package com.atguigu.springcloud.controller;
    
    import com.atguigu.springcloud.service.PaymentService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import java.util.concurrent.TimeUnit;
    
    
    @RestController
    @Slf4j
    public class PaymentController {
        @Resource
        private PaymentService paymentService;
    
        @Value("${server.port}")
        private String servicePort;
    
        /**
         * 正常访问
         *
         * @param id
         * @return
         */
        @GetMapping("/payment/hystrix/ok/{id}")
        public String paymentInfo_OK(@PathVariable("id") Integer id) {
            String result = paymentService.paymentInfo_OK(id);
            log.info("*****result:" + result);
            return result;
        }
    
        /**
         * 超时访问
         *
         * @param id
         * @return
         */
        @GetMapping("/payment/hystrix/timeout/{id}")
        public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
            String result = paymentService.paymentInfo_TimeOut(id);
            log.info("*****result:" + result);
            return result;
    
        }
    }
     
     
    
    
    

    正常测试
    启动eureka7001
    启动eureka-provider-hystrix-payment8001
    访问
    success的方法
    http://localhost:8001/payment/hystrix/ok/31
    每次调用耗费5秒钟
    http://localhost:8001/payment/hystrix/timeout/31
    上述module均OK
    以上述为根基平台,从正确->错误->降级熔断->恢复

    高并发测试

    上述在非高并发情形下,还能勉强满足 but...
    Jmeter压测测试
    下载地址
    https://jmeter.apache.org/download_jmeter.cgi
    
    

    ​ 开启Jmeter,来20000个并发压死8001,20000个请求都去访问paymentInfo_TimeOut服务

    image-20200414095859655

    填写线程数和循环次数

    image-20200414100412691

    发送HTTP Request请求:

    image-20200414100321142

    image-20200414101208736

    启动:

    image-20200414101258651

    image-20200413154322449

    再来一个访问http://localhost:8001/payment/hystrix/timeout/31
    看演示结果
    两个都在转圈圈
    为什么会被卡死:tomcat的默认工作线程数被打满了,没有多余的线程来分解压力和处理

    Jmeter压测结论

    ​ 上面还只是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死

    看热闹不嫌弃事大,80新建加入

    ​ cloud-consumer-feign-hystrix-order80

    故障和导致现象

    8001同一层次的其他接口被困死,因为tomcat线程池里面的工作线程已经被挤占完毕
    80此时调用8001,客户端访问响应缓慢,转圈圈

    上述结论

    正因为有上述故障或不佳表现 才有我们的降级/容错/限流等技术诞生

    如何解决?解决的要求

    超时导致服务器变慢(转圈)
    超时不再等待
    出错(宕机或程序运行出错)
    出错要有兜底
    解决
    对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
    对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级
    对方服务(8001)ok,调用者(80)自己有故障或有自我要求(自己的等待时间小于服务提供者)

    服务降级

    降级配置

    使用服务降级,需要通过如下的两个注解来完成:

    @HystrixCommand和@EnableCircuitBreaker

    • 主启动类激活,标注@EnableCircuitBreaker,启动短路保护。
    • 业务类启用 @HystrixCommand,在出现异常或超时,会自动调用@HystrixCommand中fallbckMethod参数所指定的方法。

    8001先从自身找问题

    设置自身调用超时时间的峰值,峰值内可以正常运行, 超过了需要有兜底的方法处理,做服务降级fallback

    8001fallback(服务器端服务降级)
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
        })
        public String paymentInfo_TimeOut(Integer id) {
            int timeNumber = 5000;
    //        int age = 10 / 0; 模拟系统运行异常
            try {
                TimeUnit.SECONDS.sleep(timeNumber);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "线程池:" + Thread.currentThread().getName() + "paymentinfo_Timeout,id:" + id + "	" + "耗时(秒)" + timeNumber;
        }
    
        private String paymentInfo_TimeOutHandler(Integer id) {
            return "线程池:" + Thread.currentThread().getName() + "8001系统繁忙或者运行报错,请稍后再试,id:" + id + "	";
        }
    
    

    上面制造了一个超时异常,限定在3秒钟,而业务需要5秒钟才能完成,超时后输出:

    image-20200414112953689

    制造一个运行时异常,查看异常后的行为:

    image-20200414113505365

    80fallback(客户端服务降级)

    Hystrix服务降级,既可以放到服务器端也可以放到客户端,前面是放到服务器端,下面是放到客户端,并且通常都是在客户端进行服务降级。

    Attention :热部署方式对java代码修改明显,但对@HystrixCommand内属性的修改不敏感,建议重启微服务

    POM

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

    YML

    server:
      port: 80
    eureka:
      client:
        register-with-eureka: false
        fetch-registry: true
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
    feign:
      hystrix:
        enabled: true
     
    
    
    

    主启动
    加上@EnableHystrix

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

    业务类

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
        //int age = 10/0;
        return paymentHystrixService.paymentInfo_TimeOut(id);
    }
    
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
        return "我是消费者80,对方支付系统繁忙请10秒种后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
    }
    
    
    

    此时在客户端发访问:http://localhost/consumer/payment/hystrix/timeout/1

    目前问题

    如果在每个业务中,都配置一个对应的Fallback方法,代码冗余太多而且耦合度高,需要定义一个统一的Fallback方法。

    解决办法

    可以在Controller中定义一个fallback方法用来处理异常或超时等,然后再标注上@DefaultProperties(defaultFallback=""),指明需要调用的fallback,然后在业务方法上标注@HystrixCommand。如下图所示:

    image-20200413154907165

    ​ 说明

    image-20200413154918420

    ​ controller配置

    package com.atguigu.springcloud.controller;
    
    import com.atguigu.springcloud.service.PaymentHystrixService;
    import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
     
    @RestController
    @Slf4j
    @DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
    public class OrderHystrixController {
        @Resource
        private PaymentHystrixService paymentHystrixService;
    
    
           @GetMapping("/consumer/payment/hystrix/ok/{id}")
        public String paymentInfo_OK(@PathVariable("id") Integer id) {
            return paymentHystrixService.paymentInfo_OK(id);
        }
    
           @GetMapping("/consumer/payment/hystrix/timeout/{id}")
        /*@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
        })*/
        @HystrixCommand
        public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
            //int age = 10/0;
            return paymentHystrixService.paymentInfo_TimeOut(id);
        }
    
        public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
            return "我是消费者80,对方支付系统繁忙请10秒种后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
        }
    
        /**
         * 全局fallback
         *
         * @return
         */
        public String payment_Global_FallbackMethod() {
            return "Global异常处理信息,请稍后重试.o(╥﹏╥)o";
        }
    }
     
     
    
    
    

    测试:http://localhost/consumer/payment/hystrix/timeout/31

    image-20200414124445473

    这样虽然结局了代码冗余的问题,但是仍然没有能够解决代码的耦合度高的问题,也即业务和异常或超时处理在一个Controller中,为了解决这个问题,我们可以在Service层来进行解耦。

    小结:

    • 该案例服务降级处理是在客户端80完成
    • 另外还是存在耦合度高的问题,如业务类PaymentController中业务和异常处理耦合

    image-20200413155436115

    在Service层解耦合

    修改cloud-consumer-feign-hystrix-order80

    根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现接口,统一为接口里面的方法进行异常处理

    PaymentFallbackService类实现PaymentFeginService接口

    @Component
    public class PaymentFallbackService implements PaymentHystrixService {
        @Override
        public String paymentInfo_OK(Integer id) {
            return "----PaymentFallbackService fall back--paymentInfo_OK";
        }
    
        @Override
        public String paymentInfo_TimeOut(Integer id) {
            return "----PaymentFallbackService fall back--paymentInfo_TimeOut";
        }
    }
    
    

    YML

    server:
      port: 80
    
    eureka:
      client:
        register-with-eureka: false
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka/ #入驻地址 不集群
    feign:
      hystrix:
        enabled: true #在feign中开启hystrix
    
    
    

    PaymentFeignClientService接口

    @Component
    @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
    public interface PaymentHystrixService {
    
    
        /**
         * 正常访问
         *
         * @param id
         * @return
         */
        @GetMapping("/payment/hystrix/ok/{id}")
        String paymentInfo_OK(@PathVariable("id") Integer id);
    
        /**
         * 超时访问
         *
         * @param id
         * @return
         */
        @GetMapping("/payment/hystrix/timeout/{id}")
        String paymentInfo_TimeOut(@PathVariable("id") Integer id);
    
    }
    
    

    测试

    • 单个eureka先启动7001

    • PaymentHystrixMain8001启动

    • 正常访问测试
      http://localhost/consumer/payment/hystrix/ok/32

    • 故意关闭微服务8001

    • 客户端自己调用提示
      此时服务端provider已经down ,但是我们做了服务降级处理, 让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器

    服务熔断

    断路器

    一句话就是家里的保险丝

    熔断是什么

    image-20200413155834797

    大神论文
    https://martinfowler.com/bliki/CircuitBreaker.html

    实操

    修改cloud-provider-hystrix-payment8001
    PaymentService

     //====服务熔断
    
        /**
         * 在10秒窗口期中10次请求有6次是请求失败的,断路器将起作用
         *
         * @param id
         * @return
         */
        @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() + "	" + "调用成功,流水号:" + serialNumber;
        }
    
        public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
            return "id 不能负数,请稍后重试,o(╥﹏╥)o id:" + id;
        }
    
    

    ​ why这些参数

    image-20200413155900972 PaymentController

     /**
         * 服务熔断
         * http://localhost:8001/payment/circuit/1
         *
         * @param id
         * @return
         */
        @GetMapping("/circuit/{id}")
        @HystrixCommand
        public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
            String result = paymentService.paymentCircuitBreaker(id);
            log.info("***result:" + result);
            return result;
        }
    
    

    com.netflix.hystrix.HystrixCommandProperties,该类提供了一系列Hystrix的默认属性。

    测试
    自测cloud-provider-hystrix-payment8001

    原理/小总结

    大神结论

    image-20200413155931647

    熔断类型

    • ​ 熔断打开
      ​ 请求不再调用当前服务,内部设置一般为MTTR(平均故障处理时间),当打开长达导所设时钟则进入半熔断状态

    • ​ 熔断关闭
      ​ 熔断关闭后不会对服务进行熔断

    • ​ 熔断半开
      ​ 部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断

      官网断路器流程图

    image-20200413160132578

    ​ 官网步骤

    image-20200413160142915

    ​ 断路器在什么情况下开始起作用

    image-20200413160152986

    ​ 断路器开启或者关闭的条件

    • ​ 当满足一定的阈值的时候(默认10秒钟超过20个请求次数)
    • ​ 当失败率达到一定的时候(默认10秒内超过50%的请求次数)
    • ​ 到达以上阈值,断路器将会开启
    • ​ 当开启的时候,所有请求都不会进行转发
    • ​ 一段时间之后(默认5秒),这个时候断路器是半开状态,会让其他一个请求进行转发. 如果成功,断路器会关闭,若失败,继续开启.重复4和5

    ​ 断路器打开之后

    image-20200413160203240

    ​ ALl配置

    image-20200413160231379

    image-20200413160241652

    image-20200413160257750

    image-20200413160305937

    服务限流

    后面高级篇讲解alibaba的Sentinel说明

    4)hystrix工作流程

    https://github.com/Netflix/Hystrix/wiki/How-it-Works
    Hystrix工作流程
    官网图例

    image-20200413160413762

    步骤说明

    image-20200413160425649

    5)服务监控hystrixDashboard

    概述

    仪表盘9001

    新建cloud-consumer-hystrix-dashboard9001
    POM

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-consumer-hystrix-dashboard9001</artifactId>
        <description>hystrix监控</description>
    
    
        <dependencies>
            <!--hystrix dashboard-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
            </dependency>
            <!--监控-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!--热部署-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    </project>
     
    
    
    

    YML

    server:
      port: 9001
     
    
    
    

    HystrixDashboardMain9001+新注解@EnableHystrixDashboard

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
    
    
    @SpringBootApplication
    @EnableHystrixDashboard
    public class HystrixDashboardMain9001 {
        public static void main(String[] args) {
            SpringApplication.run(HystrixDashboardMain9001.class);
        }
    }
    
    

    被监控的所有Provider微服务提供类(8001/8002/8003)都需要有监控依赖包

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

    启动cloud-consumer-hystrix-dashboard9001
    访问: http://localhost:9001/hystrix

    image-20200413160641665

    断路器演示(服务监控hystrixDashboard)

    修改cloud-provider-hystrix-payment8001

    Attention:新版本Hystrix需要在主启动MainAppHystrix8001中指定监控路径,否则容易出现"Unable to connect to Command Metric Stream"异常。

    package com.atguigu.springcloud;
    
    import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
    import org.springframework.context.annotation.Bean;
    
    
    @SpringBootApplication
    @EnableHystrixDashboard
    public class HystrixDashboardMain9001 {
        public static void main(String[] args) {
            SpringApplication.run(HystrixDashboardMain9001.class);
        }
    
        /**
         * 此配置是为了服务监控而配置,与服务容错本身无观,springCloud 升级之后的坑
         * ServletRegistrationBean因为springboot的默认路径不是/hystrix.stream
         * 只要在自己的项目中配置上下面的servlet即可
         * @return
         */
        @Bean
        public ServletRegistrationBean getServlet(){
            HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
            ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>(streamServlet);
            registrationBean.setLoadOnStartup(1);
            registrationBean.addUrlMappings("/hystrix.stream");
            registrationBean.setName("HystrixMetricsStreamServlet");
            return registrationBean;
        }
    }
     
    
    
    监控测试
    启动一个eureka或者3个eureka集群均可
    观察监控窗口

    ​ 9001监控8001

    image-20200413160903464

    ​ 填写监控地址
    http://localhost:8001/hystrix.stream
    ​ 测试地址
    http://localhost:8001/payment/circuit/31
    http://localhost:8001/payment/circuit/-31

    ​ 先访问正确地址,再访问错误地址,再正确地址,会发现图标断路器都是慢慢放开的.
    ​ 监控结果,成功

    image-20200413160922568

    ​ 监控结果,失败

    image-20200413160931606

    ​ 如何看?
    ​ 7色

    image-20200413160944600

    ​ 1圈

    image-20200413160953835

    ​ 1线

    image-20200413161003482

    ​ 整图说明

    image-20200413161014735

    image-20200413161035037

    ​ 整图说明2

    image-20200413161045984

    ​ 搞懂一个才能看懂复杂的

    image-20200413161053790

    12. Gateway新一代网关

    1)概述简介

    官网

    上一代zuul 1.x
    https://github.com/Netflix/zuul/wiki
    当前gateway
    https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/

    是什么

    概述

    image-20200414193716538

    image-20200414193729011

    一句话:
    SpringCloud Gateway使用的是Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架
    源码架构

    image-20200414193753233

    能干嘛

    反向代理
    鉴权
    流量控制
    熔断
    日志监控
    .....

    微服务架构中网关在哪里

    image-20200414193807956

    有Zuull了怎么又出来gateway

    我们为什么选择Gateway?
    1.netflix不太靠谱,zuul2.0一直跳票,迟迟不发布

    image-20200414193923194

    ​ 2.SpringCloud Gateway具有如下特性

    image-20200414193932982

    ​ 3.SpringCloud Gateway与Zuul的区别

    image-20200414193948646

    Zuul1.x模型

    image-20200414194007424

    Gateway模型
    WebFlux是什么

    image-20200414194030975

    https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux
    ​ 说明

    image-20200414194046187

    2)三大核心概念

    1. Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如断言为true则匹配该路由
    2. Predicate(断言):参考的是Java8的java.util.function.Predicate 开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
    3. Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改.

    总结

    image-20200414194110867

    3)Gateway工作流程

    官网总结

    image-20200414194142013

    核心逻辑
    路由转发+执行过滤器链

    4)入门配置

    新建Module

    cloud-gateway-gateway9527

    POM

         <dependencies>
            <!--需要引入spring-cloud-starter-gateway-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <!--gateway无需web和actuator-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
        </dependencies>
    
    

    Attention:请不要在该项目中引入“spring-boot-starter-web”依赖包,否则容易报“Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.”错误。

    YML

    业务类

    主启动类

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

    9527网关做路由映射

    cloud-provider-payment8001中查看controller的访问地址,/payment/get/**和/payment/lb/*

    但是目前不希望暴露8001端口,而是以9527端口对外提供服务。

    YML新增网关配置

    server:
      port: 9527
    
    spring:
      application:
        name: cloud-gateway
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
          routes:
            - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
             #匹配后提供服务的路由地址
              uri: http://localhost:8001
              predicates:
                - Path=/payment/get/** # 断言,路径相匹配的进行路由
            - id: payment_route2
              uri: http://localhost:8001
              predicates:
                Path=/payment/lb/** #断言,路径相匹配的进行路由
    
    eureka:
      instance:
        hostname: cloud-gateway-service
      client:
        fetch-registry: true
        register-with-eureka: true
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka/
    
    

    测试

    1. 启动cloud-eureka-server7001

    2. 启动 cloud-provider-payment8001

    3. 启动cloud-gateway-gateway9527

    访问说明

    image-20200414194424762

    使用8001端口来访问: http://localhost:8001/payment/get/1

    image-20200414214011058

    使用9527端口来进行访问:http://localhost:9527/payment/get/1

    image-20200414214000364

    YML配置说明

    Gateway网关路由有两种配置方式:

    1. 在配置文件yaml中配置:如“cloud-gateway-gateway9527”中YML的“routes”所示。
    2. 代码中注入RouteLocator的Bean

    官网案例

    image-20200414194503291

    现在想要以"http://localhost:9527/guonei/"的形式访问,“https://news.baidu.com/guonei”

    在"cloud-gateway-gateway9527"中做如下的配置

    com.atguigu.springcloud.config.GateWayConfig

    package com.atguigu.springcloud.config;
    
    import org.springframework.cloud.gateway.route.RouteLocator;
    import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class GateWayConfig {
        @Bean
        public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
            RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
            routes.route("path_route_atguigu",r->r.path("/guonei").uri("https://news.baidu.com/guonei")).build();
            return routes.build();
        }
    }
    
    

    访问:http://localhost:9527/guonei/

    image-20200414220612188

    5)通过服务名实现动态

    默认情况下Gatway会根据注册中心注册的服务列表, 以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能

    启动

    一个eureka7001,两个服务提供者Cloud-provider-payment8001和Cloud-provider-payment8002

    POM

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

    YML

    server:
      port: 9527
    
    spring:
      application:
        name: cloud-gateway
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
          routes:
            - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
              #匹配后提供服务的路由地址
    #          uri: http://localhost:8001
              uri: lb://cloud-payment-service
              predicates:
                - Path=/payment/get/** # 断言,路径相匹配的进行路由
            - id: payment_route2
    #          uri: http://localhost:8001
              uri: lb://cloud-payment-service
              predicates:
                Path=/payment/lb/** #断言,路径相匹配的进行路由
    
    eureka:
      instance:
        hostname: cloud-gateway-service
      client:
        fetch-registry: true
        register-with-eureka: true
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka/
    
    
    

    Attention:uri的协议lb,表示启用Gateway的负载均衡功能。
    lb://serverName是spring cloud gatway在微服务中自动为我们创建的负载均衡uri

    测试

    访问:http://localhost:9527/payment/lb,能够看到8001/8002两个端口来回切换。

    6)Predicate

    在启动“cloud-gateway-gateway9527”过程中,我们看到如下的输出:

    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [After]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Before]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Between]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Cookie]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Header]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Host]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Method]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Path]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Query]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [ReadBodyPredicateFactory]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [RemoteAddr]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Weight]
    [main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [CloudFoundryRouteService]
    
    

    实际上这些就是路由谓词工厂。

    Route Predicate Factories

    谓词工厂

    Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。

    Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个RoutePredicate工厂可以进行组合。

    Spring Cloud Gateway创建Route对象时,使用RoutePredicateFactory创建Predicate对象,,Predicate对象可以赋值给Route。Spring Cloud Gateway包含许多内置的Route Predicate Factories。

    所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and。

    常用的Route Predicate

    “RoutePredicateFactory”类的继承关系图:

    image-20200414195312185

    实现类:

    image-20200414225040992

    1. After Route Predicate

    查看官网:

    4.1. The After Route Predicate Factory

    The after route predicate factory takes one parameter, a datetime. This predicate matches requests that happen after the specified datetime. The following example configures an after route predicate:
    after路由谓词工厂接受一个日期时间参数。此谓词匹配在指定日期时间之后,所发生的请求。下面的示例配置一个after路由谓词:

    Example 1. application.yml

    spring:
    cloud:
    gateway:
    routes:
       - id: after_route
    uri: https://example.org
    predicates:
         - After=2017-01-20T17:42:47.789-07:00[America/Denver]
    
    

    This route matches any request made after Jan 20, 2017 17:42 Mountain Time (Denver).
    这条路由匹配任何,在丹佛时间2017年1月20日17点42分以后发出的请求。

    关于这个时间的取得:

    import java.time.ZonedDateTime;
    public class T2 {
         public static void main(String[] args) {
             ZonedDateTime zonedDateTime = ZonedDateTime.now();
             System.out.println(zonedDateTime);
         }
    }
    
    

    2. Before Route Predicate
    3. Between Route Predicate

    The cookie route predicate factory takes two parameters, the cookie name and a regular expression. This predicate matches cookies that have the given name and whose values match the regular expression. The following example configures a cookie route predicate factory:
    Cookie路由谓词工厂接收两个参数,cookie名称和一个正则表达式。此谓词匹配具有给定名称且其值与正则表达式匹配的cookie。下面的示例配置一个cookie路由谓词工厂:

    Example 4. application.yml

    spring:
    cloud:
    gateway:
    routes:
        - id: cookie_route
      uri: https://example.org
      predicates:
          - Cookie=chocolate, ch.p
    
    

    This route matches requests that have a cookie named chocolate whose value matches the ch.p regular expression.
    此路由匹配具有一个名为chocolate的cookie的请求,该cookie的值与ch.p正则表达式匹配。

    Cookie Route Predicate需要两个参数,一个是Cookie name,一个是正则表达式。路由规则会通过获取对应的Cookie name值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行

    修改“cloud-gateway-gateway9527”YAML中的“spring.cloud.gateway.routes[1].predicates”路由规则,添加“- Cookie=username,zzyy”

    spring:
      application:
        name: cloud-gateway
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
          routes:
            - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
              #匹配后提供服务的路由地址
    #          uri: http://localhost:8001
              uri: lb://cloud-payment-service
              predicates:
                - Path=/payment/get/** # 断言,路径相匹配的进行路由
                - After=2020-04-14T23:43:19.117+08:00[Asia/Shanghai]
                  #- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
                - Cookie=username,zzyy
                  #- Header=X-Request-Id, d+ #请求头要有X-Request-Id属性,并且值为正数
                  #- Host=**.atguigu.com
                  #- Method=GET
                  #- Query=username, d+ # 要有参数名username并且值还要是正整数才能路由
                  # 过滤
                  #filters:
                #  - AddRequestHeader=X-Request-red, blue
            - id: payment_route2
    #          uri: http://localhost:8001
              uri: lb://cloud-payment-service
              predicates:
                - Path=/payment/lb/** #断言,路径相匹配的进行路由
                - Cookie=username,zzyy
    
    
    不带cookies访问

    curl http://localhost:9527/payment/lb

    Administrator@git MINGW64 ~
    $ curl http://localhost:9527/payment/lb
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100   136  100   136    0     0   9066      0 --:--:-- --:--:-- --:--:--  9066{"timestamp":"2020-04-15T01:00:32.067+0000","path":"/payment/lb","status":404,"error":"Not Found","message":null,"requestId":"c3b7e7a0"}
    
    Administrator@git MINGW64 ~
    
    
    
    带上cookies访问

    curl http://localhost:9527/payment/lb --cookie "username=zzyy"

    Administrator@git MINGW64 ~
    $ curl http://localhost:9527/payment/lb --cookie "username=zzyy"
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100     4  100     4    0     0    266      0 --:--:-- --:--:-- --:--:--   266
    8002
    
    Administrator@git MINGW64 ~
    $ curl http://localhost:9527/payment/lb --cookie "username=zzyy"
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100     4  100     4    0     0    250      0 --:--:-- --:--:-- --:--:--   250
    8002
    
    Administrator@git MINGW64 ~
    $ curl http://localhost:9527/payment/lb --cookie "username=zzyy"
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100     4  100     4    0     0    129      0 --:--:-- --:--:-- --:--:--   129
    8001
    
    
    

    关于Curl中文乱码问题的解决

    5. Header Route Predicate

    image-20200414195058717

    6. Host Route Predicate

    image-20200414195108590

    7. Method Route Predicate

    image-20200414195117135

    8. Path Route Predicate
    9. Query Route Predicate

    image-20200414195141698

    YML

    image-20200414195150769

    10. RemoteAddr Route Predicate
    11. Weight Route Predicate

    小总结
    ALL
    image-20200414195208372

    说白了,Predicate就是为了实现一组匹配规则, 让请求过来找到对应的Route进行处理

    7)Filter的使用

    是什么

    image-20200414195416746

    Spring Cloud Gateway的filter

    SpringCloud中过滤器生命周期有两个,pre和post,类似于Spring的前置通知和后置通知。

    过滤器的种类有两种,全局过滤器 GlobalFilter和网关过滤器GatewayFilter

    常用的GatewayFilter

    5.1. The AddRequestHeader GatewayFilter Factory

    The AddRequestHeader GatewayFilter factory takes a name and value parameter. The following example configures an AddRequestHeader GatewayFilter:

    Example 13. application.yml

    spring:
    cloud:
    gateway:
    routes:
        - id: add_request_header_route
      uri: https://example.org
      filters:
          - AddRequestHeader=X-Request-red, blue
    
    

    This listing adds X-Request-red:blue header to the downstream request’s headers for all matching requests.

    此AddRequestHeader将“X-Request-red:blue”添加到所有匹配的下游请求。

    AddRequestHeader is aware of the URI variables used to match a path or host. URI variables may be used in the value and are expanded at runtime. The following example configures an AddRequestHeader GatewayFilter that uses a variable:

    AddRequestHeader 中能够使用用于匹配路径或主机的URI变量。URI变量可以在值中使用,并在运行时展开。下面的例子配置了一个AddRequestHeader ,它使用了一个URL变量:

    Example 14. application.yml

    spring:
    cloud:
    gateway:
    routes:
        - id: add_request_header_route
      uri: https://example.org
      predicates:
          - Path=/red/{segment}
      filters:
          - AddRequestHeader=X-Request-Red, Blue-{segment}
    
    

    下面在项目中,我们将使用“ AddRequestParameter”网关过滤规则,在YAML文件中增加如下的配置即可:

    image-20200414195516247

    自定义过滤器

    Hystrix提供了一些既定的规则,但是多数情况下,我们想要自定义自己的过滤规则,此时就可以通过实现“GlobalFilter”和“OrderId”两个接口来实现自定义的过滤规则。

    实例:

    package com.atguigu.springcloud.filter;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import java.util.Date;
    
    
    @Component
    @Slf4j
    public class MyLogGatewayFilter implements GlobalFilter, Ordered {
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("come in global filter: {}", new Date());
    
            ServerHttpRequest request = exchange.getRequest();
            String uname = request.getQueryParams().getFirst("uname");
            if (uname == null) {
                log.info("用户名为null,非法用户");
                exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
                return exchange.getResponse().setComplete();
            }
            // 放行
            return chain.filter(exchange);
        }
    
        /**
         * 过滤器加载的顺序 越小,优先级别越高
         *
         * @return
         */
        @Override
        public int getOrder() {
            return 0;
        }
    }
    
    
    

    ​ 测试

    启动:Cloud-provider-payment8001,Cloud-provider-payment8002,cloud-eureka-server7001和cloud-gateway-gateway9527。

    http://localhost:9527/payment/lb?uname=z3,能够正常的访问,但是如果去掉username参数则无法访问页面。

    13. SpringCloud config分布式配置中心

    1)概述

    分布式系统面临的---配置问题

    微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务,都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。

    SpringCloud提供了ConfigServer来解决这个问题,每一个微服务自己带着一个application.yml,上百个配置文件的管理将变得易如反手。

    是什么

    SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。

    1587172226398

    SpringCloud Config分为客户端和服务端两部分。

    服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并未客户端提供获取配置信息,加密/解密信息等访问接口。

    客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息,配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。

    能干嘛

    • 集中管理配置文件

    • 不同环境不同配置,动态化的配置更新,分环境比如dev/test/prod/beta/release

    • 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心同意拉去配置自己的信息

    • 当配置发生改变时,服务不需要重启即可感知到配置的变化并应用新的配置

    • 将配置信息以REST接口的形式暴露
      post/crul访问刷新即可...

    与GitHub整合配置

    由于SpringCloud Config默认使用GIt来存储配置文件(也有其他方式,比如支持SVN和本地文件),但最推荐还是Git,而且使用的是http/https访问的形式

    官网

    https://spring.io/projects/spring-cloud-bus#learn

    https://www.springcloud.cc/spring-cloud-bus.html#_quick_start

    2)Config服务端配置与测试

    在Github上创建名为“springcloud-config”的Repository,然后克隆到本地

    在克隆后的本地目录中,添加如下的yaml文件

    config-dev.yml

    config:
      info: "master branch,springcloud-config/config-dev.yml version=7" 
    
    

    config-prod.yml

    config:
      info: "master branch,springcloud-config/config-prod.yml version=1" 
    
    

    config-test.yml

    config:
      info: "master branch,springcloud-config/config-test.yml version=1" 
    
    

    然后推送到远程即可。

    新建cloud-config-center-3344模块,它即为Cloud的配置中心模块(cloudConfig Center)

    新建POM

    <dependencies>
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-bus-amqp</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-config-server</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <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.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <optional>true</optional>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
          <scope>test</scope>
      </dependency>
    
    </dependencies>
    
    

    新建YML

    server:
      port: 3344
    
    spring:
      application:
        name: cloud-config-center
      cloud:
        config:
          server:
            git:
              skipSslValidation: true # 跳过ssl认证
              uri: https://github.com/cosmoswong/springcloud-config.git
              search-paths:
                - com.springcloud-config
          label: master
    
    
    
    eureka:
      client:
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka
    
    
    

    新建主启动类

    ConfigCenterMain3344, @EnableConfigServer

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.config.server.EnableConfigServer;
    
    /**
     * @ClassName: MainAppConfigCenter3344
     * @description:
     * @author: XZQ
     * @create: 2020/3/9 16:28
     **/
    @SpringBootApplication
    @EnableConfigServer
    public class MainConfigCenter3344 {
        public static void main(String[] args) {
            SpringApplication.run(MainConfigCenter3344.class, args);
        }
    }
    
    

    修改hosts文件,添加映射规则

    127.0.0.1 config-3344.com
    
    

    测试是否获取上GitHub既存的配置

    启动服务3344,然后测试

    image-20200417170546041

    读取配置规则

    官网
    /{label}/{application}-{profile}.yml
    master分支
    dev分支
    /{application}-{profile}.yml
    /{application}/{profile}/{/label}
    重点配置细节总结

    image-20200417160354883

    1587164898226

    1587165005872

    1587165041057

    成功实现了SpringCloudConfig通过Github获取配置信息

    3)Config客户端配置与测试

    新建cloud-config-client-3355

    POM

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <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.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    
    </dependencies>
    
    

    bootstrap.yml

    server:
      port: 3355
    
    spring:
      application:
        name: config-client
      cloud:
        config:
          label: master # 分支名称
          name: config #配置文件名称
          profile: dev # 读取的后缀,上述三个综合,为master分支上的config-dev.yml的配置文件被读取,http://config-3344.com:3344/master/config-dev.yml
          uri: http://localhost:3344 #配置中心的地址
          
    eureka:
      client:
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka
    
    

    注:关于BootStrap

    1587165267830

    1587165448966

    修改config-dev.yml配置并提交到GitHub中,比如加个变量age或者版本号version

    主启动类

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class ConfigClientMain3355 {
        public static void main(String[] args) {
            SpringApplication.run(ConfigClientMain3355.class, args);
        }
    }
    
    

    业务类

    package com.atguigu.springcloud.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RefreshScope
    public class ConfigClientController {
    
        @Value("${config.info}")
        private String configInfo;
    
        @GetMapping("/configInfo")
        public String getConfigInfo() {
            return configInfo;
        }
    }
    
    
    

    测试

    http://localhost:3355/configInfo

    image-20200417172822704

    成功实现了客户端3355访问SpringCloud Config3344,并通过GitHub获取信息配置

    问题随之而来,分布式配置的动态刷新问题

    问题:在GitHub上修改配置信息,服务端3344能够及时的获悉修改,但是客户端3355在没有重启的前提下,无法及时获取到所修改的配置信息。

    4)Config客户端之动态刷新

    使用动态刷新功能,能够避免每次更新服务器端配置都要重启客户端,客户端才能获取最新配置的问题。

    承接上面的实例,接着修改cloud-config-client3355。

    POM引入actuator监控

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

    通过该依赖,当项目发生变化的时候,能够被监控方说获知。

    修改YML,暴露监控端口

    #暴露监控端点
    management:
      endpoints:
        web:
          exposure:
            include: "*"
    
    

    @refreshScope业务类Controller修改

    package com.atguigu.springcloud.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RefreshScope
    public class ConfigClientController {
    
        @Value("${config.info}")
        private String configInfo;
    
        @GetMapping("/configInfo")
        public String getConfigInfo() {
            return configInfo;
        }
    }
    
    
    

    此时修改Github上的config-dev.yml配置文件

    1587167292296

    请求“http://localhost:3355/configInfo”,发现配置并没有随着发生变化,但是服务器端已经发生了变化,这种变化并没有同步到客户端上。

    1587167410141

    此时想要让客户端从服务器端同步配置,需要执行刷新客户端的请求,而且请求必须是POST方式的。

    curl -X POST "http://localhost:3355/actuator/refresh"
    
    

    1587167671125

    再次请求“http://localhost:3355/configInfo”,能够看到配置已经被刷新。

    1587167721503

    新的问题

    如果在一个配置服务器端对应于N个客户端,若每次都要执行上面的curl请求刷新配置,则效率比较低。为此我们可以借助于消息服务,客户端在订阅了对应的topic后,服务器端将会把变更主动的推送到所有订阅的客户端,这个过程是自动完成的,无需人工干预,这里的消息服务可以是RabbitMQ或Kafka。

    14. SpringCloud Bus消息总线

    1)概述

    上一讲解的加深和扩充,一言以蔽之
    分布式自动刷新配置功能
    Spring Cloud Bus配合Spring Cloud Config使用可以实现配置的动态刷新
    是什么

    image-20200417161132654

    ​ Bus支持两种消息代理:RabbitMQ和Kafka
    能干嘛

    image-20200417161207563

    为什么被称为总线

    image-20200417161220822

    2)RabbitMQ环境配置

    1. 安装Elang,下载地址https://www.erlang.org/downloads

    2. 安装RabbitMQ,下载地址https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.3/rabbitmq-server-3.8.3.exe

    3. 进入RabbitMQ安装目录下的sbin目录
      输入以下命令启动管理功能

      rabbitmq-plugins enable rabbitmq_management
      
      
    4. 访问地址看是否成功安装
      http://localhost:15672
      1587176535991

      默认用户名和密码:guest guest

    3)SpringCloud Bus动态刷新全局广播

    全局广播动态刷新设计思想

    1. 利用消息总线触发一个客户端/bus/refresh,从而刷新所有客户端配置
    image-20200417161243055
    1. 利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,从而刷新所有客户端配置
    image-20200417161258057
    1. 相对于图1,图2的架构显然更加合适,这是因为

      • 客户端/bus/refresh,打破了微服务的职责单一性。因为微服务本身是业务模块,不应该承担配置刷新的职责

      • 破坏了微服务各节点的对等性

      • 有一定的局限性。例如,微服务在迁移时,网络地址常会发生变化,此时如果想要做到自动刷新就会带来更多的修改

    给cloud-config-center-3344配置中心服务端添加消息总线支持

    pom.xml

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

    bootstrap.yaml

    server:
      port: 3344
    
    spring:
      application:
        name: cloud-config-center
      cloud:
        config:
          server:
            git:
              skipSslValidation: true # 跳过ssl认证
              uri: https://github.com/cosmoswong/springcloud-config.git
              search-paths:
                - com.springcloud-config
          label: master
    
    rabbitmq:
      host: localhost
      port: 5672
      username: guest
      password: guest
    
    eureka:
      client:
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka
    
    # 暴露bus刷新配置的端点
    management:
      endpoints:
        web:
          exposure:
            include: "bus-refresh"
    
    
    

    给cloud-config-center-3355配置中心服务端添加消息总线支持

    pom.xml

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

    bootstrap.yaml

    server:
      port: 3355
    
    spring:
      application:
        name: config-client
      cloud:
        config:
          label: master # 分支名称
          name: config #配置文件名称
          profile: dev # 读取的后缀,上述三个综合,为master分支上的config-dev.yml的配置文件被读取,http://config-3344.com:3344/master/config-dev.yml
          uri: http://localhost:3344 #配置中心的地址
    
    
    rabbitmq: #rabbitmq相关配置,15672是web管理端口,5672是mq访问端口
      port: 5672
      host: localhost
      username: guest
      password: guest
    
    
    eureka:
      client:
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka
    
    #暴露监控端点
    management:
      endpoints:
        web:
          exposure:
            include: "*"
    
    

    给cloud-config-center-3366配置中心服务端添加消息总线支持

    pom.xml

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

    bootstrap.yaml

    server:
      port: 3366
    
    spring:
      application:
        name: config-client
      cloud:
        config:
          label: master # 分支名称
          name: config #配置文件名称
          profile: dev # 读取的后缀,上述三个综合,为master分支上的config-dev.yml的配置文件被读取,http://config-3344.com:3344/master/config-dev.yml
          uri: http://localhost:3344 #配置中心的地址
    
    
    rabbitmq: #rabbitmq相关配置,15672是web管理端口,5672是mq访问端口
      port: 5672
      host: localhost
      username: guest
      password: guest
    
    
    eureka:
      client:
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka
    
    #暴露监控端点
    management:
      endpoints:
        web:
          exposure:
            include: "*"
    
    

    测试

    分别查看

    http://eureka7001.com:7001/

    1587178740352

    http://config-3344.com:3344/master/config-dev.yml

    1587178755425

    http://localhost:3355/configInfo

    1587178767721

    http://localhost:3366/configInfo

    1587178775376

    在Github上修改“config-dev.yml”版本号为10

    1587178653209

    再次刷新并查看:http://localhost:3355/configInfo,http://localhost:3366/configInfo,http://config-3344.com:3344/master/config-dev.yml,可以看到3344上已经发生了变化,但是3355和3366上没有发生变化。

    发送Post请求

    curl -X POST "http://localhost:3344/actuator/bus-refresh"
    
    

    再次刷新并查看:http://localhost:3355/configInfo,http://localhost:3366/configInfo,能够发现版本号都变为了10。

    4)SpringCloud Bus动态刷新定点通知

    如果想要实现定点通知,也即只通知部分的客户端,则可以借助于配置中心来刷新指定的客户端。

    http://{config server host}:{config server port}/actutor/bus-refresh/{destination}
    
    

    案例:现在只通知3355,不通知3366。

    1. 修改Github上的“”文件,修改版本号为11

    2. 执行以下命令,将配置刷新到cloud-config-client3355

      curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"
      
      

      注:

      • config-client:为客户端“cloud-config-client3355”微服务名
      • 3355:为客户端“cloud-config-client3355”微服务端口
    3. 观察版本号变化:http://localhost:3355/configInfo

      image-20200418111817272
    4. 观察版本号变化:http://localhost:3366/configInfo

      image-20200418111826341

    ​ 通过以上的实例能够发现,/bus/refresh请求不再发送到所有客户端,而是发给destination参数所指定的客户端。

    5)通知总结All

    image-20200417161333338

    1. 配置中心订阅

    15. SpringCloud Stream消息驱动

    1)消息驱动概述

    是什么

    Spring Cloud Stream是一个构建消息驱动微服务的框架。

    应用程序通过inputs或者outputs来与Spring Cloud Stream中binder对象交互。

    通过绑定器,Spring Cloud Stream的binder对象负责与消息中间件交互所以,我们只需要搞清楚如何与Spring Cloud Streamk互就可以方便使用消息驱动的方式。通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动。Spring Cloud Stream为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。目前仅支持RabbitMQ, Kafka.

    1587175077556

    ​ 一句话
    ​ 屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型
    ​ 官网
    https://spring.io/projects/spring-cloud-stream
    ​ Spring Cloud Stream中文指导手册

    https://m.wang1314.com/doc/webapp/topic/20971999.html

    https://blog.csdn.net/qq_32734365/article/details/81413218#spring-cloud-stream%E4%B8%AD%E6%96%87%E6%8C%87%E5%AF%BC%E6%89%8B%E5%86%8C

    设计思想

    标准MQ

    1587175112896

    ​ 生产者/消费者之间靠消息媒介传递信息内容
    ​ Message
    ​ 消息必须走特定的通道
    ​ 消息通道MessageChannel
    ​ 消息通道里的消息如何被消费呢,谁负责收发处理
    ​ 消息通道MessageChannel的子接口SubscribableChannel,由MessageHandler消息处理器所订阅

    为什么使用Cloud Stream

    比方说我们用到了RabbitMQ和Kafka,由于这两个消息中间件的架构上的不同,如RabbitMQ有exchange, kafka有Topic和Partitions分区概念。

    中间件架构上的差异,造成项目开发过程中的困扰。如只使用其中一种消息队列,则业务扩展,增加或变更消息队列时,需要大量的改动。对此springcloud Stream提供了一种解耦合的方式。

    1587175141159

    stream凭什么可以统一底层差异

    在没有绑定器这个概念的情况下,我们的SpringBoot应用要直接与消息中间件进行信息交互的时候,由于各消息中间件构建的初衷不同,它们的实现细节上会有较大的差异性

    通过使用绑定器作为中间层,能实现应用程序与消息中间件细节之间的隔离。通过向应用程序暴露统一的Channel通道,使得应用程序无需考虑不同的消息中间件。

    Binder

    Stream对消息中间件的进一步封可以做到代码层面对中间件的无感知,甚至可以动态的切换中间件(rabbitmq切换为kafka),使得微服务开发的高度解耦,使得开发人员更多关注自己的业务。

    1587175206131

    INPUT对应于消费者
    OUTPUT对应于生产者
    Stream中的消息通信方式遵循了发布-订阅模式
    Topic主题进行广播
    在RabbitMQ就是Exchange
    在Kafka中就是Topic

    Spring Cloud Stream标准流程套路

    1587175250333

    1587175260053

    1. Binder:很方便的连接中间件,屏蔽差异

    2. Channel:通道,是队列Queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,通过Channel对队列进行配置

    3. Source和Sink:简单的理解为参照对象,从Stream发布消息就是输出,接受消息就是输入

    编码API和常用注解

    1587175279709

    2)案例说明

    RabbitMQ环境已经OK
    工程中新建三个子模块
    cloud-stream-rabbitmq-provider8801 ,作为消息试生产者进行发消息模块
    cloud-stream-rabbitmq-consumer8802,作为消息接收模块
    cloud-stream-rabbitmq-consumer8803,作为消息接收模块

    3)消息驱动之生产者

    新建“cloud-stream-rabbitmq-provider8801”

    pom.xml

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

    application.yml

    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://eureka7001.com:7001/eureka
      instance:
        lease-renewal-interval-in-seconds: 2 # 设置心跳的间隔时间,默认30
        lease-expiration-duration-in-seconds: 5 # 超过5秒间隔,默认90
        instance-id: send-8801.com # 主机名
        prefer-ip-address: true # 显示ip
    
    
    
    

    主启动类

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    
    @SpringBootApplication
    public class StreamMQMain8801 {
        public static void main(String[] args) {
            SpringApplication.run(StreamMQMain8801.class, args);
        }
    }
    
    
    

    业务类

    发送消息接口

    package com.atguigu.springcloud.service;
    
    public interface IMessageProvider {
        String send();
    }
    
    
    

    发送消息接口实现类

    package com.atguigu.springcloud.service.impl;
    
    import com.atguigu.springcloud.service.IMessageProvider;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.stream.annotation.EnableBinding;
    import org.springframework.cloud.stream.messaging.Source;
    import org.springframework.messaging.MessageChannel;
    import org.springframework.messaging.support.MessageBuilder;
    
    import java.util.UUID;
    
    
    @EnableBinding(Source.class)//定义消息的推送管道
    public class MessageProviderImpl implements IMessageProvider {
    
        @Autowired
        private MessageChannel output;//消息发送通道
    
        @Override
        public String send() {
            String serial = UUID.randomUUID().toString();
            output.send(MessageBuilder.withPayload(serial).build());
            System.out.println("*****serial***" + serial);
            return serial;
        }
    }
    
    
    

    controller

    package com.atguigu.springcloud.controller;
    
    import com.atguigu.springcloud.service.IMessageProvider;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class SendMessageController {
    
        @Autowired
        private IMessageProvider messageProvider;
    
        @GetMapping("/sendMessage")
        public String send() {
            return messageProvider.send();
        }
    }
    
    
    

    测试

    1. 启动7001 eureka

    2. 启动rabbitmq

      rabbitmq-plugins enable rabbitmq management
      
      

      http://localhost:15672/
      image-20200418150254553

    3. 启动"cloud-stream-rabbitmq-provider8801"

    4. 访问
      http://localhost:8801/sendMessage

    image-20200418150224932

    http://localhost:15672/#/
    image-20200418150730501

    4)消息驱动之消费者

    新建“cloud-stream-rabbitmq-consumer8802”module

    pom.xml

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

    yml

    application.yml

    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 # 设置要绑定的消息服务的具体设置
              group: spectrumrpcA # 不同的组存在重复消费,相同的组之间竞争消费。
    
    eureka:
      client:
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka
      instance:
        lease-renewal-interval-in-seconds: 2 # 设置心跳的间隔时间,默认30
        lease-expiration-duration-in-seconds: 5 # 超过5秒间隔,默认90
        instance-id: receive-8802.com #主机名
        prefer-ip-address: true # 显示ip
    
    
    
    

    主启动类

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    
    @SpringBootApplication
    public class StreamMQMain8802 {
        public static void main(String[] args) {
            SpringApplication.run(StreamMQMain8802.class, args);
        }
    }
    
    
    

    业务类

    package com.atguigu.springcloud.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.stream.annotation.EnableBinding;
    import org.springframework.cloud.stream.annotation.StreamListener;
    import org.springframework.cloud.stream.messaging.Sink;
    import org.springframework.messaging.Message;
    import org.springframework.stereotype.Component;
    
    @Component
    @EnableBinding(Sink.class)
    public class ReceiveMessageListenerController {
    
        @Value("${server.port}")
        private String serverPort;
    
        @StreamListener(Sink.INPUT)
        public void input(Message<String> message) {
            System.out.println("消费者1,-------" + message.getPayload() + "	 port:" + serverPort);
        }
    
    }
    
    
    

    测试8801发送8802接收消息

    http://localhost:8801/sendMessage

    image-20200418152344460

    image-20200418152402907

    http://localhost:15672/#/queues/%2F/studyExchange.spectrumrpcA

    image-20200418152500180

    5)分组消费与持久化

    依照cloud-stream-rabbitmq-consumer8802,clone出来一份cloud-stream-rabbitmq-consumer8803

    启动

    • RabbitMQ
    • cloud-eureka-server7001 服务注册
    • cloud-stream-rabbitmq-provider8801 消息生产
    • cloud-stream-rabbitmq-consumer8802 消息消费
    • cloud-stream-rabbitmq-consumer8803 消息消费

    运行后有两个问题

    有重复消费问题

    消息持久化问题

    消费

    目前是cloud-stream-rabbitmq-consumer8802和cloud-stream-rabbitmq-consumer8803同时收到了消息,存在重复消费问题

    如何解决?
    分组和持久化属性group

    生产实际案例

    1587175329398

    分组

    原理
    微服务应用放置于同一个group中,就能够保证消息只会被其中一个应用消费一次。不同的组是可以消费同一个分区的数据,同一个组内的消费者不能消费同一分区的数据,任何时候只有其中一个可以消费

    分别修改“cloud-stream-rabbitmq-consumer8802”和“cloud-stream-rabbitmq-consumer8803”的application.yml文件,将两个消费者置于同一个消费者组“spectrumrpcA”内:

    spring:
      application:
        name: cloud-stream-consumer
      cloud:
        stream:
          binders: # 在此处配置要绑定的rabbitMQ的服务信息
            ...
          bindings: # 服务的整合处理
            input: # 这个名字是一个通道的名称
             ...
              group: spectrumrpcA # 不同的组存在重复消费,相同的组之间竞争消费。
    
    

    再次访问: http://localhost:8801/sendMessage ,后台查看到消息被分别的发送到了“cloud-stream-rabbitmq-consumer8802”,“cloud-stream-rabbitmq-consumer8803”。

    此时观察RabbitMQ的控制台,可以看到“spectrumrpcA”分组下,有两个消费者:

    image-20200418155451694

    结论
    8802/8803实现了轮询分组,每次只有一个消费者,8801模块的发的消息只能被8802或8803其中一个接收到,这样避免了重复消费

    持久化

    通过上述,解决了重复消费问题,再看看持久化

    1. 停止cloud-stream-rabbitmq-consumer8802和cloud-stream-rabbitmq-consumer8803
    2. 只除掉cloud-stream-rabbitmq-consumer8802分组group:spectrumrpcA
    3. cloud-stream-rabbitmq-consumer8801发送4条消息到rabbitmq
    4. 启动cloud-stream-rabbitmq-consumer8802,启动后后台没有打出来消息
    5. 启动cloud-stream-rabbitmq-consumer8803,启动后台打出来了MQ上的消息

    小结:这个实例说明了,同一个组内的消费者,即便消费过程中发生了中断,重启后也能够继续消费消息,但是没有组归属的将不会接受到消息。

    16. SpringCloud Sleuth分布式链路跟踪

    1)概述

    为什么会出现这个技术?需要解决哪些问题?
    问题

    image-20200418173000354

    是什么
    https://cloud.spring.io/spring-cloud-sleuth/reference/html/
    Spring Cloud Sleuth提供了一套完整的服务跟踪的解决方案
    在分布式系统中提供追踪解决方案并且兼容支持了zipkin
    解决

    image-20200418173015799

    2)搭建链路监控步骤

    zipkin

    下载

    ​ SpringCloud从F版已不需要自己构建Zipkin Server了,只需要调用jar包即可
    https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/
    ​ zipkin-server-2.12.9-exec.jar

    运行jar

    java -jar zipkin-server-2.12.9-exec.jar

    运行过程如下:

    Administrator@git MINGW64 /d/Program_Files
    $       java -jar zipkin-server-2.12.9-exec.jar
                                        ********
                                      **        **
                                     *            *
                                    **            **
                                    **            **
                                     **          **
                                      **        **
                                        ********
                                          ****
                                          ****
            ****                          ****
         ******                           ****                                 ***
      ****************************************************************************
        *******                           ****                                 ***
            ****                          ****
                                           **
                                           **
    
    
                 *****      **     *****     ** **       **     **   **
                   **       **     **  *     ***         **     **** **
                  **        **     *****     ****        **     **  ***
                 ******     **     **        **  **      **     **   **
    
    :: Powered by Spring Boot ::         (v2.1.4.RELEASE)
    
    2020-04-18 18:04:53.235  INFO 13064 --- [           main] z.s.ZipkinServer                         : Starting ZipkinServer on git with PID 13064 (D:Program_Fileszipkin-server-2.12.9-exec.jar started by Administrator in D:Program_Files)
    2020-04-18 18:04:53.242  INFO 13064 --- [           main] z.s.ZipkinServer                         : The following profiles are active: shared
    2020-04-18 18:04:55.091  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.verboseExceptions: false (default)
    2020-04-18 18:04:55.092  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.verboseSocketExceptions: false (default)
    2020-04-18 18:04:55.094  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.verboseResponses: false (default)
    2020-04-18 18:04:55.149  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.useEpoll: false (default)
    2020-04-18 18:04:56.827  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.useOpenSsl: true (default)
    2020-04-18 18:04:56.829  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.maxNumConnections: 2147483647 (default)
    2020-04-18 18:04:56.831  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.numCommonWorkers: 8 (default)
    2020-04-18 18:04:56.844  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.numCommonBlockingTaskThreads: 200 (default)
    2020-04-18 18:04:56.860  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultMaxRequestLength: 10485760 (default)
    2020-04-18 18:04:56.861  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultMaxResponseLength: 10485760 (default)
    2020-04-18 18:04:56.869  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultRequestTimeoutMillis: 10000 (default)
    2020-04-18 18:04:56.886  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultResponseTimeoutMillis: 15000 (default)
    2020-04-18 18:04:56.889  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultConnectTimeoutMillis: 3200 (default)
    2020-04-18 18:04:56.906  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultServerIdleTimeoutMillis: 15000 (default)
    2020-04-18 18:04:56.918  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultClientIdleTimeoutMillis: 10000 (default)
    2020-04-18 18:04:56.920  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultHttp2InitialConnectionWindowSize: 1048576 (default)
    2020-04-18 18:04:56.938  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultHttp2InitialStreamWindowSize: 1048576 (default)
    2020-04-18 18:04:56.953  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultHttp2MaxFrameSize: 16384 (default)
    2020-04-18 18:04:56.970  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultHttp2MaxStreamsPerConnection: 2147483647 (default)
    2020-04-18 18:04:56.979  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultHttp2MaxHeaderListSize: 8192 (default)
    2020-04-18 18:04:56.984  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultHttp1MaxInitialLineLength: 4096 (default)
    2020-04-18 18:04:56.994  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultHttp1MaxHeaderSize: 8192 (default)
    2020-04-18 18:04:57.018  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultHttp1MaxChunkSize: 8192 (default)
    2020-04-18 18:04:57.021  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultUseHttp2Preface: true (default)
    2020-04-18 18:04:57.031  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultUseHttp1Pipelining: false (default)
    2020-04-18 18:04:57.048  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultBackoffSpec: exponential=200:10000,jitter=0.2 (default)
    2020-04-18 18:04:57.049  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.defaultMaxTotalAttempts: 10 (default)
    2020-04-18 18:04:57.056  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.routeCache: maximumSize=4096 (default)
    2020-04-18 18:04:57.064  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.compositeServiceCache: maximumSize=256 (default)
    2020-04-18 18:04:57.071  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.parsedPathCache: maximumSize=4096 (default)
    2020-04-18 18:04:57.078  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.headerValueCache: maximumSize=4096 (default)
    2020-04-18 18:04:57.090  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.cachedHeaders: :authority,:scheme,:method,accept-encoding,content-type (default)
    2020-04-18 18:04:57.097  INFO 13064 --- [           main] c.l.a.c.Flags                            : com.linecorp.armeria.annotatedServiceExceptionVerbosity: unhandled (default)
    2020-04-18 18:04:57.102  INFO 13064 --- [           main] c.l.a.c.Flags                            : /dev/epoll not available: java.lang.IllegalStateException: Only supported on Linux
    2020-04-18 18:04:57.111  INFO 13064 --- [           main] c.l.a.c.Flags                            : Using OpenSSL: BoringSSL, 0x1010007f
    2020-04-18 18:04:57.550  INFO 13064 --- [           main] c.l.a.c.u.SystemInfo                     : Hostname: git (from 'hostname' command)
    2020-04-18 18:05:00.626  INFO 13064 --- [oss-http-*:9411] c.l.a.s.Server                           : Serving HTTP at /0:0:0:0:0:0:0:0:9411 - http://127.0.0.1:9411/
    2020-04-18 18:05:00.635  INFO 13064 --- [           main] c.l.a.s.ArmeriaAutoConfiguration         : Armeria server started at ports: {/0:0:0:0:0:0:0:0:9411=ServerPort(/0:0:0:0:0:0:0:0:9411, [http])}
    2020-04-18 18:05:00.688  INFO 13064 --- [           main] c.d.d.core                               : DataStax Java driver 3.7.1 for Apache Cassandra
    2020-04-18 18:05:00.705  INFO 13064 --- [           main] c.d.d.c.GuavaCompatibility               : Detected Guava >= 19 in the classpath, using modern compatibility layer
    
    
    运行控制台

    http://localhost:9411/zipkin/

    image-20200418180639830

    术语

    完整的调用链路

    image-20200418173034636

    上图what

    image-20200418173051740

    名词解释
    Trace:类似于树结构的Span结合,表示一条调用链路,存在唯一标识
    span:标识调用链路来源,通俗的理解span就是一次请求信息

    服务提供者

    修改cloud-provider-payment8001 module
    POM

    修改POM.xml

     <!--包含了sleuth+zipkin-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zipkin</artifactId>
    </dependency>
    
    
    YML

    修改application.yml,添加eureka.zipkin

     zipkin:
        base-url: http://localhost:9411
        sleuth:
          sampler:
            probability: 1
    
    
    业务类PaymentController

    com.atguigu.springcloud.controller.PaymentController

        @GetMapping("/zipkin")
        public String paymentZipkin() {
            return "hellp zipkin";
        }
    
    

    服务消费者

    修改cloud-consumer-order80
    POM

    修改pom.xml添加如下配置:

     <!--包含了sleuth+zipkin-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zipkin</artifactId>
    </dependency>
    
    
    YML

    application.yaml,添加spring.zipkin

      zipkin:
        base-url: http://localhost:9411
        sleuth:
          sampler:
            probability: 1
    
    
    业务类OrderMain80

    com.xzq.springcloud.controller.OrderController

        @GetMapping("/consumer/payment/zipkin")
        public String paymentZipkin() {
            return restTemplate.getForObject(PAYMENT_URL + "/payment/zipkin", String.class);
        }
    
    

    依次启动eureka7001/Cloud-provider-payment8001/cloud-consumer-order80

    ​ 80调用8001几次测试下: http://localhost/consumer/payment/zipkin

    image-20200418184612093

    打开浏览器访问http://localhost:9411

    ​ 会出现以下界面

    image-20200418173113962

    查看

    image-20200418173127926

    ​ 查看依赖关系

    image-20200418184900101

    17. SpringCloud Alibaba入门简介

    1)why会出现SpringCloud alibaba

    Spring Cloud Netflix项目进入到维护模式

    image-20200418185923765 SpringCloud Netflix Projects Entering Maintenance Mode
    什么是维护模式

    image-20200418185209418 进入维护模式意味着什么

    image-20200418185226008

    image-20200418185239284

    2)SpringCloud alibaba带来了什么

    是什么

    image-20200418190531593

    能干嘛

    image-20200418190508878

    去哪下

    https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
    https://spring.io/projects/spring-cloud-alibaba#learn

    怎么玩

    image-20200418190827019

    3)SpringCloud alibaba学习资料获取

    image-20200418190951969

    image-20200418190920320

    18. SpringCloud Alibaba Nacos服务注册和配置中心

    1)Nacos简介

    为什么叫Nacos

    Nacos为Nameing,Configuration和Service的组合。

    是什么

    • Nacos一个更易于构建原生应用的动态服务发现、配置管理和服务管理平台

    • Nacos就是注册中心+配置中心的组合,等价于Nacos=Eureka+Config+Bus

    能干嘛

    替代Eureka做服务注册中心
    替代Config做服务配置中心

    去哪下

    下载地址:https://github.com/alibaba/Nacos

    官网文档
    https://nacos.io/zh-cn/
    https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_nacos_discovery

    各种注册中心对比

    image-20200418211953796

    据称在阿里巴巴内部,Nacos有超过10万的运行实例,已经过了类似双十一等各种大型流量的考验

    2)安装并运行Nacos

    1. 已经安装了Java8+Maven

    2. 下载Nacos

    3. 解压安装包,并运行bin目录下的startup.cmd

      D:Program_Files
      acos-server-1.2.1
      acosin>startup.cmd
      
               ,--.
             ,--.'|
         ,--,:  : |                                           Nacos 1.2.1
      ,`--.'`|  ' :                       ,---.               Running in stand alone mode, All function modules
      |   :  :  | |                      '   ,'   .--.--.    Port: 8848
      :   |    | :  ,--.--.     ,---.  /   /   | /  /    '   Pid: 23440
      |   : '  '; | /          /     .   ; ,. :|  :  /`./   Console: http://169.254.204.80:8848/nacos/index.html
      '   ' ;.    ;.--.  .-. | /    / ''   | |: :|  :  ;_
      |   | |    | \__/: . ..    ' / '   | .; :       `.      https://nacos.io
      '   : |  ; .' ," .--.; |'   ; :__|   :    |  `----.   
      |   | '`--'  /  /  ,.  |'   | '.'|     /  /  /`--'  /
      '   : |     ;  :   .'      :    : `----'  '--'.     /
      ;   |.'     |  ,     .-./     /            `--'---'
      '---'        `--`---'     `----'
      
      2020-04-18 21:13:16,013 INFO Bean 'org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration' of type [org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration$$EnhancerBySpringCGLIB$$aa5a2904] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
      
      2020-04-18 21:13:16,130 INFO Bean 'objectPostProcessor' of type [org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
      
      2020-04-18 21:13:16,133 INFO Bean 'org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler@1a245833' of type [org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
      
      2020-04-18 21:13:16,141 INFO Bean 'org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration' of type [org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration$$EnhancerBySpringCGLIB$$cf2ecbb6] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
      
      2020-04-18 21:13:16,156 INFO Bean 'methodSecurityMetadataSource' of type [org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
      
      2020-04-18 21:13:18,396 INFO Tomcat initialized with port(s): 8848 (http)
      
      2020-04-18 21:13:18,716 INFO Root WebApplicationContext: initialization completed in 6113 ms
      
      2020-04-18 21:13:33,062 INFO Initializing ExecutorService 'applicationTaskExecutor'
      
      2020-04-18 21:13:33,398 INFO Adding welcome page: class path resource [static/index.html]
      
      2020-04-18 21:13:34,037 INFO Creating filter chain: Ant [pattern='/**'], []
      
      2020-04-18 21:13:34,103 INFO Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@72f46e16, org.springframework.security.web.context.SecurityContextPersistenceFilter@314b8f2d, org.springframework.security.web.header.HeaderWriterFilter@4a8a60bc, org.springframework.security.web.csrf.CsrfFilter@2cab9998, org.springframework.security.web.authentication.logout.LogoutFilter@71104a4, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5118388b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4a3e3e8b, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@3c9168dc, org.springframework.security.web.session.SessionManagementFilter@7859e786, org.springframework.security.web.access.ExceptionTranslationFilter@669513d8]
      
      2020-04-18 21:13:34,237 INFO Exposing 2 endpoint(s) beneath base path '/actuator'
      
      
    4. 访问http://localhost:8848/nacos
      默认用户名密码都是nacos

    5. 结果页面

    image-20200418211609259

    3)Nacos作为服务注册中心演示

    基于Nacos的服务提供者

    新建module"cloudalibaba-provider-payment9001"
    POM

    pom.xml

    <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <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.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    
    YML

    application.yml

    server:
      port: 9001
    
    spring:
      application:
        name: nacos-payment-provider
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
    
    management:
      endpoints:
        web:
          exposure:
            include: "*"
    
    
    主启动
    package com.xzq.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class PaymentMain9001 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain9001.class, args);
        }
    }
    
    
    
    业务类
    package com.atguigu.springcloud.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    
    @RestController
    public class PaymentController {
    
        @Value("${server.port}")
        private String serverPort;
    
        @GetMapping("/payment/nacos/{id}")
        public String getPayment(@PathVariable("id") Integer id) {
            return "nacos register, serverport=" + serverPort + "	 id:" + id;
        }
    }
    
    
    
    测试

    http://localhost:8848/nacos/#/serviceManagement

    image-20200418214421859

    http://localhost:9001/payment/nacos/1

    image-20200418214600743

    为了下一章演示nacos集群,参考9001新建9002

    如果不想拷贝,也可以使用虚拟端口映射

    image-20200418214041569

    基于Nacos的服务消费者

    新建Module“cloudalibaba-consumer-nacos-order83”
    POM

    pom.xml

    <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.xzq.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <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.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    

    为什么Nacos支持负载均衡

    image-20200418191522191

    YML

    application.yml

    server:
      port: 83
    
    spring:
      application:
        name: nacos-order-consumer
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
    
    #消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
    service-url:
      nacos-user-service: http://nacos-payment-provider
    
    
    主启动
    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class OrderNacosMain83 {
        public static void main(String[] args) {
            SpringApplication.run(OrderNacosMain83.class, args);
        }
    }
    
    
    
    业务类

    com.atguigu.springcloud.config.ApplicationContextConfig

    package com.atguigu.springcloud.config;
    
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.RestTemplate;
    
    
    @Configuration
    public class ApplicationContextConfig {
    
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    
    
    

    com.atguigu.springcloud.controller.OrderNacosController

    package com.atguigu.springcloud.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    
    @RestController
    public class OrderNacosController {
    
        @Autowired
        private RestTemplate restTemplate;
    
        @Value("${service-url.nacos-user-service}")
        private String serverUrl;
    
        @GetMapping("/consumer/payment/nacos/{id}")
        public String paymentInfo(@PathVariable("id") Integer id) {
            return restTemplate.getForObject(serverUrl + "/payment/nacos/" + id, String.class);
        }
    
    }
    
    
    
    测试

    http://localhost:8848/nacos/#/serviceManagement?namespace=

    image-20200418220241980

    http://localhost:83/consumer/payment/nacos/1

    image-20200418220116217

    如果访问报错:

    image-20200418215443848

    这是因为没有加上负载均衡注解所导致的,需要加上“@LoadBalanced”注解:

    @Configuration
    public class ApplicationContextConfig {
    
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    
    

    服务注册中心对比

    Nacos全景图所示

    image-20200418220821957

    Nacos和CAP

    image-20200418221007045

    image-20200418191540334

    切换
    Nacos支持AP和CP模式的切换

    image-20200418204350263

    4)Nacos作为服务配置中心演示

    Nacos作为配置中心-基础配置

    cloudalibaba-config-nacos-client3377
    POM

    pom.xml

    <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <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.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    
    YML

    ​ why配置两个

    image-20200418204439892 YML
    bootstrap

    server:
      port: 3377
    spring:
      application:
        name: nacos-config-client
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 # 注册中心
          config:
            server-addr: localhost:8848 # 配置中心
            file-extension: yaml # 这里指定的文件格式需要和nacos上新建的配置文件后缀相同,否则读不到
    #        group: TEST_GROUP
    #        namespace: 4ccc4c4c-51ec-4bd1-8280-9e70942c0d0c
    
    #  ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
    
    

    ​ application

    spring:
      profiles:
        active: dev # 开发环境
    #    active: test # 测试环境
    #    active: info # 开发环境
    
    
    主启动类
    package com.atguigu;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class NacosConfigClientMain3377 {
        public static void main(String[] args) {
            SpringApplication.run(NacosConfigClientMain3377.class, args);
        }
    }
    
    
    
    业务类
    package com.atguigu.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    
    @RestController
    @RefreshScope//实现配置自动更新
    public class ConfigClientController {
        @Value("${config.info}")
        private String configInfo;
    
        @GetMapping("/config/info")
        public String getConfigInfo() {
            return configInfo;
        }
    }
    
    
    
    在Nacos中添加配置信息

    ​ Nacos中的匹配规则
    ​ 理论
    ​ Nacos中的dataid的组成格式及与SpringBoot配置文件中的匹配规则
    ​ 官网

    image-20200418204512436

    ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
    
    

    实操
    配置新增

    image-20200418204530717

    ​ nacos-config-client-dev.yaml

    Nacos界面配置对应

    image-20200418204558540

    config:
       info: "config info for dev, from nacos nacos-config-client-dev.yaml config cente. version 2"
    
    

    设置DataId

    1. 公式

      ${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
      
      
    2. prefix默认为spring.application.name的值

    3. spring.profile.active即为当前环境对应的profile,可以通过配置项spring.profile.active来配置。

    4. file-exetension为配置内容的数据格式,可以通过配置项speing.cloud.nacos.config.file-extension配置
      小总结说明

    image-20200418204618687

    ​ 历史配置
    ​ Nacos惠济路配置文件的历史版本默认保留30天,此外还有一件回滚功能
    ​ 回滚

    测试

    启动前需要在nacos客户端-配置管理-配置管理栏目下有对应的yaml配置文件

    运行cloud-config-nacos-client3377的主启动类

    在运行过程中,经常会出现的一个小问题:

    image-20200418222625860

    这是因为Nacos中关于YAML文件命名的问题,在配置中心中YAML的文件后缀名也就是yaml,但是我们通常处于习惯将“application.yml”中的“spring.cloud.nacos.config.file-extension”中将后缀名写作“yml”,这样就导致了问题的产生。

    调用接口查看配置信息:http://localhost:3377/config/info

    image-20200418223910969

    自带动态刷新

    修改下Nacos中的yaml配置文件,再次调用查看配置的接口,就会发现配置已经刷新。

    Nacos作为配置中心-分类配置

    问题

    image-20200418204640385

    ​ 多环境多项目管理

    Nacos的图形化管理界面

    ​ 配置管理

    image-20200418204657248

    ​ 命名空间

    image-20200418204706198

    Namespace+group+data ID三者关系?为什么这么设计?

    image-20200418204724970

    默认情况:Namespace=public, Group=DEFAULT GROUP,默认Cluster是DEFAULT

    Nacos默认的命名空间是public,Namespace主要用来实现隔离。

    比方说我们现在有三个环境:开发、测试、生产环境。我们就可以创建三个Namespace,不同的Namespace之间是隔离的。

    Group默认是DEFAULT GROUP,Group可以把不同的微服务划分到同一个分组里面去。

    Service就是微服务:一个Service可以包含多个Cluster (集群) ,Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,这时就可以给杭州机房的Service微服务起一个集群名称(HZ),给广州机房的Service微服务起一个集群名称(GZ) ,还可以尽量让同一个机房的微服务互相调用,以提升性能。最后是Instance,就是微服务的实例。

    案例

    三种方案加载配置

    DataID方案

    通过指定spring.profile.active和配置文件的DataID,来使不同环境下读取不同的配置
    默认空间+默认分组+新建dev和test两个DataID

    • 新建dev配置DataID

    image-20200418204800706

    • 新建test配置DataID

    image-20200418204816825

    nacos-config-client-test.yaml

    config:
       info: "config info for test, from nacos nacos-config-client-test.yaml config center. version 1"
    
    
    • 通过spring.profile.acvice属性就能进行多环境下配置文件的读取

    image-20200418204831547

    • 测试

    http://localhost:3377/config/info,测试在开发环境和测试环境下,输出内容的变化。

    想要切换到开发环境下,则修改“spring.profiles.active”为“dev”,想要切换到测试环境,则修改配置为“test”即可。

    Group方案

    通过Group实现环境区分

    • 新建Group

    image-20200418204848696

    • 在nacos图形界面控制台上新建配置文件DataID

    image-20200418204900043

    nacos-config-client-info.yaml

    config:
       info: "nacos-config-client-info.yaml. DEV_GROUP version 1"
    
    

    nacos-config-client-info.yaml

    config:
       info: "nacos-config-client-info.yaml. TEST_GROUP version 1"
    
    
    • bootstrap+application

    image-20200418204913472

    在config下增加一条group的配置即可。可配置为DEV_GROUP或TEST_GROUP

    测试:http://localhost:3377/config/info

    1587262396650

    当将“spring.cloud.nacos.config.group”切换为“DEV_GROUP”时

    1587262555638

    Namespace方案
    • 新建dev/test的Namespace

    1587263984740

    • 回到服务管理-服务列表查看

    1587264110040

    按照域名配置填写

    1587263465560

    在dev 命名空间下,创建

    nacos-config-client-dev.yaml,组采用Default_Group

    config:
       info: "nacos-config-client-dev.yaml. dev namespace "
    
    

    nacos-config-client-dev.yaml,组采用DEV_GROUP

    config:
       info: "fb53660c-6e79-46b5-b7ac-909fe37db82f DEV_GROUP nacos-config-client-dev.yaml"
    
    

    nacos-config-client-dev.yaml,组采用TEST_GROUP

    config:
       info: "fb53660c-6e79-46b5-b7ac-909fe37db82f DEV_GROUPTEST_GROUP nacos-config-client-dev.yaml"
    
    

    测试:

    http://localhost:3377/config/info

    1587263608457

    当将“spring.cloud.nacos.config.namespace”切换为“test”的命名空间,并且在该命名空间下,建立对应的“nacos-config-client-test.yaml”文件,让它们处于不同的组内,然后将“application.yml”中的“spring.profiles.active”,切换为“test”,能够看到在该命名空间,该组别下的输出。

    5)Nacos集群和持久化配置(重要)

    官网说明

    https://nacos.io/zh-cn/docs
    官网架构图

    image-20200418205235928

    上图翻译

    image-20200418205249736

    image-20200418205259416

    说明

    image-20200418205317793

    image-20200418205329312

    按照上说,我们需要mysql数据库
    官网说明
    https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html

    1587264594379

    Nacos持久化配置解释

    Nacos默认自带的是嵌入式数据库derby

    1587264694537

    derby到mysql切换配置步骤
    (1)nacos-server-1.1.4 acosconf目录下找到sql脚本“nacos-mysql.sql”,然后执行脚本

    1587265092492

    (2)nacos-server-1.1.4 acosconf目录下找到application.properties,增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码。

    spring.datasource.platform=mysql
    
    db.num=1
    db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
    db.user=root
    db.password=Admin@123
    
    

    (3)重启Nacos,可以看到是个全新的空记录界面

    Linux版Nacos+MySQL生产环境配置

    预计需要,1个nginx+3个nacos注册中心,1个mysql
    Nacos下载Liunx版

    image-20200418205432429

    https://github.com/alibaba/nacos/releases
    ​ nacos-server-1.1.4.tar.gz
    ​ 解压后安装

    集群配置步骤

    ​ 1.Linux服务器上mysql数据库配置
    ​ SQL脚本在哪里
    ​ sql语句源文件
    ​ nacos-mysql.sql
    ​ 自己LInux机器上Mysql数据库粘贴
    ​ 2.application.properties配置
    ​ 位置

    image-20200418205504689

    ​ 内容

    image-20200418205516669

    ​ 3.Linux服务器上nacos的集群配置cluster.conf
    ​ 梳理出3台nacos机器的不同服务端口号
    ​ 复制出cluster.conf

    image-20200418205534092

    ​ 内容

    image-20200418205546048

    ​ 这个IP不能写127.0.0.1,必须是Linux命令hostname -i能够识别的IP

    image-20200418205607512

    ​ 4.编辑Nacos的启动脚本startup.sh,使他能够接受不同的启动端口
    ​ /mynacos/nacos/bin 目录下有startup.sh
    ​ 在什么地方,修改什么,怎么修改
    ​ 思考

    image-20200418205622502

    ​ 修改内容

    image-20200418205635250

    image-20200418205646970

    image-20200418205655769

    执行方式

    image-20200418205708064

    ​ 5.Nginx的配置,由他作为负载均衡器
    ​ 修改nginx的配置文件

    image-20200418205721923

    ​ nginx.conf

    image-20200418205737503

    image-20200418205811558

    ​ 按照指定启动
    ​ 6.截至到此为止,1个nginx+3个nacos注册中心+mysql
    ​ 测试通过nginx访问nacos
    http://192.168.111.144:1111/nacos/#/login
    ​ 新建一个配置测试
    ​ linux服务器的mysql插入一条记录

    测试

    ​ 微服务springalibaba-provider-payment9002启动注册进nacos集群
    ​ yml

    image-20200418205830000

    ​ 结果

    高可用小总结

    image-20200418205838190

    安装docker centos

    docker pull centos
    
    

    下载nacos,上传到192.168.137.14的/opt/software中,然后将它解压到/opt/module中

    
    

    启动docker centos容器:

    [root@nacos ~]# docker run -itd -v /opt/:/opt  --name centos7 centos    
    c451d1301e0ac62ec8543e6b74f8e17e2bce56fce2f545c93671057c4f7edb59
    
    
    

    查看容器:

    [root@nacos ~]# docker exec -it centos7 /bin/bash
    [root@c451d1301e0a /]# cd /opt/
    [root@c451d1301e0a opt]# ls
    containerd  data  edu  index.jsp  module  software  test  vod
    [root@c451d1301e0a opt]# cd software/
    [root@c451d1301e0a software]# ls
    jdk-8u144-linux-x64.tar.gz  mysql-libs.zip  nacos-server-1.2.1.zip 
     mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz  
    [root@c451d1301e0a software]# ll
    
    

    安装jdk:

    #JAVA_HOME
    export JAVA_HOME=/opt/module/jdk1.8.0_144
    export PATH=$PATH:$JAVA_HOME/bin
    
    

    启动三个Docker的Nacos实例

    运行Mysql

    [root@nacos ~]# docker pull nacos/nacos-server;
    Using default tag: latest
    latest: Pulling from nacos/nacos-server
    5ad559c5ae16: Pull complete 
    be7fcb81503b: Pull complete 
    184628106033: Pull complete 
    659182d1bc33: Pull complete 
    f50076ce88c1: Pull complete 
    7409127fbcf2: Pull complete 
    110ead5a3247: Pull complete 
    2a4cb2f6d49b: Pull complete 
    Digest: sha256:ab9a49756f23ba89c389e855a5ec0ae8d81adf6875c43817e8f7091b5b56d401
    Status: Downloaded newer image for nacos/nacos-server:latest
    docker.io/nacos/nacos-server:latest
    [root@nacos ~]# 
    
    
    [root@nacos ~]# docker images;
    REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
    nacos/nacos-server   latest              00b7582cb6e6        12 days ago         724MB
    
    [root@nacos ~]# 
    
    
    [root@nacos ~]# docker run -d --name "nacos" e791337790a6
    76f702585281e8fe39afe0dfaaa09db294f7de14bce4982290497b6d29257bad
    [root@nacos ~]# docker ps 
    CONTAINER ID IMAGE        COMMAND                CREATED        STATUS       PORTS  NAMES
    76f702585281 e791337790a6 "nginx -g 'daemon of…" 10 seconds ago Up 8 seconds 80/tcp nacos
    [root@nacos ~]# 
    
    

    其他

    1. 热部署

    1. devtools to your project添加依赖:

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-devtools</artifactId>
          <scope>runtime</scope>
          <optional>true</optional>
      </dependency>
      
      
      
    2. Adding plugin to your pom.xml

      下面配置我们粘贴进聚合父类总工程的pom.xml里
      <build>
          <fileName>你自己的工程名字<fileName>
          <plugins>
              <plugin>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-maven-plugin</artifactId>
                  <configuration>
                      <fork>true</fork>
                      <addResources>true</addResources>
                  </configuration>
              </plugin>
          </plugins>
      </build>
      
      
    3. Enabling automatic build
      image-20200408123513240

    1. Update the value of
      image-20200408123537139
    2. 重启idea

    2. @Resources和@AutoWired的区别?

    3. dashboard

    image-20200408154941604

    通过修改idea的workspace.xml的方式快速打开Run Dashboard窗口

    image-20200408173923258

    <component name="RunDashboard">
        <option name="configurationTypes">
          <set>
            <option value="SpringBootApplicationConfigurationType" />
          </set>
        </option>
      </component> 
    
    

    image-20200408173941942

    image-20200408173947977

    4. 检查服务提供者的健康状况

    image-20200408194618897

    5. hutool工具包

    https://www.hutool.cn/docs/#/

    该工具包提供了一系列能够媲美common-lang的一系列功能。

    6. @Autowired和@Resource的区别

  • 相关阅读:
    LVS负载均衡
    Firewalld防火墙
    前端性能优化----yahoo前端性能团队总结的35条黄金定律
    如何做好工作?
    需求分析的故事——如何练就需求分析的火眼金晴?
    让技术人员看得懂的流程-----面向对象设计全流程概述
    关于RESTFul初步理解
    初步研究全文搜索的解决方案
    Artech的MVC4框架学习——第八章View的呈现
    Artech的MVC4框架学习——第七章Action的执行
  • 原文地址:https://www.cnblogs.com/cosmos-wong/p/12921703.html
Copyright © 2020-2023  润新知