Spring Cloud 入门手册(三)

简介: Spring Cloud 入门手册(三)

Spring Cloud 入门手册(三)


application.yml

  • zuul 路由配置可以省略,缺省以服务 id 作为访问路径
server:
  port: 3001
spring:
  application:
    name: zuul
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
  instance:
    prefer-ip-address: true
    # 界面列表中显示的格式也显示ip
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
zuul:
  routes:
    item-service: /item-service/**
    user-service: /user-service/**
    order-service: /order-service/**

主程序

添加 @EnableZuulProxy 注解

package cn.tedu.sp06;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
/**
 * @ClassName Sp06ZuulApplication
 * @Description
 * @Author keke
 * @Time 2021/7/18 20:39
 * @Version 1.0
 */
@SpringBootApplication
@EnableZuulProxy
public class Sp06ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(Sp06ZuulApplication.class, args);
    }
}

启动服务,访问测试


http://eureka1:2001


http://localhost:3001/item-service/35


http://localhost:3001/item-service/decreaseNumber


使用 postman,POST 发送以下格式数据

[
    {
        "id": 1,
        "name": "abc",
        "number": 23
    },
    {
        "id": 2,
        "name": "def",
        "number": 11
    }
]

http://localhost:3001/user-service/7

http://localhost:3001/user-service/7/score?score=100

http://localhost:3001/order-service/123abc

http://localhost:3001/order-service/

配置 zuul 开启重试,并配置 ribbon 重试参数

  • 需要开启重试,默认不开启
zuul:
  routes:
    item-service: /item-service/**
    user-service: /user-service/**
    order-service: /order-service/**
  # 启用重试
  retryable: true
ribbon:
  ConnectTimeout: 1000
  ReadTimeout: 1000
  MaxAutoRetriesNextServer: 1
  MaxAutoRetries: 1
# 针对一个指定的后台服务,来配置重试参数
item-service:
  ribbon:
    MaxAutoRetries: 0
# 暴露actuator的所有监控数据
management:
  endpoints:
    web:
      exposure:
        include: "*"

zuul 降级

创建降级类

  • getRoute()方法中指定应用降级类的服务 id,星号或 null 值可以通配所有服务
ItemFallback
package cn.tedu.sp06.fallback;
import cn.tedu.sp01.web.util.JsonResult;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
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.nio.charset.StandardCharsets;
/**
 * @ClassName ItemFallback
 * @Description 通过zuul网关,调用后台商品服务失败时,会执行这段降级代码,向客户端返回降级结果
 * @Author keke
 * @Time 2021/7/19 11:25
 * @Version 1.0
 */
@Component
public class ItemFallback implements FallbackProvider {
    /**
     * 设置当前降级类,是针对哪个服务的降级类
     * - item-service:只针对商品服务降级
     * - *: 针对所有服务都应用当前降级类
     * - null: 针对所有服务都应用当前降级类
     * @return
     */
    @Override
    public String getRoute() {
        return "item-service";
    }
    /**
     * 发送给客户端的降级结果,封装在response对象中
     * @param route
     * @param cause
     * @return
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR;
            }
            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.value();
            }
            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
            }
            /**
             * 用来关闭流
             * ByteArrayInputStream不需要关闭
             */
            @Override
            public void close() {
            }
            @Override
            public InputStream getBody() throws IOException {
                String json = JsonResult.err().code(500)
                    .msg("调用商品服务失败").toString();
                return new 
                    ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
            }
            @Override
            public HttpHeaders getHeaders() {
                // Content-Type:application/json;charset=UTF-8
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.add("Content-Type", "application/json;charset=UTF-8");
                return httpHeaders;
            }
        };
    }
}

OrderFallback

package cn.tedu.sp06.fallback;
import cn.tedu.sp01.web.util.JsonResult;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
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.nio.charset.StandardCharsets;
/**
 * @ClassName OrderFallback
 * @Description 通过zuul网关,调用后台商品服务失败时,会执行这段降级代码,向客户端返回降级结果
 * @Author keke
 * @Time 2021/7/19 11:25
 * @Version 1.0
 */
@Component
public class OrderFallback implements FallbackProvider {
    /**
     * 设置当前降级类,是针对哪个服务的降级类
     * - order-service:只针对订单服务降级
     * - *: 针对所有服务都应用当前降级类
     * - null: 针对所有服务都应用当前降级类
     * @return
     */
    @Override
    public String getRoute() {
        return "order-service";
    }
    /**
     * 发送给客户端的降级结果,封装在response对象中
     * @param route
     * @param cause
     * @return
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR;
            }
            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.value();
            }
            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
            }
            /**
             * 用来关闭流
             * ByteArrayInputStream不需要关闭
             */
            @Override
            public void close() {
            }
            @Override
            public InputStream getBody() throws IOException {
                String json = JsonResult.err().code(500)
                    .msg("调用订单服务失败").toString();
                return new 
                    ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
            }
            @Override
            public HttpHeaders getHeaders() {
                // Content-Type:application/json;charset=UTF-8
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.add("Content-Type", "application/json;charset=UTF-8");
                return httpHeaders;
            }
        };
    }
}

LoginFilter

package cn.tedu.sp06.filter;
import cn.tedu.sp01.web.util.JsonResult;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
 * @ClassName LoginFilter
 * @Description
 * @Author keke
 * @Time 2021/7/18 21:19
 * @Version 1.0
 */
@Component
public class LoginFilter extends ZuulFilter {
    /**
     * 过滤器类型: pre, routing, post, error
     * @return
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
    /**
     * 位置顺序号,zuul的前置过滤器默认有5个
     * 其中第5个过滤器中,在上下文对象中放入了serviceId
     * 在后面过滤器中,才能使用serviceId
     * @return
     */
    @Override
    public int filterOrder() {
        return 6;
    }
    /**
     * 判断针对当前请求,是否要执行过滤代码
     * @return
     */
    @Override
    public boolean shouldFilter() {
        /*
            调用item-service,需要检查权限
            调用其他服务不判断权限,可以直接访问
         */
        // 获取当前请求调用的服务id
        RequestContext currentContext = RequestContext.getCurrentContext();
        String serviceId = (String) currentContext.get(FilterConstants.SERVICE_ID_KEY);
        // 判断id是不是item-service
        return "item-service".equalsIgnoreCase(serviceId);
    }
    /**
     * 过滤代码
     * @return
     * @throws com.netflix.zuul.exception.ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        // 得到request对象
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        // 用request接收token参数
        String token = request.getParameter("token");
        // 如果收不到token,阻止向后台转发,直接向客户端返回响应
        if (StringUtils.isBlank(token)) {
            // 阻止向后台服务转发
            currentContext.setSendZuulResponse(false);
            // 向客户端直接发送响应
            String json = JsonResult.err()
                    .code(JsonResult.NOT_LOGIN)
                    .msg("Not login!")
                    .toString();
            // http 协议头属性 Content-Type:application/json;charset=UTF-8
            currentContext.addZuulRequestHeader("Content-Type",
                    "application/json;charset=UTF-8");
            currentContext.setResponseBody(json);
        }
        // 在当前zuul版本中,没有使用这个返回值
        return null;
    }
}

启动服务,访问测试

  • http://localhost:3001/item-service/35

  • http://localhost:3001/actuator/hystrix.stream

Hystrix dashboard 断路器仪表盘

Hystrix 对请求的降级和熔断,可以产生监控信息,hystrix dashboard 可以实时的进行监控

新建 maven 项目

配置依赖 pom.xml

配置 application.yml

主程序启用 eureka 服务器

启动,访问测试

新建 maven 项目

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>order-parent</artifactId>
        <groupId>cn.tedu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>sp07-hystrix-dashboard</artifactId>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
    </dependencies>
</project>

application.yml

server:
  port: 4001
spring:
  application:
    name: hystrix-dashboard
# 允许抓取日志的服务器列表
hystrix:
  dashboard:
    proxy-stream-allow-list: localhost
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
  instance:
    prefer-ip-address: true
    # 界面列表中显示的格式也显示ip
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}

主程序

  • 添加 @EnableHystrixDashBoard
package cn.tedu.sp07;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
/**
 * @ClassName Sp07HystrixDashboardApplication
 * @Description
 * @Author keke
 * @Time 2021/7/19 14:29
 * @Version 1.0
 */
@SpringBootApplication
@EnableHystrixDashboard
public class Sp07HystrixDashboardApplication {
    public static void main(String[] args) {
        SpringApplication.run(Sp07HystrixDashboardApplication.class, args);
    }
}

启动服务,访问测试

填入 hystrix 的监控端点,开启监控

  • http://localhost:3001/actuator/hystrix.stream

通过 hystrix 访问服务多次,观察监控信息

http://localhost:3001/item-service/35?token=2wasa2f3

http://localhost:3001/user-service/7

http://localhost:3001/user-service/7/score?score=100

http://localhost:3001/order-service/123abc

http://localhost:3001/order-service

hystrix 熔断

整个链路达到一定的阈值,默认情况下,10秒内产生超过20次请求,则符合第一个条件。


满足第一个条件的情况下,如果请求的错误百分比大于阈值,则打开断路器,默认为50%。


hystrix 的逻辑,先判断是否满足第一个条件,再判断第二个条件,如果两个条件都满足,则会开启断路器,断路器打开5秒后,会处于半开状态,会尝试转发请求,如果仍然失败,保持打开状态,如果成功,则关闭断路器


使用 Apache 的并发访问测试工具 ab

http://httpd.apache.org/docs/current/platform/windows.html#down

  • 用 ab 工具,以并发50次,来发送20000个请求
ab -n 20000 -c 50 http://localhost:3001/item-service/35?token=2sswbbbbw
  • 断路器为 open,所有请求都会短路,直接降级执行 fallback 方法

hystrix 配置

http://github.com/Netflix/Hystrix/wiki/Configuration

  • hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds

请求超时时间,超时后触发失败降级

hystrix.command.default.circuitBreaker.requestVolumeThreshold

10秒内请求数量,默认20,如果没有达到该数量,即使请求全部失败,也不会触发断路器打开

hystrix.command.default.circuitBreaker.errorThresoldPercentage

失败请求百分比,达到该比例则触发断路器打开

hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds

断路器打开多长时间后,再次允许尝试访问(半开),仍失败则继续保持打开状态,如成功访问则关闭断路器,默认5000

hystrix + turbine 集群聚合监控

hystrix dashboard 一次只能监控一个服务实例,使用 turbine 可以汇集监控信息,将聚合后的信息提供给 hystrix dashboard 来集中展示和监控

新建 maven 项目

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>order-parent</artifactId>
        <groupId>cn.tedu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>sp08-turbine</artifactId>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
        </dependency>
    </dependencies>
</project>

application.yml

spring:
  application:
    name: turbine
server:
  port: 5001
eureka:
  instance:
    prefer-ip-address: true
    # 界面列表中显示的格式也显示ip
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
# 从zuul的两台服务器聚合hystrix日志
turbine:
  app-config: zuul
  cluster-name-expression: new String("default")

主程序

添加 @EnableTurbine 注解

package cn.tedu.sp08;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
/**
 * @ClassName Sp08TurbineApplication
 * @Description
 * @Author keke
 * @Time 2021/7/19 22:25
 * @Version 1.0
 */
@EnableTurbine
@SpringBootApplication
public class Sp08TurbineApplication {
    public static void main(String[] args) {
        SpringApplication.run(Sp08TurbineApplication.class, args);
    }
}

启动服务,访问测试

8201服务器产生监控数据:

http://localhost:8201/abc123

http://localhost:8201/


turbine 监控路径

http://localhost:5001/turbine.stream


在 hystrix dashboard 中填入turbine 监控路径,开启监控

http://localhost:4001/hystrix


turbine聚合了order-service两台服务器的hystrix监控信息

config 配置中心

yml 配置文件保存到 git 服务器,例如 github.com或者gitee.com


微服务启动时,从服务器获取配置文件


git 上存放配置文件

新建文件夹,命名为 config

将 sp02,sp03,sp04 三个项目的yml配置文件,复制到 config 文件夹,并改名

item-service-dev.yml

user-service-dev.yml

order-service-dev.yml

最后注释掉三个项目中的 application.yml 文件

禁止配置中心的配置信息覆盖客户端配置

默认配置中心配置优先级高,配置中心配置会覆盖客户端的所有配置,包括命令行的参数配置,这样我们在 item-service 中配置的端口号启动参数会无效

item-service 启动参数

--server.port=8081

--server.port=8082

我们可以设置禁止配置中心的配置将客户端配置覆盖掉

在三个配置文件中添加下面的配置

spring:
  # 让配置中心的配置,不覆盖项目的本地配置和命令参数
  cloud:
    config:
      override-none: true

将项目上传到 git

新建 maven 项目

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>order-parent</artifactId>
        <groupId>cn.tedu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>sp09-config</artifactId>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
</project>

application.yml

server:
  port: 6001
spring:
  application:
    name: config-server
  # 连接git仓库,在指定目录下找到配置文件
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/Jasonakeke/CGBVProjects
          search-paths: codes/springcloud1/config
eureka:
  instance:
    prefer-ip-address: true
    # 界面列表中显示的格式也显示ip
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
management:
  endpoints:
    web:
      exposure:
        include: bus-refresh

主程序

添加 @EnableConfigServer 注解

package cn.tedu.sp09;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
/**
 * @ClassName Sp09ConfigApplication
 * @Description
 * @Author keke
 * @Time 2021/7/20 13:41
 * @Version 1.0
 */
@EnableConfigServer
@SpringBootApplication
public class Sp09ConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(Sp09ConfigApplication.class, args);
    }
}

sp02,sp03,sp04 的 pom.xml 添加依赖

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

启动服务,访问测试

先启动 sp05-eureka,再启动 sp09-config,最后启动 sp02-itemservice, sp03-userservice, sp04-orderservice

目录
相关文章
|
11天前
|
存储 安全 Java
Spring Security 入门
Spring Security 是 Spring 框架中的安全模块,提供强大的认证和授权功能,支持防止常见攻击(如 CSRF 和会话固定攻击)。它通过过滤器链拦截请求,核心概念包括认证、授权和自定义过滤器。配置方面,涉及密码加密、用户信息服务、认证提供者及过滤器链设置。示例代码展示了如何配置登录、注销、CSRF防护等。常见问题包括循环重定向、静态资源被拦截和登录失败未返回错误信息,解决方法需确保路径正确和添加错误提示逻辑。
Spring Security 入门
|
12天前
|
SpringCloudAlibaba Dubbo Java
【SpringCloud Alibaba系列】Dubbo基础入门篇
Dubbo是一款高性能、轻量级的开源Java RPC框架,提供面向接口代理的高性能RPC调用、智能负载均衡、服务自动注册和发现、运行期流量调度、可视化服务治理和运维等功能。
【SpringCloud Alibaba系列】Dubbo基础入门篇
|
1月前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
58 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
5月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
1月前
|
消息中间件 监控 Java
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
36 6
|
1月前
|
Java 关系型数据库 MySQL
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
58 5
|
1月前
|
缓存 监控 Java
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
42 5
|
1月前
|
Java 数据库连接 数据库
从入门到精通---深入剖析Spring DAO
在Java企业级开发中,Spring框架以其强大的功能和灵活性,成为众多开发者的首选。Spring DAO(Data Access Object)作为Spring框架中处理数据访问的重要模块,对JDBC进行了抽象封装,极大地简化了数据访问异常的处理,并能统一管理JDBC事务。本文将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring DAO,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
24 1
|
2月前
|
监控 Java 数据安全/隐私保护
如何用Spring Boot实现拦截器:从入门到实践
如何用Spring Boot实现拦截器:从入门到实践
54 5
|
3月前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。