不了解此套教程的可以移步之前章节
1.微服务解决方案 – Spring Cloud Alibaba (一)服务的注册与发现
2.微服务解决方案 – Spring Cloud Alibaba (二)服务提供者
3.微服务解决方案 – Spring Cloud Alibaba (三)服务消费者(Feign)
4.微服务解决方案 – Spring Cloud Alibaba (四)服务熔断
5.微服务解决方案 – Spring Cloud Alibaba (五)分布式配置中心
6.微服务解决方案 – Spring Cloud Alibaba (六)Dubbo远程过程调用
7.微服务解决方案 – Spring Cloud Alibaba (七)Dubbo 服务提供者
8.微服务解决方案 – Spring Cloud Alibaba (八)Dubbo 服务消费者
服务网关
当服务开始变多,我们就要开始让服务聚合起来。前端访问我们的网关,网关路由到我们的服务提供者,这个服务提供者恰好是我们Dubbo的服务消费者,消费者消费Dubbo服务提供者,而只有我们的Dubbo才与数据库打交道。
知道大概原理,就要开始技术选型,其实没得选,只有zuul和Gateway。zuul已经不更新了,那只有Gateway
Spring Cloud Gateway
Spring Cloud Gateway
不使用 Web
作为服务器,而是使用 WebFlux
作为服务器 ,Gateway
项目已经依赖了starter-webflux
,并不能依赖 starter-web
注意: 由于过滤器等功能依然需要 Servlet
支持,故可能还需要依赖 javax.servlet:javax.servlet-api
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Spring Cloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Commons -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator;
import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.support.DefaultServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/**
* ProjectName: MyShopPlus
* Package: com.laoshiren.myshop.plus.gateway
* ClassName: GatewayApplication
* Author: laoshiren
* Description:
* Date: 2020/1/13 16:30
* Version: 1.0
*/
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
private static final String ALL = "*";
private static final String MAX_AGE = "3600L";
@Bean
public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
}
@Bean
public ServerCodecConfigurer serverCodecConfigurer() {
return new DefaultServerCodecConfigurer();
}
/**
* 解决跨域
*/
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
if (!CorsUtils.isCorsRequest(request)) {
return chain.filter(ctx);
}
HttpHeaders requestHeaders = request.getHeaders();
ServerHttpResponse response = ctx.getResponse();
HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
HttpHeaders headers = response.getHeaders();
// 主要增加ACCESS_CONTROL_ALLOW_ORIGIN
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
if (requestMethod != null) {
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
}
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ALL);
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
return chain.filter(ctx);
};
}
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
如果在之前的Controller 加了@CrossOrigin(value = "*")
的话,记得将其注释掉,否则会报 The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
主要的配置文件
spring:
cloud:
# 路由网关配置
gateway:
discovery:
# 采用服务名的路由策略
locator:
enabled: true
# 配置路由规则
routes:
- id: nacos-provider
# 采用 LoadBalanceClient 方式请求
uri: lb://nacos-provider
predicates:
- Path=/web/echoprovider/**
filters:
# 此处配置去掉 1 个路径前缀,将nacos-provider这里去处,这样路由网关的路径就相对来说比较统一
# 请求路径变成 http://ip:port/web/echoprovider/ 开始后面追加改provider的接口路径
- StripPrefix=1
这样整套微服务的基础框架就算搭建完成了。
总结
回头看一下,我们服务注册与发现用的是Nacos
,文件配置中心使用的也是Nacos
。
与数据库打交道我们用Dubbo
的服务提供者,相当于整个项目作为数据库的dao
层,使用高速序列化用Kryo
。
业务层我们使用Dubbo
的服务消费者,配合Spring Cloud Nacos
将该业务层作为服务提供者,对外提供RESTFul
风格的接口。
将服务聚合我们使用Gateway
。
其中还有一些需要补足的地方,比如数据缓存(Redis
),单点登录(OAuth2
) ,数据查询(Solr,ES
),分布式事务(Seata
)和主键冲突(Leaf
)等问题。
总之,基础框架搭建好后,整合其他的技术只要满足消费者/提供者模式即可。