新版本笔记SpringCloud/Alibaba
https://www.yuque.com/docs/share/2052cf75-a1ac-436e-b12d-2293397ba413?#
环境搭建
- 新建 maven 项目,管理依赖
<!--打包方式 pom--> <packaging>pom</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <junit.version>4.12</junit.version> <log4j.version>1.2.17</log4j.version> <lombok.version>1.16.18</lombok.version> </properties> <dependencyManagement> <dependencies> <!--springCloud的依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR1</version> <type>pom</type> <scope>import</scope> </dependency> <!--SpringBoot--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.4.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <!--数据库--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!--SpringBoot 启动器--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--日志测试~--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> </dependencies> </dependencyManagement>
- 新建 maven 项目,springcloud-api,导入依赖
<!-- 因为我们在父项目中做了依赖的管理,不用指定版本号 --> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
- 编写实体类 Dept
@Data @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) //链式编程 public class Dept implements Serializable { // 部门编号 private Long deptno; // 部门名称 private String dname; // 数据库名 private String db_source; }
服务提供方
- 新建 maven 项目, springcloud-dept-provider-8001,导入依赖
<dependencies> <dependency> <groupId>com.starry</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>
- 编写 springboot 配置文件 application.yml
server: port: 8001 mybatis: type-aliases-package: com.starry.pojo mapper-locations: classpath:mybatis/mapper/*.xml spring: application: name: springcloud-dept-provider datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql:///springcloud?useUnicode=true&characterEncoding=utf-8 username: root password: root type: com.alibaba.druid.pool.DruidDataSource
- 编写 mapper
@Mapper @Repository public interface DeptMapper { boolean addDept(Dept dept); Dept queryById(Long id); List<Dept> queryAll(); }
- 编写 mapper 对应的 xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.starry.mapper.DeptMapper"> <insert id="addDept" parameterType="Dept"> insert into dept (dname, db_source) values (#{dname},DATABASE()) </insert> <select id="queryById" resultType="Dept" parameterType="Long"> select * from dept where deptno = #{deptno}; </select> <select id="queryAll" resultType="Dept"> select * from dept; </select> </mapper>
- 编写 service
public interface DeptService { boolean addDept(Dept dept); Dept queryById(Long id); List<Dept> queryAll(); }
- service实现类
@Service public class DeptServiceImpl implements DeptService { @Autowired private DeptMapper deptMapper; @Override public boolean addDept(Dept dept) { return deptMapper.addDept(dept); } @Override public Dept queryById(Long id) { return deptMapper.queryById(id); } @Override public List<Dept> queryAll() { return deptMapper.queryAll(); } }
- 编写 controller
@RestController public class DeptController { @Autowired private DeptService deptService; @PostMapping("/dept/add") public boolean addDept(Dept dept){ return deptService.addDept(dept); } @GetMapping("/dept/get/{id}") public Dept get(@PathVariable("id") Long id){ Dept dept = deptService.queryById(id); return dept; } @GetMapping("/dept/list") public List<Dept> queryAll(){ return deptService.queryAll(); } }
- 编写启动类
@SpringBootApplication public class DeptProviderApplication { public static void main(String[] args) { SpringApplication.run(DeptProviderApplication.class, args); } }
- 运行启动类,访问对应的controller,正常访问
服务消费方
- 新建 maven 项目,springcloud-dept-consumer-80,导入依赖
<dependencies> <dependency> <groupId>com.starry</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>
- 编写配置文件 application.yml
server: port: 80
- 将 RestTemplate 注入到容器
/** * 将 RestTemplate 注入到spring容器 */ @Configuration public class ConfigBean { @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
- 编写 controller
@RestController public class DeptConsumerController { /** * 使用 RestTemplate 访问远程http服务 * getForObject get请求获取对象 * postForObject post请求获取对象 * (String url, Class<T> responseType, Object... uriVariables) * url地址,返回值类型,传递参数 */ @Autowired private RestTemplate restTemplate; private static final String REST_URL_PREFIX = "http://localhost:8001"; @RequestMapping("/consumer/dept/add") public boolean add(Dept dept){ return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class); } @RequestMapping("/consumer/dept/get/{id}") public Dept get(@PathVariable("id") Long id){ return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class); } @RequestMapping("/consumer/dept/list") public List<Dept> list() { return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class); } }
- 编写启动类
@SpringBootApplication public class DeptConsumerApplication { public static void main(String[] args) { SpringApplication.run(DeptConsumerApplication.class, args); } }
- 运行启动类,访问对应的controller,正常访问
Eureka(注册中心)
eureka server
- 新建 maven 项目,导入依赖
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>
- 编写 springboot 配置文件 application.yml
server: port: 7001 eureka: instance: hostname: localhost client: register-with-eureka: false # 实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true fetch-registry: false # 此客户端是否获取eureka服务器注册表上的注册信息,默认为true service-url: defaultZone: http://localhost:7001/eureka # 注册地址
- 编写启动类 在启动类上加上
@EnableEurekaServer
注解激活 eureka-server 的配置@SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
- 运行启动类,访问 http://localhost:7001/,出现eureka的界面
总结:
- 导入依赖
- 编写配置文件
- 启动类添加
@EnableEurekaServer
注解
eureka client
将 springcloud-dept-provider-8001 注册到 eureka
-
添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency>
-
添加Eureka的配置,服务注册到哪里
eureka: client: service-url: defaultZone: http://localhost:7001/eureka
-
在启动类上加上
@EnableEurekaClient
注解@SpringBootApplication @EnableEurekaClient public class DeptProviderApplication { public static void main(String[] args) { SpringApplication.run(DeptProviderApplication.class, args); } }
-
先启动 EurekaServerApplication ,再启动 DeptProviderApplication, 访问 http://localhost:7001/ ,可以看见 springcloud-dept-provider 成功注册到eureka了
-
修改默认描述信息
eureka: client: service-url: defaultZone: http://localhost:7001/eureka instance: instance-id: springcloud-dept-provider-8001 # 修改eureka上的默认描述信息 prefer-ip-address: true # 显示服务的ip地址
修改成功
-
监控信息,导入依赖
<!-- actuator完善监控信息 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
添加信息
info: app.name: dept-provider company.name: starry
-
点击 链接
跳转到信息页
-
获取微服务信息, DeptController,添加方法
@Autowired private DiscoveryClient client; @GetMapping("/dept/discovery") public DiscoveryClient getClient(){ // 获取微服务列表 List<String> services = client.getServices(); System.out.println(services); // 根据id获取具体的微服务 applicaioinName List<ServiceInstance> instances = client.getInstances("springcloud-dept-provider"); for (ServiceInstance instance : instances) { System.out.println(instance.getHost()); System.out.println(instance.getPort()); System.out.println(instance.getUri()); System.out.println(instance.getServiceId()); } return this.client; }
-
DeptProviderApplication 启动类添加注解
@EnableDiscoveryClient
@SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient public class DeptProviderApplication { public static void main(String[] args) { SpringApplication.run(DeptProviderApplication.class, args); } }
访问controller http://localhost:8001/dept/discovery
控制台输出结果[springcloud-dept-provider] MagicBookPro 8001 http://MagicBookPro:8001 SPRINGCLOUD-DEPT-PROVIDER
总结:
- 导入依赖
- 编写配置文件
- 启动类添加
@EnableEurekaClient
注解
eureka自我保护机制
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。
如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:
-
Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
-
Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
-
当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
eureka server
eureka:
server:
enable-self-preservation: false # 是否启用自我保护
eviction-interval-timer-in-ms: 3000 # #清理间隔(单位毫秒,默认是60*1000)
eureka client
eureka:
instance:
#eureka客户端需要多长时间发送心跳给eureka服务器,表明他仍然活着,默认30秒
lease-renewal-interval-in-seconds: 5
#eureka服务器在接收到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除
lease-expiration-duration-in-seconds: 10
client:
#表示eureka client间隔多久去拉取服务器注册信息,默认为30秒
registry-fetch-interval-seconds: 30
#表示eureka client间隔多久去拉取服务器注册信息,默认为30秒
registry-fetch-interval-seconds: 30
eureka集群配置
更改本地的 hosts 配置文件 c:\windows\system32\drivers\etc
# eureka
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
- 新建 maven 项目 springcloud-eureka-7002和springcloud-eureka-7003
- 复制 springcloud-eureka-7001 的 application.yml 和 启动类
- 修改配置文件
- springcloud-eureka-7001
server: port: 7001 eureka: instance: hostname: eureka7001.com client: register-with-eureka: false # 实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true fetch-registry: false # 此客户端是否获取eureka服务器注册表上的注册信息,默认为true service-url: defaultZone: http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka # 注册地址
- springcloud-eureka-7002
server: port: 7002 eureka: instance: hostname: eureka7002.com client: register-with-eureka: false # 实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true fetch-registry: false # 此客户端是否获取eureka服务器注册表上的注册信息,默认为true service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7003.com:7003/eureka # 注册地址
- springcloud-eureka-7003
server: port: 7003 eureka: instance: hostname: eureka7003.com client: register-with-eureka: false # 实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true fetch-registry: false # 此客户端是否获取eureka服务器注册表上的注册信息,默认为true service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 注册地址
- 修改 springcloud-dept-provider-8001 的服务注册地址
eureka: client: service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka instance: instance-id: springcloud-dept-provider-8001 # 修改eureka上的默认描述信息
- springcloud-eureka-7001
- 分别启动, springcloud-eureka-7001,springcloud-eureka-7002,springcloud-eureka-7003,springcloud-dept-provider-8001
- 关闭一个服务,其他2个服务还是正常
Ribbon(负载均衡)
-
springcloud-dept-consumer-80 导入依赖
<!--Ribbon--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--erueka--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency>
-
添加配置
server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
-
启动类加上
@EnableEurekaClient
注解@SpringBootApplication @EnableEurekaClient public class DeptConsumerApplication { public static void main(String[] args) { SpringApplication.run(DeptConsumerApplication.class, args); } }
-
配置类上加上
@LoadBalanced
注解@Configuration public class ConfigBean { @Bean @LoadBalanced // 标记这个RestTemplate要做负载均衡 public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
-
修改controller中restTemplate的请求地址
/** * 由于我们做了负载均衡,每次请求的地址不能固定,根据服务名来访问 */ private static final String REST_URL_PREFIX = "http://springcloud-dept-provider";
-
服务全部启动,集群正常访问,controller也正常访问,但是返回的数据都是一样的,看不出负载均匀效果
-
新建2个数据库
-
-
新建 maven 项目 springcloud-dept-provider-8002 和 springcloud-dept-provider-8003,复制 springcloud-dept-provider-8001 的文件,修改 application.yml ,只需修改 端口号,连接的数据库 和 实例id ; 应用名称不用改,因为我们负载均衡是根据应用名来获取服务的
springcloud-dept-provider-8002
server: port: 8002 mybatis: type-aliases-package: com.starry.pojo mapper-locations: classpath:mybatis/mapper/*.xml spring: application: name: springcloud-dept-provider datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql:///springcloud2?useUnicode=true&characterEncoding=utf-8 username: root password: root type: com.alibaba.druid.pool.DruidDataSource eureka: client: service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka instance: instance-id: springcloud-dept-provider-8002 # 修改eureka上的默认描述信息 prefer-ip-address: true # 显示服务的ip地址 info: app.name: dept-provider company.name: starry
springcloud-dept-provider-8003
server: port: 8003 mybatis: type-aliases-package: com.starry.pojo mapper-locations: classpath:mybatis/mapper/*.xml spring: application: name: springcloud-dept-provider datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql:///springcloud3?useUnicode=true&characterEncoding=utf-8 username: root password: root type: com.alibaba.druid.pool.DruidDataSource eureka: client: service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka instance: instance-id: springcloud-dept-provider-8003 # 修改eureka上的默认描述信息 prefer-ip-address: true # 显示服务的ip地址 info: app.name: dept-provider company.name: starry
-
启动全部服务,集群正常访问,controller正常访问,访问 http://localhost/consumer/dept/list ,出现不同的结果,成功实现负载均衡
修改负载均衡规则
- 编写自定义规则
@Configuration
注解@Bean
注解- 返回
IRule
的实现@Configuration public class MyRule { @Bean public IRule getRule(){ return new RandomRule(); } }
- 在启动类上加上
@RibbonClient
注解@SpringBootApplication @EnableEurekaClient /** * 使用自定义的负载均衡规则,自定义的负载均衡类不能放在启动类同级目录及子目录,不然会被所有@RibbonClients共享 * name 服务提供方的名称 * configuration 自定义的IRule类 */ @RibbonClient(name = "springcloud-dept-provider",configuration = MyRule.class) public class DeptConsumerApplication { public static void main(String[] args) { SpringApplication.run(DeptConsumerApplication.class, args); } }
- 启动服务,访问 http://localhost/consumer/dept/list ,随机访问不同微服务,返回的数据也不用
总结:
- 导入依赖
- 编写配置文件,从哪获取服务
- RestTemplate 上加上
@LoadBalanced
注解 - 如果要修改负载均衡算法,自定义算法,在启动类加上
@RibbonClient
注解
Feign(服务调用)
feign,主要是社区,大家都习惯面向接口编程。这个是很多开发人员的规范。调用微服务访问两种方法:
- 微服务名字[ ribbon ]
- 接口和注解[ feign ]
使用 Feign 只需要创建一个接口并使用一个注解来配置它即可,这就类似于我们在 dao 层的接口上标注 @Mapper 注解一样。
feign集成了ribbon
-
springcloud-api 导入依赖
<dependencies> <!--feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--erueka--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
-
新建服务接口,请求 url 要和提供方的 url 一样
/** * 要使用的微服务的名称(服务提供方) */ @FeignClient(name = "springcloud-dept-provider") @Service public interface DeptClientService { @RequestMapping("/dept/add") public boolean add(Dept dept); @RequestMapping("/dept/get/{id}") public Dept get(@PathVariable("id")Long id); @RequestMapping("/dept/list") public List<Dept> list(); }
-
新建 maven 项目 springcloud-dept-consumer-feign,导入依赖
<dependencies> <!--feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--ribbon--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--erueka--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>com.starry</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>
-
application.yml
server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
-
controller 调用 springcloud-api 的 service
@RestController public class DeptConsumerController { @Autowired private DeptClientService service; @RequestMapping("/consumer/dept/add") public boolean add(Dept dept) { return service.add(dept); } @RequestMapping("/consumer/dept/get/{id}") public Dept get(@PathVariable("id") Long id) { return service.get(id); } @RequestMapping("/consumer/dept/list") public List<Dept> list() { return service.list(); } }
-
启动类 加上
@EnableFeignClients
注解@SpringBootApplication @EnableEurekaClient @EnableFeignClients public class FeignDeptConsumerApplication { public static void main(String[] args) { SpringApplication.run(FeignDeptConsumerApplication.class, args); } }
-
启动服务,正常访问 ; 配置负载均衡算法
# 服务名 springcloud-dept-provider: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 类路径
总结:
- 导入依赖
- 编写和服务提供方方法相同的接口,并加上
@FeignClient
注解并指定服务提供方的名称 - 使用方使用只需注入接口便可调用方法
- 启动类上加上
@EnableFeignClients
注解
Hystrix
服务熔断(服务提供方)
-
新建 maven 项目 springcloud-dept-provider-hystrix-8001,复制 springcloud-dept-provider-8001 ,导入 hystrix 依赖
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>com.starry</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>
-
controller中添加方法
@GetMapping("/dept/get/{id}") // 指定回退方法 @HystrixCommand(fallbackMethod = "hystrixGet") public Dept get(@PathVariable("id") Long id){ Dept dept = deptService.queryById(id); if (dept == null){ throw new RuntimeException("no this dept"); } return dept; } /** * 回退方法,出现异常或者超时会调用此方法 * @param id * @return */ public Dept hystrixGet(Long id){ return new Dept(id, "未查询到信息", "数据库中没有此信息"); }
-
启动类添加
@EnableCircuitBreaker
注解 开启熔断支持@SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker public class HystrixDeptProviderApplication { public static void main(String[] args) { SpringApplication.run(HystrixDeptProviderApplication.class, args); } }
-
service接口指定回退工厂
@FeignClient(name = "springcloud-dept-provider",fallbackFactory = DeptClientServiceFallbackFactory.class) @Service public interface DeptClientService
-
启动服务,访问对应的controller,
http://localhost/consumer/dept/get/1 ,正常返回数据
http://localhost/consumer/dept/get/9 ,调用了备用方法
服务降级(服务使用方)
-
springcloud-api 新建
DeptClientServiceFallbackFactory
类, 实现FallbackFactory
接口, 重写方法,返回值为服务实例,最后将类注入spring容器@Component public class DeptClientServiceFallbackFactory implements FallbackFactory { @Override public DeptClientService create(Throwable throwable) { return new DeptClientService() { @Override public boolean add(Dept dept) { return false; } @Override public Dept get(Long id) { return new Dept(id,"这是客户端的降级信息,这个服务已经被关闭了","没有数据~"); } @Override public List<Dept> list() { return null; } }; } }
-
修改 feign 客户端(springcloud-dept-consumer-feign)的配置 开启feign的hystrix支持
feign: hystrix: enabled: true
-
启动服务(注册中心,服务提供方,服务使用方[feign])
访问 http://localhost/consumer/dept/get/2
关闭服务提供方,返回工厂方法的数据
流监控
-
新建 maven 项目 springcloud-dept-consumer-hystrix-dashboard ,导入依赖
<dependencies> <!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--hystrix-dashboard--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> <version>1.4.6.RELEASE</version> </dependency> </dependencies>
-
编写配置文件
server: port: 9001
-
启动类加上
@EnableHystrixDashboard
注解 开启监控@SpringBootApplication @EnableHystrixDashboard public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class, args); } }
-
在需要监控的服务提供方(HystrixDeptProviderApplication)加上hystrix的servlet映射
前提: 需要导入 hystrix 和 actuator 依赖<!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--监控--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
/** * 在需要监控的服务提供方添加hystrix的servlet映射 * @return */ @Bean public ServletRegistrationBean registrationBean(){ ServletRegistrationBean bean = new ServletRegistrationBean(new HystrixMetricsStreamServlet()); bean.addUrlMappings("/actuator/hystrix.stream"); return bean; }
-
启动服务,访问 http://localhost:9001/hystrix 跳转到服务监控页间, 输入需要监控的服务提供方 ip地址+端口+/actuator/hystrix.stream
Zuul(路由网关)
- 新建 maven 项目,导入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>
- 编写配置
server: port: 9527 spring: application: name: spring-zuul eureka: client: service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka instance: instance-id: spring-zuul prefer-ip-address: true info: app.name: spring-zuul company: starry
- 编写启动类 添加
@EnableZuulProxy
注解@SpringBootApplication @EnableZuulProxy public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } }
- 启动服务(注册中心,服务提供方,服务销毁方,网关)
- 直接访问访问提供方的controller http://localhost:8001/dept/get/1,正常返回数据
- 可以看到是 ip地址+端口+uri,每个微服务的ip和端口都不一样,我们希望不直接显示微服务地址和服务名
- 修改 hosts 模拟网关的地址
127.0.0.1 starryzuul.com
- 默认的转发规则就是 API 网关地址+访问的服务名称+接口 URI;访问 http://starryzuul.com:9527/springcloud-dept-provider/dept/get/1 实现不直接显示微服务地址
- 配置文件配置路由规则
# 配置路由 zuul: routes: # 自定义服务名 provider: # 要被转发的服务名 serviceId: springcloud-dept-provider # 转发路径 path: /provider/**
- 将默认的服务器修改为自定义的服务名,服务 http://starryzuul.com:9527/provider/dept/get/9 ,正常返回结果
- 虽然可以通过自定义的访问名可以访问,但是原来的服务器也还是可以访问,我们希望只能自定义的服务名可以访问;再次添加配置
# 配置路由 zuul: routes: # 自定义服务名 provider: # 要被转发的服务名 serviceId: springcloud-dept-provider # 转发路径 path: /provider/** # 忽略的服务,不能通过这个访问名访问 # ignored-services: "*" 除了自定义的服务其它的服务都被忽略 ignored-services: springcloud-dept-provider
- 配置路由前缀
# 配置路由 zuul: routes: # 自定义服务名 provider: # 要被转发的服务名 serviceId: springcloud-dept-provider # 转发路径 path: /provider/** # 忽略的服务,不能通过这个访问名访问 ignored-services: springcloud-dept-provider # 所有路由的前缀 prefix: /starry
- 服务器前加 /starry 才能访问 ,http://starryzuul.com:9527/starry/provider/dept/get/1
- 修改 hosts 模拟网关的地址
SpringCloudConfig
准备工作
- 安装git
- gitee(码云) 新建仓库,上传 application.yml
application.yml# 下载一个项目和它的整个代码历史 $ git clone [url] # 设置提交代码时的用户信息 $ git config [--global] user.name "[name]" $ git config [--global] user.email "[email address]" # 添加当前目录的所有文件到暂存区 git add . # 提交暂存区到仓库区 $ git commit -m [message] # 推送到远程主分支 $ git push origin master
spring: profiles: active: test --- spring: profiles: dev application: name: springcloud-client-dev server: port: 7777 eureka: client: service-url: defaultZone: http://eureka7001.com:7001/eureka --- spring: profiles: test application: name: springcloud-client-dev server: port: 7778 eureka: client: service-url: defaultZone: http://eureka7002.com:7002/eureka
服务端获取gitee配置
- 新建 maven 项目 springcloud-config-server-3344 ,导入依赖
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> <version>2.1.1.RELEASE</version> </dependency> </dependencies>
- 编写配置文件
server: port: 3344 spring: application: name: springcloud-config-server cloud: config: server: git: # gitee 地址 uri: https://gitee.com/fanxingweb/springcloud-config.git
- 编写启动类,添加
@EnableConfigServer
注解@SpringBootApplication @EnableConfigServer public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); } }
- 启动应用,
- 访问 http://localhost:3344/application-dev.yml 返回 dev 环境的配置
也可以通过 http://localhost:3344/master/application-test.yml 访问 - 访问 http://localhost:3344/application-test.yml 返回 test 环境的配置
- 访问 http://localhost:3344/application-dev.yml 返回 dev 环境的配置
客户端连接访问远程访问
- 新建 maven 项目 springcloud-config-client,导入依赖
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> <version>2.1.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
- 编写配置文件
-
bootstrap.yml
# 系统级别的配置 优先加载 spring: cloud: config: # 服务端地址 uri: http://localhost:3344 # 文件名 不带后缀 name: clientConfig # 分支 label: master # 环境 profile: test
-
application.yml
# 用户级别的配置 spring: application: name: springcloud-config-client
-
- 编写启动类
@SpringBootApplication public class ClientConfigApplication { public static void main(String[] args) { SpringApplication.run(ClientConfigApplication.class, args); } }
- 测试是否能正常读取配置
@RestController public class ClientController { @Value("${spring.application.name}") private String applicationName; @Value("${eureka.client.service-url.defaultZone}") private String defaultZone; @Value("${server.port}") private String port; @RequestMapping("/config") public String getConfig(){ return "applicationName=>" + applicationName +"\t"+ "defaultZone=>" + defaultZone +"\t"+ "port=>" + port; } }
- 启动服务,由于我们配置的是 test 环境,对应端口是 7777,访问 http://localhost:7777/config ,正常显示
applicationName=>springcloud-client-dev defaultZonehttp://eureka7001.com:7001/eureka port7777