SpringCloud概述
SpringCloud是什么
Spring Cloud 为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、群集状态)。分布式系统的协调会导致锅炉板模式,使用 Spring Cloud 开发人员可以快速启动实现这些模式的服务和应用程序。它们将在任何分布式环境中很好地工作,包括开发人员自己的笔记本电脑、裸机数据中心和云铸造等托管平台。
SpringCloud和SpringBoot的关系
- SpringBoot专注于快速、方便的开发单个个体为服务
- SpringCloud关注于全局的服务治理框架
SpringCloud能干嘛
- Distributed/versioned configuration(分布式/版本配置)
- Service registration and discovery(服务注册和发现)
- Routing(路由)
- Service-to-service calls(服务到服务呼叫)
- Load balancing(负载平衡)
- Circuit Breakers(断路 器)
- Global locks(全局锁)
- Leadership election and cluster state(领导选举和集群状态)
- Distributed messaging(分布式消息传送)
参考网站
SpringCloud
SpringCloud版本选择
spring-boot-starter-parent | spring-cloud-dependencies |
---|---|
2.0.6.RELEASE(2.0.x) | Finchley(芬奇利)(不兼容springboot 1.5.x) |
2.1.4.RELEASE(2.1.x) | Greenwich(格林威治) |
SpringCloud远程调用
一、新建一个maven的父项目
1、添加依赖
<!--打包方式 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.18.12</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>8.0.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--日志测试-->
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!--logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
二、新建API子项目
1、添加依赖
<!--当前的model要用的依赖,如果父依赖已配置版本,这里就不要写了-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
2、创建实体类
package com.zhiyu.springcloud.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true)//链式写法
public class Dept implements Serializable {//实体类序列化 orm实体关系映射
private Long deptno;
private String dname;
private String db_source;//此字段存在哪个数据库
public Dept(String dname) {
this.dname = dname;
}
}
三、新建Provider子项目
1、添加依赖
<dependencies>
<!--需要得到实体类,所以需要配置api model-->
<dependency>
<groupId>org.zhiyu</groupId>
<artifactId>SpringCloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
2、新建配置文件
server:
port: 8001
#mybatis配置
mybatis:
type-aliases-package: com.zhiyu.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#Spring配置
spring:
application:
name: SpringCloud-Provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: cxy0920.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
3、编写Mapper接口及Mapper.xml文件
package com.zhiyu.springcloud.mapper;
import com.zhiyu.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DeptMapper {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
<?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.zhiyu.springcloud.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 db01.dept where deptno = #{deptno};
</select>
<select id="queryAll" resultType="Dept">
select *
from db01.dept;
</select>
</mapper>
4、编写Service层
package com.zhiyu.springcloud.service;
import com.zhiyu.springcloud.pojo.Dept;
import java.util.List;
public interface DeptService {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
package com.zhiyu.springcloud.service;
import com.zhiyu.springcloud.mapper.DeptMapper;
import com.zhiyu.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@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();
}
}
5、编写Controller层
package com.zhiyu.springcloud.controller;
import com.zhiyu.springcloud.pojo.Dept;
import com.zhiyu.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
//提供Restful服务(不需要前端)
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("/dept/add")
@ResponseBody
public boolean addDept(Dept dept){
return deptService.addDept(dept);
}
@GetMapping("/dept/queryById/{id}")
public Dept queryById(@PathVariable("id") Long id){
return deptService.queryById(id);
}
@GetMapping("/dept/queryAll")
public List<Dept> queryAll(){
return deptService.queryAll();
}
}
6、编写启动类
package com.zhiyu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//启动类
@SpringBootApplication
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
7、测试接口
访问 localhost:8001/dept/queryAll
四、新建Consumer子项目
1、导入依赖
<!--实体类+web-->
<dependencies>
<dependency>
<groupId>org.zhiyu</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>
2、编写配置文件
server:
port: 80
3、编写配置类
将RestTemplate
注册到Spring管理,再通过自动装配使用
package com.zhiyu.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConfigBean {//配置类 ---> spring ApplicationContext.xml
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
3、编写Controller
package com.zhiyu.springcloud.controller;
import com.zhiyu.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class DeptConsumerController {
//消费者没有service层
@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/queryById/"+id,Dept.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> List(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/queryAll",List.class);
}
}
4、编写启动类
package com.zhiyu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
5、测试
同时启动Provider和Consumer,通过80端口调用8001端口服务
访问 localhost:80/consumer/dept/list
Eureka
一、新建EurekaService子项目
1、导入依赖
<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>
2、编写配置文件
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端实例名称
client:
register-with-eureka: false #表示是否向Eureka服务中心注册自己
fetch-registry: false #如果为false 则表示自己为注册中心
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
3、编写启动类
package com.zhiyu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //服务断的启动类,可以接受别人注册进来
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
4、测试
访问 localhost:7001
二、将Provider注册到Eureka
Eureka项目不用修改,一下配置都在Provider项目中修改
1、导入依赖
<!--Eureka客户端-->
<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-actuator</artifactId>
</dependency>
2、添加配置
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider-8001
#info配置(一些没啥用的信息)
info:
app.name: zhiyu-SpringCloud
company.name: zhiyushijie.work
3、在启动类中添加注解
@EnableEurekaClient//开启客户端,自动注册到Eureka
@EnableDiscoveryClient//服务发现(可选)
4、编写一个获取微服务信息的Controller(可选)
若想使用此方法,必须在启动类中添加@EnableDiscoveryClient
注解
@Autowired
private DiscoveryClient client;
@GetMapping("/dept/discovery")
public Object discovery(){
//获取微服务列表清单
List<String> services = client.getServices();
System.out.println(services);
//得到一个具体的微服务信息,通过具体的微服务id,applicationName;
List<ServiceInstance> instances = client.getInstances("SpringCloud-Provider-dept");
for (ServiceInstance instance : instances) {
System.out.println(
instance.getHost()+"\t"+
instance.getPort()+"\t"+
instance.getUri()+"\t"+
instance.getServiceId()+"\t"
);
}
return this.client;
}
三、Eureka搭建集群时的问题
若想在单机搭建虚拟集群的话,必须设置域名IP映射,如果不设置的话,直接使用如127.0.0.1这样的本机IP地址的话。Eureka会排出相同的IP地址,即使每个Eureka服务端口号不同也不行。
集群搭建的Eureka服务器端配置文件
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: eureka7001.com #Eureka服务端实例名称
client:
register-with-eureka: false #表示是否向Eureka服务中心注册自己
fetch-registry: false #如果为false 则表示自己为注册中心
service-url:
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
其他集群个体配置相同,只需更换Eureka地址即可
Provider端配置文件
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-provider-8001
Ribbon
配置负载均衡
一、添加依赖
在Consumer项目中添加依赖
<!--Ribbon负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--Eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
二、编写配置文件
#Eureka配置
eureka:
client:
register-with-eureka: false #不向Eureka中注册自己
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
三、向启动类中添加注解
@EnableEurekaClient
四、向配置类中添加注解
@LoadBalanced
@Configuration
public class ConfigBean {//配置类 ---> spring ApplicationContext.xml
@Bean
@LoadBalanced//配置负载均衡实现RestTemplate(Ribbon)
public RestTemplate getRestTemplate(){//Restful风格的http请求来实现远程调用等
return new RestTemplate();
}
}
五、修改Controller中的URL
//Ribbon实现负载均衡时,应该是一个变量,通过服务名来访问
//private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
六、创建新的Provider项目
修改application.yaml
需要修改的有,端口号,连接的数据库,在Eureka中显示的ID
遇见的坑!!
配置完成后,访问http://localhost/consumer/dept/list后发现返回的数据没有改变,全部都是db01中的数据。查看数据库中数据,无异常。
原因:编写*mapper.xml文件时,查询语句时不严谨。如:
<select id="queryAll" resultType="Dept">
select * from db01.dept;
</select>
正确应去掉数据库名db01,否则的话不管访问哪个Provider都会去查询db01数据库
<select id="queryAll" resultType="Dept">
select * from dept;
</select>
七、自定义负载均衡策略
负载均衡策略实现的接口
常用的策略,IRule
接口的实现类
AvailabilityFilteringRule
过滤掉崩贵的服务,再轮询
RandomRule
随机
RoundRobinRile
轮询(默认)
1、使用设置好的负载均衡策略
在配置类中把负载均衡策略添加到Bean中即可
@Bean
public IRule myRule(){
return new RandomRule();
}
2、自定义的负载均衡策略
1、创建自定义策略的配置类
2、由于我们创建的自定义策略不在SpringBoot的启动类同级下,所以不会被SpringBoot扫描注册。
因此我们需要在SpringBoot的启动类上添加@RibbonClient
注解,并设置自定义策略的类。
@SpringBootApplication
@EnableEurekaClient
//使微服务启动时加载自定义策略
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
同时还要删除上一步在配置类中的设置的Random策略,并将策略移动到自定义的策略类中
package com.zhiyu.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyRule {
@Bean
public IRule myRule(){
return new RandomRule();
}
}
注意点:
为什么要把自定义的策略组件放到SpringBoot扫描不到的包下?
3、正式编写策略算法
新建策略类
继承IRule
接口,或者实现AbstractLoadBalancerRule
抽象类
package com.zhiyu.myrule;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class zhiyuRandomRule extends AbstractLoadBalancerRule {
private int total = 0;
private int currentIndex = 0;
public zhiyuRandomRule() {
}
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();//获得活着的服务
List<Server> allList = lb.getAllServers();//获得全部的服务
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
// int index = this.chooseRandomInt(serverCount);//生成区间随机数
// server = (Server)upList.get(index);//从活着的服务中随机选取一个
//===================================
if (total<5){
server = upList.get(currentIndex);
total++;
}else {
total = 0;
currentIndex++;
if (currentIndex>=upList.size()) {
currentIndex = 0;
}
server = upList.get(currentIndex);
}
//===================================
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
将策略配置类中的策略替换成上一步自定义的策略类
@Configuration
public class MyRule {
@Bean
public IRule myRule(){
return new zhiyuRandomRule();
}
}
又一个坑!!
配置完成后启动项目,出现报错:
Invalid bean definition with name 'zhiyuRule' defined in com.zhiyu.myrule.zhiyuRule: Bean name derived from @Bean method 'zhiyuRule' clashes with bean name for containing configuration class; please make those names unique!
百度搜索后,结论多为:没有删除ConfigBean
中的MyRule
经检查,我已经删除了此方法,还是报此错误
最后,真正原因为:自定义策略配置类类名和方法名重复!
即使大小写不同也不行,必须不同名才可以!


解决方案:
更改方法名

或者显示指定Bean的名字

八、Feign接口式调用服务
一、新建feign消费者客户端
其中application.yaml
、ConfigBean
、无需改变
二、导入依赖
<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>
<!--Eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--实体类+web-->
<dependency>
<groupId>org.zhiyu</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>
三、修改API子项目
添加依赖
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
编写Service层

package com.zhiyu.springcloud.service;
import com.zhiyu.springcloud.pojo.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")//Spring应用的name
public interface DeptClientService {
@PostMapping("/dept/add")
public boolean addDept(Dept dept);
@GetMapping("/dept/queryById/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/queryAll")
public List<Dept> queryAll();
}
四、编写新的Controller

package com.zhiyu.springcloud.controller;
import com.zhiyu.springcloud.pojo.Dept;
import com.zhiyu.springcloud.service.DeptClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class DeptConsumerController {
@Autowired
private DeptClientService deptClientService;
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept){
return this.deptClientService.addDept(dept);
}
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return this.deptClientService.queryById(id);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> List(){
return this.deptClientService.queryAll();
}
}
五、在启动类上添加注解

@SpringBootApplication
@EnableEurekaClient
//扫描Feign接口
@EnableFeignClients(basePackages = {"com.zhiyu.springcloud"})
//@ComponentScan("com.zhiyu.springcloud")
public class DeptConsumerFeign_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerFeign_80.class,args);
}
}
Hystrix
一、Hystrix服务熔断
在一个微服务出现错误时并不直接抛出异常或中断服务,而是返回设置好的错误信息,让其他服务继续运行下去,防止服务雪崩。
1、新建Hystrix项目
复制Provider-8001即可

2、添加依赖
<!--hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
3、编写Controller

package com.zhiyu.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.zhiyu.springcloud.pojo.Dept;
import com.zhiyu.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
//提供Restful服务(不需要前端)
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/dept/queryById/{id}")
@HystrixCommand(fallbackMethod = "hystrixGet")
public Dept get (@PathVariable("id") Long id){//抛出异常,不返回对象 Dept
Dept dept = deptService.queryById(id);
if (dept == null){
throw new RuntimeException("id=>"+id+",不存在该用户,或信息无法找到!");
}
return dept;
}
//备选方法
public Dept hystrixGet (@PathVariable("id") Long id){
//正常返回Dept对象,但是错误信息被写在对象里
return new Dept()
.setDeptno(id)
.setDname("id=>"+id+",没有对应的信息,null")
.setDb_source("no database in MySQL");
}
}
这里只留下了一个查询方法,方便理解思路
4、启动类添加注解
//启动类
@SpringBootApplication
@EnableEurekaClient//开启客户端,自动注册到Eureka
@EnableDiscoveryClient//服务发现(可选)
@EnableCircuitBreaker//添加熔断的支持
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class,args);
}
}
二、Hystrix服务降级
由于整体资源不足,关闭不常用的微服务
1、在API项目中添加FallbackFactory

package com.zhiyu.springcloud.service;
import com.zhiyu.springcloud.pojo.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public boolean addDept(Dept dept) {
return false;
}
@Override
public Dept queryById(Long id) {
return new Dept()
.setDeptno(id)
.setDname("没有对应信息,服务降级已被关闭")
.setDb_source("No data");
}
@Override
public List<Dept> queryAll() {
return null;
}
};
}
}
正常情况应该重写每一个降级方法
2、修改Service中的注解
3、修改Feign的Consumer客户端
修改application
配置文件
#开启降级Feign.hystrix
feign:
hystrix:
enabled: true
熔断与降级
服务熔断 | 服务降级 |
---|---|
服务端 | 客户端 |
某个服务超时或异常,引起熔断。熔断后可以继续调用此服务,但是只会返回事先设定好的错误信息 | 关闭某个整体服务,也可是单个服务。关闭之后服务不再被调用,客户端将返回事先设置好的缺省值 |
三、HystrixDashboard服务监控
1、新建Dashboard监控项目

2、导入依赖
<dependencies>
<!--Hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</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>
<!--Eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--实体类+web-->
<dependency>
<groupId>org.zhiyu</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>
3、配置端口
server:
port: 9001
4、编写启动类
package com.zhiyu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard//开启Dashboard监控
public class DeptConsumerDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9001.class,args);
}
}
5、修改Provider服务端
注意:
Dashboard只能监控启动Hystrix服务熔断的服务端请求!!
必须添加以下依赖
<!--hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--Eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--完善监控(可不用)若使用dashboard则必须导入此依赖!!-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在启动类中添加一个Bean
//增加一个Servlet
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
6、访问测试
先访问 http://127.0.0.1:7001/ 和 http://127.0.0.1:8001/
保证Eureka和服务端正常启动,并且服务端注册成功、请求可用
再访问 http://127.0.0.1:9001/hystrix
查看Dashboard是否启动成功
再访问 http://127.0.0.1:8001/actuator/hystrix.stream
查看Stream流是否存在
最后将 http://127.0.0.1:8001/actuator/hystrix.stream
地址填入,并设置监控间隔时间、名称。
点击进入即可
Zuul
路由网关,对请求做拦截、过滤等。
一、创建Zuul项目
Zuul就是一个微服务项目,可以被注册在Eureka

二、添加依赖
<dependencies>
<!--Zuul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--Hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</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>
<!--Eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--实体类+web-->
<dependency>
<groupId>org.zhiyu</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>
三、编写配置文件
server:
port: 9527
spring:
application:
name: SpringCloud-Zuul
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: Zuul9527.com
prefer-ip-address: true
info:
app.name: zhiyuSpringCloud-Zuul
zuul:
routes:
mydept.serviceId: springcloud-provider-dept #将springcloud-provider-dept路径替换成/mydept/**
mydept.path: /mydept/**
ignored-services: springcloud-provider-dept #关闭springcloud-provider-dept路径的访问
# ignored-services: "*" 隐藏全部微服务
prefix: /zhiyu #统一的访问前缀
四、编写启动类
package com.zhiyu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy//开启Zuul
public class ZuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication_9527.class,args);
}
}
五、测试
其他URL则不能再访问,这里www.zhiyu.com
在hosts文件配置了本机映射到127.0.0.1
Q.E.D.