Soul API网关学习-Day02

目标

  • 运行examples下面的 http服务
  • 学习文档,结合divde插件,发起http请求soul网关,体验http代理
  • 记录心得

soul-examples运行

在soul-example下有soul-examples-http项目,这个是基于Spring Boot的Web应用。

我们通过IDEA直接运行SoulTestHttpApplication类的main方法,直接启动Spring Boot应用。

发起http请求

我们先不通过API网关,请求http://127.0.0.1:8188/order/findById?id=1,如下:

GET http://127.0.0.1:8188/order/findById?id=1

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 40

{
  "id": "1",
  "name": "hello world findById"
}

Response code: 200 (OK); Time: 175ms; Content length: 40 bytes

接着,我们在通过API网关,请求http://127.0.0.1:9195/http/order/findById?id=1

GET http://127.0.0.1:9195/http/order/findById?id=1

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 40

{
  "id": "1",
  "name": "hello world findById"
}

Response code: 200 (OK); Time: 401ms; Content length: 40 bytes

对API网关进行压测

首先是不通过AIP网关,直接请求应用服务

$ wrk http://127.0.0.1:8188/order/findById\?id\=1
Running 10s test @ http://127.0.0.1:8188/order/findById?id=1
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.17ms    5.49ms  70.70ms   96.86%
    Req/Sec    16.04k     4.22k   20.93k    77.00%
  319363 requests in 10.01s, 33.81MB read
Requests/sec:  31919.99
Transfer/sec:      3.38MB

通过API网关请求应用服务

$ wrk http://127.0.0.1:9195/http/order/findById\?id\=1
Running 10s test @ http://127.0.0.1:9195/http/order/findById?id=1
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     9.23ms   34.79ms 363.93ms   97.24%
    Req/Sec     1.57k     1.07k    3.44k    65.82%
  30740 requests in 10.05s, 3.25MB read
Requests/sec:   3059.90
Transfer/sec:    331.69KB

把日志输出级别调整为ERROR

$ wrk http://127.0.0.1:9195/http/order/findById\?id\=1
Running 10s test @ http://127.0.0.1:9195/http/order/findById?id=1
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.19ms    6.40ms  94.56ms   97.15%
    Req/Sec     3.83k   659.86     4.59k    91.50%
  76155 requests in 10.01s, 8.06MB read
Requests/sec:   7611.12
Transfer/sec:    825.05KB

性能差距比较大,估计是两个应用都在本机运行,估计有CPU、内存等资源的竞争吧。

divde插件配置LoadBalance

同样运行soul-examples-http的Spring Booot两个应用,端口分别为:8188和8189

server:
  port: 8188
soul:
  http:
    port: 8188

在IDEA下配置如下:

在Soul Admin下查看:

我们可以修改Weight的值,观察API网关日志输出的情况

Weight都是50的情况:

2021-01-15 17:40:40.979  INFO 23062 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin  : divide selector success match , selector name :/http
2021-01-15 17:40:40.988  INFO 23062 --- [-work-threads-1] o.d.s.plugin.httpclient.WebClientPlugin  : The request urlPath is http://127.0.0.1:8188/order/findById?id=1, retryTimes is 0
2021-01-15 17:40:41.101  WARN 23062 --- [-work-threads-1] io.netty.bootstrap.Bootstrap             : Unknown channel option 'SO_TIMEOUT' for channel '[id: 0xff1a6c7c]'
2021-01-15 17:40:42.882  INFO 23062 --- [-work-threads-3] o.d.soul.plugin.base.AbstractSoulPlugin  : divide selector success match , selector name :/http
2021-01-15 17:40:42.882  INFO 23062 --- [-work-threads-3] o.d.s.plugin.httpclient.WebClientPlugin  : The request urlPath is http://127.0.0.1:8189/order/findById?id=1, retryTimes is 0
2021-01-15 17:40:42.883  WARN 23062 --- [-work-threads-3] io.netty.bootstrap.Bootstrap             : Unknown channel option 'SO_TIMEOUT' for channel '[id: 0x4ac43817]'
2021-01-15 17:40:44.746  INFO 23062 --- [-work-threads-5] o.d.soul.plugin.base.AbstractSoulPlugin  : divide selector success match , selector name :/http
2021-01-15 17:40:44.747  INFO 23062 --- [-work-threads-5] o.d.s.plugin.httpclient.WebClientPlugin  : The request urlPath is http://127.0.0.1:8189/order/findById?id=1, retryTimes is 0
2021-01-15 17:41:06.089  INFO 23062 --- [-work-threads-7] o.d.soul.plugin.base.AbstractSoulPlugin  : divide selector success match , selector name :/http
2021-01-15 17:41:06.089  INFO 23062 --- [-work-threads-7] o.d.s.plugin.httpclient.WebClientPlugin  : The request urlPath is http://127.0.0.1:8188/order/findById?id=1, retryTimes is 0

8188端口的weight的值是99,8189的weight的值是1的情况:

2021-01-15 17:42:46.959  INFO 23062 --- [-work-threads-9] o.d.soul.plugin.base.AbstractSoulPlugin  : divide selector success match , selector name :/http
2021-01-15 17:42:46.959  INFO 23062 --- [-work-threads-9] o.d.s.plugin.httpclient.WebClientPlugin  : The request urlPath is http://127.0.0.1:8188/order/findById?id=1, retryTimes is 0
2021-01-15 17:42:49.434  INFO 23062 --- [work-threads-11] o.d.soul.plugin.base.AbstractSoulPlugin  : divide selector success match , selector name :/http
2021-01-15 17:42:49.435  INFO 23062 --- [work-threads-11] o.d.s.plugin.httpclient.WebClientPlugin  : The request urlPath is http://127.0.0.1:8188/order/findById?id=1, retryTimes is 0
2021-01-15 17:42:51.096  INFO 23062 --- [work-threads-13] o.d.soul.plugin.base.AbstractSoulPlugin  : divide selector success match , selector name :/http
2021-01-15 17:42:51.096  INFO 23062 --- [work-threads-13] o.d.s.plugin.httpclient.WebClientPlugin  : The request urlPath is http://127.0.0.1:8188/order/findById?id=1, retryTimes is 0
2021-01-15 17:42:52.482  INFO 23062 --- [work-threads-15] o.d.soul.plugin.base.AbstractSoulPlugin  : divide selector success match , selector name :/http
2021-01-15 17:42:52.483  INFO 23062 --- [work-threads-15] o.d.s.plugin.httpclient.WebClientPlugin  : The request urlPath is http://127.0.0.1:8188/order/findById?id=1, retryTimes is 0
2021-01-15 17:42:54.309  INFO 23062 --- [work-threads-17] o.d.soul.plugin.base.AbstractSoulPlugin  : divide selector success match , selector name :/http
2021-01-15 17:42:54.309  INFO 23062 --- [work-threads-17] o.d.s.plugin.httpclient.WebClientPlugin  : The request urlPath is http://127.0.0.1:8188/order/findById?id=1, retryTimes is 0

部分源码解读

Connected to the target VM, address: '127.0.0.1:59655', transport: 'socket'

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.2.RELEASE)

2021-01-15 14:55:39.607  INFO 21683 --- [           main] o.d.s.e.http.SoulTestHttpApplication     : Starting SoulTestHttpApplication on BindeMacBook-Pro.local with PID 21683 (/Users/howard/github/howard/soul/soul-examples/soul-examples-http/target/classes started by howard in /Users/howard/github/howard/soul/soul-examples)
2021-01-15 14:55:39.621  INFO 21683 --- [           main] o.d.s.e.http.SoulTestHttpApplication     : No active profile set, falling back to default profiles: default
2021-01-15 14:55:40.123  INFO 21683 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.dromara.soul.springboot.starter.client.springmvc.SoulSpringMvcClientConfiguration' of type [org.dromara.soul.springboot.starter.client.springmvc.SoulSpringMvcClientConfiguration$$EnhancerBySpringCGLIB$$f02f8e4b] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-01-15 14:55:40.144  INFO 21683 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'soulHttpConfig' of type [org.dromara.soul.client.springmvc.config.SoulSpringMvcConfig] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-01-15 14:55:40.701  INFO 21683 --- [pool-1-thread-1] o.d.s.client.common.utils.RegisterUtils  : http client register success: {"appName":"http","context":"/http","path":"/http/test/**","pathDesc":"","rpcType":"http","host":"127.0.0.1","port":8188,"ruleName":"/http/test/**","enabled":true,"registerMetaData":false} 
2021-01-15 14:55:40.721  INFO 21683 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8188
2021-01-15 14:55:40.725  INFO 21683 --- [           main] o.d.s.e.http.SoulTestHttpApplication     : Started SoulTestHttpApplication in 1.506 seconds (JVM running for 2.109)
2021-01-15 14:55:40.740  INFO 21683 --- [pool-1-thread-1] o.d.s.client.common.utils.RegisterUtils  : http client register success: {"appName":"http","context":"/http","path":"/http/order/save","pathDesc":"Save order","rpcType":"http","host":"127.0.0.1","port":8188,"ruleName":"/http/order/save","enabled":true,"registerMetaData":false} 
2021-01-15 14:55:40.768  INFO 21683 --- [pool-1-thread-1] o.d.s.client.common.utils.RegisterUtils  : http client register success: {"appName":"http","context":"/http","path":"/http/order/findById","pathDesc":"Find by id","rpcType":"http","host":"127.0.0.1","port":8188,"ruleName":"/http/order/findById","enabled":true,"registerMetaData":false} 
2021-01-15 14:55:40.796  INFO 21683 --- [pool-1-thread-1] o.d.s.client.common.utils.RegisterUtils  : http client register success: {"appName":"http","context":"/http","path":"/http/order/path/**","pathDesc":"","rpcType":"http","host":"127.0.0.1","port":8188,"ruleName":"/http/order/path/**","enabled":true,"registerMetaData":false} 
2021-01-15 14:55:40.829  INFO 21683 --- [pool-1-thread-1] o.d.s.client.common.utils.RegisterUtils  : http client register success: {"appName":"http","context":"/http","path":"/http/order/path/**/name","pathDesc":"","rpcType":"http","host":"127.0.0.1","port":8188,"ruleName":"/http/order/path/**/name","enabled":true,"registerMetaData":false} 

在应用启动的时候,我们通过输出的日志信息,看到了该应用提供的HTTP API的元数据信息注册到Soul Admin控制台。元数据的数据格式如下:

{
  "appName": "http",
  "context": "/http",
  "path": "/http/order/save",
  "pathDesc": "Save order",
  "rpcType": "http",
  "host": "127.0.0.1",
  "port": 8188,
  "ruleName": "/http/order/save",
  "enabled": true,
  "registerMetaData": false
}

JSON属性说明:

属性 描述
appName 应用名称
context context
path controller request mapping path
pathDesc description
rpcType RPC类型
host 该应用的IP address
port 该应用的端口号
ruleName rule name,存储在rule表
enabled 是否开启,默认开启
registerMetaData 是否有MetaData(通常dubbo rpc会用到)

我们看一下org.dromara.soul.examples.http.controller.OrderController这个类的代码,如下:

@PostMapping("/save")
@SoulSpringMvcClient(path = "/save" , desc = "Save order")
public OrderDTO save(@RequestBody final OrderDTO orderDTO) {
    orderDTO.setName("hello world save order");
    return orderDTO;
}

@SoulSpringMvcClient这个Annotation是用来配置信元数据信息。通过SpringMvcClientBeanPostProcessor这个类在Spring Bean容器初始化时,将读取所有包含@Controller@RestController@RequestMapping的Spring Bean的元信息,并将这些数据异步的方式注册到Soul Admin。核心代码如下:

public SpringMvcClientBeanPostProcessor(final SoulSpringMvcConfig soulSpringMvcConfig) {
    ValidateUtils.validate(soulSpringMvcConfig);
    this.soulSpringMvcConfig = soulSpringMvcConfig;
    // Soul Admin URL 负责注册元数据信息
    url = soulSpringMvcConfig.getAdminUrl() + "/soul-client/springmvc-register";
    // 启动只有一个线程的线程池,负责异步将元数据注册到Soul Admin
    executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
}

@Override
public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName) throws BeansException {
    if (soulSpringMvcConfig.isFull()) {
        return bean;
    }

    Controller controller = AnnotationUtils.findAnnotation(bean.getClass(), Controller.class);
    RestController restController = AnnotationUtils.findAnnotation(bean.getClass(), RestController.class);
    RequestMapping requestMapping = AnnotationUtils.findAnnotation(bean.getClass(), RequestMapping.class);
    // 判断该bean 是否有Controller、RestController或RequestMapping Annotation.
    if (controller != null || restController != null || requestMapping != null) {
        // 在Bean Class上是否有@SoulSpringMvcClient Annotation
        SoulSpringMvcClient clazzAnnotation = AnnotationUtils.findAnnotation(bean.getClass(), SoulSpringMvcClient.class);
        String prePath = "";
        if (Objects.nonNull(clazzAnnotation)) {
            if (clazzAnnotation.path().indexOf("*") > 1) {
                String finalPrePath = prePath;
                // 元数据注册
                executorService.execute(() -> RegisterUtils.doRegister(buildJsonParams(clazzAnnotation, finalPrePath), url,
                        RpcTypeEnum.HTTP));
                return bean;
            }
            prePath = clazzAnnotation.path();
        }
        final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(bean.getClass());
        for (Method method : methods) {
            // 在该Bean中的所有Methods上是否有@SoulSpringMvcClient Annotation
            SoulSpringMvcClient soulSpringMvcClient = AnnotationUtils.findAnnotation(method, SoulSpringMvcClient.class);
            if (Objects.nonNull(soulSpringMvcClient)) {
                String finalPrePath = prePath;
                // 元数据注册
                executorService.execute(() -> RegisterUtils.doRegister(buildJsonParams(soulSpringMvcClient, finalPrePath), url,
                        RpcTypeEnum.HTTP));
            }
        }
    }
    return bean;
}

总结

  • divide插件是进行http正向代理的插件,所有http类型的请求,都是由该插件进行负载均衡的调用
  • http请求是如何接入API网关的

你可能感兴趣的:(Soul API网关学习-Day02)