springcloud(九):熔断器Hystrix和Feign的全套应用案例(二)
一、. 创建Eureka-Server 服务中心项目
1. 创建Eureka-Server 服务中心项目架构如下
2. pom.xml
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
3.配置文件application.properties
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #给当前服务起名 2 spring.application.name=eureka-server 3 4 #给当前服务指定端口号 5 server.port=8761 6 7 #register-with-eureka :表示是将自己注册到Eureka Server,默认为true。 8 #因为当前应用就是Eureka Server,所以将其设置位false 9 #★当前服务时eureka的服务端还是客户端,当前是服务端因此false 10 eureka.client.register-with-eureka=false 11 12 #fetch-registry :表示是否从Eureka Server获取注册信息,默认为true。不需要同步数据就将其设为false 13 #★是否从eureka服务中心获取信息,因为当前是服务端因此不需要在服务端获取信息 14 eureka.client.fetch-registry=false 15 16 #defaultZone :设置与Eureka Server交互的地址, 17 #查询服务和注册服务都需要依赖这个地址。默认是http://localhost:8761/eureka ; 18 #多个地址可使用 , 分隔。 19 #将本eureka服务的地址公开暴露给所有的客户端,因为只有所有的客户端知道eureka服务的地址,才能将信息 20 #注册到eureka服务中心 21 eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
4.启动类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 7 //开启eureka的服务端 8 @EnableEurekaServer 9 @SpringBootApplication 10 public class EurekaServerApplication { 11 12 public static void main(String[] args) { 13 SpringApplication.run(EurekaServerApplication.class, args); 14 } 15 16 }
二、创建一方提供者eureka-client-provider-findcla
1. 项目结构如下:
2.pom.xml文件内容
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <dependencies> 2 <!--引用公共组件--> 3 <dependency> 4 <groupId>cn.kgc</groupId> 5 <artifactId>eureka-common-clastu</artifactId> 6 <version>1.0-SNAPSHOT</version> 7 </dependency> 8 9 <dependency> 10 <groupId>org.springframework.boot</groupId> 11 <artifactId>spring-boot-starter-jdbc</artifactId> 12 </dependency> 13 <dependency> 14 <groupId>org.springframework.boot</groupId> 15 <artifactId>spring-boot-starter-web</artifactId> 16 </dependency> 17 <dependency> 18 <groupId>org.mybatis.spring.boot</groupId> 19 <artifactId>mybatis-spring-boot-starter</artifactId> 20 <version>2.1.0</version> 21 </dependency> 22 <dependency> 23 <groupId>org.springframework.cloud</groupId> 24 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 25 </dependency> 26 27 <!--修改版本号--> 28 <dependency> 29 <groupId>mysql</groupId> 30 <artifactId>mysql-connector-java</artifactId> 31 <version>5.1.38</version> 32 </dependency> 33 34 <dependency> 35 <groupId>org.springframework.boot</groupId> 36 <artifactId>spring-boot-starter-test</artifactId> 37 <scope>test</scope> 38 </dependency> 39 </dependencies>
3.application.properties属性文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #给当前服务起名字 2 spring.application.name=eureka-client-provider-findcla 3 4 #给当前服务设置端口号 5 server.port=8762 6 7 #指定eureka-server的服务地址 8 eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ 9 10 #数据源的配置 11 spring.datasource.driver-class-name=com.mysql.jdbc.Driver 12 spring.datasource.url=jdbc:mysql://192.168.117.145:3306/kh66 13 spring.datasource.username=root 14 spring.datasource.password=ok 15 16 #别名配置 17 mybatis.type-aliases-package=cn.kgc.vo
4.ClassesMapper.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.mapper; 2 3 import cn.kgc.vo.Classes; 4 import org.apache.ibatis.annotations.Select; 5 6 import java.util.List; 7 8 /** 9 * Created by Administrator on 2019/8/19. 10 */ 11 public interface ClassesMapper { 12 @Select("select * from classes") 13 List<Classes> optionData(); 14 }
5.ClassesService.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.service; 2 3 import cn.kgc.vo.Classes; 4 import org.apache.ibatis.annotations.Select; 5 6 import java.util.List; 7 8 /** 9 * Created by Administrator on 2019/8/19. 10 */ 11 public interface ClassesService { 12 13 List<Classes> optionData(); 14 }
6.ClassesServiceImpl.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.service; 2 3 import cn.kgc.mapper.ClassesMapper; 4 import cn.kgc.vo.Classes; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.stereotype.Service; 7 8 import java.util.List; 9 10 @Service 11 public class ClassesServiceImpl implements ClassesService{ 12 @Autowired 13 private ClassesMapper classesMapper; 14 15 public List<Classes> optionData() { 16 return classesMapper.optionData(); 17 } 18 }
7.CenterController.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.controller; 2 3 import cn.kgc.service.ClassesService; 4 import cn.kgc.vo.Classes; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.web.bind.annotation.RequestMapping; 7 import org.springframework.web.bind.annotation.RestController; 8 9 import java.util.List; 10 11 @RestController 12 public class CenterController { 13 @Autowired 14 private ClassesService classesService; 15 16 @RequestMapping(value = "/option.do") 17 public List<Classes> optionData() { 18 return classesService.optionData(); 19 } 20 }
8.启动类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc; 2 3 import org.mybatis.spring.annotation.MapperScan; 4 import org.springframework.boot.SpringApplication; 5 import org.springframework.boot.autoconfigure.SpringBootApplication; 6 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 7 8 9 @MapperScan("cn.kgc.mapper") 10 @EnableEurekaClient 11 @SpringBootApplication 12 public class EurekaClientProviderFindclaApplication { 13 14 public static void main(String[] args) { 15 SpringApplication.run(EurekaClientProviderFindclaApplication.class, args); 16 } 17 18 }
三、创建多方提供者eureka-client-provider-findstu
1.项目结构如下:
2.pom.xml文件如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <dependencies> 2 <!--引入公共组件--> 3 <dependency> 4 <groupId>cn.kgc</groupId> 5 <artifactId>eureka-common-clastu</artifactId> 6 <version>1.0-SNAPSHOT</version> 7 </dependency> 8 9 <dependency> 10 <groupId>org.springframework.boot</groupId> 11 <artifactId>spring-boot-starter-jdbc</artifactId> 12 </dependency> 13 <dependency> 14 <groupId>org.springframework.boot</groupId> 15 <artifactId>spring-boot-starter-web</artifactId> 16 </dependency> 17 <dependency> 18 <groupId>org.mybatis.spring.boot</groupId> 19 <artifactId>mybatis-spring-boot-starter</artifactId> 20 <version>2.1.0</version> 21 </dependency> 22 <dependency> 23 <groupId>org.springframework.cloud</groupId> 24 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 25 </dependency> 26 <!--添加版本号--> 27 <dependency> 28 <groupId>mysql</groupId> 29 <artifactId>mysql-connector-java</artifactId> 30 <version>5.1.38</version> 31 </dependency> 32 <dependency> 33 <groupId>org.springframework.boot</groupId> 34 <artifactId>spring-boot-starter-test</artifactId> 35 <scope>test</scope> 36 </dependency> 37 38 <!--<dependency> 39 <groupId>org.apache.httpcomponents</groupId> 40 <artifactId>httpclient</artifactId> 41 <version>4.5.3</version> 42 </dependency> 43 <dependency> 44 <groupId>com.netflix.feign</groupId> 45 <artifactId>feign-httpclient</artifactId> 46 <version>8.16.2</version> 47 </dependency>--> 48 </dependencies>
3..StudentMapper.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.mapper; 2 3 import cn.kgc.vo.Student; 4 import org.apache.ibatis.annotations.Delete; 5 import org.apache.ibatis.annotations.Insert; 6 import org.apache.ibatis.annotations.Update; 7 8 import java.util.List; 9 import java.util.Map; 10 11 /** 12 * Created by Administrator on 2019/8/19. 13 */ 14 public interface StudentMapper { 15 List<Map<String,Object>> showData(Student student); 16 17 @Insert("INSERT INTO kh66.student (sname, password, subject, result, cid) VALUES(#{sname},#{password},#{subject},#{result},#{cid}) ") 18 int addStu(Student student); 19 20 @Update("UPDATE kh66.student SET sname =#{sname}, password =#{password}, subject = #{subject}, result =#{result}, cid =#{cid} WHERE sid =#{sid} ") 21 int editStu(Student student); 22 23 @Delete("delete from student where sid=#{sid}") 24 int delStu(Integer sid); 25 }
4.StudentMapper.xml
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="cn.kgc.mapper.StudentMapper"> 6 <select id="showData" parameterType="Student" resultType="map"> 7 select s.*,c.cname from student s,classes c where s.cid=c.cid 8 <if test="sid!=null"> 9 and s.sid=#{sid} 10 </if> 11 <if test="cid!=null and cid!=-1"> 12 and s.cid=#{cid} 13 </if> 14 <if test="sname!=null"> 15 and sname like concat('%',#{sname},'%') 16 </if> 17 </select> 18 </mapper>
5..StudentService.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.service; 2 3 import cn.kgc.vo.Student; 4 import org.apache.ibatis.annotations.Delete; 5 import org.apache.ibatis.annotations.Insert; 6 import org.apache.ibatis.annotations.Update; 7 import org.springframework.web.bind.annotation.RequestBody; 8 9 import java.util.List; 10 import java.util.Map; 11 12 /** 13 * Created by Administrator on 2019/8/19. 14 */ 15 public interface StudentService { 16 List<Map<String,Object>> showData(Student student); 17 18 int addStu(Student student); 19 20 int editStu( Student student); 21 22 int delStu(Integer sid); 23 }
6.StudentServiceImpl.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.service; 2 3 import cn.kgc.mapper.StudentMapper; 4 import cn.kgc.vo.Student; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.stereotype.Service; 7 import org.springframework.transaction.annotation.Transactional; 8 import org.springframework.web.bind.annotation.RequestBody; 9 10 import java.util.List; 11 import java.util.Map; 12 13 @Service 14 @Transactional 15 public class StudentServiceImpl implements StudentService { 16 @Autowired 17 private StudentMapper studentMapper; 18 19 @Override 20 public List<Map<String, Object>> showData( Student student) { 21 return studentMapper.showData(student); 22 } 23 24 @Override 25 public int addStu(Student student) { 26 return studentMapper.addStu(student); 27 } 28 29 @Override 30 public int editStu(Student student) { 31 return studentMapper.editStu(student); 32 } 33 34 @Override 35 public int delStu(Integer sid) { 36 return studentMapper.delStu(sid); 37 } 38 }
7.CenterController.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.controller; 2 3 import cn.kgc.mapper.StudentMapper; 4 import cn.kgc.service.StudentService; 5 import cn.kgc.vo.Student; 6 import com.fasterxml.jackson.core.JsonProcessingException; 7 import com.fasterxml.jackson.databind.ObjectMapper; 8 import com.netflix.ribbon.proxy.annotation.Http; 9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.stereotype.Service; 11 import org.springframework.transaction.annotation.Transactional; 12 import org.springframework.web.bind.annotation.*; 13 14 import java.util.List; 15 import java.util.Map; 16 17 @RestController 18 public class CenterController{ 19 ObjectMapper om=new ObjectMapper(); 20 21 @Autowired 22 private StudentService studentService; 23 24 25 @RequestMapping(value = "/data.do") 26 public List<Map<String,Object>> showData(@RequestBody Student student){ 27 System.out.println("stu=showData>>>>>>>>student:"+student.getSid()); 28 return studentService.showData(student); 29 } 30 31 @RequestMapping(value = "/add.do") 32 public int addStu(@RequestBody Student student){ 33 return studentService.addStu(student); 34 } 35 36 @RequestMapping(value = "/edit.do") 37 public int editStu(@RequestBody Student student){ 38 return studentService.editStu(student); 39 } 40 41 42 @RequestMapping(value = "/del.do") 43 public int delStu(@RequestParam("sid") Integer sid) { 44 return studentService.delStu(sid); 45 } 46 /* 47 @RequestMapping(value = "/data.do",consumes = "application/json") 48 public String showData(@RequestBody Student student) throws Exception{ 49 System.out.println("stu=showData>>>>>>>>student:"+student.getSid()); 50 String str=om.writeValueAsString(studentService.showData(student)); 51 52 return str; 53 } 54 55 @RequestMapping(value = "/add.do",consumes = "application/json") 56 public String addStu(@RequestBody Student student) throws Exception{ 57 String str=om.writeValueAsString(studentService.addStu(student)); 58 return str; 59 } 60 61 @RequestMapping(value = "/edit.do") 62 public String editStu(@RequestBody Student student) throws Exception{ 63 String str=om.writeValueAsString(studentService.editStu(student)); 64 return str; 65 } 66 67 68 @RequestMapping(value = "/del.do") 69 public String delStu(@RequestParam("sid") Integer sid) throws Exception{ 70 String str=om.writeValueAsString(studentService.delStu(sid)); 71 return str; 72 }*/ 73 }
8.启动类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc; 2 3 import org.mybatis.spring.annotation.MapperScan; 4 import org.springframework.boot.SpringApplication; 5 import org.springframework.boot.autoconfigure.SpringBootApplication; 6 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 7 8 @MapperScan("cn.kgc.mapper") 9 @EnableEurekaClient 10 @SpringBootApplication 11 public class EurekaClientProviderFindstuApplication { 12 13 public static void main(String[] args) { 14 SpringApplication.run(EurekaClientProviderFindstuApplication.class, args); 15 } 16 17 }
四、创建调用者项目eureka-client-consumer-clastu-p
使用feign调用上述2个提供者项目里的controller拿值,并使用熔断器,进行错误请求处理
1.项目结构如下
2.pom.xml文件内容如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <dependencies> 2 <dependency> 3 <groupId>cn.kgc</groupId> 4 <artifactId>eureka-common-clastu</artifactId> 5 <version>1.0-SNAPSHOT</version> 6 </dependency> 7 <dependency> 8 <groupId>org.springframework.boot</groupId> 9 <artifactId>spring-boot-starter-web</artifactId> 10 </dependency> 11 <dependency> 12 <groupId>org.springframework.cloud</groupId> 13 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 14 </dependency> 15 <dependency> 16 <groupId>org.springframework.cloud</groupId> 17 <artifactId>spring-cloud-starter-openfeign</artifactId> 18 </dependency> 19 20 <dependency> 21 <groupId>org.springframework.boot</groupId> 22 <artifactId>spring-boot-starter-test</artifactId> 23 <scope>test</scope> 24 </dependency> 25 26 <!--<dependency> 27 <groupId>org.apache.httpcomponents</groupId> 28 <artifactId>httpclient</artifactId> 29 <version>4.5.3</version> 30 </dependency> 31 <dependency> 32 <groupId>com.netflix.feign</groupId> 33 <artifactId>feign-httpclient</artifactId> 34 <version>8.16.2</version> 35 </dependency>--> 36 </dependencies>
3.application.properties
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 spring.application.name=eureka-client-consumer-clastu-p 2 3 server.port=8766 4 5 eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ 6 7 #将feign上集成的断路器设置位有效开启状态 8 feign.hystrix.enabled=true
4.一方feign接口,ClassesProviderFeign.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package cn.kgc.feign; import cn.kgc.vo.Classes; import cn.kgc.vo.Student; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import java.util.List; import java.util.Map; @FeignClient(name = "eureka-client-provider-findcla",fallback =ClassesProviderFeignFallBack.class ) public interface ClassesProviderFeign { @RequestMapping("/option.do") public String optionData(); }
5.一方feign接口的容错类:ClassesProviderFeignFallBack.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.feign; 2 3 import cn.kgc.vo.Classes; 4 import org.springframework.cloud.openfeign.FeignClient; 5 import org.springframework.stereotype.Component; 6 import org.springframework.web.bind.annotation.RequestMapping; 7 8 import java.util.List; 9 10 //classes容错处理类 11 @Component 12 public class ClassesProviderFeignFallBack implements ClassesProviderFeign{ 13 14 @Override 15 public String optionData() { 16 return "提供者服务器请求异常,请稍后再试...."; 17 } 18 }
6.多方feign接口,StudentProviderFeign.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.feign; 2 3 import cn.kgc.vo.Classes; 4 import cn.kgc.vo.Student; 5 import org.springframework.cloud.openfeign.FeignClient; 6 import org.springframework.web.bind.annotation.RequestBody; 7 import org.springframework.web.bind.annotation.RequestMapping; 8 import org.springframework.web.bind.annotation.RequestParam; 9 10 import java.util.ArrayList; 11 import java.util.List; 12 import java.util.Map; 13 14 @FeignClient(name = "eureka-client-provider-findstu",fallback = StudentProviderFeignFallBack.class) 15 public interface StudentProviderFeign { 16 17 @RequestMapping(value = "/data.do") 18 public String showData(@RequestBody Student student); 19 20 @RequestMapping(value = "/add.do") 21 public String addStu(@RequestBody Student student); 22 23 @RequestMapping(value = "/edit.do") 24 public String editStu(@RequestBody Student student); 25 26 @RequestMapping(value = "/del.do") 27 public String delStu(@RequestParam("sid") Integer sid) ; 28 29 }
7.多方feign接口的容错类:StudentProviderFeignFallBack.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.feign; 2 3 import cn.kgc.vo.Student; 4 import org.springframework.stereotype.Component; 5 6 import java.util.List; 7 import java.util.Map; 8 9 @Component 10 public class StudentProviderFeignFallBack implements StudentProviderFeign{ 11 12 public String showData(Student student) { 13 return "提供服务器数据没有查到!"; 14 } 15 16 public String addStu(Student student) { 17 return "提供服务器数据添加失败!"; 18 } 19 20 public String editStu(Student student) { 21 return "提供服务器修改数据失败!"; 22 } 23 24 public String delStu(Integer sid) { 25 return "删除数据失败!"; 26 } 27 }
8.CenterController.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc.controller; 2 3 import cn.kgc.feign.ClassesProviderFeign; 4 import cn.kgc.feign.StudentProviderFeign; 5 import cn.kgc.vo.Classes; 6 import cn.kgc.vo.Student; 7 import feign.Headers; 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.web.bind.annotation.RequestBody; 10 import org.springframework.web.bind.annotation.RequestMapping; 11 import org.springframework.web.bind.annotation.RestController; 12 13 import java.util.HashMap; 14 import java.util.List; 15 import java.util.Map; 16 17 @RestController 18 public class CenterController { 19 20 @Autowired 21 private ClassesProviderFeign classesProviderFeign; 22 23 @Autowired 24 private StudentProviderFeign studentProviderFeign; 25 26 @RequestMapping(value = "/option.do",consumes = "application/json" ) 27 public String optionData(){ 28 return classesProviderFeign.optionData(); 29 } 30 31 @RequestMapping(value = "/data.do",consumes = "application/json") 32 public String showData(@RequestBody Student student){ 33 System.out.println("clastu=showData>>>>>>>>student:"+student.getSid()); 34 return studentProviderFeign.showData(student); 35 } 36 37 38 @RequestMapping(value = "/info.do",consumes = "application/json") 39 public Map<String,Object> getInfo(@RequestBody Student student){ 40 Map<String,Object> map=new HashMap<String,Object>(); 41 System.out.println("clastu=getInfo>>>>>>>>student:"+student.getSid()); 42 map.put("stu",studentProviderFeign.showData(student)); 43 map.put("clalist",classesProviderFeign.optionData()); 44 return map; 45 } 46 47 48 @RequestMapping(value = "/add.do",consumes = "application/json") 49 public String addStu(Student student){ 50 return studentProviderFeign.addStu(student); 51 } 52 53 @RequestMapping(value = "/edit.do",consumes = "application/json") 54 public String editStu(Student student){ 55 return studentProviderFeign.editStu(student); 56 } 57 58 @RequestMapping(value = "/del.do",consumes = "application/json") 59 public String delStu(Integer sid){ 60 return studentProviderFeign.delStu(sid); 61 } 62 }
9.启动类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.kgc; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 import org.springframework.cloud.openfeign.EnableFeignClients; 7 8 @EnableFeignClients 9 @EnableEurekaClient 10 @SpringBootApplication 11 public class EurekaClientConsumerClastuPApplication { 12 13 public static void main(String[] args) { 14 SpringApplication.run(EurekaClientConsumerClastuPApplication.class, args); 15 } 16 17 }
10.测试正常流程,
按顺序启动一、二、三、四项目
11.容错的测试
停掉三的项目,因为三项目是多方的提供者,停掉则四访问不到会走容错
注:本帖为“"Holly老师"原创,转载请注明出处,谢谢!
出处:https://www.cnblogs.com/holly8/p/11415344.html