人之所以痛苦,那是因为你在成长。--------magic_guo
在微服务项目中,各个服务都部署在不同的服务器上,业务线上服务之间相互调用;话说回来,现在流行的几个服务调用工具openfeign、RPC、dubbo;
RPC—Remote Produce Call(远程过程调用),主要是通过自定义通信规则,自定义数据格式来实现通信。其实这要求通信两端都需要统一通信规则,必须约定好数据传输格式;
在我看来最流行的是:zookeeper+dubbo和eureka+openfeign两种注册中心和服务调用;
dubbo:暴露接口;
openfeign:暴露url path;是一个声明式的restful客户端,底层是httpClient,在这个基础上进行了封装使得结构更加清晰,调用方式更加简单;并且feign中集成了Hystrix(熔断降级)和Ribbon(负载均衡),保证了系统的高可用;
熔断降级:
发生情况:系统负载过高,突发流量 或者网络异常等
熔断:
微服务相互调用时,某个服务突然挂掉,会标记该服务为不可用并暂时停止调用,之后会尝试调用,如果正常运行则调用,否则则继续停用;
降级:
主动抛弃一些非必要的数据;
相同点:
1、从可用性和可靠性出发,防止系统崩溃
2、最终让用户体验到的事某些功能暂时不能用
不同点:
熔断一般是由下游服务故障导致的,而服务降级一般是从整体系统负荷考虑,由调用方控制
我使用的是feign的服务调用:
服务调用方:shop-back
服务提供方:shop-user、shop-user1;(但是这两个服务名字都是shop-user)
中间层:shop-feign
此模块可以是一个maven工程,也可使一个spring服务工程;两种搭建方式我都是用了;感觉还是maven模块搭建比较好,是用模块搭建,部署的时候使用jar包的形式引入,不用担心调用服务出现问题;(推荐将feign搭建成一个maven模块)
本文是将feign搭建成一个web服务,和maven模块搭建一样,不同的是多了一个web依赖:
shop-feign服务搭建:
依赖解释:config客户端、eureka客户端、shop-entity公共模块、web模块、feign的依赖、熔断机制的依赖;
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.guo</groupId>
<artifactId>shop-pom</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.guo</groupId>
<artifactId>shop-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shop-feign</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.guo</groupId>
<artifactId>shop-entity</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
目录结构:
IUserService是feign调用服务的接口:
将shop-user的所有controller层的接口拿过来,将后面的中括号去掉;可以在接口上添加@RequestMapping也可以在方法上写@RequestMapping;
// value是调用的服务的模块名称;fallback是如果调用失败,则回调本地的服务;
@FeignClient(value = "shop-user", fallback = UserServiceImpl.class)
@Component
public interface IUserService {
@RequestMapping("/user/addUser")
public ResultEntity addUser(@RequestBody User user);
}
UserServiceImpl:
// 如果远程调用失败,则熔断调用本地的方法;
@Component
public class UserServiceImpl implements IUserService {
private ResultEntity getMessage() {
return ResultEntity.error("系统正在维护, 请联系管理员.......");
}
@Override
public ResultEntity addUser(User user) {
return getMessage();
}
}
启动类:
@SpringBootApplication(scanBasePackages = "com.guo", exclude = DataSourceAutoConfiguration.class)
@EnableEurekaClient
public class ShopFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ShopFeignApplication.class, args);
}
}
配置:
在config配置中心添加feign的配置:
feign:
hystrix:
enabled: true
hystrix:
shareSecurityContext: true
command:
default:
circuitBreaker:
requestVolumeThreshold: 1
sleepWindowInMilliseconds: 15000
forceOpen: false
forceClosed: false
execution:
isolation:
thread:
timeoutInMilliseconds: 5000
ribbon:
ConnectTimeout: 4000
ReadTimeout: 4000
bootstrap.yml配置:
server:
port: 8004
spring:
application:
name: shop-feign
cloud:
config:
profile: shop-feign, log, eureka-client
name: application
uri: http://localhost:9999
shop-back构建:
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.guo</groupId>
<artifactId>shop-pom</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.guo</groupId>
<artifactId>shop-back</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shop-back</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.guo</groupId>
<artifactId>shop-feign</artifactId>
</dependency>
<dependency>
<groupId>com.guo</groupId>
<artifactId>shop-entity</artifactId>
</dependency>
</dependencies>
</project>
目录结构:
UserController:
shop-back的接口,注入feign的IUserService,然后feign再根据接口,来调用真实的shop-user应用的服务:
@RestController
@Slf4j
@RequestMapping("/userController")
public class UserController {
// @Autowired
@Resource
IUserService userService;
@RequestMapping("/addUser")
public ResultEntity addUser(User user) {
return ResultEntity.success(userService.addUser(user));
}
}
启动类:
以为此模块不连接数据库,同时又有了数据源的依赖和配置,因此将此配置去除,否则服务启动不起来;
@EnableFeignClients注解:需要声明此注解是服务消费方(即调用方),,并且标识调用的feign接口位置;
@SpringBootApplication(scanBasePackages = "com.guo", exclude = DataSourceAutoConfiguration.class)
@EnableEurekaClient
@EnableFeignClients("com.guo.api")
public class ShopBackApplication {
public static void main(String[] args) {
SpringApplication.run(ShopBackApplication.class, args);
}
}
bootstrap.yml:
spring:
cloud:
config:
name: application
profile: shop-back, log, eureka-client, shop-feign
uri: http://localhost:9999
shop-user、shop-user1服务提供方构建:
在真实的线上环境,这两个服务application name是一样的,可能部署在不同的服务器上,可以利用负载均衡,来降低服务器压力;平时访问两个服务,如果一个有问题,不至于此服务停止;目的还是保持应用的高可用;
这两个模块是一样的,只不过端口不一样,在eureka注册中心,会显示两个shop-user的服务提供者,此模块在上一篇已经搭建完成:shop-user搭建
不同点:
shop-user的配置:
server:
port: 8001
spring:
application:
name: shop-user
shop-user1的配置:
server:
port: 8002
spring:
application:
name: shop-user
搭建完成后,启动相关模块和这几个模块;纠错原则:
端口是否正确、启动过程中有没有错误、eureka注册中心是否都已注册上、调试接口是否返回数据正确;
eureka注册中心:
调试接口:
在两个服务提供方都正常启动的情况下:
负载均衡:
Ribbon的的负载均衡策略有很多,可以在配置中心进行修改;我使用的是默认的轮训策略;
调用8001端口的服务:
调用8002端口的服务:
只有一个服务提供方:
关闭其中一个服务,则会一直调用启动的服务;
关闭掉全部的服务提供方:
则会调用配置的回调ServiceImpl:
本文章教学视频来自:https://www.bilibili.com/video/BV1tb4y1Q74E?p=3&t=125
静下心,慢慢来,会很快!