精华内容
下载资源
问答
  • 本章节介绍使用soul进行http请求的代理 概述 配置http请求的代理将使用到: divide插件 soul-spring-boot-starter-plugin-divide、soul-spring-boot-starter-plugin-httpclient依赖(网关端) soul-spring-...

    本文章将成系列介绍:包含但不限于高性能微服务API网关Soul的环境搭建、源码设计

    本章节介绍使用soul进行http请求的代理

    概述

    配置http请求的代理将使用到:

    • divide插件

    • soul-spring-boot-starter-plugin-divide、soul-spring-boot-starter-plugin-httpclient依赖(网关端)

    • soul-spring-boot-starter-client-springmvc依赖(业务系统端)

    • yml文件的配置

      soul:
        http:
          adminUrl: http://localhost:9095 #为你启动的soul-admin 项目的ip + 端口,注意要加http://
          port: 8189 #你本项目的启动端口
          contextPath: /http #为你的这个mvc项目在soul网关的路由前缀。 比如/order,/product等等,网关会根据你的这个前缀来进行路由.
          appName: http #你的应用名称,不配置的话,会默认取`spring.application.name`的值
          full: false #设置true 代表代理你的整个服务,false表示代理你其中某几个controller
      
    • @SoulSpringMvcClient 注解(加在controller的接口上)

      • 举例

        你可以把注解加到 Controller 类上面, 里面的path属性则为前缀,如果含有 /** 代表你的整个接口需要被网关代理。

        举例子 (1): 代表 /test/payment, /test/findByUserId 都会被网关代理。

        @RestController
        @RequestMapping("/test")
        @SoulSpringMvcClient(path = "/test/**")
        public class HttpTestController {
        
            /**
             * Post user dto.
             *
             * @param userDTO the user dto
             * @return the user dto
             */
            @PostMapping("/payment")
            public UserDTO post(@RequestBody final UserDTO userDTO) {
                return userDTO;
            }
        
            /**
             * Find by user id string.
             *
             * @param userId the user id
             * @return the string
             */
            @GetMapping("/findByUserId")
            public UserDTO findByUserId(@RequestParam("userId") final String userId) {
                UserDTO userDTO = new UserDTO();
                userDTO.setUserId(userId);
                userDTO.setUserName("hello world");
                return userDTO;
            }
        
        }
        

        举例子 (2): 代表 /order/save,会被网关代理,而/order/findById 则不会。

        @RestController
        @RequestMapping("/order")
        @SoulSpringMvcClient(path = "/order")
        public class OrderController {
        
            /**
             * Save order dto.
             *
             * @param orderDTO the order dto
             * @return the order dto
             */
            @PostMapping("/save")
            @SoulSpringMvcClient(path = "/save" , desc = "Save order")
            public OrderDTO save(@RequestBody final OrderDTO orderDTO) {
                orderDTO.setName("hello world save order");
                return orderDTO;
            }
        
            /**
             * Find by id order dto.
             *
             * @param id the id
             * @return the order dto
             */
            @GetMapping("/findById")
            public OrderDTO findById(@RequestParam("id") final String id) {
                OrderDTO orderDTO = new OrderDTO();
                orderDTO.setId(id);
                orderDTO.setName("hello world findById");
                return orderDTO;
            }
            
        }
        

    示例步骤

    1. 启动soul-admin(插件和其他信息配置的管理后台)、soul-bootstrap(网关项目);

    2. 配置业务工程(以soul-examples项下soul-examples-http为例)中的controller、application.yml,在application.yml中添加:

      soul:
        http:
          adminUrl: http://localhost:9095 #为你启动的soul-admin 项目的ip + 端口,注意要加http://
          port: 8189 #你本项目的启动端口
          contextPath: /http #为你的这个mvc项目在soul网关的路由前缀。 比如/order,/product等等,网关会根据你的这个前缀来进行路由.
          appName: http #你的应用名称,不配置的话,会默认取`spring.application.name`的值
          full: false #设置true 代表代理你的整个服务,false表示代理你其中某几个controller
      

      为controller中你所需的程序加上对应注解;

    3. 启动业务工程,会打印出对应的URI映射信息,如:

      在这里插入图片描述

    4. 在soul-admin页面中可进行divide插件配置

      divide插件介绍

      divide插件是网关处理 http协议请求的核心处理插件。

      插件设置
      • 开启插件, soul-admin --> 插件管理–> divide 设置为启用;

      • divide插件,配合如下 starter一起才能生效(网关项目中依赖即可)

        <!--if you use http proxy start this-->
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>soul-spring-boot-starter-plugin-divide</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>soul-spring-boot-starter-plugin-httpclient</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--if you use http proxy end this-->
        
      插件讲解
      • divide插件是进行http正向代理的插件,所有http类型的请求,都是由该插件进行负载均衡的调用。

      • 选择器和规则,请详细看 : 选择器与规则介绍

      • http配置,是网关匹配到流量以后,真实调用的http配置,可以配置多个,设置负载均衡权重,具体的负载均衡策略,在规则中指定。

        • 配置详解 :

          • 第一个框:hostName,一般填写 localhost,该字段暂时没使用。
          • 第二个框:http协议,一般填写 http:// 或者 https:// ,不填写默认为:http://
          • 第三个框:ip与端口,这里填写你真实服务的 ip + 端口。
          • 第四个框:负载均衡权重。

          在这里插入图片描述

        • ip + port 检测

          • 在soul-admin 会有一个定时任务来扫描 配置的ip端口,如果发现下线,则会除该 ip + port
          • 可以进行如下配置 :
            soul.upstream.check:true  默认为 ture,设置为false,不检测
            soul.upstream.scheduledTime:10  定时检测时间间隔,默认10秒
      
      选择器与规则介绍
      • 一个插件有多个选择器,一个选择器对应多种规则。选择器相当于是对流量的第一次筛选,规则就是最终的筛选。
      • 我们想象一下,在一个插件里面,我们是不是希望根据我们的配置,达到满足条件的流量,我们插件才去执行它?
      • 选择器和规则就是为了让流量在满足特定的条件下,才去执行我们想要的,这个你首先头脑要点数。
      选择器详解

      在这里插入图片描述

      • 名称:为你的选择器起一个容易分辨的名字
      • 类型:custom flow 是自定义流量。full flow 是全流量。自定义流量就是请求会走你下面的匹配方式与条件。全流量则不走。
      • 匹配方式:and 或者or 是指下面多个条件是按照and 还是or的方式来组合。
      • 条件:
        • uri:是指你根据uri的方式来筛选流量,match的方式支持模糊匹配(/**)
        • header:是指根据请求头里面的字段来筛选流量。
        • query: 是指根据uri的查询条件来进行筛选流量。
        • ip:是指根据你请求的真实ip,来筛选流量。
        • host:是指根据你请求的真实host,来筛选流量。
        • post:建议不要使用。
        • 条件匹配:
          • match : 模糊匹配,建议和uri条件搭配,支持 restful风格的匹配。(/test/**)
          • = : 前后值相等,才能匹配。
          • regEx : 正则匹配,表示前面一个值去匹配后面的正则表达式。
          • like :字符串模糊匹配。
      • 是否开启:打开才会生效
      • 打印日志:打开的时候,当匹配上的时候,会打印匹配日志。
      • 执行顺序:当多个选择器的时候,执行顺序小的优先执行。

      上述图片中表示:当请求的uri前缀是 /http,会转发到 192.168.137.1:8189 这个服务。

      选择器建议 : 可以uri 条件, match 前缀 (/contextPath),进行第一道流量筛选。

      规则详解

      当流量经过选择器匹配成功之后,会进入规则来进行最终的流量匹配。

      规则是对流量最终执行逻辑的确认。

    在这里插入图片描述

    • 名称:为你的规则起一个容易分辨的名字
    • 匹配方式:and 或者or 是指下面多个条件是按照and 还是or。
    • 条件:
      • uri:是指你根据uri的方式来筛选流量,match的方式支持模糊匹配(/**)
      • header:是指根据请求头里面的字段来筛选流量。
      • query: 是指根据uri的查询条件来进行筛选流量。
      • ip:是指根据你请求的真实ip,来筛选流量。
      • host:是指根据你请求的真实host,来筛选流量。
      • post:建议不要使用。
      • 条件匹配:
        • match : 模糊匹配,建议和uri条件搭配,支持 restful风格的匹配。(/test/**)
        • = : 前后值相等,才能匹配。
        • regEx : 正则匹配,表示前面一个值去匹配后面的正则表达式。
        • like :字符串模糊匹配。
    • 是否开启:打开才会生效。
    • 打印日志:打开的时候,当匹配上的时候,会打印匹配日志。
    • 执行顺序:当多个选择器的时候,执行顺序小的优先执行。
    • 处理:每个插件的规则处理不一样,具体的差有具体的处理,具体请查看每个对应插件的处理。
    • 上图表示:当 uri 等于 /http/order/save 的时候该规则被匹配,就会执行该规则中,负载策略是 random

    联合选择器,我们来表述一下 :当一个 请求的 uri/http/order/save, 会通过 random 的方式,转发到 192.168.137.1:8189

    规则建议: 可以uri 条件, match 最真实的uri路径,进行流量的最终筛选 。

    1. 配置完成后进行http请求映射,访问网关divide选择器对应的规则列表URI,如:http://localhost:9195/http/test/findByUserId?userId=3,则soul会自动代理到http://localhost:8189/test/findByUserId?userId=3

    源码跟踪

    根据代理时日志打印:SoulBootstrapApplication打印出的线程类跟踪AbstractSoulPlugin,WebClientPlugin两个类:

    2021-01-16 03:51:20.890 INFO 19912 — [-work-threads-3] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http
    2021-01-16 03:51:20.893 INFO 19912 — [-work-threads-3] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/**
    2021-01-16 03:51:20.895 INFO 19912 — [-work-threads-3] o.d.s.plugin.httpclient.WebClientPlugin : you request,The resulting urlPath is :http://192.168.137.1:8189/test/findByUserId?userId=3, retryTimes: 0

    AbstractSoulPlugin:

    在execute方法中进行每个插件的链式执行,分别匹配判断其选择器及规则匹配。

    AbstractSoulPlugin.execute执行divid插件doExecute方法,返回chain.execute(exchange);之后进行响应式编程的plugin.execut,执行WebClientPlugin的execute方法进行转发实时URI。

    读源码感觉只懂了不到10%,白天再参考下同学大牛的总结,今天太晚了就到这里。

    另外,选择器规则注册后为持久化数据,若需取消则需要手动配置取消规则。

    展开全文
  • Soul+Dubbo环境搭建 今天一下午,试了几个小时如何搭建环境,发现了如下几个 问题 版本不同,无法注册 首先参考芋道源码http://www.iocoder.cn/Soul/install/ 实现了一下dubbo+nacos。但是发现自己复制的2.1.2版本与...

    Soul+Dubbo环境搭建

    今天一下午,试了几个小时如何搭建环境,发现了如下几个 问题

    版本不同,无法注册

    首先参考芋道源码http://www.iocoder.cn/Soul/install/ 实现了一下dubbo+nacos。但是发现自己复制的2.1.2版本与下载的源码的soul-admin和soul-boostrap的版本不对。项目无法被注册到网关上,这个是个问题。后续希望可以通过看源码能了解甚至解决这个问题

    dubbo版本配置无法读取到it’s not a valid config! Please add <dubbo:application name="…" /> to your application config

    参考soul-example的xml配置修改为使用yml搭配注解配置出现了上述错误。调整spring版本和dubbo版本均无效果,但观察soul-admin后台可以发现还是注册成功了。应该是dubbo的校验出了问题,(soul开发者群中说是数据库验证的问题,还未完全验证,后续可尝试下)而注册到soul-boostrap的信息并没有问题
    file

    成功版本–完全使用soul-example

    soul-example采用的是dubbo+zookeeper,与nacos的方案略有不同
    file
    通过查看规则发现,基本的匹配规则和均衡规则与http的并无不同,这个引发我另一个想法,如果dubbo本省的负载均衡规则和soul的均衡规则同时配置,那么该遵守哪一个规则呢?我们可以后续通过尝试和源码解读来了解
    启动之后,我们,可以通过网关代理的接口来访问到dubbo的服务了。
    file
    另外当插件中的zookeeper的端口配置错误时,错误是这样的java.io.IOException: Packet len1213486160 is out of range! 很明显上下文中的端口是nacos的8848我却没有意识到。
    file
    另外还出现了一个可能是经过长时间未操作引起的websocket断开的问题,导致访问时会出现 107错误。这个问题虽然说重启soul和soul-bootstrap或者重新刷新规则就能解决,但是我还是对中间的websocket断线重连很有兴趣,希望后续通过源码了解到这一方面的内容

    问题

    • 版本兼容问题,低版本Soul无法注册到高版本的soul-admin/soulboostrap中
    • dubbo的负载均衡规则和soul集成的负载均衡规则的优先级
    • soul-admin和soul-boostrap断线之后重连的处理

    欢迎搜索关注本人与朋友共同开发的微信面经小程序【大厂面试助手】和公众号【微瞰技术】,以及总结的分类面试题https://github.com/zhendiao/JavaInterview

    file
    file

    展开全文
  • 2.学习文档,结合divde插件,发起http请求soul网关,体验http代理 3.记录心得,写博客分享。 一、从官方文档开始 打开 用户使用文档 - http用户 页面,开始整理关键要素。 1、接入说明: 接入前,需要先启动 soul-...

    如何读开源项目:对着文档跑demo,对着demo看代码,懂一点就开始试,有问题了问社区。

    今日目标:

    1.运行examples下面的 http服务
    2.学习文档,结合divde插件,发起http请求soul网关,体验http代理
    3.记录心得,写博客分享。

    一、从官方文档开始

    打开 用户使用文档 - http用户 页面,开始整理关键要素。

    1、接入说明:

    • 接入前,需要先启动 soul-admin
    • soul 使用 divde 插件来处理 http 请求,插件在 admin 后台开启。

    2、网关需要引入代理插件

    网关 pom 增加依赖:

      <!--if you use http proxy start this-->
       <dependency>
           <groupId>org.dromara</groupId>
           <artifactId>soul-spring-boot-starter-plugin-divide</artifactId>
            <version>${last.version}</version>
       </dependency>
    
       <dependency>
           <groupId>org.dromara</groupId>
           <artifactId>soul-spring-boot-starter-plugin-httpclient</artifactId>
            <version>${last.version}</version>
       </dependency>
    

    重启网关使配置生效。

    3、Http 服务接入网关

    接入前需确保 soul-admin 已开启 divide 插件

    3.1 soul-client方式接入

    适用于 SpringMVC 体系用户,分为:

    • spring 版本
    • spring-boot 版本

    两种版本依赖和配置有所差异,但在服务配置上都使用 @SoulSpringMVCClient,其用法并无差异。

    3.1.1 spring 版本 SpringMVC 接入

    1)被代理服务 pom.xml 引入如下依赖:

           <dependency>
               <groupId>org.dromara</groupId>
               <artifactId>soul-client-springmvc</artifactId>
               <version>${last.version}</version>
           </dependency>
    

    2)xml 中新增如下 bean 配置:

       <bean id ="springMvcClientBeanPostProcessor" class ="org.dromara.soul.client.springmvc.init.SpringMvcClientBeanPostProcessor">
            <constructor-arg  ref="soulSpringMvcConfig"/>
       </bean>
       
       <bean id="soulSpringMvcConfig" class="org.dromara.soul.client.springmvc.config.SoulSpringMvcConfig">
            <property name="adminUrl" value="http://localhost:9095"/>
            <property name="port" value="你的端口"/>
            <property name="contextPath" value="/你的contextPath"/>
            <property name="appName" value="你的名字"/>
            <property name="full" value="false"/>
       </bean>
    

    3)Controller 加上 @SoulSpringMVCClient 注解,针对其 path 属性有两种配置场景

    • 该 Controller 下所有服务均需被网关代理

      注解加在 Controller 类上,path 配置为 /前缀路径/**

    • 该 Controller 下部分服务需被网关代理

      类上注解的 path 配置为 /前缀路径,方法上注解的 path 配置为 /对应的服务路径

    4)启动项目,服务接入到网关。

    3.1.2 spring-boot 版本 SpringMVC 接入

    1)被代理服务 pom.xml 引入如下依赖:

         <dependency>
             <groupId>org.dromara</groupId>
             <artifactId>soul-spring-boot-starter-client-springmvc</artifactId>
             <version>${last.version}</version>
         </dependency>
    

    2)yml 中增加如下配置:

       soul:
         http:
           adminUrl: http://localhost:9095
           port: 你本项目的启动端口
           contextPath: /http
           appName: http
           full: false  
    
    • adminUrl:soul-admin 项目的ip + 端口,注意要加http://
    • port:本项目的启动端口,需要与实际启动端口一致
    • contextPath:本项目在soul网关的路由前缀, 比如/order ,/product 等等,网关根据这个前缀来进行路由.
    • appName:你的应用名称,不配置则默认取 spring.application.name 的值
    • full:true 表示代表代理整个服务,false 表示代理其中某几个controller

    3)Controller 加上 @SoulSpringMVCClient 注解,配置场景同 spring 版本

    4)启动项目,服务接入到网关。

    3.2 非 soul-client 方式接入

    4、用户请求

    请求方式有两点变化:

    • 修改 ip + 端口

      需要将 直接指向用户的请求,改为通过网关发起访问。

    • 加上 context path

      在 ip + 端口 后面,紧跟目标项目中配置的路由前缀

    例如:

    原请求 url:http://localhost:8080/test/save

    现请求 url:http://localhost:9195/order/test/save

    • http://localhost:9195 为网关 ip + 端口,注意默认端口为9195

    • /order 为配置的 contextpath

    二、官方示例分析

    1、打开 soul-examples 项目

    位于 soul 项目下,未集成到 soul 项目 maven 中统一管理,需要单独打开
    在这里插入图片描述

    2、定位待分析目标示例模块

    待分析目标示例为 http 示例,选择并展开
    在这里插入图片描述

    3、依赖分析

    打开 pom.xml,依赖关系如下:

            <!-- soul-client -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>soul-spring-boot-starter-client-springmvc</artifactId>
                <version>${soul.version}</version>
            </dependency>
            <!-- webflix -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-webflux</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    

    很明显是 spring-boot 项目,除了官方文档中要求引入的 soul-spring-boot-starter-client-springmvc 外,还引入了 webflix

    4、配置分析

    打开 application.yml,配置内容如下:

    # 服务信息
    server:
      port: 8188
      address: 0.0.0.0
    
    # soul-client 配置
    soul:
      http:
        adminUrl: http://localhost:9095
        port: 8188
        contextPath: /http
        appName: http
        full: false
    
    # 日志配置
    logging:
      level:
        root: info
        org.springframework.boot: info
        org.apache.ibatis: info
        org.dromara.soul.test.bonuspoint: info
        org.dromara.soul.test.lottery: debug
        org.dromara.soul.test: debug
    

    soul-client 部分的配置格式与官方文档一致,注意核对下 soul-admin 的 ip + 端口是否与实际一致。

    5、Controller 分析

    展开 org.dromara.soul.examples.http.controller 包,自带两个controller
    在这里插入图片描述

    1)HttpTestController.java
    /**
     * TestController.
     *
     * @author xiaoyu
     */
    @RestController
    @RequestMapping("/test")
    @SoulSpringMvcClient(path = "/test/**")
    public class HttpTestController {
    
        /**
         * Post user dto.
         *
         * @param userDTO the user dto
         * @return the user dto
         */
        @PostMapping("/payment")
        public UserDTO post(@RequestBody final UserDTO userDTO) {
            return userDTO;
        }
    
        /**
         * Find by user id string.
         *
         * @param userId the user id
         * @return the string
         */
        @GetMapping("/findByUserId")
        public UserDTO findByUserId(@RequestParam("userId") final String userId) {
            UserDTO userDTO = new UserDTO();
            userDTO.setUserId(userId);
            userDTO.setUserName("hello world");
            return userDTO;
        }
    
        /**
         * Gets path variable.
         *
         * @param id   the id
         * @param name the name
         * @return the path variable
         */
        @GetMapping("/path/{id}")
        public UserDTO getPathVariable(@PathVariable("id") final String id, @RequestParam("name") final String name) {
            UserDTO userDTO = new UserDTO();
            userDTO.setUserId(id);
            userDTO.setUserName("hello world");
            return userDTO;
        }
    
    
        /**
         * Test rest ful string.
         *
         * @param id the id
         * @return the string
         */
        @GetMapping("/path/{id}/name")
        public UserDTO testRestFul(@PathVariable("id") final String id) {
            UserDTO userDTO = new UserDTO();
            userDTO.setUserId(id);
            userDTO.setUserName("hello world");
            return userDTO;
        }
    
    
        /**
         * Put path variable and body string.
         *
         * @param id      the id
         * @param userDTO the user dto
         * @return the string
         */
        @PutMapping("/putPathBody/{id}")
        public UserDTO putPathVariableAndBody(@PathVariable("id") final String id, @RequestBody final UserDTO userDTO) {
            userDTO.setUserId(id);
            userDTO.setUserName("hello world");
            return userDTO;
        }
    
    }
    

    类上使用了 @SoulSpringMvcClient(path = "/test/**") 表示注册该 Controller 下所有服务。

    注册的服务清单:

    • /payment
    • /findByUserId
    • /path/{id}
    • /path/{id}/name
    • /putPathBody/{id}

    都是简单的 mock 服务

    2)OrderController.java
    /**
     * TestController.
     *
     * @author xiaoyu
     */
    @RestController
    @RequestMapping("/order")
    @SoulSpringMvcClient(path = "/order")
    public class OrderController {
    
        /**
         * Save order dto.
         *
         * @param orderDTO the order dto
         * @return the order dto
         */
        @PostMapping("/save")
        @SoulSpringMvcClient(path = "/save" , desc = "Save order")
        public OrderDTO save(@RequestBody final OrderDTO orderDTO) {
            orderDTO.setName("hello world save order");
            return orderDTO;
        }
    
        /**
         * Find by id order dto.
         *
         * @param id the id
         * @return the order dto
         */
        @GetMapping("/findById")
        @SoulSpringMvcClient(path = "/findById", desc = "Find by id")
        public OrderDTO findById(@RequestParam("id") final String id) {
            OrderDTO orderDTO = new OrderDTO();
            orderDTO.setId(id);
            orderDTO.setName("hello world findById");
            return orderDTO;
        }
    
        /**
         * Gets path variable.
         *
         * @param id   the id
         * @param name the name
         * @return the path variable
         */
        @GetMapping("/path/{id}/{name}")
        @SoulSpringMvcClient(path = "/path/**")
        public OrderDTO getPathVariable(@PathVariable("id") final String id, @PathVariable("name") final String name) {
            OrderDTO orderDTO = new OrderDTO();
            orderDTO.setId(id);
            orderDTO.setName("hello world restful: " + name);
            return orderDTO;
        }
    
        /**
         * Test rest ful order dto.
         *
         * @param id the id
         * @return the order dto
         */
        @GetMapping("/path/{id}/name")
        @SoulSpringMvcClient(path = "/path/**/name")
        public OrderDTO testRestFul(@PathVariable("id") final String id) {
            OrderDTO orderDTO = new OrderDTO();
            orderDTO.setId(id);
            orderDTO.setName("hello world restful inline " + id);
            return orderDTO;
        }
    }
    

    类上使用了 @SoulSpringMvcClient(path = "/order"),表示该 Controller 下部分服务需要注册,配合方法上的 SoulSpringMvcClient 指定具体注册的服务。

    注册的服务清单:

    • /save
    • /findById
    • /path/{id}/{name}
    • /path/{id}/name

    注意到注册 GET 请求的服务时,url 中的参数需要用 ** 代替

    6、启动示例模块

    官方文档里提到:

    需要在网关的 pom 里增加 soul-spring-boot-starter-plugin-divide 和 soul-spring-boot-starter-plugin-httpclient 依赖,并重启网关。

    需要这么麻烦么?

    百度查阅相关文章后,了解到 soul 本身提供的 soul-bootstrap 模块已经集成了这两个插件,所以启动 soul-admin 后,再启动 soul-bootstrap 就可以了。

    接下来愉快地启动示例吧

    运行 SoulTestHttpApplication.java,启动示例模块
    在这里插入图片描述

    通过控制台日志可以看到,模块启动后向网关注册了5条 http-client 元数据。

    其中 HttpTestController 统一注册了1条元数据 /http/test/**,而 OrderController 则分别注册了4条元数据。

    猜测元数据记录与 @SoulSpringMvcClient 注解一一对应。

    登录 http://localhost:9095/#/plug/divide,在 divide 插件一栏可以看到 http 服务注册信息

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
    在这里插入图片描述

    点击 Modify 按钮可以查看或修改注册的元数据信息
    在这里插入图片描述

    7、http 请求 网关

    此处使用 Postman 发起 http 请求,与浏览器直接发起请求等价。

    1)先尝试请求原 http 服务

    待访问 http 服务: /test/findByUserId,请求url:localhost:8188/test/findByUserId?userId=001
    在这里插入图片描述

    访问成功,耗时 12ms

    2)再通过网关访问代理的 http 服务

    网关 ip + 端口:http://localhost:9195

    contextPath:/http

    待访问 http 服务: /test/findByUserId

    要素拼装后请求url:localhost:9195/http/test/findByUserId?userId=001
    在这里插入图片描述

    访问成功,耗时 50ms

    至此,网关成功代理 http 服务,注意到网关代理服务耗时确实比原 http 服务长,毕竟多了一次路由转发,性能方面待后续压测。

    三、总结

    http 服务接入 soul 网关:

    • 先启动 soul-admin

    • 网关需要引入相关代理插件并重启,若使用 soul-bootstrap 则直接 soul-bootstrap 启动即可

    • http 服务接入网关方式:

      • soul-client方式,自动注册元信息
      • divide 插件方式,手动配置元信息
      • 自定义 http-client 方式
    • 将直接指向用户的请求,改为通过网关发起访问,并加上相应的 contextPath。

    参考文章

    个人知识库

    高性能微服务API网关-Soul

    展开全文
  • 目标 divide插件底层原理,负载均衡,ip端口探活介绍 负载均衡原理分析 ip端口探活端口分析 总结 divide插件底层原理,负载均衡,ip端口检测介绍 ...soul-admin提供ip+端口的检测功能,在soul-admin 会有一

    目标

    • divide插件底层原理,负载均衡,ip端口探活介绍
    • 负载均衡原理分析
    • ip端口探活端口分析
    • 总结

    divide插件底层原理,负载均衡,ip端口检测介绍

        divide插件是网关处理http协议请求的核心处理插件,divide插件是进行http正向代理的插件,所有http类型的请求,都是由该插件进行负载均衡的调用,所说的负载均衡就是divide插件根据负责均衡算法将请求转发到具体的一台后端机器上。soul-admin提供ip+端口的检测功能,在soul-admin 会有一个定时任务来扫描 配置的ip端口,如果发现下线,则会除该 ip + port。

        在之前的一篇文章中介绍过一个http请求是如何被网关代理的,在那篇文章中介绍了http协议的请求是通过divide插件处理的以及divide插件源码的分析,并且也对原理进行了一定的阐述(责任链)。

        所以本篇文章就对负载均衡以及ip端口探活方面进行进行更进一步的探讨

    soul网关负载均衡源码分析

    首先,我们来看一下divide插件负载均衡模块,以及各个模块的概要介绍

    在这里插入图片描述

    在这里插入图片描述

    • LoadBalance接口

        这是一个SPI的接口,所以soul网关的负载均衡实现是可扩展的,开发者可以根据自己的业务需求,系统情况来实现自己的负载均衡,该接口提供了一个select方法,返回一个DivideUpstream对象,该对象包含一下属性:

    • upstreamHost:存活的ip
    • protocol:协议
    • upstreamUrl:请求地址
    • weight:权重
    • status:状态,默认是true
    • timestamp:时间戳
    • warmup:热度
    @SPI
    public interface LoadBalance {
    
        /**
         * this is select one for upstream list.
         *
         * @param upstreamList upstream list
         * @param ip ip
         * @return divide upstream
         */
        DivideUpstream select(List<DivideUpstream> upstreamList, String ip);
    }
    
    • AbstractLoadBalance类

        该类是soul网关支持的负载均衡算法的基类,实现了LoadBalance接口,重写select方法,提供了抽象方法doSelect,负载均衡的具体实现类会重写doSelect方法,doSelect方法才是具体的负载均衡算法的实现,还提供了一个getWeight方法,这里特别注意一下calculateWarmupWeight方法,该方法是根据时间差,接口热度,权重计算实际权重的方法。

    在这里插入图片描述

    • HashLoadBalance

        哈希算法的负载均衡的实现

    在这里插入图片描述

    • RandomLoadBalance

        随机算法的负载均衡的实现

    在这里插入图片描述

    • RoundRobinLoadBalance

        轮询算法的负载均衡的实现,这块

    在这里插入图片描述

    • 总结

        看了soul网关负载均衡的设计,那么divide插件是怎么应用负载均衡的呢,其实仔细看之前的文章应该是可以发现额,在DividePlugindoExecute中有这样一断代码,这其实就是divide插件在进行负载均衡了DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip);

    soul网关divide插件的ip端口探活

        在soul-admin 会有一个定时任务来扫描 配置的ip端口,如果发现下线,则会除该 ip + port,不只是在soul-admin端会有定时的探活,在soul网关侧也有ip+端口的探活

    soul-admin端是否开启ip端口探活和探活的频率是可以配置的如果不配置的默认是检查的,并且频率就是十秒,配置如下:

    soul.upstream.check:true  默认为 ture,设置为false,不检测
    soul.upstream.scheduledTime:10
    

        soul网关侧是在divide插件的handler(DividePluginDataHandler)初始化的时候就初始化了,可以参考这个代码DividePluginConfiguration,这里看一下DividePluginDataHandler

    在这里插入图片描述

        下面我们就分别看一下soul-admin侧和soul网关端探活的处理

    • soul-admin侧

        在soul-admin端是通过UpstreamCheckService进行的探活的处理,下面这段代码可以看到@PostConstruct注解,该注解的作用是在spring项目启动容器初始化时候会被执行他的执行顺序是Constructor >> @Autowired >> @PostConstruct,下面代码是UpstreamCheckService,处理细节参考注释

        @PostConstruct
        public void setup() {
            // 获取divide插件
            PluginDO pluginDO = pluginMapper.selectByName(PluginEnum.DIVIDE.getName());
            if (pluginDO != null) {
                List<SelectorDO> selectorDOList = selectorMapper.findByPluginId(pluginDO.getId());
                for (SelectorDO selectorDO : selectorDOList) {
                    List<DivideUpstream> divideUpstreams = GsonUtils.getInstance().fromList(selectorDO.getHandle(), DivideUpstream.class);
                    if (CollectionUtils.isNotEmpty(divideUpstreams)) {
                        // 初始化要探活的选择器内容,UPSTREAM_MAP是一个全局的map
                        UPSTREAM_MAP.put(selectorDO.getName(), divideUpstreams);
                    }
                }
            }
            // 是否要进行探活,一会说这个check
            if (check) {
                // 创建一个定时处理的线程池,这个线程池执行scheduled方法
                new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), SoulThreadFactory.create("scheduled-upstream-task", false))
                        .scheduleWithFixedDelay(this::scheduled, 10, scheduledTime, TimeUnit.SECONDS);
            }
        }
    
    
        private void check(final String selectorName, final List<DivideUpstream> upstreamList) {
            List<DivideUpstream> successList = Lists.newArrayListWithCapacity(upstreamList.size());
            for (DivideUpstream divideUpstream : upstreamList) {
                // 根据URL,进行ip+端口探活
                final boolean pass = UpstreamCheckUtils.checkUrl(divideUpstream.getUpstreamUrl());
                if (pass) {
                    // 如果状态是false,那么更新状态
                    if (!divideUpstream.isStatus()) {
                        divideUpstream.setTimestamp(System.currentTimeMillis());
                        divideUpstream.setStatus(true);
                        log.info("UpstreamCacheManager check success the url: {}, host: {} ", divideUpstream.getUpstreamUrl(), divideUpstream.getUpstreamHost());
                    }
                    // 将存活的插件divideUpstream,存放到存活列表
                    successList.add(divideUpstream);
                } else {
                    // 设置divideUpstream状态为false
                    divideUpstream.setStatus(false);
                    log.error("check the url={} is fail ", divideUpstream.getUpstreamUrl());
                }
            }
            if (successList.size() == upstreamList.size()) {
                return;
            }
            if (successList.size() > 0) {
                // 更新探活的内容
                UPSTREAM_MAP.put(selectorName, successList);
                // 更新选择器
                updateSelectorHandler(selectorName, successList);
            } else {
                // 移除探活内容
                UPSTREAM_MAP.remove(selectorName);
                updateSelectorHandler(selectorName, null);
            }
        }
    
        private void updateSelectorHandler(final String selectorName, final List<DivideUpstream> upstreams) {
            SelectorDO selector = selectorService.findByName(selectorName);
            if (Objects.nonNull(selector)) {
                SelectorData selectorData = selectorService.buildByName(selectorName);
                if (upstreams == null) {
                    selector.setHandle("");
                    selectorData.setHandle("");
                } else {
                    String handler = GsonUtils.getInstance().toJson(upstreams);
                    selector.setHandle(handler);
                    selectorData.setHandle(handler);
                }
                // 更新数据库数据
                selectorMapper.updateSelective(selector);
                // 发布数据变动通知
                eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE,
                        Collections.singletonList(selectorData)));
            }
        }
    
    • soul网关侧

        在soul网关侧的探活代码主要是在UpstreamCacheManager,这个类是一个单例的,视同过DividePluginDataHandler的动作初始化的,代码处理逻辑和soul-admin侧类似,具体参考下面代码注释:

    @Slf4j
    public final class UpstreamCacheManager {
        // UpstreamCacheManager实例初始化
        private static final UpstreamCacheManager INSTANCE = new UpstreamCacheManager();
    
        private static final Map<String, List<DivideUpstream>> UPSTREAM_MAP = Maps.newConcurrentMap();
    
        private static final Map<String, List<DivideUpstream>> UPSTREAM_MAP_TEMP = Maps.newConcurrentMap();
    
    
        /**
         * 构造函数,初始化定时线程池执行scheduled
         */
        private UpstreamCacheManager() {
            boolean check = Boolean.parseBoolean(System.getProperty("soul.upstream.check", "false"));
            if (check) {
                new ScheduledThreadPoolExecutor(1, SoulThreadFactory.create("scheduled-upstream-task", false))
                        .scheduleWithFixedDelay(this::scheduled,
                                30, Integer.parseInt(System.getProperty("soul.upstream.scheduledTime", "30")), TimeUnit.SECONDS);
            }
        }
    
        /**
         * 返回单例的UpstreamCacheManager实例
         */
        public static UpstreamCacheManager getInstance() {
            return INSTANCE;
        }
    
        /**
         * 根据选择器ID,获取要探活的的选择器数据
         */
        public List<DivideUpstream> findUpstreamListBySelectorId(final String selectorId) {
            return UPSTREAM_MAP_TEMP.get(selectorId);
        }
    
        /**
         * 移除掉选择器数据
         */
        public void removeByKey(final String key) {
            UPSTREAM_MAP_TEMP.remove(key);
        }
    
        /**
         * 将要探活的数据存储或移除到全局缓存中
         */
        public void submit(final SelectorData selectorData) {
            final List<DivideUpstream> upstreamList = GsonUtils.getInstance().fromList(selectorData.getHandle(), DivideUpstream.class);
            if (null != upstreamList && upstreamList.size() > 0) {
                UPSTREAM_MAP.put(selectorData.getId(), upstreamList);
                UPSTREAM_MAP_TEMP.put(selectorData.getId(), upstreamList);
            } else {
                UPSTREAM_MAP.remove(selectorData.getId());
                UPSTREAM_MAP_TEMP.remove(selectorData.getId());
            }
        }
    
        private void scheduled() {
            if (UPSTREAM_MAP.size() > 0) {
                UPSTREAM_MAP.forEach((k, v) -> {
                    List<DivideUpstream> result = check(v);
                    if (result.size() > 0) {
                        UPSTREAM_MAP_TEMP.put(k, result);
                    } else {
                        UPSTREAM_MAP_TEMP.remove(k);
                    }
                });
            }
        }
    
        private List<DivideUpstream> check(final List<DivideUpstream> upstreamList) {
            List<DivideUpstream> resultList = Lists.newArrayListWithCapacity(upstreamList.size());
            for (DivideUpstream divideUpstream : upstreamList) {
                // 根据URL,进行ip+端口探活
                final boolean pass = UpstreamCheckUtils.checkUrl(divideUpstream.getUpstreamUrl());
                if (pass) {
                    if (!divideUpstream.isStatus()) {
                        divideUpstream.setTimestamp(System.currentTimeMillis());
                        divideUpstream.setStatus(true);
                        log.info("UpstreamCacheManager detect success the url: {}, host: {} ", divideUpstream.getUpstreamUrl(), divideUpstream.getUpstreamHost());
                    }
                    resultList.add(divideUpstream);
                } else {
                    // 设置divideUpstream状态为false
                    divideUpstream.setStatus(false);
                    log.error("check the url={} is fail ", divideUpstream.getUpstreamUrl());
                }
            }
            return resultList;
    
        }
    }
    

    UpstreamCheckUtils探活代码分析

    在这里插入图片描述

    总结

        该篇文章介绍了divide插件负载均衡和ip+端口探活的内容,soul网关divide插件提供了基于轮询、hash、随机三种算法的负载均衡实现,并且提供了SPI机制能够让开发者扩展实现负载均衡;soul网关divide插件和soul-admin提供了基于ip+端口的探活,当有机器上线或下线时会更新存活服务列表,实现了精准的负载均衡

    展开全文
  • Soul源码分析 —— 代理SOFA服务网关需要引入的依赖服务端需要引入的依赖服务器端需要配置的环境参数启动Sofa服务端应用碰到的几个坑在插件metadata中因为interface和MethodName和Dubbo的服务相同,导致报异常,解决...
  • 我们知道soul网关支持多种协议的,其中http协议应该是最基本也是平时使用最多的协议。 开始实践之前,我们可以梳理一下网关的基本功能,需要考虑什么问题呢? 先梳理一下加入网关后的调用流程: 客户端http请求->...
  • 启动 soul-examples-http,默认端口 8188,可以看到启动时的日志: divide 插件 divide插件是网关处理 HTTP 请求的核心处理插件。 divide插件是进行http正向代理的插件,所有http类型的请求,都是由该插件进行负载...
  • 使用 soul 代理 dubbo 服务 dubbo 服务如何注册到网关的? dubbo 插件是如何工作的? 理清 http --> 网关–> dubbo provider 整条链路经历了什么。 总结 一、使用 soul 代理 dubbo 服务 1、dubbo ...
  • 本地eclipse启动soul-test-http,相关选择器未能注册到soul-admin,后面发现yaml端口配成soul-bootstrap的9195端口,改成soul-admin的9095端口,依旧不能注册上去 疑惑和需要补充的 代理的服务具体是如何注册上去的...
  • soul网关源码学习12-http服务注册与探活 前言 上一讲讲到了http服务启动之后,通过http请求的方式把接口的数据发送到admin,接下来一起来看一下admin拿到数据之后是做了哪些操作的。 一、源码解析 服务注册 这里为了...
  • 说明 本文将包括如下内容: 如何将dubbo服务接入soul ...(1)启动soul-admin后台,操作步骤可以参考本系列第一篇文章阅读源码准备与soul基础 (2)在soul-admin后台将dubbo插件打开 路径:System Ma
  • 接入前,请正确的启动soul-admin(如有疑问,出门左转【Soul源码阅读】2.单机部署 Soul或官网文档-搭建Soul网关环境) 2.引入网关对 Spring Cloud 的插件支持 2.1 公共依赖 在 soul-bootstrap 的 pom.xml 文件中,...
  • admin项目,自动创建数据库,soul-admin是一个单纯的后端控制服务 启动soul-bootstrap,整个项目的核心,直接run起来就可以,注意端口号为http://localhost:9095/ 3.2 soul网关转发测试 启动模拟业务项目,使用soul-...
  • dubbo面试题

    千次阅读 2020-01-02 17:46:55
    服务代理层,无论是 consumer 还是 provider,Dubbo 都会给你生成代理代理之间进行网络通信。 如果胖友了解 Spring Cloud 体系,可以类比成 Feign 对于 consumer ,Spring MVC 对于 provider 。 ...
  • 1.1 在soul-bootstrap项目中引入如下插件,然后重新启动soul网关,启动步骤参见 soul源码分析_0_阅读源码准备与soul基础 <!--if you use http proxy start this--> <dependency> <groupId>org....
  • 如果有多个产品线售卖API,那么他们都会有一些共性的需求,例如签名认证、限速等,为了避免每个产品线重复...前几篇笔记里面,学习了soul网关divide插件如何进行请求代理和数据同步机制之websocket,对上述步骤里面的2
  • 结合springcloud插件,发起http请求soul网关,体验springcloud代理 一、环境准备 1、soul-bootstrap 引入网关springCloud的插件支持 在网关的 pom.xml 文件中引入如下依赖 <!--soul springCloud plugin start--...
  • com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server 出现这个错误的主要原因是因为。soul-examples里面的spring...正常启动情况下,被代理接口的访问 注册成功后
  • 3.高性能网关Soul学习之dubbo代理 本文主要目标 1.运行 soul-examples 下面的http服务 2.根据 官方文档,结合dubbo插件,发起http请求soul网关,体验http代理 dubbo代理demo 本地启动一个zk,默认端口2181 开启...
  • 结合sofa插件,发起http请求soul网关,体验sofa代理 一、启动服务: soul-admin soul-bootstrap soul-examples-sofa soul-examples-sofa 启动报如下错误,同时会影响网关测的服务调用,导致服务无法调通。 ...
  • 探寻spring mvc客户端初始化接入soul至入库的流程
  • Soul 网关开源的前世今生

    千次阅读 2020-05-27 15:03:10
    内容简介:Soul网关是我在任职某大型电商公司中间件技术部的时候所开发的。开源以后,针对不同的用户需求,进行了功能的升级,比如 支持了首先我们调研了市场上的一些API网关 本文转载自:...
  • 昨天只是极简入门,关于网关是怎么感知到我们的应用的,相信小伙伴们一定有疑问,今天先来看下 HTTP 用户如何接入 Soul,以及接入的流程是怎样的。 这是官网对于 HTTP 用户的文档,...
  • 上一节我们大概的了解了Soul网关是什么,搭建了运行环境并成功的运行了其中的soul-admin、soul-bootstrap模块。 这一节主要从以下几个方面开始学习 学习divide插件的使用 运行examples下面的http服务 使用...
  • Soul 极简入门(国产微服务网关)

    千次阅读 2020-06-21 16:57:11
    Soul是基于WebFlux实现的响应式的 API 网关,具有异步、高性能、跨语言等特点。 作者:我希望能够有一样东西像灵魂一样,保护您的微服务。在参考了 Kong、Spring Cloud Gateway等优秀的网关后,站在巨人的肩膀上,...
  • 从官网 clone 代码下来后,依次启动 soul-admin、soul-bootstrap 和 soul-examples-http 模块,启动成功后查看 soul-admin 会发现 soul-examples-http 模块里的代理配置和路由规则已经自动同步到了 soul-admin 上,...
  • (一)soul的入门使用

    千次阅读 2021-01-16 12:58:01
    (一)soul的入门使用 目标 下载soul源码和编译 启动soul-admin 接入把springboot应用,接入soul-admin 响应式编程 下载soul源码和编译 github地址:https://github.com/dromara/soul.git, git cloue ...
  • Soul网关中的divide插件

    2021-01-15 21:19:30
    今天体验的是Soul中divide插件,主要作用是用于http的代理。 请求转发 Soul官方在soul-examples模块提供了测试样例,其中的soul-examples-http模块演示的通http发起请求到soul网关,然后再到真实的服务。模块目录及...
  • 普通springboot项目接入soul网关 最近,在工作中实际经历了在springboot项目的基础上进行了soul网关的接入,那么今天记录一下具体的接入和测试过程,整个接入和测试过程还是比较简单的。 1、首先,配置并部署好soul-...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 190
精华内容 76
关键字:

soul代理端口