随着公司的业务发展,有幸经历了从单体应用迁移到分布式应用,又从分布式应用开始准备搭建微服务应用,以下是公司从零开始搭建微服务的过程,记录并分享出来,希望对大家有所帮助,我们先使用Spring Cloud GateWay作为网关,由于目前还没有服务发现组件,例如eurka,所以需要通过配置文件的方式配置Ribbon作负载均衡。所以以下重点讲解Spring Cloud GateWay和Ribbon的搭配使用。
微服务提出后,单体应用被拆分成多个服务,为了对外提供统一入口,解耦客户端与内部服务。
单体架构到微服务架构演变
网关能做统一的路由转发、熔断、限流、安全认证、日志监控等。
网关的作用
zuul与Spring Cloud Gateway对比
网关核心概念
1.路由(route) 路由是网关最基础的部分,路由信息由一个ID、一个目的URL、一组断言工厂和一组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。
2.断言(predicates) JAVA8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自Http Request中的任何信息,比如请求头和参数等。
3.过滤器(filter) 一个标准的Spring webFilter,Spring Cloud Gateway中的Filter分为两种类型,分别是Gateway Filter和Global Filter。过滤器Filter可以对请求和相应进行处理。
网关工作原理
Spring Cloud Gateway核心处理流程如上图所示,Gateway的客户端向Spring Cloud Gateway发送请求,请求首先被HttpWebHandlerAdapter进行提取组装成网关上下文,然后网关的上下文会传递到DispatcherHandler。DispatcherHandler是所有请求的分发处理器,DispatcherHandler主要负责分发请求对应的处理器。比如请求分发到对应的RoutePredicateHandlerMApping(路由断言处理映射器)。路由断言处理映射器主要作用用于路由查找,以及找到路由后返回对应的FilterWebHandler。FilterWebHandler主要负责组装Filter链并调用Filter执行一系列的Filter处理,然后再把请求转到后端对应的代理服务处理,处理完毕之后将Response返回到Gateway客户端。
Ribboon主要组件
检查实例是否存活。如何ping。实现类:
获取服务器列表。
在获取的服务器列表中进行获取。
更新服务器列表。
LoadBalancer的组成:
LoadBalancer的实现类:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
</dependencies>
@SpringBootApplication
@RestController
public class DemoGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(DemoGatewayApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r
.path("/refund/**")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://manage-test.payplatform.speiyou.cn/")
).build();
}
}
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: merchant
uri: lb://merchant-load-balanced-service
predicates:
- Path=/merchant/**
- Method=POST
- id: split
uri: lb://split-load-balanced-service
predicates:
- Path=/split/**
filters:
- RewritePath=/split, /ledger-split #重写url
- id: cashier
uri: lb://cashier-load-balanced-service
predicates:
- Path=/cashier/**
filters:
- StripPrefix=1 #将cashier过滤掉
#ribbon全局配置
ribbon:
ConnectTimeout: 1000 #服务请求连接超时时间(毫秒)
ReadTimeout: 3000 #服务请求处理超时时间(毫秒)
OkToRetryOnAllOperations: true #对超时请求启用重试机制
MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数
MaxAutoRetries: 1 # 切换实例后重试最大次数
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #负载均衡算法
NFLoadBalancerPingClassName: com.talpay.gateway.config.HealthCheck #健康检查
NFLoadBalancerPingInterval: 20 #设置健康检查间隔,单位秒,默认30秒
merchant-load-balanced-service:
ribbon:
listOfServers: 192.168.xxx.xxx:8080
split-load-balanced-service:
ribbon:
listOfServers: 192.168.xxx.xxx:8081
cashier-load-balanced-service:
ribbon:
listOfServers: 192.168.xxx.xxx:8080
@Slf4j
@Component
public class HealthCheck implements IPing{
@Autowired
private RestTemplate restTemplate;
@Value("${dingtalk.url}")
private String dingtalkURL; //钉钉报警url
@Override
public boolean isAlive(Server server) {
String url = "http://"+ server.getId()+ "/actuator/health";
try {
ResponseEntity<String> heath = restTemplate.getForEntity(url, String.class);
if (heath.getStatusCode() == HttpStatus.OK) {
log.info("ping " + url + " success ");
return true;
}
log.info("ping " + url + " error and response is " + heath.getBody());
return false;
} catch (Exception e) {
log.error("ping " + url + " failed");
DingRebotSendUtil.send(dingtalkURL,new TextMessage("网关|ping:" + url + " failed"));
return false;
}
}
}
@Component
public class LogGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest serverHttpRequest= exchange.getRequest();
String url = serverHttpRequest.getURI().toString();
System.out.println("url ------>: " + url);//打印每次请求的url
return chain.filter(exchange);
}
}
第一次触发
第二次触发
以上为真实测试数据,第一次触发链接到生产环境,第二次触发链接到仿真环境。