SpringCloud:高级上篇,负载均衡Ribbon及策略,声明式服务调用Feign及性能优化,服务容错保护Hystrix雪崩Feign线程池与信号量隔离RabbitMQ监控,微服务设计模式及项目

SpringCloud高级部分(上篇)

目录

第一章 负载均衡Ribbon. 1

一、    Ribbon在微服务中的作用... 1

1      什么是Ribbon. 1

2      Ribbon解决了什么问题... 1

二、    集中式与进程内负载均衡的区别... 2

1      负载均衡解决方案的分类... 2

2      两种负载均衡方式结构图... 2

三、    Ribbon的入门案例... 2

1      Consumer. 2

2      Consumer的配置文件... 3

3      Provider的集群部署... 4

4      启动Consumer. 6

四、    Ribbon的常见负载均衡策略... 6

五、    Ribbon指定其他负载均衡策略... 9

1      修改代码更换负载均衡策略... 9

2      修改配置文件更换负载均衡策略... 9

六、    Ribbon的点对点直连... 10

1      创建项目... 10

2      去掉Eureka的坐标添加Ribbon坐标... 10

3      修改配置文件去掉与Eureka相关的配置,添加新配置项... 10

4      修改启动类去掉报错代码... 10

第二章 声明式服务调用Feign. 1

一、    什么是Feign. 1

二、    什么是声明式,有什么作用,解决什么问题?... 1

三、    编写Feign的入门案例... 2

1      需求... 2

2      项目设计... 2

3      创建项目... 2

四、    Feign的请求参数处理... 11

1      单个参数处理... 11

2      多个参数处理... 13

五、    Feign的性能优化... 18

1      通过Gzip压缩算法,提升网络通信速度... 18

2      编写支持Gzip压缩案例... 19

3      采用Http连接池,提升Feign的并发吞吐量... 20

4      将Feign的Http客户端工具修改为HttpClient 21

六、    查看微服务日志中记录每个接口URL,状态码和耗时信息... 23

1      创建项目... 23

2      添加logback.xml文件... 23

3      Logback的输出日志级别需要时debug级别... 23

4      在启动类中添加一个方法... 23

七、    配置Feign负载均衡请求超时时间... 24

1      修改配置文件,设置超时时间... 24

第三章 服务容错保护Hystrix. 1

一、    什么是灾难性的雪崩效应... 2

二、    如何解决灾难性雪崩效应... 5

1      降级... 6

2      请求缓存... 9

3      请求合并... 14

4      服务熔断... 18

5      隔离... 21

三、    线程池隔离和信号量隔离的区别... 30

四、    Feign的雪崩处理... 30

1      Feign的服务降级处理... 30

2      降级后的异常记录... 32

五、    可视化的数据监控Hystrix-dashboard. 33

1      创建项目... 34

2      修改pom文件添加Hystrix-dashboard坐标... 34

3      修改启动类... 36

4      Hystrix-dashboard监控中心... 37

六、    使用Turbine在多个服务与集群情况下收集数据监控... 41

1      创建Turbine项目... 41

2      使用Turbine聚合多个服务... 45

3      监控集群... 48

七、    采用RabbitMQ,收集监控数据... 48

1      创建Consumer服务... 49

2      修改Consumer服务的pom文件... 49

3      修改Consumer的配置文件... 51

4      修改Consumer启动类... 52

5      创建Turbine项目... 52

6      修改Turbine的pom文件... 52

7      修改Turbine的配置文件... 54

8      修改Turbine的启动类... 54

第四章 如何设计微服务... 1

一、    微服务架构的六种常用设计模式... 1

1      代理设计模式... 1

2      聚合设计模式... 2

3      链条设计模式... 2

4      聚合链条设计模式... 2

5      数据共享设计模式... 3

6      异步消息设计模式... 3

二、    微服务设计模式实战... 4

1      需求分析... 4

2      数据库设计... 4

3      Mybatis的generatorSqlmapCustom工具使用... 7

4      设计product服务... 7

5      设计user服务... 13

6      设计Order服务... 19

7      设计Consumer. 25

8      添加业务逻辑... 28

9      业务逻辑实现... 30

 

第一章 负载均衡Ribbon

(Spring Cloud高级)

一、 Ribbon在微服务中的作用

1 什么是Ribbon

1.Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。

2.它不像spring cloud服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个spring cloud 微服务中。包括feign提供的声明式服务调用也是基于该Ribbon实现的。

3.ribbon默认提供很多种负载均衡算法,例如 轮询、随机 等等。甚至包含自定义的负载均衡算法。

2 Ribbon解决了什么问题

他解决并提供了微服务的负载均衡的问题。

二、 集中式与进程内负载均衡的区别

1 负载均衡解决方案的分类

目前业界主流的负载均衡方案可分成两类:

第一类:集中式负载均衡, 即在consumer和provider之间使用独立的负载均衡设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把 访问请求 通过某种策略转发至provider;

第二类:进程内负载均衡,将负载均衡逻辑集成到consumer,consumer从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的provider。

Ribbon就属于后者,它只是一个类库,集成于consumer进程,consumer通过它来获取到provider的地址。

2 两种负载均衡方式结构图

 

三、 Ribbon的入门案例

Ribbon中对于集群的服务采用的负载均衡的策略默认的是轮询

1 Consumer

@Service

public class UserService {

   @Autowired

   private LoadBalancerClient loadBalancerClient;//ribbon负载均衡器

  

   public List<User> getUsers(){

         //选择调用的服务的名称

      //ServiceInstance 封装了服务的基本信息,如 IP,端口

      ServiceInstance si = this.loadBalancerClient.choose("eureka-provider");

      //拼接访问服务的URL

      StringBuffer sb = new StringBuffer();

      //http://localhost:9090/user

      sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/user");

      System.out.println(sb.toString());

      //springMVC RestTemplate

      RestTemplate rt = new RestTemplate();

     

      ParameterizedTypeReference<List<User>> type = new ParameterizedTypeReference<List<User>>() {};

     

      //ResponseEntity:封装了返回值信息

      ResponseEntity<List<User>> response = rt.exchange(sb.toString(),HttpMethod.GET, null, type);

      List<User> list =response.getBody();

      return list;

   }

}

2 Consumer的配置文件

spring.application.name=eureka-consumer

server.port=9091

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

3 Provider的集群部署

3.1 将provider打包。部署到linux环境中

3.2 创建启动脚本server.sh

#!/bin/bash

 

cd `dirname $0`

 

CUR_SHELL_DIR=`pwd`

CUR_SHELL_NAME=`basename ${BASH_SOURCE}`

 

JAR_NAME="springcloud-eureka-provider-0.0.1-SNAPSHOT.jar"

JAR_PATH=$CUR_SHELL_DIR/$JAR_NAME

 

#JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:PermSize=128m"

JAVA_MEM_OPTS=""

 

#SPRING_PROFILES_ACTIV="-Dspring.profiles.active=eureka2"

SPRING_PROFILES_ACTIV=""

LOG_DIR=$CUR_SHELL_DIR/logs

LOG_PATH=$LOG_DIR/${JAR_NAME%..log

 

echo_help()

{

    echo -e "syntax: sh $CUR_SHELL_NAME start|stop"

}

 

if [ -z $1 ];then

    echo_help

    exit 1

fi

 

if [ ! -d "$LOG_DIR" ];then

    mkdir "$LOG_DIR"

fi

 

if [ ! -f "$LOG_PATH" ];then

    touch "$LOG_DIR"

fi

 

if [ "$1" == "start" ];then

 

    # check server

    PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'`

    if [ -n "$PIDS" ]; then

        echo -e "ERROR: The $JAR_NAME already started and the PID is ${PIDS}."

        exit 1

    fi

 

    echo "Starting the $JAR_NAME..."

 

    # start

    nohup java $JAVA_MEM_OPTS -jar $SPRING_PROFILES_ACTIV $JAR_PATH >> $LOG_PATH 2>&1 &

 

    COUNT=0

    while [ $COUNT -lt 1 ]; do

        sleep 1

        COUNT=`ps  --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}' | wc -l`

        if [ $COUNT -gt 0 ]; then

            break

        fi

    done

    PIDS=`ps  --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}'`

    echo "${JAR_NAME} Started and the PID is ${PIDS}."

    echo "You can check the log file in ${LOG_PATH} for details."

 

elif [ "$1" == "stop" ];then

 

    PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'`

    if [ -z "$PIDS" ]; then

        echo "ERROR:The $JAR_NAME does not started!"

        exit 1

    fi

 

    echo -e "Stopping the $JAR_NAME..."

 

    for PID in $PIDS; do

        kill $PID > /dev/null 2>&1

    done

 

    COUNT=0

    while [ $COUNT -lt 1 ]; do

        sleep 1

        COUNT=1

        for PID in $PIDS ; do

            PID_EXIST=`ps --no-heading -p $PID`

            if [ -n "$PID_EXIST" ]; then

                COUNT=0

                break

            fi

        done

    done

 

    echo -e "${JAR_NAME} Stopped and the PID is ${PIDS}."

else

    echo_help

    exit 1

fi

4 启动Consumer

四、 Ribbon的常见负载均衡策略

id策略名称策略对应的类名实现原理
1轮询策略(默认)RoundRobinRule轮询策略表示每次都顺序取下一个provider,比如一共有5个provider,第1次取第1个,第2次取第2个,第3次取第3个,以此类推
2权重轮询策略WeightedResponseTimeRule1.根据每个provider的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。 2.原理:一开始为轮询策略,并开启一个计时器,每30秒收集一次每个provider的平均响应时间,当信息足够时,给每个provider附上一个权重,并按权重随机选择provider,高权越重的provider会被高概率选中。
3随机策略RandomRule从provider列表中随机选择一个provider
4最少并发数策略BestAvailableRule选择正在请求中的并发数最小的provider,除非这个provider在熔断中。
5在“选定的负载均衡策略”基础上进行重试机制RetryRule1.“选定的负载均衡策略”这个策略是轮询策略RoundRobinRule 2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择provider不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的provider
6可用性敏感策略AvailabilityFilteringRule:过滤性能差的provider,有2种: 第一种:过滤掉在eureka中处于一直连接失败provider 第二种:过滤掉高并发的provider
7区域敏感性策略ZoneAvoidanceRule:1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的provider 2.如果这个ip区域内有一个或多个实例不可达或响应变慢,都会降低该ip区域内其他ip被选中的权重。

 

五、 Ribbon指定其他负载均衡策略

1 修改代码更换负载均衡策略

1.1 创建项目

1.2 在启动类中添加创建负载均衡策略对象的方法

@EnableEurekaClient

@SpringBootApplication

public class ConsumerApplication {

  

   @Bean

   public RandomRule createRule(){

      return new RandomRule();

   }

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

2 修改配置文件更换负载均衡策略

#设置负载均衡策略 eureka-provider为调用的服务的名称

eureka-provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

六、 Ribbon的点对点直连

1 创建项目

2 去掉Eureka的坐标添加Ribbon坐标

<!-- ribbon坐标 -->

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-ribbon</artifactId>

      </dependency>

3 修改配置文件去掉与Eureka相关的配置,添加新配置项

spring.application.name=eureka-consumer-LB

server.port=9091

#禁用 eureka

ribbon.eureka.enabled=false

#指定具体的服务实例清单

eureka-provider.ribbon.listOfServers=192.168.70.137:9090

4 修改启动类去掉报错代码

@SpringBootApplication

public class ConsumerApplication {

  

   /*@Bean

   public RandomRule createRule(){

      return new RandomRule();

   }*/

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

 

第二章 声明式服务调用Feign

(Spring Cloud高级)

 

一、 什么是Feign

Feign是一种声明式、模板化的HTTP客户端(仅在consumer中使用)。

二、 什么是声明式,有什么作用,解决什么问题?

声明式调用就像调用本地方法一样调用远程方法;无感知远程http请求。

1,Spring Cloud的声明式调用, 可以做到使用 HTTP请求远程服务时能就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。

2,它像Dubbo一样,consumer直接调用接口方法调用provider,而不需要通过常规的Http Client构造请求再解析返回数据。

3,它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。

三、 编写Feign的入门案例

1 需求

实现Ego电商平台中的商品基本操作

2 项目设计

3 创建项目

3.1 创建Product-Service

3.1.1创建项目

3.1.2修改pom文件添加依赖

<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">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.bjsxt</groupId>

  <artifactId>springcloud-ego-product-service</artifactId>

  <version>0.0.1-SNAPSHOT</version>

 

  <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-config</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

   </dependencies>

</project>

3.1.3创建Service接口

/**

 * Product服务接口

 * @author Administrator

 *

 */

@RequestMapping("/product")

public interface ProductService {

  

   //查询所有商品

   @RequestMapping(value="/findAll",method=RequestMethod.GET)

   public List<Product> findAll();

}

3.1.4创建POJO类

public class Product {

  

   private Integer id;

   private String name;

   public Integer getId() {

      return id;

   }

   public void setId(Integer id) {

      this.id = id;

   }

   public String getName() {

      return name;

   }

   public void setName(String name) {

      this.name = name;

   }

   public Product(Integer id, String name) {

      super();

      this.id = id;

      this.name = name;

   }

   public Product() {

      super();

      // TODO Auto-generated constructor stub

   }

  

}

3.2 创建Product-Provider

3.2.1创建项目

3.2.2修改pom文件添加依赖

<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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>springcloud-ego-product-provider</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-config</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

      <!-- 添加product-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

         <artifactId>springcloud-ego-product-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

3.2.3添加全局配置文件

spring.application.name=ego-product-provider

server.port=9001

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

3.2.4编写Controller

/**

 * Product-Provider服务

 * @author Administrator

 *

 */

@RestController

public class ProductController implements ProductService {

   @Override

   public List<Product> findAll() {

       List<Product> list = new ArrayList<>();

       list.add(new Product(1, "电视"));

       list.add(new Product(2, "电脑"));

       list.add(new Product(3, "冰箱"));

       list.add(new Product(4, "手电筒"));

      return list;

   }

}

3.2.5编写SpringBoot的启动类

@EnableEurekaClient

@SpringBootApplication

public class ProviderApplication {

   public static void main(String[] args) {

      SpringApplication.run(ProviderApplication.class, args);

   }

}

3.3 创建Product-consumer

3.3.1创建项目

 

3.3.2修改pom文件添加依赖

<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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>springcloud-ego-product-consumer</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-config</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

      <!-- 添加Feign坐标 -->

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-feign</artifactId>

      </dependency>

      <!-- 添加product-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

         <artifactId>springcloud-ego-product-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

3.3.3添加全局配置文件

spring.application.name=ego-product-consumer

server.port=9002

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

3.3.4编写Controller

/**

 * Product Consumer 服务

 * @author Administrator

 *

 */

@RestController

public class ProductController {

   @Autowired

   ProductConsumerService consumerservice;

   /**

    * Consumer中的查询所有商品的方法

    * @return

    */

   @RequestMapping(value="/list",method=RequestMethod.GET)

   public List<Product> getAll(){

      return this.consumerservice.findAll();

   }

}

3.3.5编写Service接口

@FeignClient(name="ego-product-provider")

public interface ProductConsumerService extends ProductService{

}

3.3.6编写SpringBoot启动类

@EnableFeignClients

@EnableDiscoveryClient

@SpringBootApplication

public class ConsumerApplication {

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

四、 Feign的请求参数处理

1 单个参数处理

1.1 修改Product-Service

/**

 * Product服务接口

 * @author Administrator

 *

 */

@RequestMapping("/product")

public interface ProductService {

  

   //查询所有商品

   @RequestMapping(value="/findAll",method=RequestMethod.GET)

   public List<Product> findAll();

  

   //根据商品ID查询商品

   @RequestMapping(value="/getProductById",method=RequestMethod.GET)

   public Product getProductById(@RequestParam("id") Integer id);

  

}

1.2 修改Product-Provider

/**

 * Product-Provider服务

 * @author Administrator

 *

 */

@RestController

public class ProductController implements ProductService {

   @Override

   public List<Product> findAll() {

       List<Product> list = new ArrayList<>();

       list.add(new Product(1, "电视"));

       list.add(new Product(2, "电脑"));

       list.add(new Product(3, "冰箱"));

       list.add(new Product(4, "手电筒"));

      return list;

   }

   @Override

   public Product getProductById(Integer id) {

      return new Product(id, "SpringCloud");

   }

}

1.3 修改Product-Consumer

/**

 * Product Consumer 服务

 * @author Administrator

 *

 */

@RestController

public class ProductController {

   @Autowired

   ProductConsumerService consumerservice;

   /**

    * Consumer中的查询所有商品的方法

    * @return

    */

   @RequestMapping(value="/list",method=RequestMethod.GET)

   public List<Product> getAll(){

      return this.consumerservice.findAll();

   }

  

   /**

    * Consumer中根据商品id查询商品

    */

   @RequestMapping(value="/get",method=RequestMethod.GET)

   public Product getProduct(@RequestParam("id") Integer id){

      return this.consumerservice.getProductById(id);

   }

  

}

 

2 多个参数处理

2.1 方式一 GET提交方式

2.1.1修改Product-Service

/**

 * Product服务接口

 * @author Administrator

 *

 */

@RequestMapping("/product")

public interface ProductService {

  

   //查询所有商品

   @RequestMapping(value="/findAll",method=RequestMethod.GET)

   public List<Product> findAll();

  

   //根据商品ID查询商品

   @RequestMapping(value="/getProductById",method=RequestMethod.GET)

   public Product getProductById(@RequestParam("id") Integer id);

  

   //添加商品传递多个参数 方式一 :GET方式

   @RequestMapping(value="/add",method=RequestMethod.GET)

   public Product addProduct(@RequestParam("id") Integer id,@RequestParam("name") String name);

}

2.1.2修改Product-Provider

/**

 * Product-Provider服务

 * @author Administrator

 *

 */

@RestController

public class ProductController implements ProductService {

   @Override

   public List<Product> findAll() {

       List<Product> list = new ArrayList<>();

       list.add(new Product(1, "电视"));

       list.add(new Product(2, "电脑"));

       list.add(new Product(3, "冰箱"));

       list.add(new Product(4, "手电筒"));

      return list;

   }

   @Override

   public Product getProductById(Integer id) {

      return new Product(id, "SpringCloud");

   }

   @Override

   public Product addProduct(Integer id, String name) {

      return new Product(id, name);

   }

2.1.3修改Product-Consumer

/**

 * Product Consumer 服务

 * @author Administrator

 *

 */

@RestController

public class ProductController {

   @Autowired

   ProductConsumerService consumerservice;

   /**

    * Consumer中的查询所有商品的方法

    * @return

    */

   @RequestMapping(value="/list",method=RequestMethod.GET)

   public List<Product> getAll(){

      return this.consumerservice.findAll();

   }

  

   /**

    * Consumer中根据商品id查询商品

    */

   @RequestMapping(value="/get",method=RequestMethod.GET)

   public Product getProduct(@RequestParam("id") Integer id){

      return this.consumerservice.getProductById(id);

   }

  

   /**

    * 商品添加 传递多个参数。方式一:GET

    */

   @RequestMapping(value="/add",method=RequestMethod.GET)

   public Product addProduct(Product product){

      return this.consumerservice.addProduct(product.getId(), product.getName());

   }

  

}

 

2.2 方式二 POST提交方式

2.2.1修改Product-Service

/**

 * Product服务接口

 * @author Administrator

 *

 */

@RequestMapping("/product")

public interface ProductService {

  

   //查询所有商品

   @RequestMapping(value="/findAll",method=RequestMethod.GET)

   public List<Product> findAll();

  

   //根据商品ID查询商品

   @RequestMapping(value="/getProductById",method=RequestMethod.GET)

   public Product getProductById(@RequestParam("id") Integer id);

  

   //添加商品传递多个参数 方式一 :GET方式

   @RequestMapping(value="/add",method=RequestMethod.GET)

   public Product addProduct(@RequestParam("id") Integer id,@RequestParam("name") String name);

  

   //添加商品传递多个参数 方式二 :POST方式

   @RequestMapping(value="/add",method=RequestMethod.POST)

   public Product addProduct2(@RequestBody Product product);

}

 

2.2.2修改Product-Provider

/**

 * Product-Provider服务

 * @author Administrator

 *

 */

@RestController

public class ProductController implements ProductService {

   @Override

   public List<Product> findAll() {

       List<Product> list = new ArrayList<>();

       list.add(new Product(1, "电视"));

       list.add(new Product(2, "电脑"));

       list.add(new Product(3, "冰箱"));

       list.add(new Product(4, "手电筒"));

      return list;

   }

   @Override

   public Product getProductById(Integer id) {

      return new Product(id, "SpringCloud");

   }

   @Override

   public Product addProduct(Integer id, String name) {

      return new Product(id, name);

   }

   @Override

   public Product addProduct2(@RequestBody Product product) {

      return product;

   }

}

 

2.2.3修改Product-Consumer

/**

 * Product Consumer 服务

 * @author Administrator

 *

 */

@RestController

public class ProductController {

   @Autowired

   ProductConsumerService consumerservice;

   /**

    * Consumer中的查询所有商品的方法

    * @return

    */

   @RequestMapping(value="/list",method=RequestMethod.GET)

   public List<Product> getAll(){

      return this.consumerservice.findAll();

   }

  

   /**

    * Consumer中根据商品id查询商品

    */

   @RequestMapping(value="/get",method=RequestMethod.GET)

   public Product getProduct(@RequestParam("id") Integer id){

      return this.consumerservice.getProductById(id);

   }

  

   /**

    * 商品添加 传递多个参数。方式一:GET

    */

   @RequestMapping(value="/add",method=RequestMethod.GET)

   public Product addProduct(Product product){

      return this.consumerservice.addProduct(product.getId(), product.getName());

   }

  

   /**

    * 商品添加 传递多个参数。方式二:POST

    */

   @RequestMapping(value="/add2",method=RequestMethod.GET)

   public Product addProduct2(Product product){

      return this.consumerservice.addProduct2(product);

   }

  

}

 

五、 Feign的性能优化

1 通过Gzip压缩算法,提升网络通信速度

1.1 gzip介绍

gzip介绍:gzip是一种数据格式,采用用deflate算法压缩data;gzip是一种流行的文件压缩算法,应用十分广泛,尤其是在Linux平台。

gzip能力:当Gzip压缩到一个纯文本文件时,效果是非常明显的,大约可以减少70%以上的文件大小。

gzip作用:网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是Gzip与搜索引擎的抓取工具有着更好的关系。例如 Google就可以通过直接读取gzip文件来比普通手工抓取 更快地检索网页。

1.2 HTTP协议中关于压缩传输的规定

第一:客户端向服务器请求中带有:Accept-Encoding:gzip, deflate 字段,向服务器表示,客户端支持的压缩格式(gzip或者deflate),如果不发送该消息头,服务器是不会压缩的。

第二:服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带Content-Encoding:gzip消息头,表示响应报文是根据该格式压缩过的。

第三:客户端接收到请求之后,先判断是否有Content-Encoding消息头,如果有,按该格式解压报文。否则按正常报文处理。

2 编写支持Gzip压缩案例

2.1 创建项目

 

2.2 修改配置文件

2.2.1只配置Consumer通过Feign到Provider的请求与相应的Gzip压缩

#-----------------------------feign gzip

#配置请求GZIP压缩

feign.compression.request.enabled=true

#配置响应GZIP压缩

feign.compression.response.enabled=true

#配置压缩支持的MIME TYPE

feign.compression.request.mime-types=text/xml,application/xml,application/json

#配置压缩数据大小的最小阀值,默认2048

feign.compression.request.min-request-size=512

2.2.2对客户端浏览器的请求以及Consumer对provider的请求与响应做Gzip压缩

#-----------------------------spring boot gzip

#是否启用压缩

server.compression.enabled=true

server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain

3 采用Http连接池,提升Feign的并发吞吐量

为什么http连接池能提升性能?

3.1 http的背景原理

a. 两台服务器建立http连接的过程是很复杂的一个过程,涉及到多个数据包的交换,并且也很耗时间。

b. Http连接需要的3次握手4次分手开销很大,这一开销对于大量的比较小的http消息来说更大。

3.2 优化解决方案

a. 如果我们直接采用http连接池,节约了大量的3次握手4次分手;这样能大大提升吞吐率。

b. feign的http客户端支持3种框架;HttpURLConnection、httpclient、okhttp;默认是HttpURLConnection。

c. 传统的HttpURLConnection是JDK自带的,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。对于网络请求这种底层相对复杂的操作,如果有可用的其他方案,也没有必要自己去管理连接对象。

d. HttpClient相比传统JDK自带的HttpURLConnection,它封装了访问http的请求头,参数,内容体,响应等等;它不仅使客户端发送HTTP请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性;另外高并发大量的请求网络的时候,还是用“连接池”提升吞吐量。

4 将Feign的Http客户端工具修改为HttpClient

4.1 创建项目

 

4.2 修改pom文件添加HttpClient的坐标

<!-- 使用Apache HttpClient替换Feign原生httpURLConnection -->

      <dependency>

         <groupId>org.apache.httpcomponents</groupId>

         <artifactId>httpclient</artifactId>

      </dependency>

      <dependency>

         <groupId>com.netflix.feign</groupId>

         <artifactId>feign-httpclient</artifactId>

         <version>8.17.0</version>

      </dependency>

4.3 修改配置文件开启HttpClient的使用

#启用httpclient

feign.httpclient.enabled=true

注意:如果使用HttpClient作为Feign的客户端工具。那么在定义接口上的注解是需要注意,如果传递的是一个自定义的对象(对象会使用json格式来专递)。需要制定类型。

4.4 Product-Service

/**

 * Product服务接口

 * @author Administrator

 *

 */

@RequestMapping("/product")

public interface ProductService {

  

   //查询所有商品

   @RequestMapping(value="/findAll",method=RequestMethod.GET)

   public List<Product> findAll();

  

   //根据商品ID查询商品

   @RequestMapping(value="/getProductById",method=RequestMethod.GET)

   public Product getProductById(@RequestParam("id") Integer id);

  

   //添加商品传递多个参数 方式一 :GET方式

   @RequestMapping(value="/add",method=RequestMethod.GET)

   public Product addProduct(@RequestParam("id") Integer id,@RequestParam("name") String name);

   //----------------------Httpclient----------------------------------------------

   //添加商品传递多个参数 方式二 :POST方式

   @RequestMapping(value="/add2",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)

   public Product addProduct2(@RequestBody Product product);

  

   //使用HttpClient工具 添加商品传递多个参数  :基于GET方式

   @RequestMapping(value="/add3",method=RequestMethod.GET,consumes=MediaType.APPLICATION_JSON_VALUE)

   public Product addProduct3(Product product);

}

 

六、 查看微服务日志中记录每个接口URL,状态码和耗时信息

1 创建项目

 

2 添加logback.xml文件

 

3 Logback的输出日志级别需要时debug级别

 <!-- 日志输出级别 -->

    <root level="DEBUG">  

        <appender-ref ref="Stdout" />  

        <appender-ref ref="RollingFile" />  

    </root>

4 在启动类中添加一个方法

@EnableFeignClients

@EnableDiscoveryClient

@SpringBootApplication

public class ConsumerApplication {

  

// NONE:不记录任何信息,默认值

// BASIC:记录请求方法、请求URL、状态码和用时

// HEADERS:在BASIC基础上再记录一些常用信息

// FULL:记录请求和相应的所有信息

   @Bean

   public Logger.Level getLog(){

      return Logger.Level.FULL;

   }

  

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

 

七、 配置Feign负载均衡请求超时时间

Feign的负载均衡底层用的就是Ribbon

 

1 修改配置文件,设置超时时间

1.1 全局配置

#全局配置

# 请求连接的超时时间 默认的时间为1秒

ribbon.ConnectTimeout=5000

# 请求处理的超时时间

ribbon.ReadTimeout=5000

1.2    根据服务名称进行局部超时配置

#局部配置

# 对所有操作请求都进行重试

ego-product-provider.ribbon.OkToRetryOnAllOperations=true

# 对当前实例的重试次数

ego-product-provider.ribbon.MaxAutoRetries=2

# 切换实例的重试次数

ego-product-providert.ribbon.MaxAutoRetriesNextServer=0

# 请求连接的超时时间

ego-product-provider.ribbon.ConnectTimeout=3000

# 请求处理的超时时间

ego-product-provider.ribbon.ReadTimeout=3000

 

第三章 服务容错保护Hystrix

(Spring Cloud高级)

 

一、 什么是灾难性的雪崩效应

 

 

 

二、 如何解决灾难性雪崩效应

降级

超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值.

隔离(线程池隔离和信号量隔离)

限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。

熔断

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

缓存

提供了请求缓存。

请求合并

提供请求合并。

1 降级

对服务做降级处理

1.1 创建项目

1.2 修改pom文件添加hystrix的坐标

<dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

      </dependency>

1.3 修改配置文件

spring.application.name=eureka-consumer-ribbon-hystrix

server.port=9010

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

1.4 修改启动类开启熔断器

@EnableCircuitBreaker //开启熔断器 断路器

@EnableEurekaClient

@SpringBootApplication

public class ConsumerApplication {

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

1.5 在项目中添加Product实体类

public class Product {

  

   private Integer id;

   private String name;

   public Integer getId() {

      return id;

   }

   public void setId(Integer id) {

      this.id = id;

   }

   public String getName() {

      return name;

   }

   public void setName(String name) {

      this.name = name;

   }

   public Product(Integer id, String name) {

      super();

      this.id = id;

      this.name = name;

   }

   public Product() {

      super();

      // TODO Auto-generated constructor stub

   }

  

}

 

1.6 修改ProductService

@Service

public class ProductService {

   @Autowired

   private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器

   @HystrixCommand(fallbackMethod="fallback")

   public List<Product> getUsers() {

      // 选择调用的服务的名称

      // ServiceInstance 封装了服务的基本信息,如 IP,端口

      ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider");

      // 拼接访问服务的URL

      StringBuffer sb = new StringBuffer();

      // http://localhost:9001/product/findAll

      sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");

      System.out.println(sb.toString());

      // springMVC RestTemplate

      RestTemplate rt = new RestTemplate();

      ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {

      };

      // ResponseEntity:封装了返回值信息

      ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);

      List<Product> list = response.getBody();

      return list;

   }

  

   //返回托底数据的方法

   public List<Product> fallback(){

      List<Product> list = new ArrayList<>();

      list.add(new Product(-1, "我是托底数据"));

      return list;

   }

}

 

1.7 以下四种情况将触发getFallback调用

(1) 方法抛出非HystrixBadRequestException异常。

(2) 方法调用超时

(3) 熔断器开启拦截调用

(4) 线程池/队列/信号量是否跑满

2 请求缓存

Hystrix为了降低访问服务的频率,支持将一个请求与返回结果做缓存处理。如果再次请求的URL没有变化,那么Hystrix不会请求服务,而是直接从缓存中将结果返回。这样可以大大降低访问服务的压力。

Hystrix自带缓存。有两个缺点:

1. 是一个本地缓存。在集群情况下缓存是不能同步的。

2. 不支持第三方缓存容器。Redis,memcache不支持的。

可以使用spring的cache。

2.1 安装Redis

2.2 创建项目

2.3 修改pom文件添加springCache坐标

<?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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>springcloud-eureka-consumer-ribbon-cache</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <packaging>jar</packaging>

   <name>springcloud-eureka-consumer</name>

   <description>Demo project for Spring Boot</description>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-config</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

     

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

      </dependency>

      <!-- springCache -->

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-data-redis</artifactId>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

 

2.4 在配置文件中配置redis链接信息

spring.application.name=eureka-consumer-ribbon-cache

server.port=9010

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

# Redis

spring.redis.database=0

#Redis服务器地址

spring.redis.host=192.168.70.139

#Redis服务器连接端口

spring.redis.port=6379

#Redis服务器连接密码(默认为空)

spring.redis.password=

#连接池最大连接数(负值表示没有限制)

spring.redis.pool.max-active=100

#连接池最大阻塞等待时间(负值表示没有限制)

spring.redis.pool.max-wait=3000

#连接池最大空闭连接数

spring.redis.pool.max-idle=200

#连接汉最小空闲连接数

spring.redis.pool.min-idle=50

#连接超时时间(毫秒)

spring.redis.pool.timeout=600

2.5 修改启动类开启缓存

@EnableCaching

@EnableEurekaClient

@SpringBootApplication

public class ConsumerApplication {

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

2.6 修改ProductService

@CacheConfig(cacheNames={"com.bjsxt.ego.product"})

@Service

public class ProductService {

   @Autowired

   private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器

   @HystrixCommand(fallbackMethod="fallback")

   public List<Product> getUsers() {

      // 选择调用的服务的名称

      // ServiceInstance 封装了服务的基本信息,如 IP,端口

      ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider");

      // 拼接访问服务的URL

      StringBuffer sb = new StringBuffer();

      // http://localhost:9001/product/findAll

      sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");

      System.out.println(sb.toString());

      // springMVC RestTemplate

      RestTemplate rt = new RestTemplate();

      ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {

      };

      // ResponseEntity:封装了返回值信息

      ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);

      List<Product> list = response.getBody();

      return list;

   }

  

   //返回托底数据的方法

   public List<Product> fallback(){

      List<Product> list = new ArrayList<>();

      list.add(new Product(-1, "我是托底数据"));

      return list;

   }

  

   //根据ID查询商品

   @Cacheable(key="'product' + #id")

   public Product getProductById(Integer id){

      System.out.println("=========Get======"+id);

      return new Product(id, "新的商品");

   }

  

   //根据ID删除商品

   @CacheEvict(key="'product' + #id")

   public void delProductById(Integer id){

      System.out.println("=========Del======"+id);

   }

}

 

2.7 修改ProductController

@RestController

public class ProductController {

   @Autowired

   private ProductService userService;

   @RequestMapping("/consumer")

   public List<Product> getUsers() {

      return this.userService.getUsers();

   }

  

   @RequestMapping(value="/get",method=RequestMethod.GET)

   public Product get(Integer id){

      return this.userService.getProductById(id);

   }

  

   @RequestMapping(value="/del",method=RequestMethod.GET)

   public void del(Integer id){

      this.userService.delProductById(id);

   }

}

3 请求合并

没合并的请求

请求合并

什么情况下使用请求合并

在微服务架构中,我们将一个项目拆分成很多个独立的模块,这些独立的模块通过远程调用来互相配合工作,但是,在高并发情况下,通信次数的增加会导致总的通信时间增加,同时,线程池的资源也是有限的,高并发环境会导致有大量的线程处于等待状态,进而导致响应延迟,为了解决这些问题,我们需要来了解Hystrix的请求合并。

请求合并的缺点

设置请求合并之后,本来一个请求可能5ms就搞定了,但是现在必须再等10ms看看还有没有其他的请求一起的,这样一个请求的耗时就从5ms增加到15ms了,不过,如果我们要发起的命令本身就是一个高延迟的命令,那么这个时候就可以使用请求合并了,因为这个时候时间窗的时间消耗就显得微不足道了,另外高并发也是请求合并的一个非常重要的场景。

3.1 创建项目

3.2 修改pom文件添加hystrix坐标

<dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

 </dependency>

3.3 修改配置文件

spring.application.name=eureka-consumer-ribbon-batch

server.port=9010

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

3.4 修改ProductService

@Service

public class ProductService {

   //利用hystrix合并请求 

    @HystrixCollapser(batchMethod = "batchProduct", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL

         collapserProperties = { 

         //请求时间间隔在20ms之内的请求会被合并为一个请求,默认为10ms

            @HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),

            //设置触发批处理执行之前,在批处理中允许的最大请求数。

            @HystrixProperty(name = "maxRequestsInBatch", value = "200"), 

    }) 

   //consumer的controller调用的方法 该方法返回值必须要返回Future类型

   public Future<Product> getProduct(Integer id){

      System.out.println("=========="+id+"==========");

      return null;

   }

  

    @HystrixCommand

   //调用Provider服务的方法

   public List<Product> batchProduct(List<Integer> ids){

      for(Integer id:ids){

         System.out.println(id);

      }

      //假设是调用provider服务后返回的list

      List<Product> list = new ArrayList<>();

       list.add(new Product(1, "电视"));

       list.add(new Product(2, "电脑"));

       list.add(new Product(3, "冰箱"));

       list.add(new Product(4, "手电筒"));

       list.add(new Product(100,"list............"));

       System.out.println("ddddddddddddddddddddddd");

       return list;

   }

}

 

3.5 修改Controller

@RestController

public class ProductController {

   @Autowired

   private ProductService userService;

  

   @RequestMapping("/consumer")

   public void  getUsers() throws Exception{

      Future<Product> p1 = this.userService.getProduct(1);

      Future<Product> p2 = this.userService.getProduct(2);

      Future<Product> p3 = this.userService.getProduct(3);

      System.out.println(p1.get().toString());

      System.out.println(p2.get().toString());

      System.out.println(p3.get().toString());

   }

}

3.6 请求合并参数介绍

4 服务熔断

4.1 创建项目

4.2 修改pom文件添加Hystrix坐标

<dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

      </dependency>

4.3 修改配置文件

spring.application.name=eureka-consumer-ribbon-breaker

server.port=9010

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

4.4 修改启动类

@EnableCircuitBreaker //开启熔断器 断路器

@EnableEurekaClient

@SpringBootApplication

public class ConsumerApplication {

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

4.5 修改ProductService

@Service

public class ProductService {

   @Autowired

   private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器

   @HystrixCommand(fallbackMethod = "fallback",

         commandProperties = {

           //默认20个;10s内请求数大于20个时就启动熔断器,当请求符合熔断条件时将触发getFallback()。

            @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,

value="10"),

            //请求错误率大于50%时就熔断,然后for循环发起请求,当请求符合熔断条件时将触发getFallback()。

            @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,

value="50"),

            //默认5秒;熔断多少秒后去尝试请求

            @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,

value="5000"),

          })

   public List<Product> getUsers(int flag) {

     

      System.out.println(flag);

      if(flag == 1){

         throw new RuntimeException();

      }

      // 选择调用的服务的名称

      // ServiceInstance 封装了服务的基本信息,如 IP,端口

      ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider");

      // 拼接访问服务的URL

      StringBuffer sb = new StringBuffer();

      // http://localhost:9001/product/findAll

      sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");

      System.out.println(sb.toString());

      // springMVC RestTemplate

      RestTemplate rt = new RestTemplate();

      ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {

      };

      // ResponseEntity:封装了返回值信息

      ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);

      List<Product> list = response.getBody();

      return list;

   }

  

   //返回托底数据的方法

   public List<Product> fallback(int flag){

      List<Product> list = new ArrayList<>();

      list.add(new Product(-1, "我是托底数据"));

      return list;

   }

}

 

4.6 修改ProductController

@RestController

public class ProductController {

   @Autowired

   private ProductService userService;

  

   @RequestMapping("/consumer")

   public List<Product> getUsers(@RequestParam("flag") Integer flag){

      return this.userService.getUsers(flag);

   }

}

4.7 熔断参数

5 隔离

5.1 线程池隔离

5.1.1创建项目

5.1.2修改pom添加hystrix坐标

<dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

 </dependency>

5.1.3修改配置文件

spring.application.name=eureka-consumer-ribbon-threadpool

server.port=9010

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

5.1.4修改启动类

@EnableCircuitBreaker //开启熔断器 断路器

@EnableEurekaClient

@SpringBootApplication

public class ConsumerApplication {

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

5.1.5修改ProductService

@Service

public class ProductService {

   @Autowired

   private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器

   @HystrixCommand(groupKey="ego-product-provider", commandKey = "getUsers",

          threadPoolKey="ego-product-provider",

               threadPoolProperties = {

                  @HystrixProperty(name = "coreSize", value = "30"),//线程池大小

                  @HystrixProperty(name = "maxQueueSize", value = "100"),//最大队列长度

                  @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),//线程存活时间

                  @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15")//拒绝请求

          },

          fallbackMethod = "fallback")

   public List<Product> getUsers() {

      System.out.println(Thread.currentThread().getName());

      // 选择调用的服务的名称

      // ServiceInstance 封装了服务的基本信息,如 IP,端口

      ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider");

      // 拼接访问服务的URL

      StringBuffer sb = new StringBuffer();

      // http://localhost:9001/product/findAll

      sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");

      System.out.println(sb.toString());

      // springMVC RestTemplate

      RestTemplate rt = new RestTemplate();

      ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {

      };

      // ResponseEntity:封装了返回值信息

      ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);

      List<Product> list = response.getBody();

      return list;

   }

  

   //返回托底数据的方法

   public List<Product> fallback(){

      System.out.println(Thread.currentThread().getName());

      List<Product> list = new ArrayList<>();

      list.add(new Product(-1, "我是托底数据"));

      return list;

   }

  

   public void showThread(){

      System.out.println(Thread.currentThread().getName());

   }

}

 

5.1.6修改ProductController

@RestController

public class ProductController {

   @Autowired

   private ProductService userService;

  

   @RequestMapping("/consumer")

   public List<Product> getUsers(){

      return this.userService.getUsers();

   }

  

   @RequestMapping("/consumer1")

   public void getUsers1(){

      this.userService.showThread();

   }

}

 

5.1.7线程池隔离参数

5.2 信号量隔离

5.2.1创建项目

5.2.2修改pom文件添加hystrix的坐标

<dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

      </dependency>

5.2.3修改配置文件

spring.application.name=eureka-consumer-ribbon-semaphore

server.port=9010

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

5.2.4修改启动类

@EnableCircuitBreaker //开启熔断器 断路器

@EnableEurekaClient

@SpringBootApplication

public class ConsumerApplication {

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

 

5.2.5修改ProductService

@Service

public class ProductService {

   @Autowired

   private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器

   @HystrixCommand(fallbackMethod = "fallback",

         commandProperties = {

            @HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY,

value="SEMAPHORE"),// 信号量 隔离

            @HystrixProperty

(name=HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="100")//信号量最大并度

          })

   public List<Product> getUsers() {

      System.out.println(Thread.currentThread().getName());

      // 选择调用的服务的名称

      // ServiceInstance 封装了服务的基本信息,如 IP,端口

      ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider");

      // 拼接访问服务的URL

      StringBuffer sb = new StringBuffer();

      // http://localhost:9001/product/findAll

      sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");

      System.out.println(sb.toString());

      // springMVC RestTemplate

      RestTemplate rt = new RestTemplate();

      ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {

      };

      // ResponseEntity:封装了返回值信息

      ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);

      List<Product> list = response.getBody();

      return list;

   }

  

   //返回托底数据的方法

   public List<Product> fallback(){

      System.out.println(Thread.currentThread().getName());

      List<Product> list = new ArrayList<>();

      list.add(new Product(-1, "我是托底数据"));

      return list;

   }

  

   public void showThread(){

      System.out.println(Thread.currentThread().getName());

   }

}

 

5.2.6信号量隔离参数

三、 线程池隔离和信号量隔离的区别

四、 Feign的雪崩处理

1 Feign的服务降级处理

1.1 创建项目

1.2 修改配置文件开启对hystrix的支持

spring.application.name=springcloud-eureka-consumer-feign-fallback

server.port=9020

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

#Feign默认是不开启Hystrix的。默认为:false

feign.hystrix.enabled=true

1.3 修改ProductConsumerService

@FeignClient(name="ego-product-provider",fallback=ProductServiceFallback.class)

public interface ProductConsumerService {

       //查询所有商品

      @RequestMapping(value="/product/findAll",method=RequestMethod.GET)

      public List<Product> findAll();

}

1.4 添加ProductServiceFallback类

@Component

public class ProductServiceFallback implements ProductConsumerService {

   //能够返回托底数据的fallback方法

   @Override

   public List<Product> findAll() {

      List<Product> list = new ArrayList<>();

      list.add(new Product(-1, "我是托底数据"));

      return list;

   }

}

 

1.5 修改ProductController

/**

 * Product Consumer 服务

 * @author Administrator

 *

 */

@RestController

public class ProductController {

   @Autowired

   ProductConsumerService consumerservice;

   /**

    * Consumer中的查询所有商品的方法

    * @return

    */

   @RequestMapping(value="/list",method=RequestMethod.GET)

   public List<Product> getAll(){

      return this.consumerservice.findAll();

   }

}

 

2 降级后的异常记录

2.1 创建项目

2.2 添加ProductServiceFallbackFactory类

@Component

public class ProductServiceFallbackFactory implements FallbackFactory<ProductConsumerService> {

   Logger logger = LoggerFactory.getLogger(ProductServiceFallbackFactory.class);

   @Override

   public ProductConsumerService create(final Throwable arg0) {

     

      return new ProductConsumerService() {

        

         //能够返回托底数据的fallback方法

         @Override

         public List<Product> findAll() {

            logger.warn("Fallback Exception: ",arg0);

            List<Product> list = new ArrayList<>();

            list.add(new Product(-1, "我是托底数据"));

            return list;

         }

      };

   }

}

 

2.3 修改ProductConsumerService

@FeignClient(name="ego-product-provider",fallbackFactory=ProductServiceFallbackFactory.class)

public interface ProductConsumerService {

       //查询所有商品

      @RequestMapping(value="/product/findAll",method=RequestMethod.GET)

      public List<Product> findAll();

}

五、 可视化的数据监控Hystrix-dashboard

Hystrix-dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard我们可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数据。

1 创建项目

2 修改pom文件添加Hystrix-dashboard坐标

<?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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>springcloud-eureka-consumer-ribbon-dashboard</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <packaging>jar</packaging>

   <name>springcloud-eureka-consumer</name>

   <description>Demo project for Spring Boot</description>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-config</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

     

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

      </dependency>

       

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-actuator</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>

        </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

 

3 修改启动类

@EnableCircuitBreaker //开启熔断器 断路器

@EnableEurekaClient

@SpringBootApplication

@EnableHystrix

@EnableHystrixDashboard

public class ConsumerApplication {

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

4 Hystrix-dashboard监控中心

4.1 创建项目

4.2 修改pom文件添加Hystrix-dashboard坐标

<?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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>springcloud-eureka-consumer-ribbon-dashboard</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <packaging>jar</packaging>

   <name>springcloud-eureka-consumer</name>

   <description>Demo project for Spring Boot</description>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-config</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

     

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

      </dependency>

        

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-actuator</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>

        </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

 

4.3 修改启动类

@EnableCircuitBreaker //开启熔断器 断路器

@EnableEurekaClient

@SpringBootApplication

@EnableHystrix

@EnableHystrixDashboard

public class ConsumerApplication {

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

4.4 启动顺序

先启动服务,在启动监控中心

4.5 访问监控中心

4.6 首页介绍

4.7 监控中心图解

 

 

六、 使用Turbine在多个服务与集群情况下收集数据监控

Turbine**是聚合服务器发送事件流数据的一个工具,hystrix的监控中,只能监控单个节点,实际生产中都为集群,因此可以通过turbine来监控集群服务。**

1 创建Turbine项目

1.1 修改pom文件添加turbine坐标

<?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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>springcloud-eureka-consumer-turbine</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <packaging>jar</packaging>

   <name>springcloud-eureka-consumer</name>

   <description>Demo project for Spring Boot</description>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-config</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

     

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

      </dependency>

       

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-actuator</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>

        </dependency>

        <!-- 添加turbine坐标 -->

           <dependency>

           <groupId>org.springframework.cloud</groupId>

           <artifactId>spring-cloud-starter-turbine</artifactId>

       </dependency>

       <dependency>

           <groupId>org.springframework.cloud</groupId>

           <artifactId>spring-cloud-netflix-turbine</artifactId>

       </dependency>

       

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

1.2 修改配置文件

spring.application.name=eureka-consumer-hystrix-turbine

server.port=1002

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

#---------------------------------------turbine--------------------------

#配置Eureka中的serviceId列表,表明监控哪些服务

turbine.appConfig=eureka-consumer-ribbon-threadpool,springcloud-eureka-consumer-feign-fallback

#指定聚合哪些集群,多个使用","分割,默认为default。可使用http://.../turbine.stream?cluster={clusterConfig之一}访问

turbine.aggregator.clusterConfig= default

# 1. clusterNameExpression指定集群名称,默认表达式appName;此时:turbine.aggregator.clusterConfig需要配置想要监控的应用名称;

# 2. 当clusterNameExpression: default时,turbine.aggregator.clusterConfig可以不写,因为默认就是default;

# 3. 当clusterNameExpression: metadata['cluster']时,假设想要监控的应用配置了eureka.instance.metadata-map.cluster: ABC,

#          则需要配置,同时turbine.aggregator.clusterConfig: ABC

turbine.clusterNameExpression="default"

1.3 修改启动类

@SpringBootApplication

@EnableTurbine

public class HystrixTurbineApplication {

   public static void main(String[] args) {

      SpringApplication.run(HystrixTurbineApplication.class, args);

   }

}

 

2 使用Turbine聚合多个服务

2.1 修改被聚合的项目的pom文件添加dashboard坐标

<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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>springcloud-eureka-consumer-feign-fallback</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-config</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

      <!-- 添加Feign坐标 -->

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-feign</artifactId>

      </dependency>

      <!-- 添加product-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

         <artifactId>springcloud-ego-product-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

     

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

      </dependency>

       

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-actuator</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>

        </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

2.2 修改被监控的服务的启动类

@EnableFeignClients

@EnableDiscoveryClient

@SpringBootApplication

@EnableHystrixDashboard

@EnableCircuitBreaker //开启熔断器 断路器

public class ConsumerApplication {

  

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

2.3 查看turbine整合服务后的监控数据地址

http://localhost:1002/turbine.stream

3 监控集群

七、 采用RabbitMQ,收集监控数据

 

 

1 创建Consumer服务

2 修改Consumer服务的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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>springcloud-eureka-consumer-ribbon-dashboard-mq</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <packaging>jar</packaging>

   <name>springcloud-eureka-consumer</name>

   <description>Demo project for Spring Boot</description>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-config</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-actuator</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-netflix-hystrix-stream</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-stream-rabbit</artifactId>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

 

3 修改Consumer的配置文件

spring.application.name=eureka-consumer-ribbon-dashboard

server.port=9010

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

spring.rabbitmq.host=192.168.70.139

spring.rabbitmq.port=5672

spring.rabbitmq.username=oldlu

spring.rabbitmq.password=123456

spring.rabbitmq.virtualHost=/

4 修改Consumer启动类

@EnableCircuitBreaker //开启熔断器 断路器

@EnableEurekaClient

@SpringBootApplication

@EnableHystrix

@EnableHystrixDashboard

public class ConsumerApplication {

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

5 创建Turbine项目

6 修改Turbine的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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>springcloud-eureka-consumer-turbine-mq</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <packaging>jar</packaging>

   <name>springcloud-eureka-consumer</name>

   <description>Demo project for Spring Boot</description>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-config</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-turbine-stream</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-stream-rabbit</artifactId>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

 

7 修改Turbine的配置文件

spring.application.name=eureka-consumer-hystrix-turbine

server.port=1002

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

spring.rabbitmq.host=192.168.70.139

spring.rabbitmq.port=5672

spring.rabbitmq.username=oldlu

spring.rabbitmq.password=123456

spring.rabbitmq.virtualHost=/

8 修改Turbine的启动类

@SpringBootApplication

@EnableTurbineStream

public class HystrixTurbineApplication {

   public static void main(String[] args) {

      SpringApplication.run(HystrixTurbineApplication.class, args);

   }

}

 

第四章 如何设计微服务

(Spring Cloud高级)

 

一、 微服务架构的六种常用设计模式

1 代理设计模式

2 聚合设计模式

 

3 链条设计模式

4 聚合链条设计模式

 

5 数据共享设计模式

6 异步消息设计模式

二、 微服务设计模式实战

1 需求分析

 

2 数据库设计

2.1 User表

CREATE DATABASE;

USE `book-user`;

/*Table structure for table `user` */

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (

  `id` INT(10) NOT NULL AUTO_INCREMENT COMMENT '自增主键',

  `user_name` VARCHAR(50) DEFAULT NULL COMMENT '用户名',

  `password` VARCHAR(50) NOT NULL COMMENT '密码',

  `email` VARCHAR(50) NOT NULL COMMENT 'email',

  `deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',

  `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

  `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

  PRIMARY KEY (`id`)

) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表';

INSERT  INTO `user`(`id`,`user_name`,`password`,`email`,`deleted`,`create_time`,`update_time`) VALUES (1,'admin','admin','suibian@163.com',0,'2016-02-02 12:56:31','2016-02-02 14:47:25');

 

2.2 Product表

CREATE DATABASE ;

USE `book-product`;

DROP TABLE IF EXISTS `product`;

CREATE TABLE `product` (

  `id` INT(10) NOT NULL AUTO_INCREMENT,

  `name` VARCHAR(100) DEFAULT NULL COMMENT '产品名称',

  `status` TINYINT(4) NOT NULL DEFAULT '0' COMMENT '产品状态:0待审,1上架,2下架,3停售,4测试',

  `price` INT(10) NOT NULL COMMENT '产品价格 单位分',

  `detail` TEXT COMMENT '产品详情',

  `deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',

  `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

  `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

  PRIMARY KEY (`id`)

) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='产品信息';

INSERT INTO `product` (`id`, `name`, `status`, `price`, `detail`, `deleted`, `create_time`, `update_time`) VALUES('1','尚学堂-实战java程序设计','1','100','尚学堂-实战java程序设计','0','2017-12-03 09:08:12','2017-12-17 16:44:39');

INSERT INTO `product` (`id`, `name`, `status`, `price`, `detail`, `deleted`, `create_time`, `update_time`) VALUES('2','尚学堂-百战程序员经典案例','1','200','尚学堂-百战程序员经典案例','0','2017-12-03 09:08:12','2017-12-17 16:44:41');

INSERT INTO `product` (`id`, `name`, `status`, `price`, `detail`, `deleted`, `create_time`, `update_time`) VALUES('3','尚学堂-人工智能基础教程','1','300','尚学堂-人工智能基础教程','0','2017-12-17 16:44:35','2017-12-17 16:45:15');

 

2.3 Orders表

CREATE DATABASE ;

USE `book-order`;

DROP TABLE IF EXISTS `orders`;

CREATE TABLE `orders` (

  `id` INT(10) NOT NULL AUTO_INCREMENT,

  `product_id` INT(10) NOT NULL DEFAULT '0' COMMENT '产品ID',

  `price` INT(10) DEFAULT '0' COMMENT '价格',

  `user_id` INT(10) DEFAULT '0' COMMENT '用户账号ID',

  `trade_id` INT(10) DEFAULT '0' COMMENT '交易号ID',

  `trade_status` TINYINT(1) DEFAULT '0' COMMENT '支付状态 0=未支付 1=已支付',

  `deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',

  `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

  `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

  PRIMARY KEY (`id`)

) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2.4 Trade表

CREATE DATABASE;

USE `book-trade`;

DROP TABLE IF EXISTS `trade`;

CREATE TABLE `trade` (

  `id` INT(10) NOT NULL AUTO_INCREMENT COMMENT 'IID',

  `order_id` INT(10) NOT NULL COMMENT '订单ID',

  `user_id` INT(10) NOT NULL COMMENT '用户ID',

  `price` INT(10) NOT NULL COMMENT '支付价',

  `pay_status` TINYINT(4) NOT NULL COMMENT '1 未付款 2 付款中 3 付款失败 4 付款完成',

  `pay_type` TINYINT(4) NOT NULL COMMENT '支付类型:1-支付宝支付,2-网银在线,3-银联,4-微信支付',

  `gateway_pay_num` VARCHAR(30) DEFAULT NULL COMMENT '网关支付流水号',

  `gateway_pay_time` DATETIME DEFAULT NULL COMMENT '网关支付时间',

  `gateway_pay_price` INT(10) NOT NULL DEFAULT '0' COMMENT '网关实际支付金额',

  `deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',

  `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

  `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

  PRIMARY KEY (`id`)

) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='交易';

3 Mybatis的generatorSqlmapCustom工具使用

 

4 设计product服务

4.1 将sql脚本导入到数据库中

4.2 创建Product服务

4.3 修改Product服务的pom文件

<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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>e-book-product-provider</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

      <!-- 添加product-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

         <artifactId>e-book-product-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

     

      <dependency>

         <groupId>org.mybatis.spring.boot</groupId>

         <artifactId>mybatis-spring-boot-starter</artifactId>

         <version>1.3.0</version>

      </dependency>

      <dependency>

         <groupId>mysql</groupId>

         <artifactId>mysql-connector-java</artifactId>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

4.4 修改Product服务的配置文件

spring.application.name=e-book-product-provider

server.port=9001

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

#--------------db----------------

mybatis.type-aliases-package=com.book.product.pojo

mybatis.mapper-locations=classpath:com/book/product/mapper/*.xml

spring.datasource.driverClassName=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/book-product?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull

spring.datasource.username=root

spring.datasource.password=root

4.5 创建Product-Service服务

 

4.6 使用工具生成Pojo、Mpper、映射配置文件,添加到项目当中

4.6.1 Product-Provider

 

4.6.2 Product-Service

 

4.7 编写一个业务逻辑-查询所有商品信息

4.7.1在Product-Service项目中添加一个ProductService接口

@RequestMapping("/product")

public interface ProductService {

  

   //查询所有商品

   @RequestMapping(value="/findAll",method=RequestMethod.GET)

   public List<Product> findAll();

}

4.7.2在Product-Provider服务中添加ProductServiceImpl类

@Service

public class ProductServiceImpl {

  

   @Autowired

   private ProductMapper productMapper;

  

   /**

    * 查询所有商品

    * @return

    */

   public List<Product> findProductAll(){

      ProductExample example = new ProductExample();

      List<Product> list = this.productMapper.selectByExampleWithBLOBs(example);

      return list;

   }

}

4.7.3在Product服务中添加controller

@RestController

public class ProductServiceController implements ProductService {

   @Autowired

   private ProductServiceImpl productServiceImple;

  

   @Override

   public List<Product> findAll() {

      return this.productServiceImple.findProductAll();

   }

}

4.7.4修改Product-provider启动类

@EnableEurekaClient

@SpringBootApplication

@MapperScan("com.book.product.mapper")

public class ProductApplication {

   public static void main(String[] args) {

      SpringApplication.run(ProductApplication.class, args);

   }

}

4.7.5访问服务

 

5 设计user服务

5.1 将sql脚本导入到数据库中

 

5.2 创建User-Provider服务

 

5.3 修改User-Provider服务的pom文件

<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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>e-book-user-provider</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

      <!-- 添加product-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

            <artifactId>e-book-user-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

      <dependency>

         <groupId>org.mybatis.spring.boot</groupId>

         <artifactId>mybatis-spring-boot-starter</artifactId>

         <version>1.3.0</version>

      </dependency>

      <dependency>

         <groupId>mysql</groupId>

         <artifactId>mysql-connector-java</artifactId>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

5.4 修改User-Provider服务的配置文件

spring.application.name=e-book-user-provider

server.port=9002

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

#--------------db----------------

mybatis.type-aliases-package=com.book.user.pojo

mybatis.mapper-locations=classpath:com/book/user/mapper/*.xml

spring.datasource.driverClassName=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/book-user?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull

spring.datasource.username=root

spring.datasource.password=root

5.5 创建User-Service服务

 

5.6 使用工具生成Pojo、Mpper、映射配置文件,添加到项目当中

5.6.1User-Provider

 

5.6.2User-Service

 

5.7 编写一个业务逻辑-用户登录

5.7.1在User-service项目中添加一个UserService接口

@RequestMapping("/user")

public interface UserService {

   // 用户登录

   @RequestMapping(value = "/login", method = RequestMethod.GET)

   public User login(@RequestParam("userName") String userName, @RequestParam("password") String password);

}

5.7.2在User-provider服务中添加UserServiceImpl类

@Service

public class UserServiceImpl {

   @Autowired

   private UserMapper userMapper;

   /**

    * 用户登录

    * @param userName

    * @param password

    * @return

    */

   public User userLogin(String userName ,String password){

      UserExample example = new UserExample();

      Criteria cri = example.createCriteria();

      cri.andUserNameEqualTo(userName);

      cri.andPasswordEqualTo(password);

      List<User> list=this.userMapper.selectByExample(example);

      return list.get(0);

   }

}

 

5.7.3在User-Provider服务中添加controller

@RestController

public class UserController implements UserService {

   @Autowired

   private UserServiceImpl userServiceImpl;

   @Override

   public User login(String userName, String password) {

      return this.userServiceImpl.userLogin(userName, password);

   }

}

5.7.4修改User-Provider启动类

@EnableEurekaClient

@SpringBootApplication

@MapperScan("com.book.user.mapper")

public class UserApplication {

   public static void main(String[] args) {

      SpringApplication.run(UserApplication.class, args);

   }

}

5.7.5访问服务

 

6 设计Order服务

6.1 将sql脚本导入到数据中

6.2 创建order-provider服务

 

6.3 修该Order-Provider的pom文件

<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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>e-book-order-provider</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

      <!-- 添加product-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

            <artifactId>e-book-order-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

      <dependency>

         <groupId>org.mybatis.spring.boot</groupId>

         <artifactId>mybatis-spring-boot-starter</artifactId>

         <version>1.3.0</version>

      </dependency>

      <dependency>

         <groupId>mysql</groupId>

         <artifactId>mysql-connector-java</artifactId>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

6.4 修改Order-Provider的配置文件

spring.application.name=e-book-order-provider

server.port=9003

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

#--------------db----------------

mybatis.type-aliases-package=com.book.order.pojo

mybatis.mapper-locations=classpath:com/book/order/mapper/*.xml

spring.datasource.driverClassName=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/book-order?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull

spring.datasource.username=root

spring.datasource.password=root

6.5 创建Order-Service服务

 

6.6 使用工具生成Pojo、Mpper、映射配置文件,添加到项目当中

6.6.1Order-Provider

6.6.2Order-Service

 

6.7 编写一个业务逻辑-查询所有订单

6.7.1在Order-Service项目中添加一个OrderService接口

/**

 * Order服务接口

 * @author Administrator

 *

 */

@RequestMapping("/order")

public interface OrderService {

       //查询所有订单

      @RequestMapping(value="/findAll",method=RequestMethod.GET)

      public List<Orders> findAll();

}

6.7.2在Order-Provider服务中添加OrderServiceImpl类

@Service

public class OrderServiceImpl {

   @Autowired

   private OrdersMapper orderMapper;

  

   /**

    * 查询所有订单

    */

   public List<Orders> findOrderAll(){

      OrdersExample example = new OrdersExample();

      return this.orderMapper.selectByExample(example);

   }

}

6.7.3在Order-Provider服务中添加一个controller

@RestController

public class OrderController implements OrderService {

   @Autowired

   private OrderServiceImpl orderServiceImpl;

  

   @Override

   public List<Orders> findAll() {

      return this.orderServiceImpl.findOrderAll();

   }

}

6.7.4修改Order-Provider启动类

@EnableEurekaClient

@SpringBootApplication

@MapperScan("com.book.order.mapper")

public class OrderApplication {

   public static void main(String[] args) {

      SpringApplication.run(OrderApplication.class, args);

   }

}

6.7.5访问服务

 

7 设计Consumer

7.1 创建项目

7.2 修改pom文件

<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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>e-book-consumer</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

      <!-- 添加Feign坐标 -->

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-feign</artifactId>

      </dependency>

      <!-- 添加e-book-user-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

         <artifactId>e-book-user-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

      <!-- 添加e-book-product-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

         <artifactId>e-book-product-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

      <!-- 添加e-book-trade-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

         <artifactId>e-book-trade-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

      <!-- 添加e-book-order-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

         <artifactId>e-book-order-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

7.3 修改配置文件

spring.application.name=e-book-consumer

server.port=9010

#设置服务注册中心地址,指向另一个注册中心

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/

7.4 修改启动类

@EnableFeignClients

@EnableDiscoveryClient

@SpringBootApplication

public class ConsumerApplication {

  

   public static void main(String[] args) {

      SpringApplication.run(ConsumerApplication.class, args);

   }

}

 

8 添加业务逻辑

模拟内容: 登录 查看产品 下订单

* 1.测试登录 账号 admin admin

* 2.查看所有产品列表

* 3.选第一款产品,下订单

* 4.实现订单交易支付

* 5.查看所有的订单信息

8.1 创建Controller

/**

 * 创建订单

 * @author Administrator

 *

 */

@RestController

public class ConsumerController {

   @Autowired

   private ConsumerOrderService orderService;

  

   @Autowired

   private ConsumerProductService productService;

  

   @Autowired

   private ConsumerTradeService tradeService;

  

   @Autowired

   private ConsumerUserService userService;

  

   /**

    * 模拟内容: 登录 查看产品 下订单

    * 1.测试登录 账号 admin admin

    * 2.查看所有产品列表

    * 3.选第一款产品,下订单

    * 4.实现订单交易支付

    * 5.查看所有的订单信息

    */

   @RequestMapping(value="/create",method=RequestMethod.GET)

   public List<Orders> createOrder(){

      return null;

   }

  

   /**

    * 用户登录

    */

   private Integer login(){

      return null;

   }

}

 

8.2 创建ConsumerOrderService

/**

 * 调用订单服务

 * @author Administrator

 *

 */

@FeignClient("e-book-order-provider")

public interface ConsumerOrderService extends OrderService {

}

8.3 创建ConsumerUserService

/**

 * 调用User服务

 * @author Administrator

 *

 */

@FeignClient("e-book-user-provider")

public interface ConsumerUserService extends UserService {

}

8.4 创建ConsumerProductService

/**

 * 调用product服务

 * @author Administrator

 *

 */

@FeignClient("e-book-product-provider")

public interface ConsumerProductService extends ProductService {

}

8.5 创建ConsumerTradeService

/**

 * 调用交易服务

 * @author Administrator

 *

 */

@FeignClient("e-book-trade-provider")

public interface ConsumerTradeService extends TradeService {

}

9 业务逻辑实现

模拟用户登录、查询商品

9.1 模拟用户登录

9.1.1在User-Serivce中添加用户登录接口

/**

 * User服务接口

 *

 * @author Administrator

 *

 */

@RequestMapping("/user")

public interface UserService {

   // 用户登录

   @RequestMapping(value = "/login", method = RequestMethod.GET)

   public User login(@RequestParam("userName") String userName, @RequestParam("password") String password);

}

9.1.2在User-Provider中添加接口实现

 

@RestController

public class UserController implements UserService {

   @Autowired

   private UserServiceImpl userServiceImpl;

   @Override

   public User login(String userName, String password) {

      return this.userServiceImpl.userLogin(userName, password);

   }

}

9.1.3在UserServiceImpl类中完成对数据库的操作

@Service

public class UserServiceImpl {

   @Autowired

   private UserMapper userMapper;

   /**

    * 用户登录

    * @param userName

    * @param password

    * @return

    */

   public User userLogin(String userName ,String password){

      UserExample example = new UserExample();

      Criteria cri = example.createCriteria();

      cri.andUserNameEqualTo(userName);

      cri.andPasswordEqualTo(password);

      List<User> list=this.userMapper.selectByExample(example);

      if(list != null && list.size() > 0){

         return list.get(0);

      }else{

         return null;

      }

   }

}

9.2 查询商品

9.2.1在Product-Service中添加查询所有商品接口

/**

 * Product服务接口

 * @author Administrator

 *

 */

@RequestMapping("/product")

public interface ProductService {

  

   //查询所有商品

   @RequestMapping(value="/findAll",method=RequestMethod.GET)

   public List<Product> findAll();

}

9.2.2在Product-Provider中添加接口实现

@RestController

public class ProductServiceController implements ProductService {

   @Autowired

   private ProductServiceImpl productServiceImple;

  

   @Override

   public List<Product> findAll() {

      return this.productServiceImple.findProductAll();

   }

}

9.2.3在ProductServiceImpl类中完成对数据库的操作

@Service

public class ProductServiceImpl {

  

   @Autowired

   private ProductMapper productMapper;

  

   /**

    * 查询所有商品

    * @return

    */

   public List<Product> findProductAll(){

      ProductExample example = new ProductExample();

      List<Product> list = this.productMapper.selectByExampleWithBLOBs(example);

      return list;

   }

}

 

9.3 创建订单

创建订单

9.3.1在Order-Service中添加创建订单接口

/**

 * Order服务接口

 * @author Administrator

 *

 */

@RequestMapping("/order")

public interface OrderService {

       //查询所有订单

      @RequestMapping(value="/findAll",method=RequestMethod.GET)

      public List<Orders> findAll();

     

      //添加订单

      @RequestMapping(value="/create",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)

      public Integer addOrder(@RequestBody Orders order);

}

 

9.3.2在Order-Provider中添加接口实现

@RestController

public class OrderController implements OrderService {

   @Autowired

   private OrderServiceImpl orderServiceImpl;

  

   @Override

   public List<Orders> findAll() {

      return this.orderServiceImpl.findOrderAll();

   }

   @Override

   public Integer addOrder(@RequestBody Orders order) {

      return this.orderServiceImpl.createOrder(order);

   }

}

 

9.3.3在OrderServiceImpl类中完成数据库操作

@Service

public class OrderServiceImpl {

   @Autowired

   private OrdersMapper orderMapper;

  

   /**

    * 查询所有订单

    */

   public List<Orders> findOrderAll(){

      OrdersExample example = new OrdersExample();

      return this.orderMapper.selectByExample(example);

   }

  

   /**

    * 添加订单

    */

   public Integer createOrder(Orders order){

      this.orderMapper.insert(order);

      return order.getId();

   }

}

 

9.4 完成交易

9.4.1在Trade-Service中添加创建交易接口

/**

 * Trade服务接口

 *

 * @author Administrator

 *

 */

@RequestMapping("/trade")

public interface TradeService {

   // 查询所有交易信息

   @RequestMapping(value = "/findAll", method = RequestMethod.GET)

   public List<Trade> findAll();

  

   @RequestMapping(value="/create" ,method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)

   public void addTrade(@RequestBody Trade trade);

}

9.4.2在Trade-Provider中添加接口实现

@RestController

public class TradeController implements TradeService {

   @Autowired

   private TradeServiceImpl tradeServiceImpl;

  

   @Override

   public List<Trade> findAll() {

      return this.tradeServiceImpl.findTradeAll();

   }

   @Override

   public void addTrade(@RequestBody Trade trade) {

      this.tradeServiceImpl.addTrade(trade);

   }

}

 

9.4.3在TradeServiceImpl类中完成对数据库的操作

@Service

public class TradeServiceImpl {

   @Autowired

   private TradeMapper tradeMapper;

  

   /**

    * 查询所有交易信息

    */

   public List<Trade> findTradeAll(){

      TradeExample example = new TradeExample();

      return this.tradeMapper.selectByExample(example);

   }

  

   /**

    * 添加交易信息

    */

   public void addTrade(Trade trade){

      this.tradeMapper.insert(trade);

   }

}

9.5 在交易中更新订单信息

修改Trade的pom文件添加Order-Service、Feign的坐标

<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">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.bjsxt</groupId>

   <artifactId>e-book-trade-provider</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.13.RELEASE</version>

      <relativePath /> <!-- lookup parent from repository -->

   </parent>

   <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

      <dependencies>

         <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Dalston.SR5</version>

            <type>pom</type>

            <scope>import</scope>

         </dependency>

      </dependencies>

   </dependencyManagement>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

      <!-- 添加product-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

         <artifactId>e-book-trade-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

     

      <!-- 添加order-service坐标 -->

      <dependency>

         <groupId>com.bjsxt</groupId>

         <artifactId>e-book-order-service</artifactId>

         <version>0.0.1-SNAPSHOT</version>

      </dependency>

      <!-- 添加Feign坐标 -->

      <dependency>

         <groupId>org.springframework.cloud</groupId>

         <artifactId>spring-cloud-starter-feign</artifactId>

      </dependency>

     

      <dependency>

         <groupId>org.mybatis.spring.boot</groupId>

         <artifactId>mybatis-spring-boot-starter</artifactId>

         <version>1.3.0</version>

      </dependency>

      <dependency>

         <groupId>mysql</groupId>

         <artifactId>mysql-connector-java</artifactId>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

         </plugin>

      </plugins>

   </build>

</project>

9.5.1修改启动类

@EnableFeignClients

@EnableEurekaClient

@SpringBootApplication

@MapperScan("com.book.trade.mapper")

public class TradeApplication {

   public static void main(String[] args) {

      SpringApplication.run(TradeApplication.class, args);

   }

}

9.5.2在Trade-Provider服务中添加接口

@FeignClient("e-book-order-provider")

public interface ProviderOrderService extends OrderService {

}

9.5.3修改Order-Service添加方法

/**

 * Order服务接口

 * @author Administrator

 *

 */

@RequestMapping("/order")

public interface OrderService {

       //查询所有订单

      @RequestMapping(value="/findAll",method=RequestMethod.GET)

      public List<Orders> findAll();

     

      //添加订单

      @RequestMapping(value="/create",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)

      public Integer addOrder(@RequestBody Orders order);

     

      //根据订单ID查询订单

      @RequestMapping(value="/findOrderById",method=RequestMethod.GET)

      public Orders findOrderById(@RequestParam("orderid") Integer orderid);

     

      //更新订单

      @RequestMapping(value="/updateOrder",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)

      public void updateOrder(@RequestBody Orders order);

}

9.5.4修改Order-Provider的OrderServiceImpl完成对数据库的操作

/**

    * 根据ID查询订单

    */

   public Orders findOrderById(Integer id){

      return this.orderMapper.selectByPrimaryKey(id);

   }

  

   /**

    * 更新订单

    */

   public void updateOrder(Orders order){

      this.orderMapper.updateByPrimaryKey(order);

   }

9.5.5修改Order-provider的controller

@RestController

public class OrderController implements OrderService {

   @Autowired

   private OrderServiceImpl orderServiceImpl;

  

   @Override

   public List<Orders> findAll() {

      return this.orderServiceImpl.findOrderAll();

   }

   @Override

   public Integer addOrder(@RequestBody Orders order) {

      return this.orderServiceImpl.createOrder(order);

   }

   @Override

   public Orders findOrderById(Integer orderid) {

      return this.orderServiceImpl.findOrderById(orderid);

   }

   @Override

   public void updateOrder(@RequestBody Orders order) {

      this.orderServiceImpl.updateOrder(order);

   }

}

9.5.6在Trade-provider的controller中完成业务实现

@RestController

public class TradeController implements TradeService {

   @Autowired

   private TradeServiceImpl tradeServiceImpl;

  

   @Autowired

   private ProviderOrderService providerOrderService;

  

   @Override

   public List<Trade> findAll() {

      return this.tradeServiceImpl.findTradeAll();

   }

   @Override

   public void addTrade(@RequestBody Trade trade) {

      //添加交易信息

       this.tradeServiceImpl.addTrade(trade);

      //根据ID查询订单

       Orders order = this.providerOrderService.findOrderById(trade.getOrderId());

       order.setTradeId(trade.getId());

      //更新订单

       this.providerOrderService.updateOrder(order);

   }

}

9.6 切记让所有的pojo都实现序列化接口

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值