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());
                    });
        }
    }
    
  • 相关阅读:
    .net从网络接口地址获取json,然后解析成对象(一)
    .net获取本地ip地址
    .net上传文件,利用npoi读取文件信息到datatable里
    .net利用NPOI生成excel文件
    .NET获取城市信息(将三字代码转换成城市名)
    JS下拉页面时一个横幅的样式和js
    整数中1出现的次数(1~n)
    连续子数组的最大和
    最小的K个数
    数组中出现次数超过一半的数字
  • 原文地址:https://www.cnblogs.com/sheldon-lou/p/13838516.html
Copyright © 2011-2022 走看看