基本组件
- 服务发现——Eureka
- 客服端负载均衡——Ribbon
- 远程调用——Feign
- 断路器——Hystrix
- 服务网关——Zuul
- 分布式配置——Spring Cloud Config
父工程环境搭建
pom依赖
子模块依赖管理
<dependencyManagement>
<dependencies>
<!-- 导入SpringCloud需要的依赖信息 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringBoot依赖信息 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--SpringBoot整合MyBatis的依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--druid依赖信息-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
</dependencies>
</dependencyManagement>
环境相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
eureka环境搭建
pom环境
<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>
- 配置中心
server:
port: 5000
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false # 自己就是注册中心,所以自己不注册自己
fetch-registry: false # 自己就是注册中心,所以不需要“从注册中心取回信息”
service-url: # 客户端访问 Eureka 时使用的地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- 主启动类
@EnableEurekaServer
@SpringBootApplication
public class eureka {
public static void main(String[] args) {
SpringApplication.run(eureka.class,args);
}
}
- 展示
基本服务提供
pom环境
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.JW.springcloud</groupId>
<artifactId>pro43-spring-cloud-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!-- <version>2.2.0.RELEASE</version>-->
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<!-- <version>2.2.4.RELEASE</version>-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.4.2</version>
</dependency>
</dependencies>
- 配置文件
server:
port: 1001
eureka:
client:
service-url:
defaultZone: http://localhost:5000/eureka/
spring:
application:
name: springcloud-provider
- controller方法
import com.JW.springcloud.Employee;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.http.HttpRequest;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springcloud.JW.springcloud.Config.ResultEntity;
import javax.servlet.http.HttpServletRequest;
@RestController
public class EmployeeHandler {
@RequestMapping("/provider/get/employee/remote")
public Employee getEmployeeRemote(HttpServletRequest request) {
int serverPort = request.getServerPort();
return new Employee(555, "tom555-" + serverPort, 555.55);
}
@RequestMapping("/provider/get/employee/by/id")
public Employee getEmployeeById(@RequestParam("empId") Integer empId) {
return new Employee(empId, "tom999-", 999.99);
}
@RequestMapping("/provider/save/emp")
public Employee saveEmp(@RequestBody Employee employee) {
return employee;
}
//熔断方法
@HystrixCommand(fallbackMethod = "getEmpBackup")
@RequestMapping("/provider/circuit/breaker/get/emp")
public ResultEntity<Employee> getEmp(@RequestParam("signal") String signal) {
if ("bang".equals(signal)) {
throw new RuntimeException();
}
return ResultEntity.successWithData(new Employee(666, "sam666", 666.66));
}
public ResultEntity<Employee> getEmpBackup(@RequestParam("signal") String signal) {
return ResultEntity.failed("circuit break workded,with signal=" + signal);
}
}
- 自定义封装返回值类型
public class ResultEntity<T> {
public static final String SUCCESS = "SUCCESS";
public static final String FAILED = "FAILED";
public static final String NO_MESSAGE = "NO_MESSAGE";
public static final String NO_DATA = "NO_DATA";
/*** 操作成功,不需要返回数据 * @return */
public static ResultEntity<String> successWithoutData() {
return new ResultEntity<String>(SUCCESS, NO_MESSAGE, NO_DATA);
}
/*** 操作成功,需要返回数据 * @param data * @return */
public static <E> ResultEntity<E> successWithData(E data) {
return new ResultEntity<E>(SUCCESS, NO_MESSAGE, data);
}
/*** 操作失败,返回错误消息 * @param message * @return */
public static <E> ResultEntity<E> failed(String message) {
return new ResultEntity<E>(FAILED, message, null);
}
private String result;
private String message;
private T data;
public ResultEntity() {
}
public ResultEntity(String result, String message, T data) {
super();
this.result = result;
this.message = message;
this.data = data;
}
@Override
public String toString() {
return "ResultEntity{" +
"result='" + result + '\'' +
", message='" + message + '\'' +
", data=" + data +
'}';
}
public static String getSUCCESS() {
return SUCCESS;
}
public static String getFAILED() {
return FAILED;
}
public static String getNoMessage() {
return NO_MESSAGE;
}
public static String getNoData() {
return NO_DATA;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
- 主启动类配置
@EnableHystrix
@EnableCircuitBreaker
@EnableEurekaClient
@SpringBootApplication
public class CrowdMain {
public static void main(String[] args) {
SpringApplication.run(CrowdMain.class, args);
}
}
Ribbon(远程调用)
pom环境
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.JW.springcloud</groupId>
<artifactId>pro43-spring-cloud-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
- 配置类
@Configuration
public class CloudConfig {
@Bean
@LoadBalanced //负载均衡器
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
server:
port: 4000
spring:
application:
name: springcloud-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:5000/eureka/
- 方法类
@RestController
public class HumanResourceHandler {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/consumer/get/employee")
public Employee getEmployeeRemote() {
// 远程调用方法的主机地址
//String host = "http://localhost:1000";
String host = "http://springcloud-provider:1000";
// 远程调用方法的具体 URL 地址
String url = "/provider/get/employee/remote";
// 远程调用方法的具体 URL 地址
String url = "/provider/get/employee/remote";
return restTemplate.getForObject(host + url, Employee.class);
}
}
基于 @LoadBalanced环境测试
- 同时开启provider服务1000,1001,1002端口
- 测试
远程调用——Feign
创建实体common工程(远程调用开放接口工程)
pom环境
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Employee
private Integer empId;
private String empName;
private Double empSalary;
远程调用接口
@Component
@FeignClient(value = "springcloud-provider")
public interface EmployeeRemoteService {
@RequestMapping("/provider/get/employee/remote")
public Employee getEmployeeRemote();
@RequestMapping("/provider/get/employee/by/id")
public Employee getEmployeeById(@RequestParam("empId") Integer empId);
@RequestMapping("/provider/save/emp")
public Employee saveEmp(@RequestBody Employee employee);
}
测试远程调用接口,创建消费者工程
pom环境
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.JW.springcloud</groupId>
<artifactId>pro43-spring-cloud-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!-- <version>2.1.2.RELEASE</version>-->
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-openfeign</artifactId>-->
<!-- <version>2.1.2.RELEASE</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<!-- <version>2.1.2.RELEASE</version>-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
@RestController
public class EmployeeFeignHandler {
@Autowired
private EmployeeRemoteService employeeRemoteService;
@RequestMapping("/feign/consumer/get/emp")
public Employee getEmployeeRemote() {
return employeeRemoteService.getEmployeeRemote();
}}
server:
port: 7000
spring:
application:
name: springcloud-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:5000/eureka/
feign:
hystrix:
enabled: true //开启熔断功能
主启动类
@SpringBootApplication
@EnableFeignClients
@EnableHystrix
@EnableCircuitBreaker
public class app {
public static void main(String[] args) {
SpringApplication.run(app.class,args);
}
}
Hystrix降级和熔断功能
以provider为例,加上如下配置
feign:
hystrix:
enabled: true
@HystrixCommand(fallbackMethod = "getEmpBackup")
@RequestMapping("/provider/circuit/breaker/get/emp")
public ResultEntity<Employee> getEmp(@RequestParam("signal") String signal) {
if ("bang".equals(signal)) {
throw new RuntimeException();
}
return ResultEntity.successWithData(new Employee(666, "sam666", 666.66));
}
public ResultEntity<Employee> getEmpBackup(@RequestParam("signal") String signal) {
return ResultEntity.failed("circuit break workded,with signal=" + signal);
}
测试,bang,生效
测试123
降级功能
- 改变common工程,加入fallbackFactory 属性
@Component
@FeignClient(value = "springcloud-provider",fallbackFactory = MyFallBackFactory.class)
public interface EmployeeRemoteService {}
- 同时加入MyFallBackFactory
@Component
public class MyFallBackFactory implements FallbackFactory<EmployeeRemoteService> {
// cause 对象是失败原因对应的异常对象
@Override
public EmployeeRemoteService create(final Throwable cause) {
return new EmployeeRemoteService() {
@RequestMapping("/provider/save/emp")
public Employee saveEmp(@RequestBody Employee employee) {
return null;
}
@RequestMapping("/provider/get/employee/by/id")
public Employee getEmployeeById(@RequestParam("empId") Integer empId) {
return null;
}
@RequestMapping("/provider/get/employee/remote")
public Employee getEmployeeRemote() {
return new Employee(444, "call provider failed,fall back here, reason is " + cause.getClass().getName() + " " + cause.getMessage(), 444.444);
}
};
}
}
测试正常访问http://localhost:4000/consumer/get/employee
- 关闭provider服务
- 成功开启降级措施
Zuu网关
pom环境
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
server:
port: 80
spring:
application:
name: springcloud-zuul
main:
allow-bean-definition-overriding: true
eureka:
client:
service-url:
defaultZone: http://localhost:5000/eureka/
zuul:
host:
connect-timeout-millis: 3000
socket-timeout-millis: 3000
ignored-services: "*" # 表示忽视直接通过application-name访问微服务,必须通过route
sensitive-headers: "*" # 在Zuul向其他微服务重定向时,保持原本的头信息(请求头、响应头)
routes: # 指定网关路由
crowd-protal:
service-id: springcloud-auth # 对应application-name
path: /** # 表示直接通过根路径访问,必须加上**,否则多层路径无法访问
crowd-project:
service-id: springcloud-provider
path: /provider/**
crowd-order:
service-id: springcloud-consumer
path: /consumer/**
主启动类
@SpringBootApplication
@EnableZuulProxy
public class zull {
public static void main(String[] args) {
SpringApplication.run(zull.class,args);
}
}
样例:创建springcloud-auth实例
controller
@Controller
public class PortalHandler {
@RequestMapping("/")
public String showPortalPage() {
// 这里实际开发中需要加载数据……
return "index";
}
}
配置
server:
port: 2000
spring:
application:
name: springcloud-con
eureka:
client:
service-url:
defaultZone: http://localhost:5000/eureka
以网关的形式去访问http://localhost/