zoukankan      html  css  js  c++  java
  • [spring guides]网关入门

    原文

    目的

    构建一个spring cloud的网关

    实现

    简单demo

    1. 通过spring initializr来初始化一个spring cloud gateway项目,Gateway属于Routing(路由)模块。Routing里面包含了Gateway,OpenFeign,Cloud LoadBalancer项目。maven会添加spring-cloud-starter-gateway依赖。

      <dependency>
      	<groupId>org.springframework.cloud</groupId>
      	<artifactId>spring-cloud-starter-gateway</artifactId>
      </dependency>
      
      
    2. 创建一个Route,路由表。spring cloud gateway通过route来把请求转发给下游服务。这个demo中我们把所有的请求都转发给HttpBin。route的配置方法有很多种,常见的可以通过配置文件的方式或者java api的方式。这里使用java api的方式。

    3. 创建一个RouteLocator的Bean,参数是RouteLocatorBuilder,他的routes方法返回的Builder对象可以方便我们编写predicate和filter来根据条件处理请求。让我们来实现把/get转发到https://httpbin.org/get,同时在header上加入hello world。

      @Bean
      public RouteLocator myRouteLocator(RouteLocatorBuilder builder) {
          return builder.routes()
                  .route("test-route1", predicateSpec -> predicateSpec
                          .path("/get")// 处理/get的请求
                          .filters(gatewayFilterSpec -> gatewayFilterSpec.addRequestHeader("hello", "world"))// 对这个请求应用一个filter,具体就是给request的header加上hello world
                          .uri("http://httpbin.org")// 请求转发到httpbin去
                  )
                  .build();
      }
      
    4. 运行测试一下

      curl http://localhost:8080/get
      
      
      // 返回
      {
          "args": {},
          "headers": {
              "Accept": "*/*",
              "Accept-Encoding": "gzip, deflate, br",
              "Cache-Control": "no-cache",
              "Content-Length": "0",
              "Forwarded": "proto=http;host="localhost:8080";for="0:0:0:0:0:0:0:1:64073"",
              "Hello": "world",//这里是加入的hello world
              "Host": "httpbin.org",
              "User-Agent": "PostmanRuntime/7.26.5",
              "X-Amzn-Trace-Id": "Root=1-5f869ff3-0db7117c0d97563a670a294f",
              "X-Forwarded-Host": "localhost:8080"
          },
          "origin": "0:0:0:0:0:0:0:1, xxx.xxx.xxx.xxx",
          "url": "http://localhost:8080/get"
      }
      
      

      可以看到请求被转发到了httpbin,因为我们并没有写自己/get

    5. 顺道测试了一下,如果我们自己写一个get 的GetMapping,那么请求会直接返回自己定义的GetMapping,而不是走到route里面定义的转发。

    使用Hystrix

    springcloud-hystrix

    简介

    它是Netflix公司出的,2018年11月17发布了最后一个版本后,就转到了维护阶段,因为功能上已经满足他们的需求。现在他们内部新的系统使用resilience4j来实现Hystrix的功能。

    分布式系统中,服务之间存在非常多的相互依赖,当某个依赖项出现不可用的情况(这个是无法避免的)的时候,Hystrix会提供一个fallback的方法,快速返回结果(虽然是错误的),来避免错误整个系统出现一连串级联的调用报错之类的。

    顺道提一下,他通过命令模式来实现,也就是说把具体方法的执行交给了命令实现对象。

    一个简单demo

    //首先通过继承HystrixCommand<String>来定义一个返回String的Command。
    public class HelloFailureCommand extends HystrixCommand<String> {
        //一般给命令起个名字
        private String name;
    
        public HelloFailureCommand(String name) {
            super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
            this.name = name;
        }
        //正常情况下会执行run方法,这里模拟run方法出错。
        @Override
        protected String run() throws Exception {
            throw new RuntimeException("this command always fails");
        }
        //提供一个fallback,用于正常run方法出错的时候调用
        @Override
        protected String getFallback() {
            return "Hello Failure " + name + "!";
        }
    }
    
    @Test
    public void testSynchronous() {
      	//调用execute来执行run
        assertEquals("Hello Failure World!", new HelloFailureCommand("World").execute());
        assertEquals("Hello Failure Bob!", new HelloFailureCommand("Bob").execute());
    }
    

    使用

    我们来实现一个circuit breaker(断路器)来实现服务降级。当服务调用失败,返回一个自定义信息。

    Maven依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    

    httpbin中有个delay(延迟)服务,地址http://httpbin.org/delay/3 表示延迟3秒后返回数据。springcloud-gateway默认的超时时间为1秒,可以通过hystrix.command.default.execution.isolation.thread.timeoutInMillseconds来设置。如下设置成4秒。

    hystrix:
      command:
        default:
          execution:
            timeout:
              enabled: true
            isolation:
              thread:
                timeoutInMilliseconds: 4000
    

    route

    @Bean
    public RouteLocator myRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("hystrix-route", predicateSpec -> predicateSpec
                        .host("*.hystrix.com")//处理host=*.hystrix.com的请求。
                        .filters(gatewayFilterSpec -> gatewayFilterSpec
                                .hystrix(config -> {
                                            config
                                                    .setName("mycmd")
                                                    .setFallbackUri("forward:/fallback");//设置错误的之后的地址
                                        }
                                ))
                        .uri("http://httpbin.org"))
                .build();
    }
    
    

    测试

    $ curl --dump-header - --header 'Host: www.hystrix.com' http://localhost:8080/delay/4
    HTTP/1.1 200 OK
    Content-Type: text/plain;charset=UTF-8
    Content-Length: 8
    
    fallback  
    

    上面设置了delay为4秒,那么httpbin的请求时间就会超过4秒,就会触发hystrix的fallback,然后根据设置的fallbackUri,转发到/fallback地址去,返回fallback的内容。

    $ curl --dump-header - --header 'Host: www.hystrix.com' http://localhost:8080/delay/2
    HTTP/1.1 200 OK
    Date: Fri, 16 Oct 2020 06:33:25 GMT
    Content-Type: application/json
    Content-Length: 470
    Server: gunicorn/19.9.0
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Credentials: true
    
    {
      "args": {},
      "data": "",
      "files": {},
      "form": {},
      "headers": {
        "Accept": "*/*",
        "Content-Length": "0",
        "Forwarded": "proto=http;host=www.hystrix.com;for="127.0.0.1:59041"",
        "Host": "httpbin.org",
        "User-Agent": "curl/7.64.1",
        "X-Amzn-Trace-Id": "Root=1-5f893eb3-40498f08215677a430065c80",
        "X-Forwarded-Host": "www.hystrix.com"
      },
      "origin": "127.0.0.1, x.x.x.x",
      "url": "http://www.hystrix.com/delay/2"
    }
    

    测试delay为2秒,httpbin在4秒内返回了数据,gateway就可以显示httpbin的返回。

    测试

    wiremock是个nb的东西。

    添加maven依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
        <exclusions>
            <exclusion>
                <artifactId>spring-boot-starter-web</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    
    // 模拟返回的body
    class TempBody {
        private HashMap<String, String> headers;
    
        public HashMap<String, String> getHeaders() {
            return headers;
        }
        public void setHeaders(HashMap<String, String> headers) {
            this.headers = headers;
        }
    }
    
    package com.lou.demo5;
    
    import org.junit.jupiter.api.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.reactive.server.WebTestClient;
    
    import static com.github.tomakehurst.wiremock.client.WireMock.*;
    import static org.assertj.core.api.Assertions.*;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
            properties = {"httpbin=http://localhost:${wiremock.server.port}"})
    @AutoConfigureWireMock(port = 0)//随机端口
    class Demo5ApplicationTests {
    
        @Autowired
        private WebTestClient webTestClient;
    
        @Test
        void contextLoads() {
          	final TempBody getBody = new TempBody();
          	final HashMap<String, String> headers = new HashMap<>();
          	headers.put("Hello", "World");
        		getBody.setHeaders(headers);
          	//通过stub 来mock http服务
            stubFor(get(urlEqualTo("/get"))
                    .willReturn(aResponse()
                            //设置返回的body
                            .withBody(JSON.toJSONString(getBody))
                            //设置类型
                            .withHeader("Content-Type", "application/json")//
                    ));
    
            stubFor(get(urlEqualTo("/delay/3"))
                    .willReturn(aResponse()
                            .withBody("fallback")
                            .withFixedDelay(3000))
            );
    
            webTestClient
                    .get().uri("/get")
                    .exchange()
                    .expectStatus().isOk()
                    .expectBody()
                    .jsonPath("$.headers.Hello").isEqualTo("World");
            webTestClient
                    .get().uri("/delay/3")
                    .header("Host", "www.hystrix.com")
                    .exchange()
                    .expectStatus().isOk()
                    .expectBody()
                    .consumeWith(entityExchangeResult -> {
                        assertThat(entityExchangeResult.getResponseBody()).isEqualTo("fallback".getBytes());
                    });
        }
    }
    
  • 相关阅读:
    hdu 5224 Tom and paper 水题
    2015 UESTC 搜索专题N题 韩爷的梦 hash
    2015 UESTC 搜索专题M题 Palindromic String 马拉车算法
    2015 UESTC 搜索专题K题 秋实大哥の恋爱物语 kmp
    2015 UESTC 搜索专题J题 全都是秋实大哥 kmp
    2015 UESTC 搜索专题F题 Eight Puzzle 爆搜
    2015 UESTC 搜索专题E题 吴队长征婚 爆搜
    2015 UESTC 搜索专题D题 基爷的中位数 二分
    2015 UESTC 搜索专题C题 基爷与加法等式 爆搜DFS
    2015 UESTC 搜索专题B题 邱老师降临小行星 记忆化搜索
  • 原文地址:https://www.cnblogs.com/sheldon-lou/p/13838516.html
Copyright © 2011-2022 走看看