Java微服务——SpringCloud实战篇2:整合Gateway、Config、Bus
如果小伙伴在阅读下列内容时,对于编写SpringCloud项目是零基础,那么请先阅读小编的另一篇博文:“Java微服务——SpringCloud实战篇1:整合Eureka、Ribbon、Hystrix、Feign”,因为下面的实战编写是接着上述博文的项目进行的,没有基础来阅读下面的内容会比较吃力。
1、创建网关服务模块
在父工程中新建一个Module,该Module也是一个Maven项目,该模块是项目中服务的统一入口,所有服务的请求都可以经过网关服务模块进行动态路由和请求过滤的操作。
创建网关服务模块的步骤:
- 将光标停在父项目上,就是上图的SpringCloud的地方,然后右键鼠标,选择new后在再选择Module,开始创建新的Module,该Module也还是JavaSE类型的Maven项目。在创建时需要注意下图所示:
- 创建好以后就会在父工程的项目结构中出现你创建的新Module,如下图所示:
3. 展开gatewayServer模块,在该模块的Pom文件中添加如下配置:
<!--开启网关服务依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--开启eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 构建相应的项目结构,结构如图所示:
其中启动类的代码如下:
/**
* @Author gyx
* @SpringBootApplication 标志为springboot项目的启动类的注解
* @EnableEurekaClient 开启eureka客户端的注解
*/
@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
其中配置文件的编写如下:
server:
port: 10010 # 配置服务端口
spring:
application:
name: gateway-server # 配置服务别名
cloud:
gateway:
routes:
# 路由Id,自定义
- id: user-service-route
# 代理单个服务地址:uri:服务地址,比如: http://127.0.0.1:8888,代理集群服务地址:uri: bl//服务别名,比如:lb://user-service
uri: lb://user-service
# 路由断言,可以配置映射的路径
predicates:
- Path=/user/**
# 注册到eureka中心
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9999/eureka
instance:
prefer-ip-address: true
- 配置好以上的内容后便可以运行启动类启动该模块了,启动后需要观察两个效果,一个是gatewayServer服务是注册到了Eureka中心上,另一个是发出访问请求是否能得到想要的数据。完成后的效果图如下:
在请求地址为http://localhost:10010/user/2,网关就会将地址路由到:http://localhost:8888/user/2,该地址是我的项目的服务提供者 userService模块中的请求服务的地址,所以说,配置文件中的路由断言的值需要根据自己服务提供者所配置的地址来定。我的服务提供者的请求访问效果图如下:
在gateway的配置文件中,还可以给路由地址配置添加或去除前缀,改变网关的请求地址。比如说给网关路由地址添加前缀的话,就需要在配置文件中给路由配置添加前缀的过滤器,添加的配置如下:
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service
# 路由断言需要更改
predicates:
- Path=/**
filters:
# 添加地址前缀
- PrefixPath=/user
配置好后,需要重启gatewayServer服务,然后输入自定义的请求,得到如下效果:
可以看到两个请求地址是由差别的,没有配置前缀的地址是:http://localhost:10010/user/2,而配置了前缀的请求地址就不需要在输入:/user 了,因为gateway会自动加上 /user。
给网关路由地址去除前缀,需要在配置文件中给路由配置去除前缀的过滤器,添加的配置如下:
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service
# 路由断言需要更改
predicates:
- Path=/api/user/**
filters:
# 1 表示去除第1个路径,2 表示去除前两个路径,以此类推
- StripPrefix=1
配置好后,需要重启gatewayServer服务,然后输入自定义的请求,得到如下效果:
这个请求地址和前面没有配置任何过滤器的地址和配置了添加前缀过滤器的地址都有所区别,在配置了去除前缀过滤器的请求地址中多了 /api,在请求路由时,gateway会自动将 /api 去除。
上面提到了过滤器,网关服务就是凭借过滤器对请求进行过滤,而在SpringCloud gateway中封装了一些默认的过滤器,下面列举几个常用的过滤器:
过滤器 | 说明 |
---|---|
AddRequestHeader | 对匹配上的请求加上Header |
AddRequestParameters | 对匹配上的请求路由加上参数 |
AddResponseHeader | 对从网关返回的响应添加Header |
StripPrefix | 对匹配上的请求路径去除前缀 |
PrefixPath | 对匹配上的请求路径添加前缀 |
而过滤器的配置有两种方式,一种是上面添加或去除前缀器的配置方式,另一种是配置默认过滤器,配置如下:
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
# 1 表示去除第1个路径,2 表示去除前两个路径,以此类推
- StripPrefix=1
# 配置添加响应头信息的过滤器
- AddResponseHeader=name2,kkb
# 默认过滤器,对所有路由都生效
default-filters:
# 配置添加响应头信息的过滤器
- AddResponseHeader=name1,gyx
配置好之后重启gatewayServer服务,然后发起请求,并查看请求之后返回的响应头信息,效果如下:
可以看到请求的响应头信息中多了我框住的内容,两种配置过滤器的方法都有作用,配置中的第一个参数就是响应头信息的键,第二个参数就是响应头信息的值。
在网关服务中不仅可以使用它自己封装的过滤器,我们也可以自定义过滤器,自定义过滤器也有两种方法:一种是自定义局部过滤器,另一个是自定义全局过滤器。
先介绍如何自定义局部过滤器,首先先创建一个过滤器类,类的编写如下:
/**
* @Author gyx
* @Component 将该过滤器类的实例化交给在Spring容器的注解
* 自定义局部过滤器时注意:过滤器类的名字必须是XXXGatewayFilterFactory
* 其中XXX是自定义的,而之后的GatewayFilterFactory是默认的,不可更改
* 该过滤器类的作用是:将请求地址栏中拼接了指定键的键值对在控制台输出
*/
@Component
public class MyFilterGatewayFilterFactory extends AbstractGatewayFilterFactory<MyFilterGatewayFilterFactory.Config> {
//该属性的值是默认的,不可更改
private static final String PARAM_NAME = "param";
/**
* 构造方法
*/
public MyFilterGatewayFilterFactory() {
super(Config.class);
}
/**
* 重写父类的方法
*
*/
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList(PARAM_NAME);
}
/**
* 重写父类的方法
* 该方法进行相应的操作
*/
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// http://localhost:10010/api/user/8?name=lxs config.param ==> name
//获取请求参数中param对应的参数名 的参数值
ServerHttpRequest request = exchange.getRequest();
if(request.getQueryParams().containsKey(config.param)){
request.getQueryParams().get(config.param).
forEach(value -> System.out.printf("------------局部过滤器--------%s = %s- -----", config.param, value));
}
return chain.filter(exchange);
};
}
/**
* 配置参数类
*/
public static class Config{
//对应在配置过滤器的时候指定的参数名
private String param;
public String getParam() {
return param;
}
public void setParam(String param) {
this.param = param;
}
}
}
写好过滤器类之后需要更改配置文件中的配置,更改如下:
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
# 1 表示去除第1个路径,2 表示去除前两个路径,以此类推
- StripPrefix=1
# 配置添加响应头信息的过滤器
- AddResponseHeader=name2,kkb
# 配置自定义局部过滤器,等号前面的值是你的类名去掉GatewayFilterFactory之后所得,
# 等号后面的值是规定请求地址中拼接的键值对中的键
- MyFilter=name
# 默认过滤器,对所有路由都生效
default-filters:
# 配置添加响应头信息的过滤器
- AddResponseHeader=name1,gyx
配置好之后重启gatewayServer服务,然后发起请求,并查看请求之后IDEA中的控制台的输出,效果如下:
可以看到控制台中输出了我在请求地址中拼接的键值对。注意:拼接的键值对的键必须和你在配置文件中配置的一致,不然控制台是不会打印出效果的。
下面介绍如何自定义全局过滤器,需要创建一个全局过滤器类,类的编写如下:
/**
* @Author: Gyx
* @Date: 2021/4/20 21:25
*
* 自定义全局过滤器
* 该过滤器类的作用是:过滤请求地址中请求头信息是否有token信息
* 有token信息的请求放行,没有token信息的请求会输出状态码:401
*/
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
/**
* 重写父接口的方法
* 该方法是过滤器进行逻辑处理的方法
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("--------------全局过滤器MyGlobalFilter------------------");
String token = exchange.getRequest().getHeaders().getFirst("token");
if(StringUtils.isBlank(token)){
//设置响应状态码为未授权:401
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
/**
* 定义过滤器的优先级,值越小,越先执行
* @return
*/
@Override
public int getOrder() {
return 1;
}
}
创建好类之后,无需在配置文件中添加配置,直接重启gatewayServer服务,然后访问请求地址,你会得到一个401的响应码,而在IDEA的控制台也会输出效果,效果如下:
出现401表示全局过滤器配置成功,那该如何获得数据呢,就需要在请求头中加入token的信息,需要借助Postman应用,发起在请求头添加了token信息的请求,就可以获得信息,效果如下:
从两个控制台输出的对比中可以看出全局过滤器的优先级比局部过滤器的优先级高,在第一次的控制台输出中没有显示局部过滤器执行成功的提示,原因是因为全局过滤器执行成功后,将请求头信息中没有token信息的请求过滤了,之后便无法再调用局部过滤器,而有token信息的请求便会被放行,然后再调用局部过滤器进行执行,然后输出效果。
2、创建配置中心服务模块
在父工程中新建一个Module,该Module也是一个Maven项目,该模块的作用主要是:实现项目中每个服务可以从远端仓库(GitHub或Gitee)中拉取配置文件的功能,因此做到统一管理服务的配置文件。
创建配置中心服务模块的步骤:
-
和上面配置网关服务模块的第一步类似,需要新建一个Module,具体就不多做展示,可以参照上面网关服务模块来操作。
-
创建好以后就会在父工程的项目结构中出现你创建的新Module,如下图所示:
- 展开configServer模块,在该模块的Pom文件中添加如下配置:
<!--eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--配置中心服务依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
- 构建相应的项目结构,结构如图所示:
其中启动类的编写如下:
/**
* @Author gyx
* @EnableConfigServer 标识为配置中心服务的注解
*/
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
其中配置文件的编写如下:
server:
port: 10020 # 配置服务端口
spring:
application:
name: config-server # 服务别名
cloud:
config:
server:
git:
uri: https://gitee.com/XXX # 配置自己远程仓库网址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9999/eureka
instance:
prefer-ip-address: true
在上面的配置文件中需要配置远程仓库的地址,那么就需要创建你自己的远程仓库,以下是在Gitee上创建远程仓库的操作步骤,小伙伴可以跟着来创建属于自己的远程仓库,也可以自己去网上找关于GitHub创建远程仓库的步骤操作,两者都可以作为存放配置文件的远程仓库,随你们喜欢。
在Gitee上创建远程仓库的步骤如下:
-
访问Gitee官网创建自己的Gitee账户,创建账户很简单,我就不多做演示了。
-
创建好自己的Gitee账户之后,登录便可以进入Gitee的控制台界面,界面如下:
- 创建远程仓库,步骤如下:
出现上面的界面图便是说明仓库建立成功,之后便可以获取自己远程仓库的地址,获取的方法如下:
将刚刚复制的远程仓库地址粘贴到configServer服务的配置文件中,然后启动configServer服务,能成功启动说明配置中心已经配置完成,效果如下:
3、更改服务提供者模块的结构
要实现从远程仓库拉取配置文件的功能,就需要在远程仓库中创建好配置文件,创建步骤如下:
配置好以后便可以点提交,之后便会在你的仓库中创建出新文件,效果如下:
配置好远程仓库后,需要更改服务提供者的配置文件,将原来的配置文件删除,新增一个配置文件,配置文件的编写如下:
spring:
cloud:
config:
# 要与仓库中的配置文件的application保持一致,也就是X-X中“-”前面的
name: user
# 要与仓库中的配置文件的profile保持一致,也就是X-X中“-”后面的
profile: dev
# 要与仓库中的配置文件所属的版本(分支)一样
label: master
discovery:
# 使用配置中心
enabled: true
# 配置中心的别名
service-id: config-server
配置好之后便可以启动服务提供者服务,查看效果,注意:一定要先把配置中心服务启动好,再启动服务提供者服务。
出现上面的访问效果,便表示项目中配置中心的配置已经完全配置好。
如果出现以下效果,就是配置中心没有启动或者是在服务提供者服务启动之后才启动。
4、添加总线服务组件
在上面介绍了配置中心服务模块的实现,那么在项目中使用配置中心服务来管理配置文件会有一个问题:在服务启动后,更改远程仓库中的配置文件,无法在服务不重启的情况下获得更改后的配置信息,下面就来确认一下是否是这样的状况。
- 先将远程仓库中的配置文件做些更改,方便在代码中验证效果,更改如下:
- 修改一下服务提供者的逻辑代码,修改的类和内容如下:
/**
* @Author: Gyx
* @Date: 2021/4/19 15:05
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
/**
* 添加点,获取配置文件中test.name的值
*/
@Value("${test.name}")
private String name;
@GetMapping("/{id}")
public User findById(@PathVariable("id") Long id){
//添加点:在控制台输出结果
System.out.println("配置文件中test.name为:"+name);
return userService.findById(id);
}
}
配置上面的内容之后,便重启服务提供者服务,记住配置中心服务一定是先启动或正在运行的状态,启动好之后,访问请求地址,查看IDEA控制台中的输出效果。
然后不重启服务,更改远程仓库中的配置文件内容,看看会不会有改变,更改如下:
更改好之后再次访问请求地址,在控制台查看效果,效果如下:
可以看到控制台并没有输出我更改的信息:kkb,而是输出了之前的:gyx。
由此便可以知道,在服务不重启的情况下,是无法获取远程仓库中更改后的配置信息的,那怎样才可以在服务不重启的情况下,让服务可以第一时间就获取更改后的配置信息。
要想实现上述功能,就需要在配置中心服务和服务提供者服务中添加Bus中线组件,通过广播的形式,将更改了远程仓库配置操作的消息发送给服务,让服务可以自动更新配置。
具体的操作步骤如下:
- 是Spring Cloud Bus底层是基于RabbitMQ实现的,默认使用本地的消息队列服务,所以需要提前启动本地RabbitMQ服务(安装RabbitMQ以后才会有该服务)
https://gitee.com/gong-yanxing/my-config1.git
访问该地址可以下载RabbitMQ安装包,下载步骤:
下载好后便可以安装RabbitMQ,安装好后进入本地的服务列表启动以下该项服务:
开启了RabbitMQ服务后便可以进行下面的步骤:
- 修改configServer服务模块的Pom文件,添加如下依赖:
<!--SpringCloudBus依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
- 更改configServer服务模块的配置文件,添加以下配置:
server:
port: 10020
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/gong-yanxing/my-config.git
# 添加配置rabbitmq,与默认值一致可以无需配置
rabbitmq:
host: localhost # 默认配置
username: guest # 默认配置
password: guest # 默认配置
port: 5672 # 默认配置
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9999/eureka
instance:
prefer-ip-address: true
# 添加配置
management:
endpoints:
web:
exposure:
# 暴露触发信息总线的地址
include: bus-refresh
- 更改userService服务模块的Pom文件,添加以下的依赖:
<!--SpringCloudBus相关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 更改userService服务模块中UserController类,添加以下配置:
/**
* @Author: Gyx
* @Date: 2021/4/19 15:05
* @RefreshScope 开启刷新配置的注解
*/
@RestController
@RequestMapping("/user")
@RefreshScope //添加的注解
public class UserController {
@Resource
private UserService userService;
@Value("${test.name}")
private String name;
@GetMapping("/{id}")
public User findById(@PathVariable("id") Long id){
System.out.println("配置文件中test.name为:"+name);
return userService.findById(id);
}
}
上述步骤完成后,需要重启configServer服务模块和userService服务模块,然后访问请求地址,先获取更改配置前的控制台输出效果。
- 更改远程仓库的配置文件,之后使用Postman发起post请求,请求URL为:http://127.0.0.1:10020/actuator/bus-refresh,发完之后,再次访问请求地址,在控制台就会输出新的信息。
控制台输出了更新后的信息就表示总线组件配置完成了。
以上便是SpringCloud实战篇的所有内容了,希望可以帮到你们,感谢阅读,如果觉得小编的文章写得不错,可以关注小编,我将会不定期更新关于Java相关内容的博文。