转载

实战分布式治理方案之SpringCloudNetflix学习篇(二)

基于之前文章 实战分布式治理方案之SpringCloudNetflix学习篇(一) ,开始升级对Spring Cloud Netflix的学习,下面即将搭建的是:

1.Fegin 分布式服务调用
2.Hyxtrix 分布式服务熔断
3.ZipKin 分布式链路追踪  
4.Config 分布式配置中心
5.Zuul 分布式API网关
复制代码

1.Fegin搭建和熔断使用

1.Fegin构建

Feign 是一个声明式的伪 Http 客户端,它使得写 Http 客户端变得更简单。使用 Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用 Feign 注解和 JAX-RS 注解。Feign 支持可插拔的编码器和解码器。Feign 默认集成了 Ribbon,并和 Eureka 结合,默认实现了负载均衡的效果

  • Feign 采用的是基于接口的注解
  • Feign 整合了 ribbon

1)创建服务消费者 在pom中引用:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
复制代码

2)在@SpringBootApplication下增加下面两个注解:

@EnableDiscoveryClient
@EnableFeignClients
复制代码

3)yml和之前Ribbon相同只需要修改端口号和服务名

4)创建服务间接口调用

package com.funtl.hello.spring.cloud.web.admin.feign.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "your-Producer")
public interface  FeginAdminService {
    //你生产者服务提供的Rest接口名称
    @RequestMapping(value = "hi", method = RequestMethod.GET)
    public String siHi(@RequestParam(value = "message") String message);
}
复制代码

5)创建需要被前端调用的Rest API

package com.funtl.hello.spring.cloud.web.admin.feign.controller;

import com.funtl.hello.spring.cloud.web.admin.feign.service.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FeginAdminController {

    @Autowired
    private  FeginAdminService  feginAdminService;

    @RequestMapping(value = "hiWebF", method = RequestMethod.GET)
    public String siHi(@RequestParam String message) {//注意注解
        return feginAdminService.sayHi(message);
    }
}
复制代码

运行结果:

实战分布式治理方案之SpringCloudNetflix学习篇(二)

2.为Fegin使用熔断机制:

简单叙述: 为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,被调用服务并不能保证 100% 可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet 容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的 “雪崩” 效应。一般在同步调用机制的治理框架中使用熔断机制,当服务访问出现不可达时,使用熔断机制会对整个项目进行保护。较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阀值(Hystrix 是 5 秒 20 次) 熔断器将会被打开。

Fegin是自带熔断机制的所以不必要导入其他依赖,不过可以增加一个Spring Cloud Netflix提供的仪表视图

1)导入Spring Cloud Netflix提供的仪表视图

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
复制代码

2)在application类中加入注解 @EnableHystrixDashboard

3)正常使用熔断步骤如下:

  1. 先在接口层FeginAdminService 修改注解,加入回调方法 @FeignClient(value = "community-service-admin", fallback = AdminServiceHystrix.class)
  2. 然后再加入回调内容
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Component
public class FeginAdminServiceHystrix implements FeginAdminService {

    @Override
    public String siHi(String message) {
        return "Hi,your message is :/"" + message + "/" but request error.";
    }
}
复制代码

3.yml设置打开熔断

feign:
  hystrix:
    enabled: true
复制代码

4)仪表使用:

/**
 * 增加仪表熔断功能,多配置了一个servlet来访问
 * 浏览器端访问 http://localhost:你的端口/hystrix
 */
@Configuration
public class HystrixDashboardConfiguration {

    @Bean
    public ServletRegistrationBean getServlet() {
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
}
复制代码

完成以下操作后:

开始调用:

  1. 先停掉生产者
  2. 开始调用

结果如下:

实战分布式治理方案之SpringCloudNetflix学习篇(二)

熔断仪表访问如下:

实战分布式治理方案之SpringCloudNetflix学习篇(二)

2.ZipKin链路跟踪

ZipKin 是一个开放源代码的分布式跟踪系统,简单来说可以帮你理清服务的调用链。 随着服务的越来越多,对调用链的分析会越来越复杂。它们之间的调用关系也许如下:

实战分布式治理方案之SpringCloudNetflix学习篇(二)

这样不言而喻ZipKin的重要性了。

1).导入依赖

<dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin</artifactId>
        </dependency>
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-server</artifactId>
        </dependency>
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-autoconfigure-ui</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
复制代码

2).application中加入两个注解:

  1. @EnableEurekaClient
  2. @EnableZipkinServer

3)yml加入:

management:
  metrics:
    web:
      server:
        auto-time-requests: false
复制代码

4)其他服务加入被追踪 在 所有需要被追踪的项目(就当前教程而言,除了 dependencies 项目外都需要被追踪,包括 Eureka Server) 中增加 spring-cloud-starter-zipkin 依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
复制代码

在这些项目的 application.yml 配置文件中增加 Zipkin Server 的地址即可

spring:
  zipkin:
    base-url: http://localhost:9411
复制代码

运行如下:

实战分布式治理方案之SpringCloudNetflix学习篇(二)

3.Spring Cloud Config配置中心

在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在 Spring Cloud 中,有分布式配置中心组件 Spring Cloud Config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程 Git 仓库中。在 Spring Cloud Config 组件中,分两个角色,一是 Config Server,二是 Config Client。

服务端配置

1)创建自己Git的配置中心仓库

实战分布式治理方案之SpringCloudNetflix学习篇(二)

2.引入配置中心依赖,依旧引入Eureka并向Eureka注册

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
复制代码

3).application启动类中增加下面注解:

  1. @EnableConfigServer
  2. @EnableEurekaClient

4)向yml文件中加入:

spring:
  cloud:
    config:
      label: master
      server:
        git:
          uri: https://github.com/topsale/your-config
          search-paths: respo 目录
          username:yourname
          password:yourpassword
复制代码

客户端配置:

1)引入Config包

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
复制代码

2)客户端使用

spring
  cloud:
    config:
      uri: http://localhost:8888    //Config服务端地址
      name: your-Feign   //要访问的文件名  your-Feign-dev.yml和上述git文件名保持一致
      label: master
      profile: dev
复制代码

然后调用服务端:

实战分布式治理方案之SpringCloudNetflix学习篇(二)

启动客户端:

实战分布式治理方案之SpringCloudNetflix学习篇(二)

5.Zuul 分布式API网关

Zuul 的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如 /api/user 转发到到 User 服务,/api/admin转发到到 admin服务。Zuul 默认和 Ribbon 结合实现了负载均衡的功能。其实完全可以理解为过滤器服务。

#创建路由网关 1)引入依赖

<!--导入zuul网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
复制代码

2)Application启动类中加入:

  1. @EnableEurekaClient
  2. @EnableZuulProxy

3)yml中配置增加:

zuul:
  routes:
    api-a:
      path: /api/a/**
      serviceId: your-Ribbon
    api-b:
      path: /api/b/**
      serviceId: your-Feign
复制代码

4)增加过滤功能:

package com.gn.community.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * Zuul 的服务过滤演示
 */
@Component
public class LoginFilter extends ZuulFilter {

    private static final Logger logger = LoggerFactory.getLogger(LoginFilter.class);

    /**
     * 配置过滤类型,有四种不同生命周期的过滤器类型
     * 1. pre:路由之前
     * 2. routing:路由之时
     * 3. post:路由之后
     * 4. error:发送错误调用
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 配置过滤的顺序
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 配置是否需要过滤:true/需要,false/不需要
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤器的具体业务代码
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        logger.info("{} >>> {}", request.getMethod(), request.getRequestURL().toString());
        //在请求入参中判断是否存在token
        String token = request.getParameter("token");
        if (token == null) {
            logger.warn("Token is empty");
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(401);
            try {
                context.getResponse().getWriter().write("Token is empty");
            } catch (IOException e) {
            }
        } else {
            logger.info("OK");
        }
        return null;
    }
}
复制代码

5)增加回调内容

package com.gn.community.provider;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * 访问失败时的回调
 */
@Component
public class WebAdminFeignFallbackProvider implements FallbackProvider {

    @Override
    public String getRoute() {
        // ServiceId,如果需要所有调用都支持回退,则 return "*" 或 return null
        return "your-feign";  //你调用的服务名
    }

    /**
     * 如果请求服务失败,则返回指定的信息给调用者
     * @param route
     * @param cause
     * @return
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            /**
             * 网关向 api 服务请求失败了,但是消费者客户端向网关发起的请求是成功的,
             * 不应该把 api 的 404,500 等问题抛给客户端
             * 网关和 api 服务集群对于客户端来说是黑盒
             * @return
             * @throws IOException
             */
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.OK.value();
            }

            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.OK.getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                ObjectMapper objectMapper = new ObjectMapper();
                Map<String, Object> map = new HashMap<>();
                map.put("status", 200);
                map.put("message", "无法连接,请检查您的网络");
                return new ByteArrayInputStream(objectMapper.writeValueAsString(map).getBytes("UTF-8"));
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                // 和 getBody 中的内容编码一致
                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
                return headers;
            }
        };
    }
}
复制代码

启动服务: 发起调用,由于过滤中增加了入参token判断,所以返回失败

实战分布式治理方案之SpringCloudNetflix学习篇(二)

加入token后访问正常。

实战分布式治理方案之SpringCloudNetflix学习篇(二)

如果关闭Fegin服务也不会出现整体崩盘的情况:

实战分布式治理方案之SpringCloudNetflix学习篇(二)

SpringCloudNetflix学习篇到此结束了。

在此继续感谢前锋教育的李老师 李老师B站地址

原文  https://juejin.im/post/5e882a67e51d454711410b25
正文到此结束
Loading...