springcloud7

服务网关是什么

  • 一般的流量入口是商品模块,假设商品模块有100个,则nginx要配置100个,为了减少配置量,有一个服务网关的概念

    • pc要访问互联网,需要网关进行路由转发,类比到springcloud,就有了服务网关的概念
    • 访问服务时,首先仍然是nginx,但是连接的不再是商品模块,而是zuul服务网关,由它来负责转发,这样可以减少nginx的配置量,此时由服务网关负责路由到100台商品模块
    • 服务网关除了zuul,还有gateway,后者源码更复杂,但是原理都是一样的
  • 服务网关可以做流量入口,所有微服务的请求必须经过网关

  • 因为所有流量都要经过网关,因此可以做权限校验

  • 服务网关除了路由到自己的微服务,还可以路由到其他的第三方应用

zuul服务网关搭建

导入jar包

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

启动类

  • package com.xiangxue.jack;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Primary;
    
    
    @SpringBootApplication
    @EnableZuulProxy
    //@EnableZuulServer
    public class MicroZuulApplication {
          
          
        public static void main(String[] args) {
          
          
            SpringApplication.run(MicroZuulApplication.class,args);
        }
    
        @Bean
        @RefreshScope
        @ConfigurationProperties("zuul")
        @Primary
        public ZuulProperties zuulProperties() {
          
          
            return new ZuulProperties();
        }
    }
    
    
    • @EnableZuulProxy注解

配置文件

  • # 必须连接Eureka,从Eureka中获取每一个服务的服务列表
    # zuul天生就有ribbon和hystrix的功能
    eureka.client.serviceUrl.defaultZone=http://admin:admin@localhost:8763/eureka/
    # 使用路径方式匹配路由规则。
    # 参数key结构: zuul.routes.customName.path=xxx
    # 用于配置路径匹配规则。
    # 其中customName自定义。通常使用要调用的服务名称,方便后期管理
    # 可使用的通配符有: * ** ?
    # ? 单个字符
    # * 任意多个字符,不包含多级路径
    # ** 任意多个字符,包含多级路径
    zuul.routes.micro-web.path=/web/**
    
    # 参数key结构: zuul.routes.customName.url=xxx
    # url用于配置符合path的请求路径路由到的服务地址。
    #zuul.routes.micro-order.url=http://localhost:8080/
    
    # key结构 : zuul.routes.customName.serviceId=xxx
    # serviceId用于配置符合path的请求路径路由到的服务名称。
    zuul.routes.micro-web.serviceId=micro-web-no
    
    #zuul.routes.micro-web1.path=/web/path/**
    #zuul.routes.micro-web1.serviceId=micro-web-no
    # ignored service id pattern
    # 配置不被zuul管理的服务列表。多个服务名称使用逗号','分隔。
    # 配置的服务将不被zuul代理。
    #zuul.ignored-services=eureka-application-service
    
    # prefix URL pattern 前缀路由匹配
    # 配置请求路径前缀,所有基于此前缀的请求都由zuul网关提供代理。
    #zuul.prefix=/api
    
    management.endpoints.web.exposure.include=*
    
    #指定全局的headers传输,对所有路由的微服务
    #zuul.sensitive-headers=Cookie,Set-Cookie,Authorization
    
    #添加host头信息,标识最初的服务端请求地址
    zuul.add-host-header=true
    
    #默认添加  X-Forwarded-*头域
    zuul.add-proxy-headers=true
    
    #路由到zuul本地的/local的接口
    zuul.routes.zuul-server.path=/local/**
    zuul.routes.zuul-server.url=forward:/local
    
    zuul.routes.blog.path=/blog/**
    zuul.routes.blog.url=http://localhost:8003/
    
    #全局关闭重试
    zuul.retryable=false
    #关闭该服务的重试
    zuul.routes.micro-web.retryable=false
    
    • zuul.routes.micro-web.path=/web/**

      zuul.routes.micro-web.serviceId=micro-web-no

      如果请求的url中开头是/web,就会把它的请求路由到这个serviceId

      • zuul.routes.micro-web.path中的micro-web随便起,关键等号后面的内容要配合起来

测试

  • 启动Eureka、micro-order-no、micro-web-no、config-server、zuul这几个项目

请求接口查看zuul所有的路由规则

  • http://localhost:7070/actuator/routes

    • #访问/web/**时,会路由到服务名为micro-web的服务
      "/web/**": "micro-web-no",
      #访问/local/**时,表示会路由到自己本身
      "/local/**": "forward:/local",
      #只会路由到一台主机
      "/blog/**": "http://localhost:8003/"
      
  • http://localhost:7070/web/user/queryUser

    • 请求到Web工程的UserController的queryUser方法

zuul的路由配置

  • # 此方式相当于给所有新发现的服务默认排除zuul网关访问方式,只有配置了路由网关的服务才可以通过zuul网关访问
    # 通配方式配置排除列表。
    zuul.ignored-services=*
    
    • 排除所有的
  • # 通配方式配置排除网关代理路径。所有符合ignored-patterns的请求路径都不被zuul网关代理。
    zuul.ignored-patterns=/**/local/**
    
  • # http://localhost:6060/actuator/hystrix.stream
    #针对某个服务传输指定的headers信息 ,默认是过滤掉 Cookie,Set-Cookie,Authorization 这三个信息的
    #这里置空就是不要过滤掉这三个
    zuul.routes.micro-web.sensitive-headers=
    
    • 在权限校验中讲到
    • 会把敏感资源过滤掉,请求过程中默认会把带了Cookie,Set-Cookie,Authorization这三种信息的过滤掉
    • 所以添加这条配置,并设置为null,就是不拦截敏感请求头

动态路由

  • 需要用到分布式配置中心

  • 把zuul中的application.properties改为application1.properties,并且把分布式配置中心的配置文件拷贝过来

    扫描二维码关注公众号,回复: 13117092 查看本文章
    • spring.application.name=api-gateway
      server.port=7070
      
      eureka.client.serviceUrl.defaultZone=http://admin:admin@localhost:8763/eureka/
      
      spring.cloud.config.profile=dev
      spring.cloud.config.label=master
      #这种配置是configserver还单机情况,直接连接这个单机服务就行
      spring.cloud.config.uri=http://localhost:8085/
      #configserver高可用配置
      #开启configserver服务发现功能
      #spring.cloud.config.discovery.enabled=true
      #服务发现的服务名称
      #spring.cloud.config.discovery.service-id=config-server
      
      #如果连接不上获取配置有问题,快速响应失败
      spring.cloud.config.fail-fast=true
      #默认重试的间隔时间,默认1000ms
      spring.cloud.config.retry.multiplier=1000
      #下一间隔时间的乘数,默认是1.1
      #spring.cloud.config.retry.initial-interval=1.1
      #最大间隔时间,最大2000ms
      spring.cloud.config.retry.max-interval=2000
      #最大重试次数,默认6次
      spring.cloud.config.retry.max-attempts=6
      
      ribbon.ConnectTimeout=1000
      ribbon.ReadTimeout=2000
      hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
      
      spring.rabbitmq.host=192.168.67.139
      spring.rabbitmq.port=5672
      spring.rabbitmq.username=admin
      spring.rabbitmq.password=admin
      
      management.endpoints.web.exposure.include=*
      
    • 这里仍然是用github作为配置文件存放地

zuul的主类需要修正

  • @Bean
    @RefreshScope
    @ConfigurationProperties("zuul")
    @Primary
    public ZuulProperties zuulProperties() {
          
          
        return new ZuulProperties();
    }
    
    • 增加@RefreshScope的注解,这个注解的里面又有一个@Scope(“refresh”)的注解,跟之前自定义的zookper实现的动态配置是相同的处理
      • 这个注解起到标识作用,标识这个ZuulProperties可能会动态刷新,不会在一级缓存中管理,而是在自己定义的缓存中管理

zuul中的过滤器

  • 有四种过滤器
    • pre:前置
    • post:后置,路由过滤器做完了,下游系统把值返回以后调用
    • error:调用其余3种过滤器有异常了,就会进入error的过滤器
    • route:路由,很少用,route过滤器是负责做路由的,自己写路由规则,但是因为zuul中已经把路由写得比较完善了,没有什么好补充的

过滤器的调用顺序

  • 请求来到zuul,会有过滤器,先调pre的过滤器,再调route的过滤器,而路由过滤器是做后端服务调用的,下游系统会把结果返回给route的过滤器,再调用post的过滤器

自定义过滤器

前置过滤器

  • package com.xiangxue.jack.filter;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Slf4j
    //@Component
    public class AccessFilter extends ZuulFilter {
          
          
        @Override
        public String filterType() {
          
          
            // 标明是前置过滤器
            return FilterConstants.PRE_TYPE;
        }
    
        @Override
        public int filterOrder() {
          
          
            // 优先级,值越小越优先执行
            return 0;
        }
    
        @Override
        public boolean shouldFilter() {
          
          
            // 判断是否调用该过滤器
            // 拿到当前上下文对象,从中再获取sendZuulResponse的值,这个值默认是true
            return RequestContext.getCurrentContext().sendZuulResponse();
        }
    
        @Override
        public Object run() throws ZuulException {
          
          
            //获取上下文
            RequestContext ctx = RequestContext.getCurrentContext();
            //获取Request
            HttpServletRequest request = ctx.getRequest();
            //获取请求参数accessToken
            String accessToken = request.getParameter("accessToken");
            //使用String工具类
            if (StringUtils.isBlank(accessToken)) {
          
          
                log.warn("accessToken is empty");
                //设置为false不进行路由
                ctx.setSendZuulResponse(false);  //进行拦截
                ctx.setResponseStatusCode(401);
                try {
          
          
                    ctx.getResponse().getWriter().write("accessToken is empty");
                } catch (Exception e) {
          
          
                }
                return null;
            }
            log.info("access is ok");
            return null;
        }
    }
    
    
  • package com.xiangxue.jack.filter;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Slf4j
    //@Component
    public class PreLogFilter extends ZuulFilter {
          
          
    
        /*
        * pre: 这种过滤器在请求被路由之前调用。可利用这种过滤器实现身份验证、在集群中选择请求的微服务,记录调试信息等。
          routing: 这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用apache httpclient或netflix ribbon请求微服务。
          post: 这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的http header、收集统计信息和指标、将响应从微服务发送给客户端等。
          error: 在其他阶段发送错误时执行该过滤器。
        *
        * */
        @Override
        public String filterType() {
          
          
            return FilterConstants.PRE_TYPE;
        }
    
        @Override
        public int filterOrder() {
          
          
            return 1;
        }
    
        @Override
        public boolean shouldFilter() {
          
          
            return RequestContext.getCurrentContext().sendZuulResponse();
        }
    
        @Override
        public Object run() throws ZuulException {
          
          
            RequestContext currentContext = RequestContext.getCurrentContext();
            HttpServletRequest request = currentContext.getRequest();
            log.info("zuul pre filter-->" + request.getRequestURL() + "-->" + request.getMethod());
            return null;
        }
    }
    
    
  • 为了打印日志

路由过滤器

  • package com.xiangxue.jack.filter;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import com.netflix.zuul.util.HTTPRequestUtils;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.Header;
    import org.apache.http.HttpHost;
    import org.apache.http.HttpRequest;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.config.CookieSpecs;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPatch;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.client.methods.HttpPut;
    import org.apache.http.conn.HttpClientConnectionManager;
    import org.apache.http.entity.ContentType;
    import org.apache.http.entity.InputStreamEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClientBuilder;
    import org.apache.http.message.BasicHeader;
    import org.apache.http.message.BasicHttpEntityEnclosingRequest;
    import org.apache.http.message.BasicHttpRequest;
    import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
    import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
    import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientConnectionManagerFactory;
    import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientFactory;
    import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
    import org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException;
    import org.springframework.http.HttpHeaders;
    import org.springframework.util.LinkedMultiValueMap;
    import org.springframework.util.MultiValueMap;
    import org.springframework.web.util.UriTemplate;
    import org.springframework.web.util.UriUtils;
    import org.springframework.web.util.WebUtils;
    
    import javax.annotation.PostConstruct;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.*;
    import java.util.concurrent.TimeUnit;
    import java.util.regex.Pattern;
    
    import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.REQUEST_ENTITY_KEY;
    import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.REQUEST_URI_KEY;
    
    @Slf4j
    //@Component
    public class RoutingFilter extends ZuulFilter {
          
          
    
        private ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
    
        private ApacheHttpClientFactory httpClientFactory;
    
        private CloseableHttpClient httpClient;
    
        private HttpClientConnectionManager connectionManager;
    
        private boolean urlDecoded = true;
    
        private boolean addHostHeader = false;
    
        public static final String IGNORED_HEADERS = "ignoredHeaders";
    
        public static final Pattern FORM_FEED_PATTERN = Pattern.compile("\f");
    
        public static final Pattern COLON_PATTERN = Pattern.compile(":");
    
        @Override
        public String filterType() {
          
          
            return FilterConstants.ROUTE_TYPE;
        }
    
        @Override
        public int filterOrder() {
          
          
            return 0;
        }
    
        @PostConstruct
        public void init() {
          
          
            connectionManagerFactory = new DefaultApacheHttpClientConnectionManagerFactory();
            this.connectionManager = newConnectionManager();
            httpClientFactory = new DefaultApacheHttpClientFactory(HttpClientBuilder.create());
            httpClient = newClient();
        }
    
        protected HttpClientConnectionManager newConnectionManager() {
          
          
            return connectionManagerFactory.newConnectionManager(
                    false,
                    200,
                    20,
                    -1, TimeUnit.MILLISECONDS,
                    null);
        }
    
        @Override
        public boolean shouldFilter() {
          
          
            return RequestContext.getCurrentContext().sendZuulResponse();
        }
    
        protected CloseableHttpClient newClient() {
          
          
            final RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectionRequestTimeout(
                            -1)
                    .setSocketTimeout(10000)
                    .setConnectTimeout(2000)
                    .setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
            return httpClientFactory.createBuilder().setDefaultRequestConfig(requestConfig)
                    .setConnectionManager(this.connectionManager).disableRedirectHandling()
                    .build();
        }
    
        public String buildZuulRequestURI(HttpServletRequest request) {
          
          
            RequestContext context = RequestContext.getCurrentContext();
            String uri = request.getRequestURI();
            String contextURI = (String) context.get(REQUEST_URI_KEY);
            if (contextURI != null) {
          
          
                try {
          
          
                    uri = contextURI;
                    if (this.urlDecoded) {
          
          
                        uri = UriUtils.encodePath(contextURI, characterEncoding(request));
                    }
                } catch (Exception e) {
          
          
                    log.debug(
                            "unable to encode uri path from context, falling back to uri from request",
                            e);
                }
            }
            return uri;
        }
    
        private String characterEncoding(HttpServletRequest request) {
          
          
            return request.getCharacterEncoding() != null ? request.getCharacterEncoding()
                    : WebUtils.DEFAULT_CHARACTER_ENCODING;
        }
    
        private String getVerb(HttpServletRequest request) {
          
          
            String sMethod = request.getMethod();
            return sMethod.toUpperCase();
        }
    
        public boolean isIncludedHeader(String headerName) {
          
          
            String name = headerName.toLowerCase();
            RequestContext ctx = RequestContext.getCurrentContext();
            if (ctx.containsKey(IGNORED_HEADERS)) {
          
          
                Object object = ctx.get(IGNORED_HEADERS);
                if (object instanceof Collection && ((Collection<?>) object).contains(name)) {
          
          
                    return false;
                }
            }
            switch (name) {
          
          
                case "host":
                    if (addHostHeader) {
          
          
                        return true;
                    }
                case "connection":
                case "content-length":
                case "server":
                case "transfer-encoding":
                case "x-application-context":
                    return false;
                default:
                    return true;
            }
        }
    
        public MultiValueMap<String, String> buildZuulRequestHeaders(
                HttpServletRequest request) {
          
          
            RequestContext context = RequestContext.getCurrentContext();
            MultiValueMap<String, String> headers = new HttpHeaders();
            Enumeration<String> headerNames = request.getHeaderNames();
            if (headerNames != null) {
          
          
                while (headerNames.hasMoreElements()) {
          
          
                    String name = headerNames.nextElement();
                    if (isIncludedHeader(name)) {
          
          
                        Enumeration<String> values = request.getHeaders(name);
                        while (values.hasMoreElements()) {
          
          
                            String value = values.nextElement();
                            headers.add(name, value);
                        }
                    }
                }
            }
            Map<String, String> zuulRequestHeaders = context.getZuulRequestHeaders();
            for (String header : zuulRequestHeaders.keySet()) {
          
          
                if (isIncludedHeader(header)) {
          
          
                    headers.set(header, zuulRequestHeaders.get(header));
                }
            }
            if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
          
          
                headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
            }
            return headers;
        }
    
        public MultiValueMap<String, String> buildZuulRequestQueryParams(
                HttpServletRequest request) {
          
          
            Map<String, List<String>> map = HTTPRequestUtils.getInstance().getQueryParams();
            MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
            if (map == null) {
          
          
                return params;
            }
            for (String key : map.keySet()) {
          
          
                for (String value : map.get(key)) {
          
          
                    params.add(key, value);
                }
            }
            return params;
        }
    
        protected InputStream getRequestBody(HttpServletRequest request) {
          
          
            InputStream requestEntity = null;
            try {
          
          
                requestEntity = (InputStream) RequestContext.getCurrentContext()
                        .get(REQUEST_ENTITY_KEY);
                if (requestEntity == null) {
          
          
                    requestEntity = request.getInputStream();
                }
            } catch (IOException ex) {
          
          
                log.error("error during getRequestBody", ex);
            }
            return requestEntity;
        }
    
        @Override
        public Object run() throws ZuulException {
          
          
            RequestContext currentContext = RequestContext.getCurrentContext();
            StringBuilder sb = currentContext.getFilterExecutionSummary();
            HttpServletRequest request = currentContext.getRequest();
            MultiValueMap<String, String> headers = this
                    .buildZuulRequestHeaders(request);
            MultiValueMap<String, String> params = this
                    .buildZuulRequestQueryParams(request);
            String verb = getVerb(request);
            InputStream requestEntity = getRequestBody(request);
            String uri = this.buildZuulRequestURI(request);
            try {
          
          
                // 跳进这个方法看
                CloseableHttpResponse response = forward(this.httpClient, verb, uri, request,
                        headers, params, requestEntity);
                setResponse(response);
            } catch (Exception ex) {
          
          
                throw new ZuulRuntimeException(ex);
            }
            return null;
        }
    
        private void setResponse(HttpResponse response) throws IOException {
          
          
            RequestContext.getCurrentContext().set("zuulResponse", response);
            this.setResponse(response.getStatusLine().getStatusCode(),
                    response.getEntity() == null ? null : response.getEntity().getContent(),
                    revertHeaders(response.getAllHeaders()));
        }
    
        private MultiValueMap<String, String> revertHeaders(Header[] headers) {
          
          
            MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
            for (Header header : headers) {
          
          
                String name = header.getName();
                if (!map.containsKey(name)) {
          
          
                    map.put(name, new ArrayList<String>());
                }
                map.get(name).add(header.getValue());
            }
            return map;
        }
    
        public void setResponse(int status, InputStream entity,
                                MultiValueMap<String, String> headers) throws IOException {
          
          
            RequestContext context = RequestContext.getCurrentContext();
            context.setResponseStatusCode(status);
            if (entity != null) {
          
          
                context.setResponseDataStream(entity);
            }
    
            boolean isOriginResponseGzipped = false;
            for (Map.Entry<String, List<String>> header : headers.entrySet()) {
          
          
                String name = header.getKey();
                for (String value : header.getValue()) {
          
          
                    context.addOriginResponseHeader(name, value);
    
                    if (name.equalsIgnoreCase(HttpHeaders.CONTENT_ENCODING)
                            && HTTPRequestUtils.getInstance().isGzipped(value)) {
          
          
                        isOriginResponseGzipped = true;
                    }
                    if (name.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) {
          
          
                        context.setOriginContentLength(value);
                    }
                    if (isIncludedHeader(name)) {
          
          
                        context.addZuulResponseHeader(name, value);
                    }
                }
            }
            context.setResponseGZipped(isOriginResponseGzipped);
        }
    
        private HttpHost getHttpHost() {
          
          
            HttpHost httpHost = new HttpHost("127.0.0.1", 8083);
            return httpHost;
        }
    
        
        private CloseableHttpResponse forward(CloseableHttpClient httpclient, String verb,
                                              String uri, HttpServletRequest request, MultiValueMap<String, String> headers,
                                              MultiValueMap<String, String> params, InputStream requestEntity)
                throws Exception {
          
          
    
            ContentType contentType = null;
    //        URL host = RequestContext.getCurrentContext().getRouteHost();
            HttpHost httpHost = getHttpHost();
            if (request.getContentType() != null) {
          
          
                contentType = ContentType.parse(request.getContentType());
            }
    
            InputStreamEntity entity = new InputStreamEntity(requestEntity, request.getContentLength(),
                    contentType);
    
            HttpRequest httpRequest = buildHttpRequest(verb, uri, entity, headers, params,
                    request);
            try {
          
          
                log.debug(httpHost.getHostName() + " " + httpHost.getPort() + " "
                        + httpHost.getSchemeName());
                // 把这个请求请求到具体的服务去
                CloseableHttpResponse zuulResponse = forwardRequest(httpclient, httpHost,
                        httpRequest);
                return zuulResponse;
            } finally {
          
          
    
            }
        }
    
        private CloseableHttpResponse forwardRequest(CloseableHttpClient httpclient,
                                                     HttpHost httpHost, HttpRequest httpRequest) throws IOException {
          
          
            return httpclient.execute(httpHost, httpRequest);
        }
    
        private String getEncodedQueryString(HttpServletRequest request) {
          
          
            String query = request.getQueryString();
            return (query != null) ? "?" + query : "";
        }
    
        public String getQueryString(MultiValueMap<String, String> params) {
          
          
            if (params.isEmpty()) {
          
          
                return "";
            }
            StringBuilder query = new StringBuilder();
            Map<String, Object> singles = new HashMap<>();
            for (String param : params.keySet()) {
          
          
                int i = 0;
                for (String value : params.get(param)) {
          
          
                    query.append("&");
                    query.append(param);
                    if (!"".equals(value)) {
          
           // don't add =, if original is ?wsdl, output is
                        // not ?wsdl=
                        String key = param;
                        // if form feed is already part of param name double
                        // since form feed is used as the colon replacement below
                        if (key.contains("\f")) {
          
          
                            key = (FORM_FEED_PATTERN.matcher(key).replaceAll("\f\f"));
                        }
                        // colon is special to UriTemplate
                        if (key.contains(":")) {
          
          
                            key = COLON_PATTERN.matcher(key).replaceAll("\f");
                        }
                        key = key + i;
                        singles.put(key, value);
                        query.append("={");
                        query.append(key);
                        query.append("}");
                    }
                    i++;
                }
            }
    
            UriTemplate template = new UriTemplate("?" + query.toString().substring(1));
            return template.expand(singles).toString();
        }
    
        protected HttpRequest buildHttpRequest(String verb, String uri,
                                               InputStreamEntity entity, MultiValueMap<String, String> headers,
                                               MultiValueMap<String, String> params, HttpServletRequest request) {
          
          
            HttpRequest httpRequest;
            String uriWithQueryString = uri + (false
                    ? getEncodedQueryString(request) : this.getQueryString(params));
    
            switch (verb.toUpperCase()) {
          
          
                case "POST":
                    HttpPost httpPost = new HttpPost(uriWithQueryString);
                    httpRequest = httpPost;
                    httpPost.setEntity(entity);
                    break;
                case "PUT":
                    HttpPut httpPut = new HttpPut(uriWithQueryString);
                    httpRequest = httpPut;
                    httpPut.setEntity(entity);
                    break;
                case "PATCH":
                    HttpPatch httpPatch = new HttpPatch(uriWithQueryString);
                    httpRequest = httpPatch;
                    httpPatch.setEntity(entity);
                    break;
                case "DELETE":
                    BasicHttpEntityEnclosingRequest entityRequest = new BasicHttpEntityEnclosingRequest(
                            verb, uriWithQueryString);
                    httpRequest = entityRequest;
                    entityRequest.setEntity(entity);
                    break;
                default:
                    httpRequest = new BasicHttpRequest(verb, uriWithQueryString);
                    log.debug(uriWithQueryString);
            }
    
            httpRequest.setHeaders(convertHeaders(headers));
            return httpRequest;
        }
    
        private Header[] convertHeaders(MultiValueMap<String, String> headers) {
          
          
            List<Header> list = new ArrayList<>();
            for (String name : headers.keySet()) {
          
          
                for (String value : headers.get(name)) {
          
          
                    list.add(new BasicHeader(name, value));
                }
            }
            return list.toArray(new BasicHeader[0]);
        }
    }
    
    

路由规则配置拓展—正则表达式

  • package com.xiangxue.jack.routeMapper;
    
    import org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapper;
    
    //@Configuration
    public class PatternServiceRouteMapperConfig {
          
          
    
    //    @Bean
        public PatternServiceRouteMapper patternServiceRouteMapper() {
          
          
    
            /*
             * servicePattern 指定微服务的正则
             * routePattern  指定路由正则
             * */
            return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)",
                    "${version}/${name}");
        }
    }
    
    
    • 除非服务很多,不然没必要用这种规则

敏感头测试

  • 注释掉"zuul.routes.micro-web.sensitive-headers="这条配置

  • 调用http://localhost:7070/web/user/printInfo

    • 此时打印头信息,但是里面没有cookie和权限信息

    • 利用postman在这个请求方法里面添加cookie信息,但是打印头信息里面没有打印出cookie信息

micro-admin工程

  • 当实例变得很多后,可以方便管理,只有五六台实例用处并不大

  • localhost:8282/login

    • 账号和密码都是amin
  • 监控整个系统的状态和实例个数

  • sprint boot admin项目

    • 应用墙
      • 随便点击一个应用进入分为
      • Insights
        • 细节,一些监控属性,像mysql,eureka,进程,线程,垃圾回收情况
        • 性能,一些具体的指标
        • 环境,配置的变量值
        • 配置属性,在工程内涉及到的属性
        • 计划任务
      • 日志配置
      • JVM
        • java管理扩展
        • 线程转储
        • 内存转储
      • 映射
        • 请求url相关信息
      • 缓存
    • 应用
      • 每个应用下面会有多少个正在运行的实例
    • 日志报表

搭建

jar包导入

  • <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-server</artifactId>
        <version>LATEST</version>
    </dependency>
    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-server-ui</artifactId>
        <version>LATEST</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    

其余要被admin管理,客户端必须配置的jar包

  • <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

启动类

  • package com.xiangxue.jack;
    
    import de.codecentric.boot.admin.server.config.EnableAdminServer;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableAdminServer
    public class MicroAdminApplication {
          
          
        public static void main(String[] args) {
          
          
            SpringApplication.run(MicroAdminApplication.class,args);
        }
    }
    
    • @EnableAdminServer

登陆账号密码配置

  • package com.xiangxue.jack;
    
    import de.codecentric.boot.admin.server.config.AdminServerProperties;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
    
    @Configuration
    public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
          
          
    
        private final String adminContextPath;
    
        public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
          
          
            this.adminContextPath = adminServerProperties.getContextPath();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
          
          
            SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
            successHandler.setTargetUrlParameter("redirectTo");
            http.authorizeRequests()
                    .antMatchers(adminContextPath + "/assets/**").permitAll()
                    .antMatchers(adminContextPath + "/login").permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()
                    .logout().logoutUrl(adminContextPath + "/logout").and()
                    .httpBasic().and()
                    .csrf().disable();
        }
    }
    
    

配置文件

  • spring.application.name=micro-admin
    server.port=8282
    #是否注册到eureka
    eureka.client.registerWithEureka=true
    #是否从eureka中拉取注册信息
    eureka.client.fetchRegistry=true
    eureka.client.serviceUrl.defaultZone=http://admin:admin@localhost:8763/eureka/
    
    # 安全配置
    spring.security.user.name=admin
    spring.security.user.password=admin
    eureka.instance.metadata-map.user.name=${spring.security.user.name}
    eureka.instance.metadata-map.user.password=${spring.security.user.password}
    

猜你喜欢

转载自blog.csdn.net/Markland_l/article/details/115410868
今日推荐