一、前言
上一章芝法酱躺平攻略(12)展望了微服务下常见的技术需求与常见解决方案,本期来讲解第一部分,Nginx与SpringCloud-Gateway。
本章将实践在nginx和spring-cloud-gateway的简单使用。
二、nginx的安装与简单使用
2.1 nginx的安装
首先,去官网查找最新稳定版,把下载的nginx包放到DOWNLOAD文件夹下。
而后把该包解压到SOFTWARE文件夹下
mkdir ~/SOFTWARE/nginx
tar -xzvf nginx-1.22.1.tar.gz -C ~/SOFTWARE/nginx
而后安装编译所需的依赖
sudo apt-get install gcc
sudo apt-get install libpcre3 libpcre3-dev
sudo apt-get install zlib1g zlib1g-dev
sudo apt-get install openssl
sudo apt-get install libssl-dev
而后运行编译安装
sudo ./configure --prefix="/home/hataksumo/SOFTWARE/nginx/bin"
sudo make
sudo make install
安装后,目录结构是这样的:
conf 是存放配置文件的地方
logs是存放log的地方
sbin是存放nginx运行程序的地方
进入conf,打开nginx.conf,做合适的配置,而后进入sbin,运行nginx。
从浏览器打开localhost:配置监听的ip,就可以看到nginx的页面
2.2 nginx常用的命令
sudo ./nginx #运行nginx
sudo ./nginx -s reload #重新加载nginx的配置
sudo ./nginx -s stop #关闭nginx
更多的命令可以在官网上查看
2.3 nginx的反向代理配置
我们先看一个简单的配置,实现简单的负载均衡
upstream test{
server 172.25.0.1:8001 weight=3;
server 172.25.0.1:8002 weight=2;
server 172.25.0.1:8003 weight=1;
}
server {
listen 80;
server_name localhost;
location /test{
proxy_pass http://test/api;
}
}
}
其中upstream 表示一个负载均衡,可以配置不同服务的权重
server 段表示配置一个服务器,既可以配置http,也可以配置https。
java端口,按芝法酱躺平攻略(13)那样配置打包程序,并添加如下controller测试代码:
@Api(tags = "test-测试接口")
@RequestMapping("/api/test")
@Slf4j
@ZfRestController
@RequiredArgsConstructor
public class TestApi {
private final AppProperty mAppProperty;
@GetMapping("/hello")
public String sayHello(){
return mAppProperty.getMyName();
}
}
从浏览器访问http://localhost/test/test/hello,反复刷新,查看现象
2.4 nginx的静态资源配置
location /img {
root /home/hataksumo/res;
proxy_store on;
access_log /home/hataksumo/res/img/log;
proxy_temp_path /home/hataksumo/res/img/cache;
proxy_redirect off;
gzip on;
gzip_comp_level 9;
}
三、spring-cloud-gateway的简单使用
3.1 网关简介
网关的英文是router,直译过来就是路由。网关的主要职责就是根据请求,做一些处理后转发到其他服务器,当其他服务器信息处理好后,再把处理好的信息返回给客户端。
其他服务器处理信息是需要时间的,网关服务器不应当在这个时间中忙等,所以需要一个不同的架构。在Java中是基于netty的nio模式。
spring-cloud-gateway使用拦截器来处理从前端发给后端得信息,也可以处理从第三方服务返回的信息。
3.2 响应式编程
小编私以为,gateway这块实在没那么多好讲的,核心的知识点反倒是响应式编程。
SpringBoot 提供了Mono机制做响应式编程,当一个请求过来后,并不是开启一个新线程,而是由几个固定线程依次处理过来的请求。当该请求需要请求第三方服务时,将会被跳过处理其他请求。当第三方消息返回后,再加入处理队列依次处理。
在gateway中的controller写法和普通服务基本一致,只是需要返回一个Mono对象,做异步处理。
3.2.1 从helloworld开始
先上代码:
@RequestMapping("/api/test")
@ZfRestController
public class HelloController {
@GetMapping("hello")
Mono<RestResponse<String>> hello(){
RestResponse<String> r = RestResponse.ok("hello");
return Mono.just(r);
}
}
Mono.just是一种阻塞的模式,直接返回数据。
3.2.2 我们来异步一下
@GetMapping("/fibonacii/{n}")
Mono<RestResponse<BigInteger>> fibonacii(@PathVariable(name = "n") int n){
CompletableFuture<RestResponse<BigInteger>> future = CompletableFuture.supplyAsync(()->{
log.info("计算"+n+" 当前线程 "+Thread.currentThread().getId());
BigInteger res = getFibonacii(n);
log.info("计算"+n+" 当前线程 "+Thread.currentThread().getId()+"已完成===========================");
return res;}).thenApply(v->RestResponse.ok(v));
log.info("^_^返回"+n+" 当前线程 "+Thread.currentThread().getId());
return Mono.fromFuture(future);
}
为了方便测试,我们把netty的worker线程调到2
@SpringBootApplication(
scanBasePackages = {"indi.zhifa.recipe.bailan.framework","indi.zhifa.recipe.bailan5.gateway"}
)
public class GatewayApplication {
public static void main(String[] args){
System.setProperty("reactor.netty.ioWorkerCount","2");
ApplicationContext context = SpringApplication.run(GatewayApplication.class, args);
}
}
浏览器请求,http://localhost:9000/api/test/fibonacii/521003,不断刷新,查看控制台,可以观察到以下现象:
返回线程总是129和101。当然,我们的例子是开了个新线程跑斐波那契数列,如果是从第三方服务拉去数据,或者调用复杂的数据库查询,毫无疑问这就能省去大量的等待时间。
3.2.3 关于db的操作
在异步模式下,目前不是很推荐操作mysql数据库,mongodb倒很推荐。
可以引入下列库做响应式
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
3.3 网关简单负载均衡测试
其实这部分非常简单,没有太多好说的。
日常开发可以直接接入nacos,把微服务全开放了。其实体量有限的线上项目也可以这么干。
如果是个百人的大项目,可以开发一个动态路由,其实也不难写。
这里给出网关的简单配置:
server:
# 服务端口
port: 9000
spring:
application:
name: "bailan5-gateway"
servlet:
multipart:
enabled : true
max-file-size: "256MB"
max-request-size: "256MB"
cloud:
nacos:
server-addr: localhost:8848
config:
namespace: tmpTest
name: gateway
file-extension: yml
group: bailan5
discovery:
namespace: tmpTest
register-enabled: true
gateway:
discovery:
locator:
enabled: true
启动之前的test服务,通过访问localhost:9000/test/api/test/hello即可访问test服务的对应接口
3.3 网关的拦截器配置
官方默认提供了大量默认的拦截器,可以通过yml配置。
这里仅仅讲解自定义的拦截器。下面用接口执行时间为例做代码展示。
@Slf4j
@Component
public class CustomFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 前置处理,直接写就行
String info = String.format("Method:{%s} Host:{%s} Path:{%s} Query:{%s}",
exchange.getRequest().getMethod().name(),
exchange.getRequest().getURI().getHost(),
exchange.getRequest().getURI().getPath(),
exchange.getRequest().getQueryParams());
log.info(info);
exchange.getAttributes().put("startTime", System.currentTimeMillis());
// 后置处理,放到then
return chain.filter(exchange).then( Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute("startTime");
if (startTime != null) {
Long executeTime = (System.currentTimeMillis() - startTime);
log.info(exchange.getRequest().getURI().getRawPath() + " : " + executeTime + "ms");
}
}));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}